diff --git a/__tests__/functions/deployment-confirmation.test.js b/__tests__/functions/deployment-confirmation.test.js index ff32b40f..deaeec5a 100644 --- a/__tests__/functions/deployment-confirmation.test.js +++ b/__tests__/functions/deployment-confirmation.test.js @@ -75,6 +75,7 @@ beforeEach(() => { data = { deployment_confirmation_timeout: 60, deploymentType: 'branch', + deploymentApiType: 'deploy', environment: 'production', environmentUrl: 'https://example.com', log_url: 'https://github.com/corp/test/actions/runs/12345', @@ -141,6 +142,56 @@ test('successfully prompts for deployment confirmation and gets confirmed by the }) }) +test('successfully prompts for destroy confirmation and gets confirmed by the original actor', async () => { + // Mock that the user adds a +1 reaction + octokit.rest.reactions.listForIssueComment.mockResolvedValueOnce({ + data: [ + { + user: {login: 'monalisa'}, + content: '+1' + } + ] + }) + + data.deploymentType = 'destroy' + data.deploymentApiType = 'destroy' + + const result = await deploymentConfirmation(context, octokit, data) + + expect(result).toBe(true) + expect(octokit.rest.issues.createComment).toHaveBeenCalledWith({ + body: expect.stringContaining('Deployment Type: `destroy`'), + issue_number: 1, + owner: 'corp', + repo: 'test', + headers: API_HEADERS + }) + expect(core.debug).toHaveBeenCalledWith( + 'deployment confirmation comment id: 124' + ) + expect(core.info).toHaveBeenCalledWith( + `🕒 waiting ${COLORS.highlight}60${COLORS.reset} seconds for deployment confirmation` + ) + expect(core.info).toHaveBeenCalledWith( + `✅ deployment confirmed by ${COLORS.highlight}monalisa${COLORS.reset} - sha: ${COLORS.highlight}abc123${COLORS.reset}` + ) + + expect(octokit.rest.reactions.listForIssueComment).toHaveBeenCalledWith({ + comment_id: 124, + owner: 'corp', + repo: 'test', + headers: API_HEADERS + }) + + expect(octokit.rest.issues.updateComment).toHaveBeenCalledWith({ + body: expect.stringContaining('✅ Deployment confirmed by __monalisa__'), + comment_id: 124, + owner: 'corp', + repo: 'test', + headers: API_HEADERS + }) +}) + test('successfully prompts for deployment confirmation and gets confirmed by the original actor with some null data params in the issue comment', async () => { data.params = null data.parsed_params = null diff --git a/__tests__/functions/environment-targets.test.js b/__tests__/functions/environment-targets.test.js index ca4748cf..c600ec51 100644 --- a/__tests__/functions/environment-targets.test.js +++ b/__tests__/functions/environment-targets.test.js @@ -24,6 +24,7 @@ const environment = 'production' const body = '.deploy' const trigger = '.deploy' const noop_trigger = '.noop' +const destroy_trigger = '.destroy' const stable_branch = 'main' const environmentUrls = 'production|https://example.com,development|https://dev.example.com,staging|http://staging.example.com' @@ -35,6 +36,7 @@ test('checks the comment body and does not find an explicit environment target', body, trigger, noop_trigger, + destroy_trigger, stable_branch ) ).toStrictEqual({ @@ -43,6 +45,7 @@ test('checks the comment body and does not find an explicit environment target', environmentObj: { target: 'production', noop: false, + destroy: false, stable_branch_used: false, params: null, parsed_params: null, @@ -61,6 +64,7 @@ test('checks the comment body and finds an explicit environment target for devel '.deploy development', trigger, noop_trigger, + destroy_trigger, stable_branch ) ).toStrictEqual({ @@ -69,6 +73,7 @@ test('checks the comment body and finds an explicit environment target for devel environmentObj: { target: 'development', noop: false, + destroy: false, stable_branch_used: false, params: null, parsed_params: null, @@ -87,6 +92,7 @@ test('checks the comment body and finds an explicit environment target for devel '.deploy development | something1 something2 something3', trigger, noop_trigger, + destroy_trigger, stable_branch ) ).toStrictEqual({ @@ -95,6 +101,7 @@ test('checks the comment body and finds an explicit environment target for devel environmentObj: { target: 'development', noop: false, + destroy: false, stable_branch_used: false, params: 'something1 something2 something3', parsed_params: {_: ['something1', 'something2', 'something3']}, @@ -120,6 +127,7 @@ test('checks the comment body and finds an explicit environment target and an ex '.deploy 82c238c277ca3df56fe9418a5913d9188eafe3bc development | something1 something2 something3', trigger, noop_trigger, + destroy_trigger, stable_branch ) ).toStrictEqual({ @@ -128,6 +136,7 @@ test('checks the comment body and finds an explicit environment target and an ex environmentObj: { target: 'development', noop: false, + destroy: false, stable_branch_used: false, params: 'something1 something2 something3', parsed_params: {_: ['something1', 'something2', 'something3']}, @@ -153,6 +162,7 @@ test('checks the comment body and finds an explicit environment target and an ex '.noop 82c238c277ca3df56fe9418a5913d9188eafe3bc development | something1 something2 something3', trigger, noop_trigger, + destroy_trigger, stable_branch ) ).toStrictEqual({ @@ -161,6 +171,7 @@ test('checks the comment body and finds an explicit environment target and an ex environmentObj: { target: 'development', noop: true, + destroy: false, stable_branch_used: false, params: 'something1 something2 something3', parsed_params: {_: ['something1', 'something2', 'something3']}, @@ -186,6 +197,7 @@ test('checks the comment body and finds an explicit environment target and an ex '.noop 82c238c277ca3df56fe9418a5913d9188eafe3bc development | --cpu=2 --memory=4G --env=development --port=8080 --name=my-app -q my-queue', trigger, noop_trigger, + destroy_trigger, stable_branch ) ).toStrictEqual({ @@ -194,6 +206,7 @@ test('checks the comment body and finds an explicit environment target and an ex environmentObj: { target: 'development', noop: true, + destroy: false, stable_branch_used: false, params: '--cpu=2 --memory=4G --env=development --port=8080 --name=my-app -q my-queue', @@ -228,6 +241,7 @@ test('checks the comment body and finds an explicit environment target and an ex '.noop f0e4c2f76c58916ec258f246851bea091d14d4247a2fc3e18694461b1816e13b development | something1 something2 something3', trigger, noop_trigger, + destroy_trigger, stable_branch ) ).toStrictEqual({ @@ -236,6 +250,7 @@ test('checks the comment body and finds an explicit environment target and an ex environmentObj: { target: 'development', noop: true, + destroy: false, stable_branch_used: false, params: 'something1 something2 something3', parsed_params: {_: ['something1', 'something2', 'something3']}, @@ -261,6 +276,7 @@ test('checks the comment body and finds an explicit environment target and an ex '.noop 82c238c277ca3df56fe9418a5913d9188eafe3bc ', trigger, noop_trigger, + destroy_trigger, stable_branch ) ).toStrictEqual({ @@ -269,6 +285,7 @@ test('checks the comment body and finds an explicit environment target and an ex environmentObj: { target: 'production', noop: true, + destroy: false, stable_branch_used: false, params: null, parsed_params: null, @@ -289,6 +306,7 @@ test('checks the comment body and finds an explicit environment target for devel '.deploy main development + something1 | something2 something3', trigger, noop_trigger, + destroy_trigger, stable_branch, null, null, @@ -303,6 +321,7 @@ test('checks the comment body and finds an explicit environment target for devel environmentObj: { target: 'development', noop: false, + destroy: false, stable_branch_used: true, params: 'something1 | something2 something3', parsed_params: {_: ['something1', '|', 'something2', 'something3']}, @@ -328,6 +347,7 @@ test('checks the comment body and finds an explicit environment target for stagi '.noop staging', trigger, noop_trigger, + destroy_trigger, stable_branch ) ).toStrictEqual({ @@ -336,6 +356,7 @@ test('checks the comment body and finds an explicit environment target for stagi environmentObj: { target: 'staging', noop: true, + destroy: false, stable_branch_used: false, params: null, parsed_params: null, @@ -354,6 +375,7 @@ test('checks the comment body and finds an explicit environment target for stagi '.noop main staging', trigger, noop_trigger, + destroy_trigger, stable_branch ) ).toStrictEqual({ @@ -362,6 +384,7 @@ test('checks the comment body and finds an explicit environment target for stagi environmentObj: { target: 'staging', noop: true, + destroy: false, stable_branch_used: true, params: null, parsed_params: null, @@ -380,6 +403,7 @@ test('checks the comment body and finds an explicit environment target for stagi '.noop staging', trigger, noop_trigger, + destroy_trigger, stable_branch, null, null, @@ -393,6 +417,7 @@ test('checks the comment body and finds an explicit environment target for stagi environmentObj: { target: 'staging', noop: true, + destroy: false, stable_branch_used: false, params: null, parsed_params: null, @@ -424,6 +449,7 @@ test('checks the comment body and finds an explicit environment target for stagi '.noop main to staging | something1 something2 something3', trigger, noop_trigger, + destroy_trigger, stable_branch, null, null, @@ -437,6 +463,7 @@ test('checks the comment body and finds an explicit environment target for stagi environmentObj: { target: 'staging', noop: true, + destroy: false, stable_branch_used: true, params: 'something1 something2 something3', parsed_params: {_: ['something1', 'something2', 'something3']}, @@ -473,6 +500,7 @@ test('checks the comment body and uses the default production environment target '.deploy', trigger, noop_trigger, + destroy_trigger, stable_branch, null, null, @@ -486,6 +514,7 @@ test('checks the comment body and uses the default production environment target environmentObj: { target: 'production', noop: false, + destroy: false, stable_branch_used: false, params: null, parsed_params: null, @@ -515,6 +544,7 @@ test('checks the comment body and finds an explicit environment target for a pro '.deploy production', trigger, noop_trigger, + destroy_trigger, stable_branch, null, null, @@ -528,6 +558,7 @@ test('checks the comment body and finds an explicit environment target for a pro environmentObj: { target: 'production', noop: false, + destroy: false, params: null, parsed_params: null, stable_branch_used: false, @@ -551,6 +582,7 @@ test('checks the comment body and finds an explicit environment target for a pro '.deploy production', trigger, noop_trigger, + destroy_trigger, stable_branch, null, null, @@ -565,6 +597,7 @@ test('checks the comment body and finds an explicit environment target for a pro target: 'production', stable_branch_used: false, noop: false, + destroy: false, params: null, parsed_params: null, sha: null @@ -590,6 +623,7 @@ test('checks the comment body and finds an explicit environment target for a pro '.deploy production', trigger, noop_trigger, + destroy_trigger, stable_branch, null, null, @@ -604,6 +638,7 @@ test('checks the comment body and finds an explicit environment target for a pro target: 'production', stable_branch_used: false, noop: false, + destroy: false, params: null, parsed_params: null, sha: null @@ -626,6 +661,7 @@ test('checks the comment body and finds an explicit environment target for stagi '.noop to staging', trigger, noop_trigger, + destroy_trigger, stable_branch ) ).toStrictEqual({ @@ -635,6 +671,7 @@ test('checks the comment body and finds an explicit environment target for stagi target: 'staging', stable_branch_used: false, noop: true, + destroy: false, params: null, parsed_params: null, sha: null @@ -652,6 +689,7 @@ test('checks the comment body and finds a noop deploy to the stable branch and d '.noop main', trigger, noop_trigger, + destroy_trigger, stable_branch ) ).toStrictEqual({ @@ -661,6 +699,7 @@ test('checks the comment body and finds a noop deploy to the stable branch and d target: 'production', stable_branch_used: true, noop: true, + destroy: false, params: null, parsed_params: null, sha: null @@ -678,6 +717,7 @@ test('checks the comment body and finds a noop deploy to the stable branch and d '.noop main | foo=bar', trigger, noop_trigger, + destroy_trigger, stable_branch ) ).toStrictEqual({ @@ -687,6 +727,7 @@ test('checks the comment body and finds a noop deploy to the stable branch and d target: 'production', stable_branch_used: true, noop: true, + destroy: false, params: 'foo=bar', parsed_params: {_: ['foo=bar']}, sha: null @@ -704,6 +745,7 @@ test('checks the comment body and finds an explicit environment target for produ '.deploy to production', trigger, noop_trigger, + destroy_trigger, stable_branch ) ).toStrictEqual({ @@ -713,6 +755,7 @@ test('checks the comment body and finds an explicit environment target for produ target: 'production', stable_branch_used: false, noop: false, + destroy: false, params: null, parsed_params: null, sha: null @@ -730,6 +773,7 @@ test('checks the comment body on a noop deploy and does not find an explicit env '.noop', // comment body trigger, noop_trigger, + destroy_trigger, stable_branch ) ).toStrictEqual({ @@ -739,6 +783,7 @@ test('checks the comment body on a noop deploy and does not find an explicit env target: 'production', stable_branch_used: false, noop: true, + destroy: false, params: null, parsed_params: null, sha: null @@ -756,6 +801,7 @@ test('checks the comment body on a deployment and does not find any matching env '.deploy to chaos', trigger, noop_trigger, + destroy_trigger, stable_branch ) ).toStrictEqual({ @@ -763,6 +809,7 @@ test('checks the comment body on a deployment and does not find any matching env environmentUrl: null, environmentObj: { noop: null, + destroy: null, params: null, parsed_params: null, stable_branch_used: null, @@ -788,6 +835,7 @@ test('checks the comment body on a stable branch deployment and finds a matching '.deploy main to production', trigger, noop_trigger, + destroy_trigger, stable_branch ) ).toStrictEqual({ @@ -797,6 +845,7 @@ test('checks the comment body on a stable branch deployment and finds a matching target: 'production', stable_branch_used: true, noop: false, + destroy: false, params: null, parsed_params: null, sha: null @@ -814,6 +863,7 @@ test('checks the comment body on a stable branch deployment and finds a matching '.deploy main production', trigger, noop_trigger, + destroy_trigger, stable_branch ) ).toStrictEqual({ @@ -823,6 +873,7 @@ test('checks the comment body on a stable branch deployment and finds a matching target: 'production', stable_branch_used: true, noop: false, + destroy: false, params: null, parsed_params: null, sha: null @@ -840,6 +891,7 @@ test('checks the comment body on a stable branch deployment and uses the default '.deploy main', trigger, noop_trigger, + destroy_trigger, stable_branch ) ).toStrictEqual({ @@ -849,6 +901,7 @@ test('checks the comment body on a stable branch deployment and uses the default target: 'production', stable_branch_used: true, noop: false, + destroy: false, params: null, parsed_params: null, sha: null @@ -866,6 +919,7 @@ test('checks the comment body on a stable branch deployment and does not find a '.deploy main chaos', trigger, noop_trigger, + destroy_trigger, stable_branch ) ).toStrictEqual({ @@ -873,6 +927,7 @@ test('checks the comment body on a stable branch deployment and does not find a environmentUrl: null, environmentObj: { noop: null, + destroy: null, params: null, parsed_params: null, stable_branch_used: null, @@ -898,6 +953,7 @@ test('checks the comment body on a lock request and uses the default environment '.lock', // comment body '.lock', // lock trigger '.unlock', // unlock trigger + '.destroy', // destroy trigger null, // stable_branch not used for lock/unlock requests null, // context null, // octokit @@ -917,6 +973,7 @@ test('checks the comment body on a lock request with a reason and uses the defau '.lock --reason making a small change to our api because reasons', // comment body '.lock', // lock trigger '.unlock', // unlock trigger + '.destroy', // destroy trigger null, // stable_branch not used for lock/unlock requests null, // context null, // octokit @@ -936,6 +993,7 @@ test('checks the comment body on a lock request with a reason and uses the expli '.lock production --reason small change to mappings for risk rating - - 92*91-2408| ', // comment body '.lock', // lock trigger '.unlock', // unlock trigger + '.destroy', // destroy trigger null, // stable_branch not used for lock/unlock requests null, // context null, // octokit @@ -955,6 +1013,7 @@ test('checks the comment body on an unlock request and uses the default environm '.unlock', // comment body '.lock', // lock trigger '.unlock', // unlock trigger + '.destroy', // destroy trigger null, // stable_branch not used for lock/unlock requests null, // context null, // octokit @@ -974,6 +1033,7 @@ test('checks the comment body on an unlock request and uses the default environm '.unlock --reason oh wait this command does not need a reason.. oops', // comment body '.lock', // lock trigger '.unlock', // unlock trigger + '.destroy', // destroy trigger null, // stable_branch not used for lock/unlock requests null, // context null, // octokit @@ -993,6 +1053,7 @@ test('checks the comment body on an unlock request and uses the development envi '.unlock development --reason oh wait this command does not need a reason.. oops', // comment body '.lock', // lock trigger '.unlock', // unlock trigger + '.destroy', // destroy trigger null, // stable_branch not used for lock/unlock requests null, // context null, // octokit @@ -1012,6 +1073,7 @@ test('checks the comment body on a lock info alias request and uses the default '.wcid', // comment body '.lock', // lock trigger '.unlock', // unlock trigger + '.destroy', // destroy trigger null, // stable_branch not used for lock/unlock requests null, // context null, // octokit @@ -1031,6 +1093,7 @@ test('checks the comment body on a lock request and uses the production environm '.lock production', // comment body '.lock', // lock trigger '.unlock', // unlock trigger + '.destroy', // destroy trigger null, // stable_branch not used for lock/unlock requests null, // context null, // octokit @@ -1050,6 +1113,7 @@ test('checks the comment body on an unlock request and uses the development envi '.unlock development', // comment body '.lock', // lock trigger '.unlock', // unlock trigger + '.destroy', // destroy trigger null, // stable_branch not used for lock/unlock requests null, // context null, // octokit @@ -1069,6 +1133,7 @@ test('checks the comment body on a lock info alias request and uses the developm '.wcid development', // comment body '.lock', // lock trigger '.unlock', // unlock trigger + '.destroy', // destroy trigger null, // stable_branch not used for lock/unlock requests null, // context null, // octokit @@ -1088,6 +1153,7 @@ test('checks the comment body on a lock info request and uses the development en '.lock --info development', // comment body '.lock', // lock trigger '.unlock', // unlock trigger + '.destroy', // destroy trigger null, // stable_branch not used for lock/unlock requests null, // context null, // octokit @@ -1107,6 +1173,7 @@ test('checks the comment body on a lock info request and uses the development en '.lock -d development', // comment body '.lock', // lock trigger '.unlock', // unlock trigger + '.destroy', // destroy trigger null, // stable_branch not used for lock/unlock requests null, // context null, // octokit diff --git a/__tests__/main.test.js b/__tests__/main.test.js index 5bf83e77..12f3b02b 100644 --- a/__tests__/main.test.js +++ b/__tests__/main.test.js @@ -71,6 +71,7 @@ beforeEach(() => { process.env.INPUT_PRODUCTION_ENVIRONMENTS = 'production' process.env.INPUT_STABLE_BRANCH = 'main' process.env.INPUT_NOOP_TRIGGER = '.noop' + process.env.INPUT_DESTROY_TRIGGER = '.destroy' process.env.INPUT_LOCK_TRIGGER = '.lock' process.env.INPUT_UNLOCK_TRIGGER = '.unlock' process.env.INPUT_HELP_TRIGGER = '.help' diff --git a/badges/coverage.svg b/badges/coverage.svg index 1b3eb315..f20a65f6 100644 --- a/badges/coverage.svg +++ b/badges/coverage.svg @@ -1 +1 @@ -Coverage: 100%Coverage100% \ No newline at end of file +Coverage: 95.47%Coverage95.47% \ No newline at end of file diff --git a/src/functions/deployment-confirmation.js b/src/functions/deployment-confirmation.js index f31f2e2c..41fa6f95 100644 --- a/src/functions/deployment-confirmation.js +++ b/src/functions/deployment-confirmation.js @@ -37,6 +37,7 @@ export async function deploymentConfirmation(context, octokit, data) { "url": ${data.environmentUrl ? `"${data.environmentUrl}"` : null} }, "deployment": { + "type": "${data.deploymentApiType}", "logs": "${data.log_url}" }, "git": { diff --git a/src/functions/environment-targets.js b/src/functions/environment-targets.js index c276d45b..7dd17d85 100644 --- a/src/functions/environment-targets.js +++ b/src/functions/environment-targets.js @@ -11,6 +11,7 @@ import {parseParams} from './params' // :param body: The body of the comment // :param trigger: The trigger used to initiate the deployment // :param noop_trigger: The trigger used to initiate a noop deployment +// :param destroy_trigger: The trigger used to initiate a destroy deployment // :param stable_branch: The stable branch // :param environment: The default environment // :param param_separator: The separator used to seperate the command from the parameters @@ -20,6 +21,7 @@ async function onDeploymentChecks( body, trigger, noop_trigger, + destroy_trigger, stable_branch, environment, param_separator @@ -71,9 +73,18 @@ async function onDeploymentChecks( `${escapedNoopTrigger}\\s+((?![a-f0-9]{40}[a-f0-9]{24})[a-f0-9]{40}|[a-f0-9]{64})`, 'i' ) + const escapedDestroyTrigger = destroy_trigger.replace( + /[-[\]/{}()*+?.\\^$|]/g, + '\\$&' + ) + const destroyRegex = new RegExp( + `${escapedDestroyTrigger}\\s+((?![a-f0-9]{40}[a-f0-9]{24})[a-f0-9]{40}|[a-f0-9]{64})`, + 'i' + ) const match = bodyFmt.trim().match(regex) const noopMatch = bodyFmt.trim().match(noopRegex) + const destroyMatch = bodyFmt.trim().match(destroyRegex) if (match) { sha = match[1] // The captured SHA value // if a sha was used, then we need to remove it from the body for env checks @@ -88,6 +99,15 @@ async function onDeploymentChecks( core.info( `📍 detected SHA in noop command: ${COLORS.highlight}${sha}${COLORS.reset}` ) + } else if (destroyMatch) { + sha = destroyMatch[1] // The captured SHA value + // if a sha was used, then we need to remove it from the body for env checks + bodyFmt = bodyFmt.replace(new RegExp(`\\s*${sha}\\s*`, 'g'), '').trim() + core.info( + `📍 detected SHA in destroy command: ${COLORS.highlight}${sha}${COLORS.reset}` + ) + } else { + core.debug('no SHA detected in command') } // Loop through all the environment targets to see if an explicit target is being used @@ -99,6 +119,7 @@ async function onDeploymentChecks( target: target, stable_branch_used: false, noop: false, + destroy: false, params: paramsTrim, parsed_params: parsed_params, sha: sha @@ -111,6 +132,20 @@ async function onDeploymentChecks( target: target, stable_branch_used: false, noop: true, + destroy: false, + params: paramsTrim, + parsed_params: parsed_params, + sha: sha + } + } + // If the body on a destroy trigger contains the target + else if (bodyFmt.replace(destroy_trigger, '').trim() === target) { + core.debug(`found environment target for destroy trigger: ${target}`) + return { + target: target, + stable_branch_used: false, + noop: false, + destroy: true, params: paramsTrim, parsed_params: parsed_params, sha: sha @@ -125,6 +160,7 @@ async function onDeploymentChecks( target: target, stable_branch_used: false, noop: false, + destroy: false, params: paramsTrim, parsed_params: parsed_params, sha: sha @@ -139,6 +175,22 @@ async function onDeploymentChecks( target: target, stable_branch_used: false, noop: true, + destroy: false, + params: paramsTrim, + parsed_params: parsed_params, + sha: sha + } + } + // If the body with 'to ' contains the target on a destroy trigger + else if (bodyFmt.replace(destroy_trigger, '').trim() === `to ${target}`) { + core.debug( + `found environment target for destroy trigger (with 'to'): ${target}` + ) + return { + target: target, + stable_branch_used: false, + noop: false, + destroy: true, params: paramsTrim, parsed_params: parsed_params, sha: sha @@ -156,6 +208,7 @@ async function onDeploymentChecks( target: target, stable_branch_used: true, noop: false, + destroy: false, params: paramsTrim, parsed_params: parsed_params, sha: sha @@ -173,6 +226,26 @@ async function onDeploymentChecks( target: target, stable_branch_used: true, noop: true, + destroy: false, + params: paramsTrim, + parsed_params: parsed_params, + sha: sha + } + } + // If the body with 'to ' contains the target on a stable branch destroy trigger + else if ( + bodyFmt.replace(`${destroy_trigger} ${stable_branch}`, '').trim() === + `to ${target}` + ) { + core.debug( + `found environment target for stable branch destroy trigger (with 'to'): ${target}` + ) + core.info(`🤠 Whoa partner! A ${COLORS.highlight}destroy${COLORS.reset} command to the ${COLORS.highlight}${stable_branch}${COLORS.reset} branch?! That takes some confidence. You are BOLD.`) + return { + target: target, + stable_branch_used: true, + noop: false, + destroy: true, params: paramsTrim, parsed_params: parsed_params, sha: sha @@ -187,6 +260,7 @@ async function onDeploymentChecks( target: target, stable_branch_used: true, noop: false, + destroy: false, params: paramsTrim, parsed_params: parsed_params, sha: sha @@ -203,6 +277,26 @@ async function onDeploymentChecks( target: target, stable_branch_used: true, noop: true, + destroy: false, + params: paramsTrim, + parsed_params: parsed_params, + sha: sha + } + } + // If the body on a stable branch destroy trigger contains the target + else if ( + bodyFmt.replace(`${destroy_trigger} ${stable_branch}`, '').trim() === + target + ) { + core.debug( + `found environment target for stable branch destroy trigger: ${target}` + ) + core.info(`🤠 Whoa partner! A ${COLORS.highlight}destroy${COLORS.reset} command to the ${COLORS.highlight}${stable_branch}${COLORS.reset} branch?! That takes some confidence. You are BOLD.`) + return { + target: target, + stable_branch_used: true, + noop: false, + destroy: true, params: paramsTrim, parsed_params: parsed_params, sha: sha @@ -215,6 +309,7 @@ async function onDeploymentChecks( target: environment, stable_branch_used: false, noop: false, + destroy: false, params: paramsTrim, parsed_params: parsed_params, sha: sha @@ -227,6 +322,20 @@ async function onDeploymentChecks( target: environment, stable_branch_used: false, noop: true, + destroy: false, + params: paramsTrim, + parsed_params: parsed_params, + sha: sha + } + } + // If the body matches the destroy_trigger phrase exactly, just use the default environment + else if (bodyFmt.trim() === destroy_trigger) { + core.debug('using default environment for destroy trigger') + return { + target: environment, + stable_branch_used: false, + noop: false, + destroy: true, params: paramsTrim, parsed_params: parsed_params, sha: sha @@ -239,6 +348,7 @@ async function onDeploymentChecks( target: environment, stable_branch_used: true, noop: false, + destroy: false, params: paramsTrim, parsed_params: parsed_params, sha: sha @@ -251,6 +361,21 @@ async function onDeploymentChecks( target: environment, stable_branch_used: true, noop: true, + destroy: false, + params: paramsTrim, + parsed_params: parsed_params, + sha: sha + } + } + // If the body matches the stable branch phrase exactly on a destroy trigger, just use the default environment + else if (bodyFmt.trim() === `${destroy_trigger} ${stable_branch}`) { + core.debug('using default environment for stable branch destroy trigger') + core.info(`🤠 Whoa partner! A ${COLORS.highlight}destroy${COLORS.reset} command to the ${COLORS.highlight}${stable_branch}${COLORS.reset} branch?! That takes some confidence. You are BOLD.`) + return { + target: environment, + stable_branch_used: true, + noop: false, + destroy: true, params: paramsTrim, parsed_params: parsed_params, sha: sha @@ -263,6 +388,7 @@ async function onDeploymentChecks( target: false, stable_branch_used: null, noop: null, + destroy: null, params: null, parsed_params: null, sha: null @@ -274,6 +400,7 @@ async function onDeploymentChecks( // :param body: The body of the comment // :param lock_trigger: The trigger used to initiate the lock command // :param unlock_trigger: The trigger used to initiate the unlock command +// :param destroy_trigger: The trigger used to initiate the destroy command // :param environment: The default environment from the Actions inputs // :returns: The environment target if found, false otherwise async function onLockChecks( @@ -281,6 +408,7 @@ async function onLockChecks( body, lock_trigger, unlock_trigger, + destroy_trigger, environment ) { // if the body contains the globalFlag, exit right away as environments are not relevant @@ -319,6 +447,12 @@ async function onLockChecks( return environment } + // if the body matches the destroy trigger exactly, just use the default environment + if (body.trim() === destroy_trigger.trim()) { + core.debug('using default environment for destroy request') + return environment + } + // if the body matches the lock info alias exactly, just use the default environment if (body.trim() === lockInfoAlias.trim()) { core.debug('using default environment for lock info request') @@ -334,6 +468,9 @@ async function onLockChecks( } else if (body.replace(unlock_trigger, '').trim() === target) { core.debug(`found environment target for unlock request: ${target}`) return target + } else if (body.replace(destroy_trigger, '').trim() === target) { + core.debug(`found environment target for destroy request: ${target}`) + return target } else if (body.replace(lockInfoAlias, '').trim() === target) { core.debug(`found environment target for lock info request: ${target}`) return target @@ -406,6 +543,7 @@ async function findEnvironmentUrl(environment, environment_urls) { // :param body: The comment body // :param trigger: The trigger prefix // :param alt_trigger: Usually the noop trigger prefix +// :param destroy_trigger: The destroy trigger prefix // :param stable_branch: The stable branch (only used for branch deploys) // :param context: The context of the Action // :param octokit: The Octokit instance @@ -419,6 +557,7 @@ export async function environmentTargets( body, trigger, alt_trigger, + destroy_trigger, stable_branch, context, octokit, @@ -445,6 +584,7 @@ export async function environmentTargets( body, trigger, alt_trigger, + destroy_trigger, environment ) if (environmentDetected !== false) { @@ -476,6 +616,7 @@ export async function environmentTargets( body, trigger, alt_trigger, + destroy_trigger, stable_branch, environment, param_separator diff --git a/src/functions/inputs.js b/src/functions/inputs.js index 1e8835d7..4022aea6 100644 --- a/src/functions/inputs.js +++ b/src/functions/inputs.js @@ -35,6 +35,7 @@ export function getInputs() { const stable_branch = core.getInput('stable_branch') const noop_trigger = core.getInput('noop_trigger') const lock_trigger = core.getInput('lock_trigger') + const destroy_trigger = core.getInput('destroy_trigger') const production_environments = stringToArray( core.getInput('production_environments') ) @@ -99,6 +100,7 @@ export function getInputs() { stable_branch: stable_branch, noop_trigger: noop_trigger, lock_trigger: lock_trigger, + destroy_trigger: destroy_trigger, production_environments: production_environments, environment_targets: environment_targets, unlock_trigger: unlock_trigger, diff --git a/src/main.js b/src/main.js index 6dc38e25..5db812bf 100644 --- a/src/main.js +++ b/src/main.js @@ -98,6 +98,7 @@ export async function run() { [ inputs.trigger, inputs.noop_trigger, + inputs.destroy_trigger, inputs.lock_trigger, inputs.unlock_trigger, inputs.lock_info_alias @@ -120,13 +121,16 @@ export async function run() { // check if the comment is a trigger and what type of trigger it is const isDeploy = await triggerCheck(body, inputs.trigger) + const isDestroy = await triggerCheck(body, inputs.destroy_trigger) const isNoopDeploy = await triggerCheck(body, inputs.noop_trigger) const isLock = await triggerCheck(body, inputs.lock_trigger) const isUnlock = await triggerCheck(body, inputs.unlock_trigger) const isHelp = await triggerCheck(body, inputs.help_trigger) const isLockInfoAlias = await triggerCheck(body, inputs.lock_info_alias) - if (isDeploy || isNoopDeploy) { + if (isDeploy || isNoopDeploy || isDestroy) { + // deployments, noops, and destroy commands are all treated as "deployment events" + // yes, even destroy commands because they are the same as a .deploy command but with one extra output set core.setOutput('type', 'deploy') } else if (isLock) { core.setOutput('type', 'lock') @@ -144,6 +148,12 @@ export async function run() { return 'safe-exit' } + if (isDestroy) { + core.info(`🗑️ destroy command detected`) + core.setOutput('destroy', 'true') + core.saveState('destroy', 'true') + } + // If we made it this far, the action has been triggered in one manner or another core.setOutput('triggered', 'true') @@ -390,6 +400,7 @@ export async function run() { body, // comment body inputs.trigger, // trigger inputs.noop_trigger, // noop trigger + inputs.destroy_trigger, // destroy trigger inputs.stable_branch, // ref context, // context object octokit, // octokit object @@ -607,12 +618,18 @@ export async function run() { // Add a comment to the PR letting the user know that a deployment has been started // Format the success message + var deploymentApiType var deploymentType if (precheckResults.noopMode) { deploymentType = 'noop' + deploymentApiType = 'noop' + } else if (isDestroy) { + deploymentType = 'destroy' + deploymentApiType = 'destroy' } else { deploymentType = environmentObj.environmentObj.sha !== null ? 'sha' : 'branch' + deploymentApiType = 'deploy' } const log_url = `${process.env.GITHUB_SERVER_URL}/${context.repo.owner}/${context.repo.repo}/actions/runs/${github_run_id}` @@ -625,6 +642,7 @@ export async function run() { sha: precheckResults.sha, ref: precheckResults.ref, deploymentType: deploymentType, + deploymentApiType: deploymentApiType, environment: environment, environmentUrl: environmentObj.environmentUrl, deployment_confirmation_timeout: @@ -680,6 +698,7 @@ export async function run() { "url": ${environmentObj.environmentUrl ? `"${environmentObj.environmentUrl}"` : null} }, "deployment": { + "type": "${deploymentApiType}", "timestamp": "${deployment_start_time}", "logs": "${log_url}" }, @@ -776,6 +795,7 @@ export async function run() { // Construct the deployment payload that will be sent to the GitHub API during the deployment creation const payload = { type: 'branch-deploy', + deployment_type: deploymentApiType, sha: precheckResults.sha, params: params, parsed_params: parsed_params, @@ -857,6 +877,8 @@ export async function run() { core.saveState('bypass', 'true') core.error(error.stack) core.setFailed(error.message) + console.log(error.message) + console.log(error.stack) } }