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))