Also closes #3

commit 725de26fe331470f9fe3d17bad4c365ff8772443
Author: Rhys Arkins <rhys@keylocation.sg>
Date:   Tue Jan 10 13:32:07 2017 +0100

    Improve console messages

commit 28ec77634b86ddb7ed8410053d671bad59133058
Author: Rhys Arkins <rhys@keylocation.sg>
Date:   Tue Jan 10 12:25:26 2017 +0100

    Refactor github helper

commit 5cd2cf8eb7049868b0d1bc0d225a6cc490ec3317
Author: Rhys Arkins <rhys@keylocation.sg>
Date:   Tue Jan 10 11:20:27 2017 +0100

    Support multiple repos

commit 52e531c13078e448cecb651b94ce76e24df3d60d
Author: Rhys Arkins <rhys@keylocation.sg>
Date:   Tue Jan 10 11:19:24 2017 +0100

    Hardcode base branch
This commit is contained in:
Rhys Arkins 2017-01-10 13:32:32 +01:00
Родитель 67d774dcd3
Коммит d021a36905
5 изменённых файлов: 198 добавлений и 123 удалений

Просмотреть файл

@ -1,9 +1,12 @@
{
"private": true,
"dependencies": {
"better-console": "1.0.0",
"colors": "1.1.2",
"gh-got": "5.0.0",
"got": "6.6.3",
"lodash": "4.17.4",
"manakin": "0.4.6",
"mkdirp": "0.5.1",
"nodegit": "0.16.0",
"rimraf": "2.5.4",

Просмотреть файл

@ -1,6 +1,5 @@
module.exports = {
verbose: false,
baseBranch: 'master',
templates: {
branchName: (params) => {
return `renovate/${params.depName}-${params.newVersionMajor}.x`;

Просмотреть файл

@ -1,102 +1,150 @@
const ghGot = require('gh-got');
const console = require('better-console');
var config = {};
module.exports = {
// Initialize GitHub by getting base branch SHA
init: function(setConfig, repoName, packageFile) {
config = setConfig;
config.repoName = repoName;
config.packageFile = packageFile;
config.userName = repoName.split('/')[0];
init: init,
// Package File
getPackageFile: getPackageFile,
getPackageFileContents: getPackageFileContents,
writePackageFile: writePackageFile,
// Branch
createBranch: createBranch,
// PR
checkForClosedPr: checkForClosedPr,
createPr: createPr,
getPr: getPr,
updatePr: updatePr,
};
return ghGot(`repos/${config.repoName}/git/refs/head`, {token: config.token}).then(res => {
// First, get the SHA for base branch
// Initialize GitHub by getting base branch and SHA
function init(setConfig, repoName, packageFile) {
config = setConfig;
config.repoName = repoName;
config.packageFile = packageFile;
return ghGot(`repos/${config.repoName}`, { token: config.token })
.then(res => {
config.owner = res.body.owner.login;
config.defaultBranch = res.body.default_branch;
})
.then(() => {
return ghGot(`repos/${config.repoName}/git/refs/head`, { token: config.token })
.then(res => {
// Get the SHA for base branch
res.body.forEach(function(branch) {
// Loop through all branches because the base branch may not be the first
if (branch.ref === `refs/heads/${config.baseBranch}`) {
if (branch.ref === `refs/heads/${config.defaultBranch}`) {
// This is the SHA we will create new branches from
config.baseSHA = branch.object.sha;
}
});
}).catch(function(err) {
console.log('init error: ' + err);
});
},
createBranch: function(branchName) {
return ghGot.post(`repos/${config.repoName}/git/refs`, {
token: config.token,
body: {
ref: `refs/heads/${branchName}`,
sha: config.baseSHA,
},
}).catch(function(err) {
console.error('GitHub init error: ' + err);
throw err;
});
}
// Package File
function getPackageFile(branchName) {
return getFile(config.packageFile, branchName);
}
function getPackageFileContents() {
return getFileContents(config.packageFile);
}
function writePackageFile(branchName, oldFileSHA, 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,
body: {
ref: `refs/heads/${branchName}`,
sha: config.baseSHA,
},
});
}
// Pull Request
function checkForClosedPr(branchName, prTitle) {
return ghGot(`repos/${config.repoName}/pulls?state=closed&head=${config.owner}:${branchName}`, {
token: config.token
}).then(res => {
return res.body.some((pr) => {
return pr.title === prTitle && pr.head.label === `${config.owner}:${branchName}`;
});
},
createPr: function(branchName, title, body) {
return ghGot.post(`repos/${config.repoName}/pulls`, {
token: config.token,
body: {
title: title,
head: branchName,
base: config.baseBranch,
body: body,
}
}).then(res => {
return res.body;
});
},
checkForClosedPr(branchName, prTitle) {
return ghGot(`repos/${config.repoName}/pulls?state=closed&head=${config.userName}:${branchName}`, {
token: config.token
}).then(res => {
return res.body.some((pr) => {
return pr.title === prTitle && pr.head.label === `${config.userName}:${branchName}`;
});
}).catch((err) => {
console.error('Error checking if PR already existed');
});
},
getFile: getFile,
getFileContents: function(filePath, branchName) {
return getFile(filePath, branchName).then(res => {
return JSON.parse(new Buffer(res.body.content, 'base64').toString());
});
},
getPr: function(branchName) {
return ghGot(`repos/${config.repoName}/pulls?state=open&base=${config.baseBranch}&head=${config.userName}:${branchName}`, {
token: config.token,
}).then(res => {
if (res.body.length) {
return res.body[0];
}
return null;
});
},
writeFile: function(branchName, oldFileSHA, filePath, fileContents, message) {
return ghGot.put(`repos/${config.repoName}/contents/${filePath}`, {
token: config.token,
body: {
branch: branchName,
sha: oldFileSHA,
message: message,
content: new Buffer(fileContents).toString('base64')
}
});
},
updatePr: function(prNo, title, body) {
return ghGot.patch(`repos/${config.repoName}/pulls/${prNo}`, {
token: config.token,
body: {
title: title,
body: body,
},
});
},
};
}).catch((err) => {
console.error('Error checking if PR already existed');
});
}
function createPr(branchName, title, body) {
return ghGot.post(`repos/${config.repoName}/pulls`, {
token: config.token,
body: {
title: title,
head: branchName,
base: config.defaultBranch,
body: body,
}
}).then(res => {
return res.body;
});
}
function getPr(branchName) {
return ghGot(`repos/${config.repoName}/pulls?state=open&base=${config.defaultBranch}&head=${config.owner}:${branchName}`, {
token: config.token,
}).then(res => {
if (res.body.length) {
return res.body[0];
}
return null;
});
}
function updatePr(prNo, title, body) {
return ghGot.patch(`repos/${config.repoName}/pulls/${prNo}`, {
token: config.token,
body: {
title: title,
body: body,
},
});
}
// Generic File operations
function getFile(filePath, branchName) {
branchName = branchName || config.baseBranch;
branchName = branchName || config.defaultBranch;
return ghGot(`repos/${config.repoName}/contents/${filePath}?ref=${branchName}`, {
token: config.token,
});
}
function getFileContents(filePath, branchName) {
return getFile(filePath, branchName).then(res => {
return JSON.parse(new Buffer(res.body.content, 'base64').toString());
});
}
function writeFile(branchName, oldFileSHA, filePath, fileContents, message) {
return ghGot.put(`repos/${config.repoName}/contents/${filePath}`, {
token: config.token,
body: {
branch: branchName,
sha: oldFileSHA,
message: message,
content: new Buffer(fileContents).toString('base64')
}
});
}

Просмотреть файл

@ -76,7 +76,7 @@ function getDependencyUpgrades(depName, currentVersion) {
return getDependency(depName)
.then(res => {
if (!res.body['versions']) {
console.log(depName + ' versions is null');
console.error(depName + ' versions is null');
}
const allUpgrades = {};
if (isRange(currentVersion)) {

Просмотреть файл

@ -1,3 +1,6 @@
// Set colours for console globally
require('manakin').global;
const semver = require('semver');
const stable = require('semver-stable');
@ -9,18 +12,29 @@ const config = initConfig();
validateArguments();
npm.init(config);
const repoName = Object.keys(config.repositories)[0];
const packageFile = config.repositories[repoName] || 'package.json';
initGitHub()
.then(getPackageFileContents)
.then(determineUpgrades)
.then(processUpgradesSequentially)
config.repositories.reduce((repoPromise, repo) => {
return repoPromise.then(() => {
const repoName = repo.name;
return repo.packageFiles.reduce((packageFilePromise, packageFile) => {
return packageFilePromise.then(() => {
return initGitHub(repoName, packageFile)
.then(getPackageFileContents)
.then(determineUpgrades)
.then(processUpgradesSequentially)
.then(() => {
console.success(`Repo ${repoName} ${packageFile} done`);
})
.catch(err => {
console.error('renovate caught error: ' + err);
});
});
}, repoPromise);
});
}, Promise.resolve())
.then(() => {
console.log('Done');
})
.catch(err => {
console.log('renovate caught error: ' + err);
if (config.repositories.length < 1) {
console.success('All repos done');
}
});
function initConfig() {
@ -39,17 +53,28 @@ function initConfig() {
const repoName = process.argv[2];
const packageFile = process.argv[3] || 'package.json';
if (repoName) {
cliConfig.repositories = {};
cliConfig.repositories[repoName] = packageFile;
cliConfig.repositories = [
{
name: repoName,
packageFiles: [packageFile],
},
];
}
const combinedConfig = Object.assign(defaultConfig, customConfig, cliConfig);
if (Array.isArray(combinedConfig.repositories)) {
const newRepositories = {};
combinedConfig.repositories.forEach(repo => {
newRepositories[repo] = 'package.json';
});
combinedConfig.repositories = newRepositories;
}
// First, convert any strings to objects
combinedConfig.repositories.forEach(function(repo, index) {
if (typeof repo === 'string') {
combinedConfig.repositories[index] = {
name: repo,
};
}
});
// Add 'package.json' if missing
combinedConfig.repositories.forEach(function(repo, index) {
if (!repo.packageFiles || !repo.packageFiles.length) {
repo.packageFiles = ['package.json'];
}
});
if (combinedConfig.verbose) {
console.log('config = ' + JSON.stringify(combinedConfig));
}
@ -63,33 +88,31 @@ function validateArguments() {
process.exit(1);
}
// We also need a repository
if (typeof Object.keys(config.repositories).length === 0) {
console.error('Error: A repository must be configured');
if (!config.repositories || config.repositories.length === 0) {
console.error('Error: At least one repository must be configured');
}
}
function initGitHub() {
if (config.verbose) {
console.log('Initializing GitHub');
}
function initGitHub(repoName, packageFile) {
console.info('Initializing GitHub repo ' + repoName + ', ' + packageFile);
return github.init(config, repoName, packageFile);
}
function getPackageFileContents() {
console.log('Getting package file contents');
return github.getFileContents(config.packageFile);
console.info('Getting package file contents');
return github.getPackageFileContents();
}
function determineUpgrades(packageFileContents) {
console.log('Determining required upgrades');
console.info('Determining required upgrades');
return npm.getAllDependencyUpgrades(packageFileContents);
}
function processUpgradesSequentially(upgrades) {
if (Object.keys(upgrades).length) {
console.log('Processing upgrades');
console.info('Processing upgrades');
} else {
console.log('No upgrades to process');
console.info('No upgrades to process');
}
if (config.verbose) {
console.log('All upgrades: ' + JSON.stringify(upgrades));
@ -123,7 +146,9 @@ function updateDependency({ upgradeType, depType, depName, currentVersion, newVe
// This allows users to close an unwanted upgrade PR and not worry about seeing it raised again
return github.checkForClosedPr(branchName, prTitle).then((prExisted) => {
if (prExisted) {
console.log(`${depName}: Skipping due to existing PR found.`);
if (config.verbose) {
console.log(`${depName}: Skipping due to existing PR found.`);
}
return;
}
@ -131,7 +156,7 @@ function updateDependency({ upgradeType, depType, depName, currentVersion, newVe
.then(ensureCommit)
.then(ensurePr)
.catch(error => {
console.log('Error updating dependency depName: ' + error);
console.error(`Error updating dependency ${depName}: ${error}`);
// Don't throw here - we don't want to stop the other renovations
});
});
@ -142,8 +167,8 @@ function updateDependency({ upgradeType, depType, depName, currentVersion, newVe
// 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
console.log('Error creating branch: ' + branchName);
console.log('Response body: ' + error.response.body);
console.error('Error creating branch: ' + branchName);
console.error('Response body: ' + error.response.body);
throw error;
}
// Otherwise we swallow this error and continue
@ -151,7 +176,7 @@ function updateDependency({ upgradeType, depType, depName, currentVersion, newVe
}
function ensureCommit() {
// Retrieve the package.json from this renovate branch
return github.getFile(config.packageFile, branchName).then(res => {
return github.getPackageFile(branchName).then(res => {
const currentSHA = res.body.sha;
const currentFileContent = new Buffer(res.body.content, 'base64').toString();
const currentJson = JSON.parse(currentFileContent);
@ -161,7 +186,7 @@ function updateDependency({ upgradeType, depType, depName, currentVersion, newVe
console.log(`${depName}: Updating to ${newVersion} in branch ${branchName}`);
}
const newPackageContents = packageJson.setNewValue(currentFileContent, depType, depName, newVersion);
return github.writeFile(branchName, currentSHA, packageFile, newPackageContents, commitMessage);
return github.writePackageFile(branchName, currentSHA, newPackageContents, commitMessage);
} else {
if (config.verbose) {
console.log(`${depName}: branch ${branchName} is already up-to-date`);
@ -178,12 +203,12 @@ function updateDependency({ upgradeType, depType, depName, currentVersion, newVe
console.log(`${depName}: PR #${pr.number} already up-to-date`);
}
} else {
console.log(`${depName}: Updating PR #${pr.number}`);
console.info(`${depName}: Updating PR #${pr.number}`);
return github.updatePr(pr.number, prTitle, prBody);
}
} else {
return github.createPr(branchName, prTitle, prBody).then((pr) => {
console.log(`${depName}: Created PR #${pr.number}`);
console.info(`${depName}: Created PR #${pr.number}`);
});
}
});