Skip to content
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
85 changes: 72 additions & 13 deletions tools/rebase-pr.js
Original file line number Diff line number Diff line change
Expand Up @@ -59,32 +59,80 @@ _main(...process.argv.slice(2)).catch(err => {

// Helpers
async function _main(repository, prNumber) {
console.log(`Determining target branch for PR ${prNumber} on ${repository}.`);
const targetBranch = await determineTargetBranch(repository, prNumber);
console.log(`Target branch is ${targetBranch}.`);
await exec(`git fetch origin ${targetBranch}`);
console.log(`Rebasing current branch on ${targetBranch}.`);
await exec(`git rebase origin/${targetBranch}`);
console.log(`Getting refs and SHAs for PR ${prNumber} on ${repository}.`);
const target = await determineTargetRefAndSha(repository, prNumber);
console.log(`Fetching target branch: ${target.baseRef}.`);
await exec(`git fetch origin ${target.baseRef}`);

// The sha of the latest commit on the target branch.
const {stdout: shaOfTargetBranchLatest} = await exec(`git rev-parse origin/${target.baseRef}`);
// The sha of the latest commit on the PR.
const {stdout: shaOfPRLatest} = await exec(`git rev-parse HEAD`);
// The first common SHA in the history of the target branch and the latest commit in the PR.
const {stdout: commonAncestorSha} =
await exec(`git merge-base origin/${target.baseRef} ${shaOfPRLatest}`);

// Log known refs and shas
console.log(`--------------------------------`);
console.log(` Target Branch: ${target.baseRef}`);
console.log(` Latest Commit for Target Branch: ${shaOfTargetBranchLatest.trim()}`);
console.log(` Latest Commit for PR: ${shaOfPRLatest.trim()}`);
console.log(` First Common Ancestor SHA: ${commonAncestorSha.trim()}`);
console.log(`--------------------------------`);
Comment thread
gkalpak marked this conversation as resolved.
Outdated
console.log();


// Get the count of commits between the latest commit from origin and the common ancestor SHA.
const {stdout: commitCount} =
await exec(`git rev-list --count origin/${target.baseRef}...${commonAncestorSha.trim()}`);
console.log(`Checking ${commitCount.trim()} commits for changes in the CircleCI config file.`);

// Check if the files changed between the latest commit from origin and the common ancestor SHA
// includes the CircleCI config.
const {stdout: circleCIConfigChanged} = await exec(
`git diff --name-only origin/${target.baseRef} ${commonAncestorSha.trim()} -- .circleci/config.yml`);

if (!!circleCIConfigChanged) {
throw Error(`
CircleCI config on ${target.baseRef} has been modified since commit ${commonAncestorSha.slice(0, 7)},
which this PR is based on.

Please rebase the PR on ${target.baseRef} after fetching from upstream.

Rebase instructions for PR Author, please run the following commands:

git fetch upstream ${target.baseRef};
git checkout ${target.headRef};
git rebase origin/${target.baseRef};
git push --force-with-lease;
`);
} else {
console.log('No change found in the CircleCI config file, continuing.');
}
console.log();

// Rebase the PR.
console.log(`Rebasing current branch on ${target.baseRef}.`);
await exec(`git rebase origin/${target.baseRef}`);
console.log('Rebase successful.');

}

function determineTargetBranch(repository, prNumber) {
const pullsUrl = `https://api.github.com/repos/${repository}/pulls/${prNumber}`;
async function requestDataFromGithub(url) {
// GitHub requires a user agent: https://developer.github.com/v3/#user-agent-required
const options = {headers: {'User-Agent': repository}};
const options = {headers: {'User-Agent': 'angular'}};

return new Promise((resolve, reject) => {
https
.get(
pullsUrl, options,
url, options,
(res) => {
const {statusCode} = res;
const contentType = res.headers['content-type'];
let rawData = '';

res.on('data', (chunk) => { rawData += chunk; });
res.on('end', () => {

let error;
if (statusCode !== 200) {
error = new Error(
Expand All @@ -101,8 +149,7 @@ function determineTargetBranch(repository, prNumber) {
}
Comment thread
gkalpak marked this conversation as resolved.
Outdated

try {
const parsedData = JSON.parse(rawData);
resolve(parsedData['base']['ref']);
resolve(JSON.parse(rawData));
} catch (e) {
reject(e);
}
Expand All @@ -111,3 +158,15 @@ function determineTargetBranch(repository, prNumber) {
.on('error', (e) => { reject(e); });
});
}

async function determineTargetRefAndSha(repository, prNumber) {
Comment thread
gkalpak marked this conversation as resolved.
Outdated
const pullsUrl = `https://api.github.com/repos/${repository}/pulls/${prNumber}`;

const result = await requestDataFromGithub(pullsUrl);
return {
baseRef: result.base.ref,
baseSha: result.base.sha,
headRef: result.head.ref,
headSha: result.head.sha,
};
}