ci: verify no unintentional circleci config changes (#33737)
PR Close #33737
This commit is contained in:
parent
478f8821a0
commit
8969f35bb5
|
@ -59,24 +59,73 @@ _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(`--------------------------------`);
|
||||
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'];
|
||||
|
@ -84,7 +133,6 @@ function determineTargetBranch(repository, prNumber) {
|
|||
|
||||
res.on('data', (chunk) => { rawData += chunk; });
|
||||
res.on('end', () => {
|
||||
|
||||
let error;
|
||||
if (statusCode !== 200) {
|
||||
error = new Error(
|
||||
|
@ -101,8 +149,7 @@ function determineTargetBranch(repository, prNumber) {
|
|||
}
|
||||
|
||||
try {
|
||||
const parsedData = JSON.parse(rawData);
|
||||
resolve(parsedData['base']['ref']);
|
||||
resolve(JSON.parse(rawData));
|
||||
} catch (e) {
|
||||
reject(e);
|
||||
}
|
||||
|
@ -111,3 +158,15 @@ function determineTargetBranch(repository, prNumber) {
|
|||
.on('error', (e) => { reject(e); });
|
||||
});
|
||||
}
|
||||
|
||||
async function determineTargetRefAndSha(repository, prNumber) {
|
||||
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,
|
||||
};
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue