Tool: Bring back mergefreeze API integration (#6905)

* Tool: Bring back mergefreeze API integration

Context: https://github.com/fleetdm/fleet/pull/5628#issuecomment-1196175485

Unfortunately, the API doesn't work.

* Lay out how this would work in the database - but instead, do it ephemerally for now

* Remove model

* Maintain state (the easy way for now)
This commit is contained in:
Mike McNeil 2022-07-27 00:36:31 -05:00 committed by GitHub
parent 8369f6b1b5
commit 39fea292b9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 142 additions and 63 deletions

View File

@ -27,7 +27,13 @@ module.exports = {
let GitHub = require('machinepack-github'); let GitHub = require('machinepack-github');
let IS_FROZEN = false;// « Set this to `true` whenever a freeze is in effect, then set it back to `false` when the freeze ends. // Is this in use?
// > For context on the history of this bit of code, which has gone been
// > implemented a couple of different ways, and gone back and forth, check out:
// > https://github.com/fleetdm/fleet/pull/5628#issuecomment-1196175485
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// let IS_FROZEN = false;// « Set this to `true` whenever a freeze is in effect, then set it back to `false` when the freeze ends.
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
let GITHUB_USERNAMES_OF_BOTS_AND_MAINTAINERS = [// « Used in multiple places below. let GITHUB_USERNAMES_OF_BOTS_AND_MAINTAINERS = [// « Used in multiple places below.
'sailsbot', 'sailsbot',
@ -71,6 +77,10 @@ module.exports = {
let GITHUB_USERNAME_OF_DRI_FOR_LABELS = 'noahtalerman';// « Used below (FUTURE: Remove this capability as Fleet has outgrown it. 2022-05-05) let GITHUB_USERNAME_OF_DRI_FOR_LABELS = 'noahtalerman';// « Used below (FUTURE: Remove this capability as Fleet has outgrown it. 2022-05-05)
if (!sails.config.custom.mergeFreezeAccessToken) {
throw new Error('An access token for the MergeFreeze API (sails.config.custom.mergeFreezeAccessToken) is required to enable automated unfreezing/freezing of changes based on the files they change. Please ask for help in #g-digital-experience, whether you are testing locally or using this as a live webhook.');
}
if (!sails.config.custom.slackWebhookUrlForGithubBot) { if (!sails.config.custom.slackWebhookUrlForGithubBot) {
throw new Error('No Slack webhook URL configured for the GitHub bot to notify with alerts! (Please set `sails.config.custom.slackWebhookUrlForGithubBot`.)'); throw new Error('No Slack webhook URL configured for the GitHub bot to notify with alerts! (Please set `sails.config.custom.slackWebhookUrlForGithubBot`.)');
}//• }//•
@ -218,18 +228,78 @@ module.exports = {
isGithubUserMaintainerOrDoesntMatter: GITHUB_USERNAMES_OF_BOTS_AND_MAINTAINERS.includes(sender.login.toLowerCase()) isGithubUserMaintainerOrDoesntMatter: GITHUB_USERNAMES_OF_BOTS_AND_MAINTAINERS.includes(sender.login.toLowerCase())
}); });
// Check whether the "main" branch is currently frozen (i.e. a feature freeze)
// [?] https://docs.mergefreeze.com/web-api#get-freeze-status
let isMainBranchFrozen = (await sails.helpers.http.get('https://www.mergefreeze.com/api/branches/fleetdm/fleet/main', { access_token: sails.config.custom.mergeFreezeAccessToken })).frozen;//eslint-disable-line camelcase
// Now, if appropriate, auto-approve the change. // Now, if appropriate, auto-approve the change.
if (isAutoApproved) { if (isAutoApproved) {
// [?] https://docs.github.com/en/rest/reference/pulls#create-a-review-for-a-pull-request // [?] https://docs.github.com/en/rest/reference/pulls#create-a-review-for-a-pull-request
await sails.helpers.http.post(`https://api.github.com/repos/${owner}/${repo}/pulls/${prNumber}/reviews`, { await sails.helpers.http.post(`https://api.github.com/repos/${owner}/${repo}/pulls/${prNumber}/reviews`, {
event: 'APPROVE' event: 'APPROVE'
}, baseHeaders); }, baseHeaders);
} else if (IS_FROZEN) {
// [?] https://docs.github.com/en/rest/reference/pulls#create-a-review-for-a-pull-request // Since we're only using a single instance, and because the worst case scenario is that we refreeze some
await sails.helpers.http.post(`https://api.github.com/repos/${owner}/${repo}/pulls/${prNumber}/reviews`, { // all-markdown PRs that had already been frozen, instead of using the database, we'll just use a little
event: 'REQUEST_CHANGES', // in-memory pocket here of PRs seen by this instance of the Sails app. To get around any issues with this,
body: 'The repository is currently frozen for an upcoming release. \n> After the freeze has ended, please dismiss this review. \n\nIn case of emergency, you can dismiss this review and merge now.' // users can edit and resave the PR description to trigger their PR to be unfrozen.
}, baseHeaders); // FUTURE: Go through the trouble to migrate the database and make a little Platform model to hold this state in.
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// Grab the set of GitHub pull request numbers the bot considers "unfrozen".
sails.pocketOfPrNumbersUnfrozen = sails.pocketOfPrNumbersUnfrozen || [];
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// If "main" is explicitly frozen, then unfreeze this PR because it no longer contains
// (or maybe never did contain) changes to freezeworthy files.
if (isMainBranchFrozen) {
sails.pocketOfPrNumbersUnfrozen = _.union(sails.pocketOfPrNumbersUnfrozen, [ prNumber ]);
// [?] See May 6th, 2022 changelog, which includes this code sample:
// (https://www.mergefreeze.com/news)
// (but as of July 26, 2022, I didn't see it documented here: https://docs.mergefreeze.com/web-api#post-freeze-status)
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// > You may now freeze or unfreeze a single PR (while maintaining the overall freeze) via the Web API.
// ```
// curl -d "frozen=true&user_name=Scooby Doo&unblocked_prs=[3]" -X POST https://www.mergefreeze.com/api/branches/mergefreeze/core/master/?access_token=[Your access token]
// ```
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
await sails.helpers.http.post(`https://www.mergefreeze.com/api/branches/fleetdm/fleet/main?access_token=${encodeURIComponent(sails.config.custom.mergeFreezeAccessToken)}`, {
frozen: true,
user_name: 'fleet-release',//eslint-disable-line camelcase
unblocked_prs: sails.pocketOfPrNumbersUnfrozen,//eslint-disable-line camelcase
});
}//fi
} else {
// If "main" is explicitly frozen, then freeze this PR because it now contains
// (or maybe always did contain) changes to freezeworthy files.
if (isMainBranchFrozen) {
sails.pocketOfPrNumbersUnfrozen = _.difference(sails.pocketOfPrNumbersUnfrozen, [ prNumber ]);
// [?] See explanation above.
await sails.helpers.http.post(`https://www.mergefreeze.com/api/branches/fleetdm/fleet/main?access_token=${encodeURIComponent(sails.config.custom.mergeFreezeAccessToken)}`, {
frozen: true,
user_name: 'fleet-release',//eslint-disable-line camelcase
unblocked_prs: sails.pocketOfPrNumbersUnfrozen,//eslint-disable-line camelcase
});
}//fi
// Is this in use?
// > For context on the history of this bit of code, which has gone been
// > implemented a couple of different ways, and gone back and forth, check out:
// > https://github.com/fleetdm/fleet/pull/5628#issuecomment-1196175485
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// if (IS_FROZEN) {
// // [?] https://docs.github.com/en/rest/reference/pulls#create-a-review-for-a-pull-request
// await sails.helpers.http.post(`https://api.github.com/repos/${owner}/${repo}/pulls/${prNumber}/reviews`, {
// event: 'REQUEST_CHANGES',
// body: 'The repository is currently frozen for an upcoming release. \n> After the freeze has ended, please dismiss this review. \n\nIn case of emergency, you can dismiss this review and merge now.'
// }, baseHeaders);
// }//fi
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
} }
} }

