diff --git a/src/defaults.js b/src/defaults.js index f435656772f0e2db2cba2a1bc675060359735b35..e63c1d8e5428a8a40892525f29697a3d9a439162 100644 --- a/src/defaults.js +++ b/src/defaults.js @@ -3,8 +3,7 @@ module.exports = { templates: { branchName: params => `renovate/${params.depName}-${params.newVersionMajor}.x`, commitMessage: params => `Update dependency ${params.depName} to version ${params.newVersion}`, - prBody: params => - `This Pull Request updates dependency ${params.depName}` + + prBody: params => `This Pull Request updates dependency ${params.depName}` + ` from version ${params.currentVersion} to ${params.newVersion}.`, prTitleMajor: params => `Update dependency ${params.depName} to version ${params.newVersionMajor}.x`, prTitleMinor: params => `Update dependency ${params.depName} to version ${params.newVersion}`, diff --git a/src/helpers/github.js b/src/helpers/github.js index 6a32bd95881d2158a336bd271ed4d9bd09735d08..fda760be6c525fb9fd89090c1d0675353bfeac6e 100644 --- a/src/helpers/github.js +++ b/src/helpers/github.js @@ -24,8 +24,9 @@ function init(setConfig, repoName, packageFile) { config.packageFile = packageFile; function getRepo() { - return ghGot(`repos/${config.repoName}`, { token: config.token }) - .then(res => res.body); + return ghGot(`repos/${config.repoName}`, { token: config.token }).then( + res => res.body, + ); } function processRepo(repo) { @@ -34,8 +35,9 @@ function init(setConfig, repoName, packageFile) { } function getRepoSHA() { - return ghGot(`repos/${config.repoName}/git/refs/head`, { token: config.token }) - .then((res) => { + return ghGot(`repos/${config.repoName}/git/refs/head`, { + token: config.token, + }).then((res) => { // Get the SHA for base branch res.body.forEach((branch) => { // Loop through all branches because the base branch may not be the first @@ -58,7 +60,6 @@ function init(setConfig, repoName, packageFile) { } // Package File - function getPackageFile(branchName) { return getFile(config.packageFile, branchName); } @@ -68,11 +69,16 @@ function getPackageFileContents() { } function writePackageFile(branchName, oldFileSHA, fileContents, message) { - return writeFile(branchName, oldFileSHA, config.packageFile, fileContents, message); + return writeFile( + branchName, + oldFileSHA, + config.packageFile, + fileContents, + message, + ); } // Branch - function createBranch(branchName) { return ghGot.post(`repos/${config.repoName}/git/refs`, { token: config.token, @@ -84,36 +90,35 @@ function createBranch(branchName) { } // Pull Request - function checkForClosedPr(branchName, prTitle) { - return ghGot(`repos/${config.repoName}/pulls?state=closed&head=${config.owner}:${branchName}`, { - token: config.token, - }).then(res => - res.body.some(pr => pr.title === prTitle && pr.head.label === `${config.owner}:${branchName}`)) - .catch((error) => { - console.error(`Error checking if PR already existed: ${error}`); - }); + return ghGot( + `repos/${config.repoName}/pulls?state=closed&head=${config.owner}:${branchName}`, + { token: config.token }, + ) + .then( + res => res.body.some( + pr => pr.title === prTitle && + pr.head.label === `${config.owner}:${branchName}`, + ), + ) + .catch((error) => { + console.error(`Error checking if PR already existed: ${error}`); + }); } function createPr(branchName, title, body) { - return ghGot.post(`repos/${config.repoName}/pulls`, { - token: config.token, - body: { - title, - head: branchName, - base: config.defaultBranch, - body, - }, - }).then(res => res.body); + return ghGot + .post(`repos/${config.repoName}/pulls`, { + token: config.token, + body: { title, head: branchName, base: config.defaultBranch, body }, + }) + .then(res => res.body); } function getPr(branchName) { - const gotString = - `repos/${config.repoName}/pulls?` + + const gotString = `repos/${config.repoName}/pulls?` + `state=open&base=${config.defaultBranch}&head=${config.owner}:${branchName}`; - return ghGot(gotString, { - token: config.token, - }).then((res) => { + return ghGot(gotString, { token: config.token }).then((res) => { if (res.body.length) { return res.body[0]; } @@ -124,23 +129,22 @@ function getPr(branchName) { function updatePr(prNo, title, body) { return ghGot.patch(`repos/${config.repoName}/pulls/${prNo}`, { token: config.token, - body: { - title, - body, - }, + body: { title, body }, }); } // Generic File operations - function getFile(filePath, branchName = config.defaultBranch) { - return ghGot(`repos/${config.repoName}/contents/${filePath}?ref=${branchName}`, { - token: config.token, - }); + return ghGot( + `repos/${config.repoName}/contents/${filePath}?ref=${branchName}`, + { token: config.token }, + ); } function getFileContents(filePath, branchName) { - return getFile(filePath, branchName).then(res => JSON.parse(new Buffer(res.body.content, 'base64').toString())); + return getFile(filePath, branchName).then( + res => JSON.parse(new Buffer(res.body.content, 'base64').toString()), + ); } function writeFile(branchName, oldFileSHA, filePath, fileContents, message) { diff --git a/src/helpers/npm.js b/src/helpers/npm.js index 9bbd85b5bed0ffec5c03839371e8c985de6a90e8..eb4762b50e21573ae8b26c01649dfa2dd062eed3 100644 --- a/src/helpers/npm.js +++ b/src/helpers/npm.js @@ -34,30 +34,33 @@ module.exports = { const currentVersion = packageContents[depType][depName]; if (!isValidVersion(currentVersion)) { if (config.verbose) { - console.log(`${depName}: Skipping invalid version ${currentVersion}`); + console.log( + `${depName}: Skipping invalid version ${currentVersion}`, + ); } return; } - allDependencyChecks.push(getDependencyUpgrades(depName, currentVersion) - .then((res) => { - if (res.length > 0) { - if (config.verbose) { - console.log(`${depName}: Upgrades = ${JSON.stringify(res)}`); - } - res.forEach((upgrade) => { - allDependencyUpgrades.push({ - depType, - depName, - currentVersion, - upgradeType: upgrade.type, - newVersion: upgrade.version, + allDependencyChecks.push( + getDependencyUpgrades(depName, currentVersion).then((res) => { + if (res.length > 0) { + if (config.verbose) { + console.log(`${depName}: Upgrades = ${JSON.stringify(res)}`); + } + res.forEach((upgrade) => { + allDependencyUpgrades.push({ + depType, + depName, + currentVersion, + upgradeType: upgrade.type, + newVersion: upgrade.version, + }); }); - }); - } else if (config.verbose) { - console.log(`${depName}: No upgrades required`); - } - return Promise.resolve(); - })); + } else if (config.verbose) { + console.log(`${depName}: No upgrades required`); + } + return Promise.resolve(); + }), + ); }); }); return Promise.all(allDependencyChecks).then(() => allDependencyUpgrades); @@ -66,12 +69,13 @@ module.exports = { function getDependency(depName) { // supports scoped packages, e.g. @user/package - return got(`https://registry.npmjs.org/${depName.replace('/', '%2F')}`, { json: true }); + return got(`https://registry.npmjs.org/${depName.replace('/', '%2F')}`, { + json: true, + }); } function getDependencyUpgrades(depName, currentVersion) { - return getDependency(depName) - .then((res) => { + return getDependency(depName).then((res) => { if (!res.body.versions) { console.error(`${depName} versions is null`); } @@ -79,11 +83,11 @@ function getDependencyUpgrades(depName, currentVersion) { let workingVersion = currentVersion; if (isRange(currentVersion)) { // Pin ranges to their maximum satisfying version - const maxSatisfying = semver.maxSatisfying(Object.keys(res.body.versions), currentVersion); - allUpgrades.pin = { - type: 'pin', - version: maxSatisfying, - }; + const maxSatisfying = semver.maxSatisfying( + Object.keys(res.body.versions), + currentVersion, + ); + allUpgrades.pin = { type: 'pin', version: maxSatisfying }; workingVersion = maxSatisfying; } const currentMajor = semver.major(workingVersion); @@ -95,9 +99,12 @@ function getDependencyUpgrades(depName, currentVersion) { if (semver.gt(version, workingVersion)) { // Group by major versions const thisMajor = semver.major(version); - if (!allUpgrades[thisMajor] || semver.gt(version, allUpgrades[thisMajor].version)) { + if ( + !allUpgrades[thisMajor] || + semver.gt(version, allUpgrades[thisMajor].version) + ) { allUpgrades[thisMajor] = { - type: (thisMajor > currentMajor) ? 'major' : 'minor', + type: thisMajor > currentMajor ? 'major' : 'minor', version, }; } @@ -114,7 +121,7 @@ function getDependencyUpgrades(depName, currentVersion) { function isRange(input) { // Pinned versions also return true for semver.validRange - // We need to check first that they're not "valid" to get only ranges + // We need to check first that they're not 'valid' to get only ranges return !semver.valid(input) && semver.validRange(input); } diff --git a/src/helpers/packageJson.js b/src/helpers/packageJson.js index 97deb6c81159f2982c5d1b4957e858846c27cf1b..4c3b44faf1fa4a31b60d816bb029a9a24bab9412 100644 --- a/src/helpers/packageJson.js +++ b/src/helpers/packageJson.js @@ -12,14 +12,15 @@ module.exports = { const newString = `"${newVersion}"`; let newFileContent = null; // Skip ahead to depType section - let searchIndex = currentFileContent.indexOf(`"${depType}"`) + depType.length; + let searchIndex = currentFileContent.indexOf(`"${depType}"`) + + depType.length; // Iterate through the rest of the file for (; searchIndex < currentFileContent.length; searchIndex += 1) { // First check if we have a hit for the old version if (currentFileContent.substring(searchIndex, searchIndex + searchString.length) === searchString) { // Now test if the result matches - const testContent = - currentFileContent.substr(0, searchIndex) + newString + + const testContent = currentFileContent.substr(0, searchIndex) + + newString + currentFileContent.substr(searchIndex + searchString.length); // Compare the parsed JSON structure of old and new if (_.isEqual(parsedContents, JSON.parse(testContent))) { diff --git a/src/index.js b/src/index.js index c69bce6183b099f60083c2d367e27c128328a6e6..600ee517d59ba401ece334c2491c9956ee234c3e 100644 --- a/src/index.js +++ b/src/index.js @@ -1,5 +1,6 @@ // Set colours for console globally -require('manakin').global; // eslint-disable-line no-unused-expressions +// eslint-disable-next-line no-unused-expressions +require('manakin').global; const semver = require('semver'); @@ -22,15 +23,16 @@ config.repositories.forEach((repo) => { }); }); // Print something nice once the chain is done -p.then(() => { - if (config.repositories.length > 1) { // eslint-disable-line promise/always-return - console.success('All repos done'); - } -}) -// This shouldn't happen as we want to catch errors within individual chains -.catch((error) => { - console.log(`Unexpected error: ${error}`); -}); +p + .then(() => { + // eslint-disable-next-line promise/always-return + if (config.repositories.length > 1) { + console.success('All repos done'); + } + }) + .catch((error) => { + console.log(`Unexpected error: ${error}`); + }); // This function reads in all configs and merges them function initConfig() { @@ -62,9 +64,7 @@ function initConfig() { // First, convert any strings to objects combinedConfig.repositories.forEach((repo, index) => { if (typeof repo === 'string') { - combinedConfig.repositories[index] = { - name: repo, - }; + combinedConfig.repositories[index] = { name: repo }; } }); // Add 'package.json' if missing @@ -95,15 +95,16 @@ function validateArguments() { // This function manages the queue per-package file function processRepoPackageFile(repoName, packageFile) { return initGitHub(repoName, packageFile) - .then(getPackageFileContents) - .then(determineUpgrades) - .then(processUpgradesSequentially) - .then(() => { // eslint-disable-line promise/always-return - console.success(`Repo ${repoName} ${packageFile} done`); - }) - .catch((err) => { - console.error(`renovate caught error: ${err}`); - }); + .then(getPackageFileContents) + .then(determineUpgrades) + .then(processUpgradesSequentially) + // eslint-disable-next-line promise/always-return + .then(() => { + console.success(`Repo ${repoName} ${packageFile} done`); + }) + .catch((error) => { + console.error(`renovate caught error: ${error}`); + }); } function initGitHub(repoName, packageFile) { @@ -133,24 +134,56 @@ function processUpgradesSequentially(upgrades) { // We are processing each upgrade sequentially for two major reasons: // 1. Reduce chances of GitHub API rate limiting // 2. Edge case collision of branch name, e.g. dependency also listed as dev dependency - return upgrades.reduce((promise, upgrade) => - promise.then(() => updateDependency(upgrade)), Promise.resolve()); + return upgrades.reduce( + (promise, upgrade) => promise.then(() => updateDependency(upgrade)), Promise.resolve()); } function updateDependency({ upgradeType, depType, depName, currentVersion, newVersion }) { const newVersionMajor = semver.major(newVersion); - const branchName = config.templates.branchName({ depType, depName, currentVersion, newVersion, newVersionMajor }); + const branchName = config.templates.branchName({ + depType, + depName, + currentVersion, + newVersion, + newVersionMajor, + }); let prTitle = ''; if (upgradeType === 'pin') { - prTitle = config.templates.prTitlePin({ depType, depName, currentVersion, newVersion, newVersionMajor }); + prTitle = config.templates.prTitlePin({ + depType, + depName, + currentVersion, + newVersion, + newVersionMajor, + }); } else if (upgradeType === 'minor') { // Use same title for range or minor - prTitle = config.templates.prTitleMinor({ depType, depName, currentVersion, newVersion, newVersionMajor }); + prTitle = config.templates.prTitleMinor({ + depType, + depName, + currentVersion, + newVersion, + newVersionMajor, + }); } else { - prTitle = config.templates.prTitleMajor({ depType, depName, currentVersion, newVersion, newVersionMajor }); + prTitle = config.templates.prTitleMajor({ + depType, + depName, + currentVersion, + newVersion, + newVersionMajor, + }); } - const prBody = config.templates.prBody({ depName, currentVersion, newVersion }); - const commitMessage = config.templates.commitMessage({ depName, currentVersion, newVersion }); + const prBody = config.templates.prBody({ + depName, + currentVersion, + newVersion, + }); + const commitMessage = config.templates.commitMessage({ + depName, + currentVersion, + newVersion, + }); // Check if same PR already existed and skip if so // This allows users to close an unwanted upgrade PR and not worry about seeing it raised again @@ -174,8 +207,7 @@ function updateDependency({ upgradeType, depType, depName, currentVersion, newVe } function ensureBranch() { // Save an API call by attempting to create branch without checking for existence first - return github.createBranch(branchName) - .catch((error) => { + return github.createBranch(branchName).catch((error) => { // Check in case it's because the branch already existed if (error.response.body.message !== 'Reference already exists') { // In this case it means we really do have a problem and can't continue @@ -190,7 +222,10 @@ function updateDependency({ upgradeType, depType, depName, currentVersion, newVe // Retrieve the package.json from this renovate branch return github.getPackageFile(branchName).then((res) => { const currentSHA = res.body.sha; - const currentFileContent = new Buffer(res.body.content, 'base64').toString(); + const currentFileContent = new Buffer( + res.body.content, + 'base64', + ).toString(); const currentJson = JSON.parse(currentFileContent); if (currentJson[depType][depName] === newVersion) { if (config.verbose) { @@ -200,11 +235,22 @@ function updateDependency({ upgradeType, depType, depName, currentVersion, newVe } // Branch must need updating if (config.verbose) { - console.log(`${depName}: Updating to ${newVersion} in branch ${branchName}`); + console.log( + `${depName}: Updating to ${newVersion} in branch ${branchName}`, + ); } - const newPackageContents = - packageJson.setNewValue(currentFileContent, depType, depName, newVersion); - return github.writePackageFile(branchName, currentSHA, newPackageContents, commitMessage); + const newPackageContents = packageJson.setNewValue( + currentFileContent, + depType, + depName, + newVersion, + ); + return github.writePackageFile( + branchName, + currentSHA, + newPackageContents, + commitMessage, + ); }); } @@ -233,7 +279,9 @@ function updateDependency({ upgradeType, depType, depName, currentVersion, newVe // Check if existing PR needs updating if (existingPr.title === prTitle || existingPr.body === prBody) { if (config.verbose) { - console.log(`${depName}: PR #${existingPr.number} already up-to-date`); + console.log( + `${depName}: PR #${existingPr.number} already up-to-date`, + ); } return Promise.resolve(); }