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.
diff --git a/README.md b/README.md
index b212dd9..5f98862 100644
--- a/README.md
+++ b/README.md
@@ -115,6 +115,7 @@
 | `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 @@
 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 @@
           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 @@
       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 @@
  * @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 @@
                     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 @@
  * @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 @@
  * @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 @@
             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 @@
 }
 /**
  * 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 @@
         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 @@
         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 @@
  * @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 @@
   cancelFutureDuplicates: boolean,
   jobNameRegexps: string[],
   skipEventTypes: string[],
+  selfRunId: number,
+  selfPreservation: boolean,
   workflowRuns: Map<
     number,
     rest.ActionsListWorkflowRunsResponseWorkflowRunsItem
@@ -815,6 +819,12 @@
       `\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 @@
         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 @@
  * @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 @@
   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 @@
     cancelFutureDuplicates,
     jobNameRegexps,
     skipEventTypes,
+    selfRunId,
+    selfPreservation,
     workflowRuns
   )
   return await cancelTheRunsPerGroup(
@@ -1105,13 +1119,14 @@
  * @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 @@
   notifyPRMessageStart: string,
   jobNameRegexps: string[],
   skipEventTypes: string[],
-  cancelFutureDuplicates: boolean
+  cancelFutureDuplicates: boolean,
+  selfPreservation: boolean
 ): Promise<number[]> {
   core.info(
     '\n###################################################################################\n'
@@ -1192,7 +1208,8 @@
     notifyPRMessageStart,
     jobNameRegexps,
     skipEventTypes,
-    reason
+    reason,
+    selfPreservation
   )
 }
 
@@ -1242,7 +1259,7 @@
 
 /**
  * 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 @@
   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 @@
     notifyPRMessageStart,
     jobNameRegexps,
     skipEventTypes,
-    cancelFutureDuplicates
+    cancelFutureDuplicates,
+    selfPreservation
   )
 
   verboseOutput('cancelledRuns', JSON.stringify(cancelledRuns))