parent
a8b432d55d
commit
e6117a3a49
|
@ -63,26 +63,19 @@ var_6: &job_defaults
|
|||
docker:
|
||||
- image: *default_docker_image
|
||||
|
||||
# After checkout, rebase on top of master.
|
||||
# Similar to travis behavior, but not quite the same.
|
||||
# See https://discuss.circleci.com/t/1662
|
||||
# After checkout, rebase on top of target branch.
|
||||
var_7: &post_checkout
|
||||
run:
|
||||
name: Post checkout step
|
||||
name: Rebase PR on target branch
|
||||
command: >
|
||||
if [[ -n "${CIRCLE_PR_NUMBER}" ]]; then
|
||||
# Fetch the head and merge commits for this PR.
|
||||
git fetch origin +refs/pull/$CIRCLE_PR_NUMBER/head:pr/$CIRCLE_PR_NUMBER/head
|
||||
git fetch origin +refs/pull/$CIRCLE_PR_NUMBER/merge:pr/$CIRCLE_PR_NUMBER/merge || (echo "Could not fetch merge result with master for this PR. Please check and fix any merge conflicts." ; exit 1)
|
||||
# Checkout the merged PR for testing as CircleCI will just use the PR head otherwise.
|
||||
git checkout -qf pr/$CIRCLE_PR_NUMBER/merge
|
||||
# Reset the merge commit into its PR head.
|
||||
git reset pr/$CIRCLE_PR_NUMBER/head
|
||||
# Commit the merge changes into the head of the PR.
|
||||
# This way we keep the last commit message.
|
||||
# User is required for rebase.
|
||||
git config user.name "angular-ci"
|
||||
git config user.email "angular-ci"
|
||||
git commit . --amend --no-edit
|
||||
# Rebase PR on top of target branch.
|
||||
node tools/rebase-pr.js angular/angular ${CIRCLE_PR_NUMBER}
|
||||
else
|
||||
echo "This build is not over a PR, nothing to do."
|
||||
fi
|
||||
|
||||
var_8: &yarn_install
|
||||
|
|
|
@ -0,0 +1,113 @@
|
|||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
/**
|
||||
* **Usage:**
|
||||
* ```
|
||||
* node rebase-pr <github-repository> <pull-request-number>
|
||||
* ```
|
||||
* **Example:**
|
||||
* ```
|
||||
* node rebase-pr angular/angular 123
|
||||
* ```
|
||||
*
|
||||
* Rebases the current branch on top of the GitHub PR target branch.
|
||||
*
|
||||
* **Context:**
|
||||
* Since a GitHub PR is not necessarily up to date with its target branch, it is useful to rebase
|
||||
* prior to testing it on CI to ensure more up to date test results.
|
||||
*
|
||||
* **Implementation details:**
|
||||
* This script obtains the base for a GitHub PR via the
|
||||
* [GitHub PR API](https://developer.github.com/v3/pulls/#get-a-single-pull-request), then
|
||||
* fetches that branch, and rebases the current branch on top of it.
|
||||
*
|
||||
* **NOTE:**
|
||||
* This script cannot use external dependencies or be compiled because it needs to run before the
|
||||
* environment is setup.
|
||||
* Use only features supported by the NodeJS versions used in the environment.
|
||||
*/
|
||||
|
||||
// This script uses `console` to print messages to the user.
|
||||
// tslint:disable:no-console
|
||||
|
||||
// Imports
|
||||
const util = require('util');
|
||||
const https = require('https');
|
||||
const child_process = require('child_process');
|
||||
const exec = util.promisify(child_process.exec);
|
||||
|
||||
// CLI validation
|
||||
if (process.argv.length != 4) {
|
||||
console.error(`This script requires the GitHub repository and PR number as arguments.`);
|
||||
console.error(`Example: node tools/rebase-pr.js angular/angular 123`);
|
||||
process.exitCode = 1;
|
||||
return;
|
||||
}
|
||||
|
||||
// Run
|
||||
_main(...process.argv.slice(2)).catch(err => {
|
||||
console.log('Failed to rebase on top of target branch.\n');
|
||||
console.error(err);
|
||||
process.exitCode = 1;
|
||||
});
|
||||
|
||||
// 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('Rebase successful.');
|
||||
}
|
||||
|
||||
function determineTargetBranch(repository, prNumber) {
|
||||
const pullsUrl = `https://api.github.com/repos/${repository}/pulls/${prNumber}`;
|
||||
// GitHub requires a user agent: https://developer.github.com/v3/#user-agent-required
|
||||
const options = {headers: {'User-Agent': repository}};
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
https
|
||||
.get(
|
||||
pullsUrl, 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(
|
||||
`Request Failed.\nStatus Code: ${statusCode}.\nResponse: ${rawData}`);
|
||||
} else if (!/^application\/json/.test(contentType)) {
|
||||
error = new Error(
|
||||
'Invalid content-type.\n' +
|
||||
`Expected application/json but received ${contentType}`);
|
||||
}
|
||||
|
||||
if (error) {
|
||||
reject(error);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const parsedData = JSON.parse(rawData);
|
||||
resolve(parsedData['base']['ref']);
|
||||
} catch (e) {
|
||||
reject(e);
|
||||
}
|
||||
});
|
||||
})
|
||||
.on('error', (e) => { reject(e); });
|
||||
});
|
||||
}
|
Loading…
Reference in New Issue