You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@airflow.apache.org by po...@apache.org on 2020/12/27 13:52:57 UTC

[airflow-cancel-workflow-runs] 41/44: Adds self-preservation mechanism. (#12)

This is an automated email from the ASF dual-hosted git repository.

potiuk pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/airflow-cancel-workflow-runs.git

commit f06d03cd576a179ea5169d048dbd8c8d73757b52
Author: Jarek Potiuk <ja...@potiuk.com>
AuthorDate: Sun Nov 1 17:41:54 2020 +0100

    Adds self-preservation mechanism. (#12)
    
    This mechanism is useful in some edge-cases, especially when you
    re-run a build and you base the cancelling on named jobs, the
    jobs might get the name in the previous run and when re-running,
    the action might cancel its own run.
---
 README.md     |  8 ++++++++
 action.yml    |  5 +++++
 dist/index.js | 27 ++++++++++++++++++---------
 src/main.ts   | 34 +++++++++++++++++++++++++++-------
 4 files changed, 58 insertions(+), 16 deletions(-)

diff --git a/README.md b/README.md
index b212dd9..5f98862 100644
--- a/README.md
+++ b/README.md
@@ -115,6 +115,7 @@ If you want a comprehensive solution, you should use the action as follows:
 | `notifyPRMessageStart`   | no       |              | Only for workflow_run events triggered by the PRs. If not empty, it notifies those PRs with the message specified at the start of the workflow - adding the link to the triggered workflow_run.                  |
 | `jobNameRegexps`         | no       |              | An array of job name regexps. Only runs containing any job name matching any of of the regexp in this array are considered for cancelling in `failedJobs` and `namedJobs` and `allDuplicateNamedJobs` modes.     |
 | `skipEventTypes`         | no       |              | Array of event names that should be skipped when cancelling (JSON-encoded string). This might be used in order to skip direct pushes or scheduled events.                                                        |
+| `selfPreservation`       | no       | true         | Do not cancel self.                                                                                                                                                                                              |
 | `workflowFileName`       | no       |              | Name of the workflow file. It can be used if you want to cancel a different workflow than yours.                                                                                                                 |
 
 
@@ -566,6 +567,11 @@ match.
 Note that the match must be identical. If there are two jobs that have a different Branch
 they will both match the same pattern, but they are not considered duplicates.
 
+Also, this is one of the jobs It has also self-preservation turned off.
+This means that in case the job determines that it is itself a duplicate it will cancel itself. That's
+why checking for duplicates of self-workflow should be the last step in the cancelling process.
+
+
 ```yaml
 on:
   push:
@@ -584,7 +590,9 @@ jobs:
           cancelMode: allDuplicatedNamedJobs
           token: ${{ secrets.GITHUB_TOKEN }}
           jobNameRegexps: '["Branch: .* Repo: .* Event: .* "]'
+          selfPreservation: false
           notifyPRCancel: true
+
 ```
 
 
diff --git a/action.yml b/action.yml
index 541094a..f678dc6 100644
--- a/action.yml
+++ b/action.yml
@@ -46,6 +46,11 @@ inputs:
       In case of duplicate canceling, cancel also future duplicates leaving only the "freshest" running
       job and not all the future jobs. By default it is set to true.
     required: false
+  selfPreservation:
+    description: |
+      Do not cancel your own run. There are cases where selfPreservation should be disabled but it is
+      enabled by default. You can disable it by setting 'false' as value.
+    required: false
   jobNameRegexps:
     description: |
       Array of job name regexps (JSON-encoded string). Used by `failedJobs` and `namedJobs` cancel modes
diff --git a/dist/index.js b/dist/index.js
index 2a806db..09fdb5d 100644
--- a/dist/index.js
+++ b/dist/index.js
@@ -2013,17 +2013,23 @@ function findPullRequestForRunItem(repositoryInfo, runItem) {
  * @param cancelFutureDuplicates - whether to cancel future duplicates
  * @param jobNameRegexps - regexps for job names
  * @param skipEventTypes - array of event names to skip
+ * @param selfPreservation - whether the run will cancel itself if requested
+ * @param selfRunId - my own run id
  * @param workflowRuns - map of workflow runs found
  * @parm maps with string key and array of run items as value. The key might be
  *       * source group id (allDuplicates mode)
  *       * matching job name (allDuplicatedMatchingJobNames mode)
  */
-function filterAndMapWorkflowRunsToGroups(repositoryInfo, triggeringRunInfo, cancelMode, cancelFutureDuplicates, jobNameRegexps, skipEventTypes, workflowRuns) {
+function filterAndMapWorkflowRunsToGroups(repositoryInfo, triggeringRunInfo, cancelMode, cancelFutureDuplicates, jobNameRegexps, skipEventTypes, selfRunId, selfPreservation, workflowRuns) {
     return __awaiter(this, void 0, void 0, function* () {
         const mapOfWorkflowRunCandidates = new Map();
         for (const [key, runItem] of workflowRuns) {
             core.info(`\nChecking run number: ${key} RunId: ${runItem.id} Url: ${runItem.url} Status ${runItem.status}` +
                 ` Created at ${runItem.created_at}\n`);
+            if (runItem.id === selfRunId && selfPreservation) {
+                core.info(`\nI have self-preservation built in. I refuse to cancel myself :)\n`);
+                continue;
+            }
             yield checkCandidateForCancelling(repositoryInfo, runItem, triggeringRunInfo, cancelMode, cancelFutureDuplicates, jobNameRegexps, skipEventTypes, mapOfWorkflowRunCandidates);
         }
         return mapOfWorkflowRunCandidates;
@@ -2083,7 +2089,7 @@ function cancelAllRunsInTheGroup(repositoryInfo, sortedRunItems, notifyPRCancel,
                     yield notifyPR(repositoryInfo, selfRunId, pullRequestNumber, reason);
                 }
             }
-            core.info(`\nCancelling run: ${runItem}.\n`);
+            core.info(`\nCancelling run: ${runItem.id}.\n`);
             yield cancelRun(repositoryInfo, runItem.id);
             cancelledRuns.push(runItem.id);
         }
@@ -2143,13 +2149,14 @@ function cancelTheRunsPerGroup(repositoryInfo, mapOfWorkflowRunCandidatesCandida
  * @param jobNameRegexps - array of regular expressions to match hob names in case of named modes
  * @param skipEventTypes - array of event names that should be skipped
  * @param reason - reason for cancelling
+ * @param selfPreservation - whether the run will cancel itself if requested
  * @return array of canceled workflow run ids
  */
-function findAndCancelRuns(repositoryInfo, selfRunId, triggeringRunInfo, cancelMode, cancelFutureDuplicates, notifyPRCancel, notifyPRMessageStart, jobNameRegexps, skipEventTypes, reason) {
+function findAndCancelRuns(repositoryInfo, selfRunId, triggeringRunInfo, cancelMode, cancelFutureDuplicates, notifyPRCancel, notifyPRMessageStart, jobNameRegexps, skipEventTypes, reason, selfPreservation) {
     return __awaiter(this, void 0, void 0, function* () {
         const statusValues = ['queued', 'in_progress'];
         const workflowRuns = yield getWorkflowRunsMatchingCriteria(repositoryInfo, statusValues, cancelMode, triggeringRunInfo);
-        const mapOfWorkflowRunCandidatesCandidatesToCancel = yield filterAndMapWorkflowRunsToGroups(repositoryInfo, triggeringRunInfo, cancelMode, cancelFutureDuplicates, jobNameRegexps, skipEventTypes, workflowRuns);
+        const mapOfWorkflowRunCandidatesCandidatesToCancel = yield filterAndMapWorkflowRunsToGroups(repositoryInfo, triggeringRunInfo, cancelMode, cancelFutureDuplicates, jobNameRegexps, skipEventTypes, selfRunId, selfPreservation, workflowRuns);
         return yield cancelTheRunsPerGroup(repositoryInfo, mapOfWorkflowRunCandidatesCandidatesToCancel, cancelMode, cancelFutureDuplicates, notifyPRCancel, selfRunId, reason);
     });
 }
@@ -2207,15 +2214,16 @@ function getTriggeringRunInfo(repositoryInfo, runId) {
  * @param selfRunId - number of own run id
  * @param triggeringRunInfo - information about the workflow that triggered the run
  * @param cancelMode - cancel mode used
- * @param cancelFutureDuplicates - whether to cancel future duplicates for duplicate cancelling
  * @param notifyPRCancel - whether to notify in PRs about cancelling
  * @param notifyPRCancelMessage - message to send when cancelling the PR (overrides default message
  *        generated automatically)
  * @param notifyPRMessageStart - whether to notify PRs when the action starts
  * @param jobNameRegexps - array of regular expressions to match hob names in case of named modes
  * @param skipEventTypes - array of event names that should be skipped
+ * @param cancelFutureDuplicates - whether to cancel future duplicates for duplicate cancelling
+ * @param selfPreservation - whether the run will cancel itself if requested
  */
-function performCancelJob(repositoryInfo, selfRunId, triggeringRunInfo, cancelMode, notifyPRCancel, notifyPRCancelMessage, notifyPRMessageStart, jobNameRegexps, skipEventTypes, cancelFutureDuplicates) {
+function performCancelJob(repositoryInfo, selfRunId, triggeringRunInfo, cancelMode, notifyPRCancel, notifyPRCancelMessage, notifyPRMessageStart, jobNameRegexps, skipEventTypes, cancelFutureDuplicates, selfPreservation) {
     return __awaiter(this, void 0, void 0, function* () {
         core.info('\n###################################################################################\n');
         core.info(`All parameters: owner: ${repositoryInfo.owner}, repo: ${repositoryInfo.repo}, ` +
@@ -2259,7 +2267,7 @@ function performCancelJob(repositoryInfo, selfRunId, triggeringRunInfo, cancelMo
             throw Error(`Wrong cancel mode ${cancelMode}! Please correct it.`);
         }
         core.info('\n###################################################################################\n');
-        return yield findAndCancelRuns(repositoryInfo, selfRunId, triggeringRunInfo, cancelMode, cancelFutureDuplicates, notifyPRCancel, notifyPRMessageStart, jobNameRegexps, skipEventTypes, reason);
+        return yield findAndCancelRuns(repositoryInfo, selfRunId, triggeringRunInfo, cancelMode, cancelFutureDuplicates, notifyPRCancel, notifyPRMessageStart, jobNameRegexps, skipEventTypes, reason, selfPreservation);
     });
 }
 /**
@@ -2304,7 +2312,7 @@ function verboseOutput(name, value) {
 }
 /**
  * Performs sanity check of the parameters passed. Some of the parameter combinations do not work so they
- * are verified and in case od unexpected combination found, approrpriate error is raised.
+ * are verified and in case od unexpected combination found, appropriate error is raised.
  *
  * @param eventName - name of the event to act on
  * @param runId - run id of the triggering event
@@ -2401,6 +2409,7 @@ function run() {
         const notifyPRMessageStart = core.getInput('notifyPRMessageStart');
         const sourceRunId = parseInt(core.getInput('sourceRunId')) || selfRunId;
         const jobNameRegexpsString = core.getInput('jobNameRegexps');
+        const selfPreservation = (core.getInput('selfPreservation') || 'true').toLowerCase() === 'true';
         const cancelFutureDuplicates = (core.getInput('cancelFutureDuplicates') || 'true').toLowerCase() === 'true';
         const jobNameRegexps = jobNameRegexpsString
             ? JSON.parse(jobNameRegexpsString)
@@ -2427,7 +2436,7 @@ function run() {
         const triggeringRunInfo = yield getTriggeringRunInfo(repositoryInfo, sourceRunId);
         produceBasicOutputs(triggeringRunInfo);
         yield notifyActionStart(repositoryInfo, triggeringRunInfo, selfRunId, notifyPRMessageStart);
-        const cancelledRuns = yield performCancelJob(repositoryInfo, selfRunId, triggeringRunInfo, cancelMode, notifyPRCancel, notifyPRCancelMessage, notifyPRMessageStart, jobNameRegexps, skipEventTypes, cancelFutureDuplicates);
+        const cancelledRuns = yield performCancelJob(repositoryInfo, selfRunId, triggeringRunInfo, cancelMode, notifyPRCancel, notifyPRCancelMessage, notifyPRMessageStart, jobNameRegexps, skipEventTypes, cancelFutureDuplicates, selfPreservation);
         verboseOutput('cancelledRuns', JSON.stringify(cancelledRuns));
     });
 }
diff --git a/src/main.ts b/src/main.ts
index abf39f1..3c4eab1 100644
--- a/src/main.ts
+++ b/src/main.ts
@@ -790,6 +790,8 @@ async function findPullRequestForRunItem(
  * @param cancelFutureDuplicates - whether to cancel future duplicates
  * @param jobNameRegexps - regexps for job names
  * @param skipEventTypes - array of event names to skip
+ * @param selfPreservation - whether the run will cancel itself if requested
+ * @param selfRunId - my own run id
  * @param workflowRuns - map of workflow runs found
  * @parm maps with string key and array of run items as value. The key might be
  *       * source group id (allDuplicates mode)
@@ -802,6 +804,8 @@ async function filterAndMapWorkflowRunsToGroups(
   cancelFutureDuplicates: boolean,
   jobNameRegexps: string[],
   skipEventTypes: string[],
+  selfRunId: number,
+  selfPreservation: boolean,
   workflowRuns: Map<
     number,
     rest.ActionsListWorkflowRunsResponseWorkflowRunsItem
@@ -815,6 +819,12 @@ async function filterAndMapWorkflowRunsToGroups(
       `\nChecking run number: ${key} RunId: ${runItem.id} Url: ${runItem.url} Status ${runItem.status}` +
         ` Created at ${runItem.created_at}\n`
     )
+    if (runItem.id === selfRunId && selfPreservation) {
+      core.info(
+        `\nI have self-preservation built in. I refuse to cancel myself :)\n`
+      )
+      continue
+    }
     await checkCandidateForCancelling(
       repositoryInfo,
       runItem,
@@ -908,7 +918,7 @@ async function cancelAllRunsInTheGroup(
         await notifyPR(repositoryInfo, selfRunId, pullRequestNumber, reason)
       }
     }
-    core.info(`\nCancelling run: ${runItem}.\n`)
+    core.info(`\nCancelling run: ${runItem.id}.\n`)
     await cancelRun(repositoryInfo, runItem.id)
     cancelledRuns.push(runItem.id)
   }
@@ -999,6 +1009,7 @@ async function cancelTheRunsPerGroup(
  * @param jobNameRegexps - array of regular expressions to match hob names in case of named modes
  * @param skipEventTypes - array of event names that should be skipped
  * @param reason - reason for cancelling
+ * @param selfPreservation - whether the run will cancel itself if requested
  * @return array of canceled workflow run ids
  */
 async function findAndCancelRuns(
@@ -1011,7 +1022,8 @@ async function findAndCancelRuns(
   notifyPRMessageStart: string,
   jobNameRegexps: string[],
   skipEventTypes: string[],
-  reason: string
+  reason: string,
+  selfPreservation: boolean
 ): Promise<number[]> {
   const statusValues = ['queued', 'in_progress']
   const workflowRuns = await getWorkflowRunsMatchingCriteria(
@@ -1027,6 +1039,8 @@ async function findAndCancelRuns(
     cancelFutureDuplicates,
     jobNameRegexps,
     skipEventTypes,
+    selfRunId,
+    selfPreservation,
     workflowRuns
   )
   return await cancelTheRunsPerGroup(
@@ -1105,13 +1119,14 @@ async function getTriggeringRunInfo(
  * @param selfRunId - number of own run id
  * @param triggeringRunInfo - information about the workflow that triggered the run
  * @param cancelMode - cancel mode used
- * @param cancelFutureDuplicates - whether to cancel future duplicates for duplicate cancelling
  * @param notifyPRCancel - whether to notify in PRs about cancelling
  * @param notifyPRCancelMessage - message to send when cancelling the PR (overrides default message
  *        generated automatically)
  * @param notifyPRMessageStart - whether to notify PRs when the action starts
  * @param jobNameRegexps - array of regular expressions to match hob names in case of named modes
  * @param skipEventTypes - array of event names that should be skipped
+ * @param cancelFutureDuplicates - whether to cancel future duplicates for duplicate cancelling
+ * @param selfPreservation - whether the run will cancel itself if requested
  */
 async function performCancelJob(
   repositoryInfo: RepositoryInfo,
@@ -1123,7 +1138,8 @@ async function performCancelJob(
   notifyPRMessageStart: string,
   jobNameRegexps: string[],
   skipEventTypes: string[],
-  cancelFutureDuplicates: boolean
+  cancelFutureDuplicates: boolean,
+  selfPreservation: boolean
 ): Promise<number[]> {
   core.info(
     '\n###################################################################################\n'
@@ -1192,7 +1208,8 @@ async function performCancelJob(
     notifyPRMessageStart,
     jobNameRegexps,
     skipEventTypes,
-    reason
+    reason,
+    selfPreservation
   )
 }
 
@@ -1242,7 +1259,7 @@ function verboseOutput(name: string, value: string): void {
 
 /**
  * Performs sanity check of the parameters passed. Some of the parameter combinations do not work so they
- * are verified and in case od unexpected combination found, approrpriate error is raised.
+ * are verified and in case od unexpected combination found, appropriate error is raised.
  *
  * @param eventName - name of the event to act on
  * @param runId - run id of the triggering event
@@ -1380,6 +1397,8 @@ async function run(): Promise<void> {
   const notifyPRMessageStart = core.getInput('notifyPRMessageStart')
   const sourceRunId = parseInt(core.getInput('sourceRunId')) || selfRunId
   const jobNameRegexpsString = core.getInput('jobNameRegexps')
+  const selfPreservation =
+    (core.getInput('selfPreservation') || 'true').toLowerCase() === 'true'
   const cancelFutureDuplicates =
     (core.getInput('cancelFutureDuplicates') || 'true').toLowerCase() === 'true'
   const jobNameRegexps = jobNameRegexpsString
@@ -1449,7 +1468,8 @@ async function run(): Promise<void> {
     notifyPRMessageStart,
     jobNameRegexps,
     skipEventTypes,
-    cancelFutureDuplicates
+    cancelFutureDuplicates,
+    selfPreservation
   )
 
   verboseOutput('cancelledRuns', JSON.stringify(cancelledRuns))