Skip to content

Commit efac7b6

Browse files
filipesilvavikerman
authored andcommitted
ci: rebase PRs on target branch
1 parent b32ad32 commit efac7b6

3 files changed

Lines changed: 108 additions & 16 deletions

File tree

.appveyor.yml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,13 @@ skip_branch_with_pr: true
99

1010
install:
1111
- ps: Install-Product node $env:nodejs_version
12+
- ps: |
13+
if (Test-Path env:APPVEYOR_PULL_REQUEST_NUMBER) {
14+
# user is required for rebase.
15+
git config user.name "angular-ci"
16+
git config user.email "angular-ci"
17+
# Rebase on target branch.
18+
node tools\rebase-pr.js angular/angular-cli $env:APPVEYOR_PULL_REQUEST_NUMBER }
1219
# --network-timeout is a workaround for https://github.com/yarnpkg/yarn/issues/6221
1320
- yarn --frozen-lockfile --network-timeout=500000
1421
- yarn webdriver-update

.circleci/config.yml

Lines changed: 9 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -25,26 +25,19 @@ anchor_1: &defaults
2525
docker:
2626
- image: *docker_image
2727

28-
# After checkout, rebase on top of master.
29-
# Similar to travis behavior, but not quite the same.
30-
# See https://discuss.circleci.com/t/1662
28+
# After checkout, rebase on top of target branch.
3129
anchor_2: &post_checkout
3230
run:
33-
name: Post checkout step
31+
name: Rebase PR on target branch
3432
command: >
3533
if [[ -n "${CIRCLE_PR_NUMBER}" ]]; then
36-
# Fetch the head and merge commits for this PR.
37-
git fetch origin +refs/pull/$CIRCLE_PR_NUMBER/head:pr/$CIRCLE_PR_NUMBER/head
38-
git fetch origin +refs/pull/$CIRCLE_PR_NUMBER/merge:pr/$CIRCLE_PR_NUMBER/merge
39-
# Checkout the merged PR for testing as CircleCI will just use the PR head otherwise.
40-
git checkout -qf pr/$CIRCLE_PR_NUMBER/merge
41-
# Reset the merge commit into its PR head.
42-
git reset pr/$CIRCLE_PR_NUMBER/head
43-
# Commit the merge changes into the head of the PR.
44-
# This way we keep the last commit message.
45-
git config user.name "angular-ci"
46-
git config user.email "angular-ci"
47-
git commit . --amend --no-edit
34+
# User is required for rebase.
35+
git config user.name "angular-ci"
36+
git config user.email "angular-ci"
37+
# Rebase PR on top of target branch.
38+
node tools/rebase-pr.js angular/angular-cli ${CIRCLE_PR_NUMBER}
39+
else
40+
echo "This build is not over a PR, nothing to do."
4841
fi
4942
anchor_3: &restore_cache
5043
restore_cache:

tools/rebase-pr.js

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
/**
2+
* @license
3+
* Copyright Google Inc. All Rights Reserved.
4+
*
5+
* Use of this source code is governed by an MIT-style license that can be
6+
* found in the LICENSE file at https://angular.io/license
7+
*/
8+
9+
// tslint:disable:no-console
10+
// ** IMPORTANT **
11+
// This script cannot use external dependencies because it needs to run before they are installed.
12+
13+
const util = require('util');
14+
const https = require('https');
15+
const child_process = require('child_process');
16+
const exec = util.promisify(child_process.exec);
17+
18+
function determineTargetBranch(repository, prNumber) {
19+
const pullsUrl = `https://api.github.com/repos/${repository}/pulls/${prNumber}`;
20+
// GitHub requires a user agent: https://developer.github.com/v3/#user-agent-required
21+
const options = { headers: { 'User-Agent': repository } };
22+
23+
return new Promise((resolve, reject) => {
24+
https.get(pullsUrl, options, (res) => {
25+
const { statusCode } = res;
26+
const contentType = res.headers['content-type'];
27+
28+
let error;
29+
if (statusCode !== 200) {
30+
error = new Error(`Request Failed.\nStatus Code: ${statusCode}.\nResponse: ${res}.\n' +`);
31+
} else if (!/^application\/json/.test(contentType)) {
32+
error = new Error('Invalid content-type.\n' +
33+
`Expected application/json but received ${contentType}`);
34+
}
35+
if (error) {
36+
reject(error);
37+
res.resume();
38+
return;
39+
}
40+
41+
res.setEncoding('utf8');
42+
let rawData = '';
43+
res.on('data', (chunk) => { rawData += chunk; });
44+
res.on('end', () => {
45+
try {
46+
const parsedData = JSON.parse(rawData);
47+
resolve(parsedData['base']['ref']);
48+
} catch (e) {
49+
reject(e);
50+
}
51+
});
52+
}).on('error', (e) => {
53+
reject(e);
54+
});
55+
});
56+
}
57+
58+
if (process.argv.length != 4) {
59+
console.error(`This script requires the GitHub repository and PR number as arguments.`);
60+
console.error(`Example: node scripts/rebase-pr.js angular/angular 123`);
61+
process.exitCode = 1;
62+
return;
63+
}
64+
65+
const repository = process.argv[2];
66+
const prNumber = process.argv[3];
67+
let targetBranch;
68+
69+
70+
return Promise.resolve()
71+
.then(() => {
72+
console.log(`Determining target branch for PR ${prNumber} on ${repository}.`);
73+
return determineTargetBranch(repository, prNumber);
74+
})
75+
.then(target => {
76+
targetBranch = target;
77+
console.log(`Target branch is ${targetBranch}.`);
78+
})
79+
.then(() => {
80+
console.log(`Fetching ${targetBranch} from origin.`);
81+
return exec(`git fetch origin ${targetBranch}`);
82+
})
83+
.then(target => {
84+
console.log(`Rebasing current branch on ${targetBranch}.`);
85+
return exec(`git rebase origin/${targetBranch}`);
86+
})
87+
.then(() => console.log('Rebase successfull.'))
88+
.catch(err => {
89+
console.log('Failed to rebase on top or target branch.\n');
90+
console.error(err);
91+
process.exitCode = 1;
92+
});

0 commit comments

Comments
 (0)