View File

@ -55,7 +55,10 @@
// Make sure backend globals aren't indadvertently tolerated in our client-side JS: // Make sure backend globals aren't indadvertently tolerated in our client-side JS:
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
"sails": false, "sails": false,
"User": false "User": false,
"HistoricalUsageSnapshot": false,
"Quote": false,
"Subscription": false,
// ...and any other backend globals (e.g. `"Organization": false`) // ...and any other backend globals (e.g. `"Organization": false`)
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
} }

View File

@ -1,6 +1,5 @@
module.exports = { module.exports = {
friendlyName: 'Freeze open pull requests', friendlyName: 'Freeze open pull requests',
@ -22,62 +21,69 @@ module.exports = {
}, },
fn: async function ({dry: isDryRun, limit: maxNumPullRequestsToCheck }) { fn: async function () {
// Is this in use?
sails.log('Running custom shell script... (`sails run freeze-open-pull-requests`)'); // > For context on the history of this bit of code, which has gone been
// > implemented a couple of different ways, and gone back and forth, check out:
let owner = 'fleetdm'; // > https://github.com/fleetdm/fleet/pull/5628#issuecomment-1196175485
let repo = 'fleet'; throw new Error('Not currently in use. See comments in this script for more information.');
let baseHeaders = {
'User-Agent': 'sails run freeze-open-pull-requests',
'Authorization': `token ${sails.config.custom.githubAccessToken}`
};
// Fetch open pull requests
// [?] https://docs.github.com/en/rest/pulls/pulls#list-pull-requests
let openPullRequests = await sails.helpers.http.get(`https://api.github.com/repos/${owner}/${repo}/pulls`, {
state: 'open',
per_page: maxNumPullRequestsToCheck,//eslint-disable-line camelcase
}, baseHeaders);
if (openPullRequests.length > maxNumPullRequestsToCheck) {
openPullRequests = openPullRequests.slice(0,maxNumPullRequestsToCheck);
}
let SECONDS_TO_WAIT = 5;
sails.log(`Examining and potentially freezing ${openPullRequests.length} PRs very soon… (To cancel, press CTRL+C quickly within ${SECONDS_TO_WAIT}s!)`);
await sails.helpers.flow.pause(SECONDS_TO_WAIT*1000);
// For all open pull requests…
await sails.helpers.flow.simultaneouslyForEach(openPullRequests, async(pullRequest)=>{
let prNumber = pullRequest.number;
let prAuthor = pullRequest.user.login;
require('assert')(prAuthor !== undefined);
// Freeze, if appropriate.
// (Check versus the intersection of DRIs for all changed files to make sure SOMEONE is preapproved for all of them.)
let isAuthorPreapproved = await sails.helpers.githubAutomations.getIsPrPreapproved.with({
prNumber: prNumber,
isGithubUserMaintainerOrDoesntMatter: true// « doesn't matter here because no auto-approval is happening. Worst case, a community PR to an area with a "*" in the DRI mapping remains unfrozen.
});
if (isDryRun) {
sails.log(`#${prNumber} by @${prAuthor}:`, isAuthorPreapproved ? 'Would have skipped freeze…' : 'Would have frozen…');
} else {
sails.log(`#${prNumber} by @${prAuthor}:`, isAuthorPreapproved ? 'Skipping freeze…' : 'Freezing…');
if (!isAuthorPreapproved) {
// [?] https://docs.github.com/en/rest/reference/pulls#create-a-review-for-a-pull-request
await sails.helpers.http.post(`https://api.github.com/repos/${owner}/${repo}/pulls/${prNumber}/reviews`, {
event: 'REQUEST_CHANGES',
body: 'The repository has been frozen for an upcoming release. In case of emergency, you can dismiss this review and merge.'
}, baseHeaders);
}//fi
}
});
} }
// fn: async function ({dry: isDryRun, limit: maxNumPullRequestsToCheck }) {
// sails.log('Running custom shell script... (`sails run freeze-open-pull-requests`)');
// let owner = 'fleetdm';
// let repo = 'fleet';
// let baseHeaders = {
// 'User-Agent': 'sails run freeze-open-pull-requests',
// 'Authorization': `token ${sails.config.custom.githubAccessToken}`
// };
// // Fetch open pull requests
// // [?] https://docs.github.com/en/rest/pulls/pulls#list-pull-requests
// let openPullRequests = await sails.helpers.http.get(`https://api.github.com/repos/${owner}/${repo}/pulls`, {
// state: 'open',
// per_page: maxNumPullRequestsToCheck,//eslint-disable-line camelcase
// }, baseHeaders);
// if (openPullRequests.length > maxNumPullRequestsToCheck) {
// openPullRequests = openPullRequests.slice(0,maxNumPullRequestsToCheck);
// }
// let SECONDS_TO_WAIT = 5;
// sails.log(`Examining and potentially freezing ${openPullRequests.length} PRs very soon… (To cancel, press CTRL+C quickly within ${SECONDS_TO_WAIT}s!)`);
// await sails.helpers.flow.pause(SECONDS_TO_WAIT*1000);
// // For all open pull requests…
// await sails.helpers.flow.simultaneouslyForEach(openPullRequests, async(pullRequest)=>{
// let prNumber = pullRequest.number;
// let prAuthor = pullRequest.user.login;
// require('assert')(prAuthor !== undefined);
// // Freeze, if appropriate.
// // (Check versus the intersection of DRIs for all changed files to make sure SOMEONE is preapproved for all of them.)
// let isAuthorPreapproved = await sails.helpers.githubAutomations.getIsPrPreapproved.with({
// prNumber: prNumber,
// isGithubUserMaintainerOrDoesntMatter: true// « doesn't matter here because no auto-approval is happening. Worst case, a community PR to an area with a "*" in the DRI mapping remains unfrozen.
// });
// if (isDryRun) {
// sails.log(`#${prNumber} by @${prAuthor}:`, isAuthorPreapproved ? 'Would have skipped freeze…' : 'Would have frozen…');
// } else {
// sails.log(`#${prNumber} by @${prAuthor}:`, isAuthorPreapproved ? 'Skipping freeze…' : 'Freezing…');
// if (!isAuthorPreapproved) {
// // [?] https://docs.github.com/en/rest/reference/pulls#create-a-review-for-a-pull-request
// await sails.helpers.http.post(`https://api.github.com/repos/${owner}/${repo}/pulls/${prNumber}/reviews`, {
// event: 'REQUEST_CHANGES',
// body: 'The repository has been frozen for an upcoming release. In case of emergency, you can dismiss this review and merge.'
// }, baseHeaders);
// }//fi
// }
// });
// }
}; };