2021-08-06 07:52:02 +00:00
module . exports = {
friendlyName : 'Receive from GitHub' ,
description : 'Receive webhook requests and/or incoming auth redirects from GitHub.' ,
2022-05-06 05:12:50 +00:00
extendedDescription : 'Useful for automation, visibility of changes, and abuse monitoring.' ,
2021-08-06 07:52:02 +00:00
inputs : {
botSignature : { type : 'string' , } ,
action : { type : 'string' , example : 'opened' , defaultsTo : 'ping' , moreInfoUrl : 'https://developer.github.com/v3/activity/events/types' } ,
2021-11-05 21:13:30 +00:00
sender : { required : true , type : { } , example : { login : 'johnabrams7' } } ,
repository : { required : true , type : { } , example : { name : 'fleet' , owner : { login : 'fleetdm' } } } ,
2021-08-06 07:52:02 +00:00
changes : { type : { } , description : 'Only present when webhook request is related to an edit on GitHub.' } ,
issue : { type : { } } ,
comment : { type : { } } ,
pull _request : { type : { } } , //eslint-disable-line camelcase
label : { type : { } } ,
} ,
2021-11-05 21:13:30 +00:00
fn : async function ( { botSignature , action , sender , repository , changes , issue , comment , pull _request : pr , label } ) {
2021-08-06 07:52:02 +00:00
2022-07-27 06:19:47 +00:00
// Since we're only using a single instance, and because the worst case scenario is that we refreeze some
// all-markdown PRs that had already been frozen, instead of using the database, we'll just use a little
// in-memory pocket here of PRs seen by this instance of the Sails app. To get around any issues with this,
// users can edit and resave the PR description to trigger their PR to be unfrozen.
// 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 || [ ] ;
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
2022-07-27 05:36:31 +00:00
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// let IS_FROZEN = false;// « Set this to `true` whenever a freeze is in effect, then set it back to `false` when the freeze ends.
2022-09-20 03:09:02 +00:00
// > ^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
2022-07-27 05:36:31 +00:00
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
2022-05-05 18:56:32 +00:00
2021-09-27 21:01:49 +00:00
let GITHUB _USERNAMES _OF _BOTS _AND _MAINTAINERS = [ // « Used in multiple places below.
2022-09-20 03:09:02 +00:00
// FUTURE: move this array into website/config/custom.js alongside the other similar config
// and reference here as e.g. `sails.config.custom.githubUsernamesOfBotsAndMaintainers`
// Bots
'vercel[bot]' ,
2021-09-27 21:01:49 +00:00
'fleet-release' ,
2022-09-20 03:09:02 +00:00
// Humans
2021-09-27 21:01:49 +00:00
'noahtalerman' ,
'mike-j-thomas' ,
'mikermcneil' ,
'lukeheath' ,
'zwass' ,
'rachelelysia' ,
'gillespi314' ,
'mna' ,
'edwardsb' ,
'eashaw' ,
2021-11-13 01:13:20 +00:00
'drewbakerfdm' ,
2021-12-06 20:28:48 +00:00
'lucasmrod' ,
2021-12-29 19:53:04 +00:00
'ksatter' ,
2022-03-09 16:30:43 +00:00
'charlottechance' ,
2022-03-17 23:29:21 +00:00
'zwinnerman-fleetdm' ,
2022-04-01 19:45:12 +00:00
'hollidayn' ,
2022-04-29 21:35:57 +00:00
'roperzh' ,
2022-05-24 22:38:58 +00:00
'zhumo' ,
2022-07-13 15:40:05 +00:00
'ghernandez345' ,
2022-07-25 23:36:04 +00:00
'rfairburn' ,
2022-08-03 15:39:13 +00:00
'artemist-work' ,
2022-09-19 22:13:10 +00:00
'marcosd4h' ,
2022-10-11 16:35:23 +00:00
'zayhanlon' ,
2022-11-21 18:59:57 +00:00
'bradmacd' ,
'alexmitchelliii' ,
2022-12-07 14:23:38 +00:00
'jarodreyes' ,
2021-08-06 07:52:02 +00:00
] ;
2022-05-06 05:12:50 +00:00
let GREEN _LABEL _COLOR = 'C2E0C6' ; // « Used in multiple places below. (FUTURE: Use the "+" prefix for this instead of color. 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)
2021-08-06 07:52:02 +00:00
2022-07-27 05:36:31 +00:00
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.' ) ;
}
2021-08-06 07:52:02 +00:00
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`.)' ) ;
} //•
if ( ! sails . config . custom . githubBotWebhookSecret ) {
throw new Error ( 'No GitHub bot webhook secret configured! (Please set `sails.config.custom.githubBotWebhookSecret`.)' ) ;
} //•
if ( sails . config . custom . githubBotWebhookSecret !== botSignature ) {
2021-11-05 21:13:30 +00:00
throw new Error ( 'Received unexpected GitHub webhook request with botSignature set to: ' + botSignature ) ;
2021-08-06 07:52:02 +00:00
} //•
if ( ! sails . config . custom . githubAccessToken ) {
throw new Error ( 'No GitHub access token configured! (Please set `sails.config.custom.githubAccessToken`.)' ) ;
} //•
let issueOrPr = ( pr || issue || undefined ) ;
let ghNoun = this . req . get ( 'X-GitHub-Event' ) ; // See https://developer.github.com/v3/activity/events/types/
// sails.log(ghNoun, action, {sender, repository, issue, comment, pr, label});
if (
2021-11-05 21:13:30 +00:00
( ghNoun === 'issues' && [ 'opened' , 'reopened' ] . includes ( action ) )
2021-08-06 07:52:02 +00:00
) {
// ██╗███████╗███████╗██╗ ██╗███████╗
// ██║██╔════╝██╔════╝██║ ██║██╔════╝
// ██║███████╗███████╗██║ ██║█████╗
// ██║╚════██║╚════██║██║ ██║██╔══╝
// ██║███████║███████║╚██████╔╝███████╗
// ╚═╝╚══════╝╚══════╝ ╚═════╝ ╚══════╝
//
// ██╗ ██████╗ ██████╗ ███████╗███╗ ██╗███████╗██████╗ ██╗ ██████╗ ███████╗ ██████╗ ██████╗ ███████╗███╗ ██╗███████╗██████╗ ██╗
// ██╔╝██╔═══██╗██╔══██╗██╔════╝████╗ ██║██╔════╝██╔══██╗ ██╔╝ ██╔══██╗██╔════╝██╔═══██╗██╔══██╗██╔════╝████╗ ██║██╔════╝██╔══██╗╚██╗
// ██║ ██║ ██║██████╔╝█████╗ ██╔██╗ ██║█████╗ ██║ ██║ ██╔╝ ██████╔╝█████╗ ██║ ██║██████╔╝█████╗ ██╔██╗ ██║█████╗ ██║ ██║ ██║
// ██║ ██║ ██║██╔═══╝ ██╔══╝ ██║╚██╗██║██╔══╝ ██║ ██║ ██╔╝ ██╔══██╗██╔══╝ ██║ ██║██╔═══╝ ██╔══╝ ██║╚██╗██║██╔══╝ ██║ ██║ ██║
// ╚██╗╚██████╔╝██║ ███████╗██║ ╚████║███████╗██████╔╝ ██╔╝ ██║ ██║███████╗╚██████╔╝██║ ███████╗██║ ╚████║███████╗██████╔╝██╔╝
// ╚═╝ ╚═════╝ ╚═╝ ╚══════╝╚═╝ ╚═══╝╚══════╝╚═════╝ ╚═╝ ╚═╝ ╚═╝╚══════╝ ╚═════╝ ╚═╝ ╚══════╝╚═╝ ╚═══╝╚══════╝╚═════╝ ╚═╝
//
// // Handle opened/reopened issue by commenting on it.
// let owner = repository.owner.login;
// let repo = repository.name;
// let issueNumber = issueOrPr.number;
// let newBotComment;
// if (action === 'opened') {
// if (issueOrPr.state !== 'open') {
// newBotComment = '';// « checked below
// } else {
// newBotComment =
// `@${issueOrPr.user.login} Thanks for posting! We'll take a look as soon as possible.\n`+
// `\n`+
// `In the mean time, there are a few ways you can help speed things along:\n`+
// ` - look for a workaround. _(Even if it's just temporary, sharing your solution can save someone else a lot of time and effort.)_\n`+
// ` - tell us why this issue is important to you and your team. What are you trying to accomplish? _(Submissions with a little bit of human context tend to be easier to understand and faster to resolve.)_\n`+
// ` - make sure you've provided clear instructions on how to reproduce the bug from a clean install.\n`+
// ` - double-check that you've provided all of the requested version and dependency information. _(Some of this info might seem irrelevant at first, like which database adapter you're using, but we ask that you include it anyway. Oftentimes an issue is caused by a confluence of unexpected factors, and it can save everybody a ton of time to know all the details up front.)_\n`+
// ` - read the [code of conduct](https://sailsjs.com/documentation/contributing/code-of-conduct).\n`+
// ` - if appropriate, ask your business to [spons or your issue](https://sailsjs.com/support). _(Open source is our passion, and our core maintainers volunteer many of their nights and weekends working on Sails. But you only get so many nights and weekends in life, and stuff gets done a lot faster when you can work on it during normal daylight hours.)_\n`+
// ` - let us know if you are using a 3rd party plugin; whether that's a database adapter, a non-standard view engine, or any other dependency maintained by someone other than our core team. _(Besides the name of the 3rd party package, it helps to include the exact version you're using. If you're unsure, check out [this list of all the core packages we maintain](https://sailsjs.com/architecture).)_ \n`+
// `<hr/>\n`+
// `\n`+
// `Please remember: never post in a public forum if you believe you've found a genuine security vulnerability. Instead, [disclose it responsibly](https://sailsjs.com/security).\n`+
// `\n`+
// `For help with questions about Sails, [click here](http://sailsjs.com/support).\n`;
// }
// } else {
2022-05-05 15:53:58 +00:00
// let wasReopenedByBot = GITHUB_USERNAMES_OF_BOTS_AND_MAINTAINERS.includes(sender.login.toLowerCase());
2021-08-06 07:52:02 +00:00
// if (wasReopenedByBot) {
// newBotComment = '';// « checked below
// } else {
// let greenLabels = _.filter(issueOrPr.labels, ({color}) => color === GREEN_LABEL_COLOR);
// await sails.helpers.flow.simultaneouslyForEach(greenLabels, async(greenLabel)=>{
2022-08-22 18:11:54 +00:00
// await sails.helpers.http.del('https://api.github.com/repos/'+encodeURIComponent(owner)+'/'+encodeURIComponent(repo)+'/issues/'+encodeURIComponent(issueNumber)+'/labels/'+encodeURIComponent(greenLabel.name),
2022-08-08 21:58:48 +00:00
// {},
// {
// 'User-Agent': 'Fleetie pie',
// 'Authorization': 'token '+sails.config.custom.githubAccessToken
// }
// );
2021-08-06 07:52:02 +00:00
// });//∞ß
// newBotComment =
// `Oh hey again, @${issueOrPr.user.login}. Now that this issue is reopened, we'll take a fresh look as soon as we can!\n`+
// `<hr/>\n`+
// `\n`+
// `Please remember: never post in a public forum if you believe you've found a genuine security vulnerability. Instead, [disclose it responsibly](https://sailsjs.com/security).\n`+
// `\n`+
// `For help with questions about Sails, [click here](http://sailsjs.com/support).\n`;
// }
// }
// // Now that we know what to say, add our comment.
// if (newBotComment) {
2022-08-08 21:58:48 +00:00
// await sails.helpers.http.post('https://api.github.com/repos/'+encodeURIComponent(owner)'/'+encodeURIComponent(repo)+'/issues/'+encodeURIComponent(issueNumber)+'/comments',
// {'body': newBotComment},
// {'Authorization': 'token '+sails.config.custom.githubAccessToken}
// );
2021-08-06 07:52:02 +00:00
// }//fi
2023-01-14 22:56:16 +00:00
} else if (
2023-01-14 23:10:30 +00:00
( ghNoun === 'issues' && [ 'closed' ] . includes ( action ) )
2023-01-14 22:56:16 +00:00
) {
// ██╗███████╗███████╗██╗ ██╗███████╗ ██████╗██╗ ██████╗ ███████╗███████╗██████╗
// ██║██╔════╝██╔════╝██║ ██║██╔════╝ ██╔════╝██║ ██╔═══██╗██╔════╝██╔════╝██╔══██╗
// ██║███████╗███████╗██║ ██║█████╗ ██║ ██║ ██║ ██║███████╗█████╗ ██║ ██║
// ██║╚════██║╚════██║██║ ██║██╔══╝ ██║ ██║ ██║ ██║╚════██║██╔══╝ ██║ ██║
// ██║███████║███████║╚██████╔╝███████╗ ╚██████╗███████╗╚██████╔╝███████║███████╗██████╔╝
// ╚═╝╚══════╝╚══════╝ ╚═════╝ ╚══════╝ ╚═════╝╚══════╝ ╚═════╝ ╚══════╝╚══════╝╚═════╝
//
2023-02-06 16:45:29 +00:00
//
2023-01-14 22:56:16 +00:00
// Handle closed issue by commenting on it.
2023-02-07 22:07:54 +00:00
let owner = repository . owner . login ;
let repo = repository . name ;
let issueNumber = issueOrPr . number ;
let newBotComment ;
let baseHeadersForGithubApiRequests = {
'User-Agent' : 'Fleetie pie' ,
'Authorization' : ` token ${ sails . config . custom . githubAccessToken } `
} ;
if ( ! sails . config . custom . openAiSecret ) {
throw new Error ( 'sails.config.custom.openAiSecret not set. Cannot respond with haiku.' ) ;
} //•
// Grab issue title and body, then truncate the length of the body so that it fits
// within the maximum length tolerated by OpenAI. Then combine those into a prompt
// generate a haiku based on this issue.
let issueSummary = '# ' + issueOrPr . title + '\n' + _ . trunc ( issueOrPr . body , 2000 ) ;
// Generate haiku
// [?] https://beta.openai.com/docs/api-reference/completions/create
let openAiReport = await sails . helpers . http . post ( 'https://api.openai.com/v1/completions' , {
model : 'text-davinci-003' ,
prompt : ` You are an empathetic product designer. I will give you a Github issue with information about a particular improvement to Fleet, an open-source device management and security platform. You will write a haiku about how this improvement could benefit users or contributors. Be detailed and specific in the haiku. Do not use hyperbole. Be matter-of-fact. Be positive. Do not make Fleet (or anyone) sound bad. But be honest. If appropriate, mention imagery from nature, or from a glass city in the clouds. Do not give orders. \n \n The first GitHub issue is: \n ${ issueSummary } ` ,
temperature : 0.7 ,
max _tokens : 256 //eslint-disable-line camelcase
} , {
Authorization : ` Bearer ${ sails . config . custom . openAiSecret } `
} )
. tolerate ( ( err ) => {
sails . log ( 'Failed to generate haiku using OpenAI. Error details from OpenAI:' , err ) ;
} ) ;
if ( ! openAiReport ) { // If OpenAI could not be reached…
newBotComment = 'I couldn\'t think of a haiku this time. (See fleetdm.com logs for more information.)' ;
} else { // Otherwise, haiku was successfully generated…
newBotComment = openAiReport . choices [ 0 ] . text ;
newBotComment = newBotComment . replace ( /^\s*\n*[^\n:]*Haiku[^\n:]*:\s*/i , '' ) ; // « eliminate "*Haiku:" prefix line, if one is generated
}
// Now that we know what to say, add our comment.
await sails . helpers . http . post ( 'https://api.github.com/repos/' + encodeURIComponent ( owner ) + '/' + encodeURIComponent ( repo ) + '/issues/' + encodeURIComponent ( issueNumber ) + '/comments' ,
{ 'body' : newBotComment } ,
baseHeadersForGithubApiRequests
) ;
2023-01-14 22:56:16 +00:00
2021-08-06 07:52:02 +00:00
} else if (
2021-11-05 21:13:30 +00:00
( ghNoun === 'pull_request' && [ 'opened' , 'reopened' , 'edited' ] . includes ( action ) )
2021-08-06 07:52:02 +00:00
) {
// ██████╗ ██╗ ██╗██╗ ██╗ ██████╗ ███████╗ ██████╗ ██╗ ██╗███████╗███████╗████████╗
// ██╔══██╗██║ ██║██║ ██║ ██╔══██╗██╔════╝██╔═══██╗██║ ██║██╔════╝██╔════╝╚══██╔══╝
// ██████╔╝██║ ██║██║ ██║ ██████╔╝█████╗ ██║ ██║██║ ██║█████╗ ███████╗ ██║
// ██╔═══╝ ██║ ██║██║ ██║ ██╔══██╗██╔══╝ ██║▄▄ ██║██║ ██║██╔══╝ ╚════██║ ██║
// ██║ ╚██████╔╝███████╗███████╗ ██║ ██║███████╗╚██████╔╝╚██████╔╝███████╗███████║ ██║
// ╚═╝ ╚═════╝ ╚══════╝╚══════╝ ╚═╝ ╚═╝╚══════╝ ╚══▀▀═╝ ╚═════╝ ╚══════╝╚══════╝ ╚═╝
//
// ██╗ ██████╗ ██████╗ ███████╗███╗ ██╗███████╗██████╗ ██╗ ███████╗██████╗ ██╗████████╗███████╗██████╗ ██╗ ██████╗ ███████╗ ██████╗ ██████╗ ███████╗███╗ ██╗███████╗██████╗ ██╗
// ██╔╝██╔═══██╗██╔══██╗██╔════╝████╗ ██║██╔════╝██╔══██╗ ██╔╝ ██╔════╝██╔══██╗██║╚══██╔══╝██╔════╝██╔══██╗ ██╔╝ ██╔══██╗██╔════╝██╔═══██╗██╔══██╗██╔════╝████╗ ██║██╔════╝██╔══██╗╚██╗
// ██║ ██║ ██║██████╔╝█████╗ ██╔██╗ ██║█████╗ ██║ ██║ ██╔╝ █████╗ ██║ ██║██║ ██║ █████╗ ██║ ██║ ██╔╝ ██████╔╝█████╗ ██║ ██║██████╔╝█████╗ ██╔██╗ ██║█████╗ ██║ ██║ ██║
// ██║ ██║ ██║██╔═══╝ ██╔══╝ ██║╚██╗██║██╔══╝ ██║ ██║ ██╔╝ ██╔══╝ ██║ ██║██║ ██║ ██╔══╝ ██║ ██║ ██╔╝ ██╔══██╗██╔══╝ ██║ ██║██╔═══╝ ██╔══╝ ██║╚██╗██║██╔══╝ ██║ ██║ ██║
// ╚██╗╚██████╔╝██║ ███████╗██║ ╚████║███████╗██████╔╝ ██╔╝ ███████╗██████╔╝██║ ██║ ███████╗██████╔╝ ██╔╝ ██║ ██║███████╗╚██████╔╝██║ ███████╗██║ ╚████║███████╗██████╔╝██╔╝
// ╚═╝ ╚═════╝ ╚═╝ ╚══════╝╚═╝ ╚═══╝╚══════╝╚═════╝ ╚═╝ ╚══════╝╚═════╝ ╚═╝ ╚═╝ ╚══════╝╚═════╝ ╚═╝ ╚═╝ ╚═╝╚══════╝ ╚═════╝ ╚═╝ ╚══════╝╚═╝ ╚═══╝╚══════╝╚═════╝ ╚═╝
//
2022-02-18 22:41:31 +00:00
let owner = repository . owner . login ;
2022-02-17 09:43:59 +00:00
let repo = repository . name ;
let prNumber = issueOrPr . number ;
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// Want to do more?
//
// For some working, recent, easily-tweaked example code that manages a conversation with the GitHub bot
// to get help submitters of PRs/issues get them up to spec, see:
// https://github.com/fleetdm/fleet/blob/0a59adc2dd65bce5c1201a752e9c218faea7be35/website/api/controllers/webhooks/receive-from-github.js#L145-L216
//
// To potentially reuse:
2021-08-06 07:52:02 +00:00
// let newBotComment =
// `Oh hey again, @${issueOrPr.user.login}. Now that this pull request is reopened, it's on our radar. Please let us know if there's any new information we should be aware of!\n`+
// `<hr/>\n`+
// `\n`+
// `Please remember: never post in a public forum if you believe you've found a genuine security vulnerability. Instead, [disclose it responsibly](https://sailsjs.com/security).\n`+
// `\n`+
// `For help with questions about Sails, [click here](http://sailsjs.com/support).\n`;
2022-02-17 09:43:59 +00:00
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
if ( action === 'edited' && pr . state !== 'open' ) { // PR edited ‡
// This is an edit to an already-closed pull request.
// (Do nothing.)
2022-02-17 10:40:46 +00:00
} else { // Either:
// PR opened ‡ (Newly opened.)
// PR reopened ‡ (This is a closed pull request, being reopened. `action === 'reopened'`)
2022-02-17 09:43:59 +00:00
let baseHeaders = {
'User-Agent' : 'Fleetie pie' ,
'Authorization' : ` token ${ sails . config . custom . githubAccessToken } `
} ;
2022-05-06 05:12:50 +00:00
require ( 'assert' ) ( sender . login !== undefined ) ;
2022-03-26 03:55:32 +00:00
2022-05-06 05:12:50 +00:00
// Check whether auto-approval is warranted.
let isAutoApproved = await sails . helpers . githubAutomations . getIsPrPreapproved . with ( {
2022-12-05 19:58:21 +00:00
repo : repo ,
2022-05-06 05:12:50 +00:00
prNumber : prNumber ,
githubUserToCheck : sender . login ,
isGithubUserMaintainerOrDoesntMatter : GITHUB _USERNAMES _OF _BOTS _AND _MAINTAINERS . includes ( sender . login . toLowerCase ( ) )
2022-02-17 09:43:59 +00:00
} ) ;
2022-12-05 19:58:21 +00:00
let isHandbookPR = false ;
if ( repo === 'fleet' ) {
isHandbookPR = await sails . helpers . githubAutomations . getIsPrOnlyHandbookChanges . with ( { prNumber : prNumber } ) ;
}
2022-08-02 03:27:54 +00:00
2022-07-27 05:36:31 +00:00
// Check whether the "main" branch is currently frozen (i.e. a feature freeze)
// [?] https://docs.mergefreeze.com/web-api#get-freeze-status
2023-02-18 01:37:58 +00:00
let mergeFreezeMainBranchStatusReport = await sails . helpers . http . get ( 'https://www.mergefreeze.com/api/branches/fleetdm/fleet/main' , { access _token : sails . config . custom . mergeFreezeAccessToken } ) //eslint-disable-line camelcase
. tolerate ( [ 'non200Response' , 'requestFailed' , { name : 'TimeoutError' } ] , ( err ) => {
// If the MergeFreeze API returns a non 200 response, log a warning and continue under the assumption that the main branch is not frozen.
sails . log . warn ( 'When sending a request to the MergeFreeze API to get the status of the main branch, MergeFreeze did not respond with a 2xx status code. (Error details forthcoming in just a sec.) First, how to remediate: If the main branch is frozen, it will need to be manually unfrozen before PR #' + prNumber + ' can be merged. Raw underlying error from MergeFreeze: ' + err . stack ) ;
return { frozen : false } ;
} ) ;
2022-07-27 16:21:29 +00:00
sails . log ( '#' + prNumber + ' is under consideration... The MergeFreeze API claims that it current main branch "frozen" status is:' , mergeFreezeMainBranchStatusReport . frozen ) ;
2023-02-18 01:37:58 +00:00
let isMainBranchFrozen = mergeFreezeMainBranchStatusReport . frozen ;
2022-07-27 05:36:31 +00:00
2022-08-02 03:27:54 +00:00
// Add the #handbook label to PRs that only make changes to the handbook.
if ( isHandbookPR ) {
// [?] https://docs.github.com/en/rest/issues/labels#add-labels-to-an-issue
2022-08-02 04:14:56 +00:00
await sails . helpers . http . post ( ` https://api.github.com/repos/ ${ owner } / ${ repo } /issues/ ${ prNumber } /labels ` , {
labels : [ '#handbook' ]
2022-08-02 03:27:54 +00:00
} , baseHeaders ) ;
}
2022-03-26 03:55:32 +00:00
// Now, if appropriate, auto-approve the change.
2022-02-17 09:43:59 +00:00
if ( isAutoApproved ) {
// [?] https://docs.github.com/en/rest/reference/pulls#create-a-review-for-a-pull-request
2022-02-18 22:41:31 +00:00
await sails . helpers . http . post ( ` https://api.github.com/repos/ ${ owner } / ${ repo } /pulls/ ${ prNumber } /reviews ` , {
2022-02-17 09:43:59 +00:00
event : 'APPROVE'
} , baseHeaders ) ;
2022-07-27 05:36:31 +00:00
// 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 ] ) ;
2022-07-27 06:19:47 +00:00
sails . log ( '#' + prNumber + ' autoapproved, main branch is frozen... prNumbers unfrozen:' , sails . pocketOfPrNumbersUnfrozen ) ;
2022-07-27 05:36:31 +00:00
// [?] 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 ) } ` , {
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 ] ) ;
2022-07-27 06:19:47 +00:00
sails . log ( '#' + prNumber + ' not autoapproved, main branch is frozen... prNumbers unfrozen:' , sails . pocketOfPrNumbersUnfrozen ) ;
2022-07-27 05:36:31 +00:00
// [?] See explanation above.
await sails . helpers . http . post ( ` https://www.mergefreeze.com/api/branches/fleetdm/fleet/main?access_token= ${ encodeURIComponent ( sails . config . custom . mergeFreezeAccessToken ) } ` , {
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
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
2022-02-17 09:43:59 +00:00
}
2022-04-16 00:20:13 +00:00
2022-02-17 09:43:59 +00:00
}
2021-11-05 21:13:30 +00:00
} else if ( ghNoun === 'issue_comment' && [ 'created' ] . includes ( action ) && ( issueOrPr && issueOrPr . state === 'open' ) ) {
2021-08-06 07:52:02 +00:00
// ██████╗ ██████╗ ███╗ ███╗███╗ ███╗███████╗███╗ ██╗████████╗
// ██╔════╝██╔═══██╗████╗ ████║████╗ ████║██╔════╝████╗ ██║╚══██╔══╝
// ██║ ██║ ██║██╔████╔██║██╔████╔██║█████╗ ██╔██╗ ██║ ██║
// ██║ ██║ ██║██║╚██╔╝██║██║╚██╔╝██║██╔══╝ ██║╚██╗██║ ██║
// ╚██████╗╚██████╔╝██║ ╚═╝ ██║██║ ╚═╝ ██║███████╗██║ ╚████║ ██║
// ╚═════╝ ╚═════╝ ╚═╝ ╚═╝╚═╝ ╚═╝╚══════╝╚═╝ ╚═══╝ ╚═╝
//
// ██╗ ██████╗ ███╗ ██╗ ██████╗ ██████╗ ███████╗███╗ ██╗ ██████╗ ██████╗ ██████╗ ██████╗ ██╗███████╗███████╗██╗ ██╗███████╗██╗
// ██╔╝██╔═══██╗████╗ ██║ ▄ ██╗▄██╔═══██╗██╔══██╗██╔════╝████╗ ██║▄ ██╗▄ ██╔══██╗██╔══██╗ ██╔═══██╗██╔══██╗ ██║██╔════╝██╔════╝██║ ██║██╔════╝╚██╗
// ██║ ██║ ██║██╔██╗ ██║ ████╗██║ ██║██████╔╝█████╗ ██╔██╗ ██║ ████╗ ██████╔╝██████╔╝ ██║ ██║██████╔╝ ██║███████╗███████╗██║ ██║█████╗ ██║
// ██║ ██║ ██║██║╚██╗██║ ▀╚██╔▀██║ ██║██╔═══╝ ██╔══╝ ██║╚██╗██║▀╚██╔▀ ██╔═══╝ ██╔══██╗ ██║ ██║██╔══██╗ ██║╚════██║╚════██║██║ ██║██╔══╝ ██║
// ╚██╗╚██████╔╝██║ ╚████║ ╚═╝ ╚██████╔╝██║ ███████╗██║ ╚████║ ╚═╝ ██║ ██║ ██║ ╚██████╔╝██║ ██║ ██║███████║███████║╚██████╔╝███████╗██╔╝
// ╚═╝ ╚═════╝ ╚═╝ ╚═══╝ ╚═════╝ ╚═╝ ╚══════╝╚═╝ ╚═══╝ ╚═╝ ╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═╝ ╚═╝╚══════╝╚══════╝ ╚═════╝ ╚══════╝╚═╝
//
// Handle newly-created comment by ungreening its parent issue/PR (if appropriate).
let owner = repository . owner . login ;
let repo = repository . name ;
let issueNumber = issueOrPr . number ;
2022-05-05 15:53:58 +00:00
let wasPostedByBot = GITHUB _USERNAMES _OF _BOTS _AND _MAINTAINERS . includes ( sender . login . toLowerCase ( ) ) ;
2021-08-06 07:52:02 +00:00
if ( ! wasPostedByBot ) {
2021-11-05 21:13:30 +00:00
let greenLabels = _ . filter ( issueOrPr . labels , ( { color } ) => color === GREEN _LABEL _COLOR ) ;
await sails . helpers . flow . simultaneouslyForEach ( greenLabels , async ( greenLabel ) => {
2022-08-22 18:11:54 +00:00
await sails . helpers . http . del ( 'https://api.github.com/repos/' + encodeURIComponent ( owner ) + '/' + encodeURIComponent ( repo ) + '/issues/' + encodeURIComponent ( issueNumber ) + '/labels/' + encodeURIComponent ( greenLabel . name ) ,
2022-08-08 21:58:48 +00:00
{ } ,
{
'User-Agent' : 'Fleetie Pie' ,
'Authorization' : 'token ' + sails . config . custom . githubAccessToken
}
) ;
2021-08-06 07:52:02 +00:00
} ) ; //∞ß
} //fi
} else if (
2023-01-20 21:06:35 +00:00
( ghNoun === 'issue_comment' && [ 'deleted' ] . includes ( action ) && ! GITHUB _USERNAMES _OF _BOTS _AND _MAINTAINERS . includes ( comment . user . login . toLowerCase ( ) ) ) ||
( ghNoun === 'commit_comment' && [ 'created' ] . includes ( action ) && ! GITHUB _USERNAMES _OF _BOTS _AND _MAINTAINERS . includes ( comment . user . login . toLowerCase ( ) ) ) ||
2022-05-05 15:53:58 +00:00
( ghNoun === 'label' && false /* label change notifications temporarily disabled until digital experience team has time to clean up labels. FUTURE: turn this back on after doing that cleanup to facilitate gradual ongoing maintenance and education rather than herculean cleanup efforts and retraining */ && [ 'created' , 'edited' , 'deleted' ] . includes ( action ) && GITHUB _USERNAME _OF _DRI _FOR _LABELS !== sender . login . toLowerCase ( ) ) || //« exempt label changes made by the directly responsible individual for labels, because otherwise when process changes/fiddlings happen, they can otherwise end up making too much noise in Slack
( ghNoun === 'issue_comment' && [ 'created' ] . includes ( action ) && issueOrPr . state !== 'open' && ( issueOrPr . closed _at ) && ( ( new Date ( issueOrPr . closed _at ) ) . getTime ( ) < Date . now ( ) - 7 * 24 * 60 * 60 * 1000 ) && ! GITHUB _USERNAMES _OF _BOTS _AND _MAINTAINERS . includes ( sender . login . toLowerCase ( ) ) )
2021-08-06 07:52:02 +00:00
) {
// ██╗███╗ ██╗███████╗ ██████╗ ██████╗ ███╗ ███╗ ██╗ ██╗███████╗
// ██║████╗ ██║██╔════╝██╔═══██╗██╔══██╗████╗ ████║ ██║ ██║██╔════╝
// ██║██╔██╗ ██║█████╗ ██║ ██║██████╔╝██╔████╔██║ ██║ ██║███████╗
// ██║██║╚██╗██║██╔══╝ ██║ ██║██╔══██╗██║╚██╔╝██║ ██║ ██║╚════██║
// ██║██║ ╚████║██║ ╚██████╔╝██║ ██║██║ ╚═╝ ██║ ╚██████╔╝███████║
// ╚═╝╚═╝ ╚═══╝╚═╝ ╚═════╝ ╚═╝ ╚═╝╚═╝ ╚═╝ ╚═════╝ ╚══════╝
//
// ██╗██╗███╗ ██╗ ███████╗██╗ █████╗ ██████╗██╗ ██╗██╗
// ██╔╝██║████╗ ██║ ██╔════╝██║ ██╔══██╗██╔════╝██║ ██╔╝╚██╗
// ██║ ██║██╔██╗ ██║ ███████╗██║ ███████║██║ █████╔╝ ██║
// ██║ ██║██║╚██╗██║ ╚════██║██║ ██╔══██║██║ ██╔═██╗ ██║
// ╚██╗██║██║ ╚████║ ███████║███████╗██║ ██║╚██████╗██║ ██╗██╔╝
// ╚═╝╚═╝╚═╝ ╚═══╝ ╚══════╝╚══════╝╚═╝ ╚═╝ ╚═════╝╚═╝ ╚═╝╚═╝
//
// Handle deleted issue/PR comments, new/modified/deleted commit comments,
// new/edited/deleted labels, and new comments on closed issues/PRs by
// posting to the Fleet Slack.
// > FUTURE: also post to Slack about deleted issues, new repos, and deleted repos
await sails . helpers . http . post (
2023-01-20 21:06:35 +00:00
sails . config . custom . slackWebhookUrlForGithubBot , //« #g-marketing channel (Fleet Slack workspace)
2021-08-06 07:52:02 +00:00
{
text :
(
( ghNoun === 'issue_comment' && action === 'deleted' ) ?
` @ ${ sender . login } just deleted a GitHub comment that was originally posted at ${ ( new Date ( comment . created _at ) ) . toString ( ) } by @ ${ comment . user . login } in ${ issueOrPr . html _url } . \n \n Formerly, the comment read: \n \` \` \` \n ${ comment . body } \n \` \` \` `
: ( ghNoun === 'commit_comment' ) ?
` @ ${ sender . login } just created a new GitHub commit comment in ${ repository . owner . login } / ${ repository . name } : \n \n > ${ comment . html _url } \n \` \` \` \n ${ comment . body } \n \` \` \` `
: ( ghNoun === 'label' && action === 'edited' ) ?
` @ ${ sender . login } just edited a GitHub label "* ${ label . name } *" (# ${ label . color } ) in ${ repository . owner . login } / ${ repository . name } . \n \n Changes: \n \` \` \` \n ${ Object . keys ( changes ) . length === 0 ? 'GitHub did not report any changes. This usually means the label description was updated (because label descriptions are not available via the GitHub API.)' : require ( 'util' ) . inspect ( changes , { depth : null } )} \n \` \` \` \n \n > To manage labels in ${ repository . owner . login } / ${ repository . name } , visit https://github.com/ ${ encodeURIComponent ( repository . owner . login ) } / ${ encodeURIComponent ( repository . name ) } /labels `
: ( ghNoun === 'label' ) ?
` @ ${ sender . login } just ${ action } a GitHub label "* ${ label . name } *" (# ${ label . color } ) in ${ repository . owner . login } / ${ repository . name } . \n \n > To manage labels in ${ repository . owner . login } / ${ repository . name } , visit https://github.com/ ${ encodeURIComponent ( repository . owner . login ) } / ${ encodeURIComponent ( repository . name ) } /labels `
:
2021-09-21 20:54:34 +00:00
` @ ${ sender . login } just created a zombie comment in a GitHub issue or PR that had already been closed for >7 days ( ${ issueOrPr . html _url } ): \n \n > ${ comment . html _url } \n \` \` \` \n ${ comment . body } \n \` \` \` `
2021-08-06 07:52:02 +00:00
) + ` \n `
} ,
2021-11-05 21:13:30 +00:00
{ 'Content-Type' : 'application/json' }
2021-08-06 07:52:02 +00:00
)
2021-11-05 21:13:30 +00:00
. timeout ( 5000 )
. retry ( [ { name : 'TimeoutError' } , 'non200Response' , 'requestFailed' ] ) ;
2021-08-06 07:52:02 +00:00
} else {
// ███╗ ███╗██╗███████╗ ██████╗
// ████╗ ████║██║██╔════╝██╔════╝
// ██╔████╔██║██║███████╗██║
// ██║╚██╔╝██║██║╚════██║██║
// ██║ ╚═╝ ██║██║███████║╚██████╗
// ╚═╝ ╚═╝╚═╝╚══════╝ ╚═════╝
//
// FUTURE: more potential stuff
//
// For reference: (as of Apr 16, 2019)
// Ping : (no "action" included)
// Issue : opened, edited, deleted, transferred, pinned, unpinned, closed, reopened, assigned, unassigned, labeled, unlabeled, milestoned, demilestoned (https://developer.github.com/v3/activity/events/types/#issuesevent)
// PR : opened, closed, reopened, edited, assigned, unassigned, review requested, review request removed, labeled, unlabeled, synchronized, ready for review (https://developer.github.com/v3/activity/events/types/#pullrequestevent)
// Comment (pr or issue) : created, edited, deleted (https://developer.github.com/v3/activity/events/types/#issuecommentevent)
// Label : created, edited, deleted (https://developer.github.com/v3/activity/events/types/#labelevent)
// Commit comment : created (https://developer.github.com/v3/activity/events/types/#commitcommentevent)
// PR review : submitted, edited, dismissed (https://developer.github.com/v3/activity/events/types/#pullrequestreviewevent
// PR review comment : created, edited, deleted (https://developer.github.com/v3/activity/events/types/#pullrequestreviewcommentevent)
// Branch, tag, or repo : created, deleted (https://developer.github.com/v3/activity/events/types/#createevent -- note thate "ref_type" can be either "tag", "branch", or "repository")
}
}
} ;