2017-02-14 07:08:40 +00:00
|
|
|
const handlebars = require('handlebars');
|
2017-06-29 05:29:41 +00:00
|
|
|
const packageJsonHelper = require('./package-json');
|
|
|
|
const npm = require('./npm');
|
|
|
|
const yarn = require('./yarn');
|
|
|
|
const prWorker = require('../pr');
|
|
|
|
let logger = require('../../logger');
|
2017-02-14 07:08:40 +00:00
|
|
|
|
|
|
|
module.exports = {
|
|
|
|
getParentBranch,
|
|
|
|
ensureBranch,
|
2017-06-22 07:03:36 +00:00
|
|
|
updateBranch,
|
2017-06-25 05:36:13 +00:00
|
|
|
removeStandaloneBranches,
|
2017-02-14 07:08:40 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
async function getParentBranch(branchName, config) {
|
|
|
|
// Check if branch exists
|
2017-04-21 08:12:41 +00:00
|
|
|
if ((await config.api.branchExists(branchName)) === false) {
|
2017-06-27 11:44:03 +00:00
|
|
|
logger.info(`Branch needs creating`);
|
2017-02-14 07:08:40 +00:00
|
|
|
return undefined;
|
|
|
|
}
|
2017-06-27 11:44:03 +00:00
|
|
|
logger.info(`Branch already exists`);
|
2017-06-08 04:18:21 +00:00
|
|
|
// Check if needs rebasing
|
|
|
|
if (
|
2017-06-20 06:02:17 +00:00
|
|
|
config.rebaseStalePrs ||
|
|
|
|
(config.automergeEnabled && config.automergeType === 'branch-push')
|
2017-06-08 04:18:21 +00:00
|
|
|
) {
|
|
|
|
const isBranchStale = await config.api.isBranchStale(branchName);
|
|
|
|
if (isBranchStale) {
|
2017-06-27 11:44:03 +00:00
|
|
|
logger.info(`Branch is stale and needs rebasing`);
|
2017-06-08 04:18:21 +00:00
|
|
|
return undefined;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-02-14 07:08:40 +00:00
|
|
|
// Check for existing PR
|
|
|
|
const pr = await config.api.getBranchPr(branchName);
|
|
|
|
// Decide if we need to rebase
|
|
|
|
if (!pr) {
|
2017-06-27 11:44:03 +00:00
|
|
|
logger.debug(`No PR found`);
|
2017-02-14 07:08:40 +00:00
|
|
|
// We can't tell if this branch can be rebased so better not
|
|
|
|
return branchName;
|
|
|
|
}
|
|
|
|
if (pr.isUnmergeable) {
|
|
|
|
logger.debug('PR is unmergeable');
|
|
|
|
if (pr.canRebase) {
|
2017-06-28 11:20:31 +00:00
|
|
|
logger.info(`Branch is not mergeable and needs rebasing`);
|
|
|
|
if (config.isGitLab) {
|
2017-06-22 09:56:23 +00:00
|
|
|
logger.info(`Deleting unmergeable branch in order to recreate/rebase`);
|
|
|
|
await config.api.deleteBranch(branchName);
|
|
|
|
}
|
2017-06-28 11:20:31 +00:00
|
|
|
// Setting parentBranch back to undefined means that we'll use the default branch
|
|
|
|
return undefined;
|
2017-02-14 07:08:40 +00:00
|
|
|
}
|
|
|
|
// Don't do anything different, but warn
|
2017-06-27 11:44:03 +00:00
|
|
|
logger.warn(`Branch is not mergeable but can't be rebased`);
|
2017-02-14 07:08:40 +00:00
|
|
|
}
|
2017-06-27 11:44:03 +00:00
|
|
|
logger.debug(`Branch does not need rebasing`);
|
2017-02-14 07:08:40 +00:00
|
|
|
return branchName;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Ensure branch exists with appropriate content
|
2017-04-15 18:32:01 +00:00
|
|
|
async function ensureBranch(upgrades) {
|
2017-06-25 05:36:13 +00:00
|
|
|
logger.trace({ config: upgrades }, 'ensureBranch');
|
2017-04-15 18:32:01 +00:00
|
|
|
// Use the first upgrade for all the templates
|
|
|
|
const branchName = handlebars.compile(upgrades[0].branchName)(upgrades[0]);
|
2017-02-14 07:08:40 +00:00
|
|
|
// parentBranch is the branch we will base off
|
|
|
|
// If undefined, this will mean the defaultBranch
|
2017-04-21 08:12:41 +00:00
|
|
|
const parentBranch = await module.exports.getParentBranch(
|
|
|
|
branchName,
|
2017-04-21 08:25:49 +00:00
|
|
|
upgrades[0]
|
2017-04-21 08:12:41 +00:00
|
|
|
);
|
2017-06-29 17:50:26 +00:00
|
|
|
|
2017-04-21 08:12:41 +00:00
|
|
|
const commitMessage = handlebars.compile(upgrades[0].commitMessage)(
|
2017-04-21 08:25:49 +00:00
|
|
|
upgrades[0]
|
2017-04-21 08:12:41 +00:00
|
|
|
);
|
2017-04-15 18:32:01 +00:00
|
|
|
const api = upgrades[0].api;
|
2017-06-28 20:33:27 +00:00
|
|
|
const versions = upgrades[0].versions;
|
2017-06-03 13:25:13 +00:00
|
|
|
const cacheFolder = upgrades[0].yarnCacheFolder;
|
2017-04-15 18:32:01 +00:00
|
|
|
const packageFiles = {};
|
2017-04-17 02:54:42 +00:00
|
|
|
const commitFiles = [];
|
2017-04-15 18:32:01 +00:00
|
|
|
for (const upgrade of upgrades) {
|
2017-07-01 04:44:41 +00:00
|
|
|
if (upgrade.upgradeType === 'lockFileMaintenance') {
|
|
|
|
logger.debug('branch lockFileMaintenance');
|
2017-06-02 06:06:44 +00:00
|
|
|
try {
|
2017-06-29 05:29:41 +00:00
|
|
|
const newYarnLock = await yarn.maintainLockFile(upgrade);
|
2017-06-02 06:06:44 +00:00
|
|
|
if (newYarnLock) {
|
|
|
|
commitFiles.push(newYarnLock);
|
|
|
|
}
|
|
|
|
} catch (err) {
|
|
|
|
logger.debug(err);
|
|
|
|
throw new Error('Could not maintain yarn.lock file');
|
2017-04-17 02:54:42 +00:00
|
|
|
}
|
2017-04-15 18:32:01 +00:00
|
|
|
} else {
|
2017-04-17 02:54:42 +00:00
|
|
|
// See if this is the first time editing this file
|
|
|
|
if (!packageFiles[upgrade.packageFile]) {
|
|
|
|
// If we are rebasing then existing content will be from master
|
2017-04-21 08:12:41 +00:00
|
|
|
packageFiles[upgrade.packageFile] = await api.getFileContent(
|
|
|
|
upgrade.packageFile,
|
2017-04-21 08:25:49 +00:00
|
|
|
parentBranch
|
2017-04-21 08:12:41 +00:00
|
|
|
);
|
2017-04-17 02:54:42 +00:00
|
|
|
}
|
|
|
|
const newContent = packageJsonHelper.setNewValue(
|
|
|
|
packageFiles[upgrade.packageFile],
|
|
|
|
upgrade.depType,
|
|
|
|
upgrade.depName,
|
2017-06-22 07:03:36 +00:00
|
|
|
upgrade.newVersion,
|
|
|
|
logger
|
2017-04-21 08:12:41 +00:00
|
|
|
);
|
2017-04-17 02:54:42 +00:00
|
|
|
if (packageFiles[upgrade.packageFile] === newContent) {
|
|
|
|
logger.debug('packageFile content unchanged');
|
|
|
|
delete packageFiles[upgrade.packageFile];
|
|
|
|
} else {
|
|
|
|
logger.debug('Updating packageFile content');
|
|
|
|
packageFiles[upgrade.packageFile] = newContent;
|
|
|
|
}
|
2017-04-15 18:32:01 +00:00
|
|
|
}
|
2017-02-14 07:08:40 +00:00
|
|
|
}
|
2017-04-15 18:32:01 +00:00
|
|
|
if (Object.keys(packageFiles).length > 0) {
|
2017-06-20 06:02:17 +00:00
|
|
|
logger.info(
|
2017-05-10 07:26:09 +00:00
|
|
|
`${Object.keys(packageFiles).length} package file(s) need updating.`
|
|
|
|
);
|
2017-04-15 18:32:01 +00:00
|
|
|
for (const packageFile of Object.keys(packageFiles)) {
|
2017-04-17 02:54:42 +00:00
|
|
|
logger.debug(`Adding ${packageFile}`);
|
2017-04-15 18:32:01 +00:00
|
|
|
commitFiles.push({
|
|
|
|
name: packageFile,
|
|
|
|
contents: packageFiles[packageFile],
|
|
|
|
});
|
2017-06-02 06:06:44 +00:00
|
|
|
try {
|
2017-06-29 05:29:41 +00:00
|
|
|
const yarnLockFile = await yarn.getLockFile(
|
2017-06-02 06:06:44 +00:00
|
|
|
packageFile,
|
|
|
|
packageFiles[packageFile],
|
2017-06-03 13:25:13 +00:00
|
|
|
api,
|
2017-06-28 20:33:27 +00:00
|
|
|
cacheFolder,
|
|
|
|
versions.yarn
|
2017-06-02 06:06:44 +00:00
|
|
|
);
|
|
|
|
if (yarnLockFile) {
|
|
|
|
// Add new yarn.lock file too
|
2017-06-20 06:02:17 +00:00
|
|
|
logger.info(`Adding ${yarnLockFile.name}`);
|
2017-06-02 06:06:44 +00:00
|
|
|
commitFiles.push(yarnLockFile);
|
|
|
|
}
|
2017-06-29 05:29:41 +00:00
|
|
|
const packageLockFile = await npm.getLockFile(
|
2017-06-02 06:29:36 +00:00
|
|
|
packageFile,
|
|
|
|
packageFiles[packageFile],
|
2017-06-28 20:33:27 +00:00
|
|
|
api,
|
|
|
|
upgrades[0].versions.npm,
|
|
|
|
versions.npm
|
2017-06-02 06:29:36 +00:00
|
|
|
);
|
|
|
|
if (packageLockFile) {
|
|
|
|
// Add new package-lock.json file too
|
2017-06-20 06:02:17 +00:00
|
|
|
logger.info(`Adding ${packageLockFile.name}`);
|
2017-06-02 06:29:36 +00:00
|
|
|
commitFiles.push(packageLockFile);
|
|
|
|
}
|
|
|
|
} catch (err) {
|
2017-06-28 20:33:27 +00:00
|
|
|
logger.info('Could not generate necessary lock file');
|
|
|
|
throw err;
|
2017-06-02 06:29:36 +00:00
|
|
|
}
|
2017-04-15 18:32:01 +00:00
|
|
|
}
|
2017-04-17 02:54:42 +00:00
|
|
|
}
|
|
|
|
if (commitFiles.length) {
|
2017-06-27 11:44:03 +00:00
|
|
|
logger.debug(`${commitFiles.length} file(s) to commit`);
|
2017-04-15 18:32:01 +00:00
|
|
|
// API will know whether to create new branch or not
|
2017-04-21 08:12:41 +00:00
|
|
|
await api.commitFilesToBranch(
|
|
|
|
branchName,
|
|
|
|
commitFiles,
|
|
|
|
commitMessage,
|
2017-04-21 08:25:49 +00:00
|
|
|
parentBranch
|
2017-04-21 08:12:41 +00:00
|
|
|
);
|
2017-06-08 04:18:21 +00:00
|
|
|
} else {
|
2017-06-27 11:44:03 +00:00
|
|
|
logger.debug(`No files to commit`);
|
2017-06-08 04:18:21 +00:00
|
|
|
}
|
|
|
|
if (!api.branchExists(branchName)) {
|
|
|
|
// Return now if no branch exists
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
const config = upgrades[0];
|
|
|
|
if (config.automergeEnabled === false || config.automergeType === 'pr') {
|
|
|
|
// No branch automerge
|
2017-04-17 02:54:42 +00:00
|
|
|
return true;
|
2017-02-14 07:08:40 +00:00
|
|
|
}
|
2017-06-08 04:18:21 +00:00
|
|
|
logger.debug('Checking if we can automerge branch');
|
|
|
|
const branchStatus = await api.getBranchStatus(branchName);
|
|
|
|
if (branchStatus === 'success') {
|
2017-06-27 11:44:03 +00:00
|
|
|
logger.info(`Automerging branch`);
|
2017-06-08 04:18:21 +00:00
|
|
|
try {
|
|
|
|
await api.mergeBranch(branchName, config.automergeType);
|
|
|
|
} catch (err) {
|
2017-06-27 11:44:03 +00:00
|
|
|
logger.error(`Failed to automerge branch`);
|
2017-06-08 04:18:21 +00:00
|
|
|
logger.debug(JSON.stringify(err));
|
|
|
|
throw err;
|
|
|
|
}
|
|
|
|
} else {
|
2017-06-27 11:44:03 +00:00
|
|
|
logger.debug(`Branch status is "${branchStatus}" - skipping automerge`);
|
2017-06-08 04:18:21 +00:00
|
|
|
}
|
|
|
|
// Return true as branch exists
|
|
|
|
return true;
|
2017-02-14 07:08:40 +00:00
|
|
|
}
|
2017-06-22 07:03:36 +00:00
|
|
|
|
2017-06-25 05:36:13 +00:00
|
|
|
async function updateBranch(upgrades) {
|
|
|
|
await removeStandaloneBranches(upgrades);
|
2017-06-22 07:03:36 +00:00
|
|
|
const upgrade0 = upgrades[0];
|
2017-06-29 17:50:26 +00:00
|
|
|
// Delete the semanticPrefix for this branch if feature is not enabled
|
2017-06-30 04:04:15 +00:00
|
|
|
if (upgrade0.semanticCommits) {
|
2017-06-29 17:50:26 +00:00
|
|
|
logger.debug('Branch has semantic commits enabled');
|
|
|
|
} else {
|
|
|
|
logger.debug('Branch has semantic commits disabled');
|
|
|
|
delete upgrade0.semanticPrefix;
|
|
|
|
}
|
2017-06-22 07:03:36 +00:00
|
|
|
// Use templates to generate strings
|
|
|
|
const branchName = handlebars.compile(upgrade0.branchName)(upgrade0);
|
|
|
|
const prTitle = handlebars.compile(upgrade0.prTitle)(upgrade0);
|
|
|
|
|
2017-06-25 05:36:13 +00:00
|
|
|
logger = upgrade0.logger.child({
|
2017-06-22 07:03:36 +00:00
|
|
|
repository: upgrade0.repository,
|
|
|
|
branch: branchName,
|
|
|
|
});
|
|
|
|
|
|
|
|
logger.info(
|
2017-06-27 11:44:03 +00:00
|
|
|
`Branch has ${upgrades.length} upgrade(s): ${upgrades.map(
|
2017-06-22 07:03:36 +00:00
|
|
|
upgrade => upgrade.depName
|
|
|
|
)}`
|
|
|
|
);
|
|
|
|
|
|
|
|
try {
|
|
|
|
if (
|
2017-07-01 04:44:41 +00:00
|
|
|
upgrade0.upgradeType !== 'lockFileMaintenance' &&
|
2017-06-22 07:03:36 +00:00
|
|
|
upgrade0.groupName === null &&
|
|
|
|
!upgrade0.recreateClosed &&
|
|
|
|
(await upgrade0.api.checkForClosedPr(branchName, prTitle))
|
|
|
|
) {
|
2017-06-27 11:44:03 +00:00
|
|
|
logger.info(`Skipping branch as matching closed PR already existed`);
|
2017-06-22 07:03:36 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
const branchCreated = await module.exports.ensureBranch(upgrades);
|
|
|
|
if (branchCreated) {
|
|
|
|
const pr = await prWorker.ensurePr(upgrades, logger);
|
|
|
|
if (pr) {
|
|
|
|
await prWorker.checkAutoMerge(pr, upgrade0, logger);
|
|
|
|
}
|
|
|
|
}
|
2017-06-28 20:33:27 +00:00
|
|
|
} catch (err) {
|
|
|
|
logger.error(`Error updating branch: ${err.message}`);
|
|
|
|
logger.debug(JSON.stringify(err));
|
2017-06-22 07:03:36 +00:00
|
|
|
// Don't throw here - we don't want to stop the other renovations
|
|
|
|
}
|
|
|
|
}
|
2017-06-25 05:36:13 +00:00
|
|
|
|
|
|
|
async function removeStandaloneBranches(upgrades) {
|
2017-07-01 04:44:41 +00:00
|
|
|
if (upgrades.length <= 1) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (upgrades[0].upgradeType === 'lockFileMaintenance') {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
for (const upgrade of upgrades) {
|
|
|
|
const standaloneBranchName = handlebars.compile(upgrade.branchName)(
|
|
|
|
upgrade
|
|
|
|
);
|
|
|
|
upgrade.logger.debug(`Need to delete branch ${standaloneBranchName}`);
|
|
|
|
try {
|
|
|
|
await upgrade.api.deleteBranch(standaloneBranchName);
|
|
|
|
} catch (err) {
|
|
|
|
upgrade.logger.debug(`Couldn't delete branch ${standaloneBranchName}`);
|
2017-06-25 05:36:13 +00:00
|
|
|
}
|
2017-07-01 04:44:41 +00:00
|
|
|
// Rename to group branchName
|
|
|
|
upgrade.branchName = upgrade.groupBranchName;
|
2017-06-25 05:36:13 +00:00
|
|
|
}
|
|
|
|
}
|