Adds allDuplicatedNamedJobs mode (#11)
diff --git a/README.md b/README.md
index e81c66b..b212dd9 100644
--- a/README.md
+++ b/README.md
@@ -25,6 +25,7 @@
- [Fail-fast source workflow runs with failed jobs and corresponding triggered runs](#fail-fast-source-workflow-runs-with-failed-jobs-and-corresponding-triggered-runs)
- [Fail-fast for triggered workflow runs with failed jobs](#fail-fast-for-triggered-workflow-runs-with-failed-jobs)
- [Cancel another workflow run](#cancel-another-workflow-run)
+ - [Cancel all duplicates for named jobs](#cancel-all-duplicates-for-named-jobs)
- [Repositories that do not use Pull Requests from forks](#repositories-that-do-not-use-pull-requests-from-forks)
- [Cancel duplicate runs for "self" workflow](#cancel-duplicate-runs-for-self-workflow)
- [Cancel "self" workflow run](#cancel-self-workflow-run)
@@ -112,20 +113,21 @@
| `notifyPRCancel` | no | | Boolean. If set to true, it notifies the cancelled PRs with a comment containing reason why they are being cancelled. |
| `notifyPRCancelMessage` | no | | Optional cancel message to use instead of the default one when notifyPRCancel is true. It is only used in 'self' cancelling mode. |
| `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` cancel modes. |
+| `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. |
| `workflowFileName` | no | | Name of the workflow file. It can be used if you want to cancel a different workflow than yours. |
The job cancel modes work as follows:
-| Cancel Mode | No `sourceRunId` specified | The `sourceRunId` set to `${{ github.event.workflow_run.id }}` |
-|-----------------|------------------------------------------------------------------------|-------------------------------------------------------------------------------------|
-| `duplicates` | Cancels duplicate runs from the same repo/branch as current run. | Cancels duplicate runs for the same repo/branch as the source run. |
-| `allDuplicates` | Cancels duplicate runs from all running workflows. | Cancels duplicate runs from all running workflows. |
-| `self` | Cancels self run. | Cancel the `sourceRunId` run. |
-| `failedJobs` | Cancels all runs of own workflow that have matching jobs that failed. | Cancels all runs of the `sourceRunId` workflow that have matching jobs that failed. |
-| `namedJobs` | Cancels all runs of own workflow that have matching jobs. | Cancels all runs of the `sourceRunId` workflow that have matching jobs. |
+| Cancel Mode | No `sourceRunId` specified | The `sourceRunId` set to `${{ github.event.workflow_run.id }}` |
+|--------------------------|------------------------------------------------------------------------------|-------------------------------------------------------------------------------------|
+| `duplicates` | Cancels duplicate runs from the same repo/branch as current run. | Cancels duplicate runs for the same repo/branch as the source run. |
+| `allDuplicates` | Cancels duplicate runs from all running workflows. | Cancels duplicate runs from all running workflows. |
+| `self` | Cancels self run. | Cancel the `sourceRunId` run. |
+| `failedJobs` | Cancels all runs of own workflow that have matching jobs that failed. | Cancels all runs of the `sourceRunId` workflow that have matching jobs that failed. |
+| `namedJobs` | Cancels all runs of own workflow that have matching jobs. | Cancels all runs of the `sourceRunId` workflow that have matching jobs. |
+| `allDuplicatedNamedJobs` | Cancels all duplicate runs of own workflow that share matching jobs pattern. | Cancels all runs of the `sourceRunId` workflow that share matching job pattern. |
## Outputs
@@ -163,7 +165,7 @@
name: "Cancel duplicate workflow runs"
runs-on: ubuntu-latest
steps:
- - uses: potiuk/cancel-workflow-runs@v4_1
+ - uses: potiuk/cancel-workflow-runs@master
name: "Cancel duplicate workflow runs"
with:
cancelMode: allDuplicates
@@ -231,7 +233,7 @@
name: "Cancel duplicate workflow runs"
runs-on: ubuntu-latest
steps:
- - uses: potiuk/cancel-workflow-runs@v4_1
+ - uses: potiuk/cancel-workflow-runs@master
name: "Cancel duplicate workflow runs"
with:
cancelMode: duplicates
@@ -292,7 +294,7 @@
sourceHeadSha: ${{ steps.cancel.outputs.sourceHeadSha }}
sourceEvent: ${{ steps.cancel.outputs.sourceEvent }}
steps:
- - uses: potiuk/cancel-workflow-runs@v4_1
+ - uses: potiuk/cancel-workflow-runs@master
id: cancel
name: "Cancel duplicate CI runs"
with:
@@ -305,7 +307,7 @@
that you will not see in the list of checks.
You can checks the status of those images in:
- - uses: potiuk/cancel-workflow-runs@v4_1
+ - uses: potiuk/cancel-workflow-runs@master
name: "Cancel duplicate Cancelling runs"
with:
cancelMode: namedJobs
@@ -355,7 +357,7 @@
runs-on: ubuntu-latest
steps:
- name: "Cancel the self CI workflow run"
- uses: potiuk/cancel-workflow-runs@v4_1
+ uses: potiuk/cancel-workflow-runs@master
with:
cancelMode: self
notifyPRCancel: true
@@ -384,7 +386,7 @@
runs-on: ubuntu-latest
steps:
- name: "Cancel the self Cancelling workflow run"
- uses: potiuk/cancel-workflow-runs@v4_1
+ uses: potiuk/cancel-workflow-runs@master
with:
cancelMode: self
notifyPRCancel: true
@@ -417,7 +419,7 @@
name: "Fail fast CI runs"
runs-on: ubuntu-latest
steps:
- - uses: potiuk/cancel-workflow-runs@v4_1
+ - uses: potiuk/cancel-workflow-runs@master
name: "Fail fast CI runs"
with:
cancelMode: failedJobs
@@ -458,7 +460,7 @@
name: "Fail fast CI runs"
runs-on: ubuntu-latest
steps:
- - uses: potiuk/cancel-workflow-runs@v4_1
+ - uses: potiuk/cancel-workflow-runs@master
name: "Fail fast CI. Source run: ${{ github.event.workflow_run.id }}"
id: cancel-failed
with:
@@ -481,7 +483,7 @@
echo "::set-output name=matching-regexp::${REGEXP}"
- name: "Cancel triggered 'Cancelling' runs for the cancelled failed runs"
if: steps.cancel-failed.outputs.cancelledRuns != '[]'
- uses: potiuk/cancel-workflow-runs@v4_1
+ uses: potiuk/cancel-workflow-runs@master
with:
cancelMode: namedJobs
token: ${{ secrets.GITHUB_TOKEN }}
@@ -516,7 +518,7 @@
name: "Fail fast Canceling runs"
runs-on: ubuntu-latest
steps:
- - uses: potiuk/cancel-workflow-runs@v4_1
+ - uses: potiuk/cancel-workflow-runs@master
name: "Fail fast Canceling runs"
with:
cancelMode: failedJobs
@@ -541,7 +543,7 @@
runs-on: ubuntu-latest
steps:
- name: "Cancel the self CI workflow run"
- uses: potiuk/cancel-workflow-runs@v4_1
+ uses: potiuk/cancel-workflow-runs@master
with:
cancelMode: duplicates
cancelFutureDuplicates: true
@@ -549,6 +551,42 @@
workflowFileName: other_workflow.yml
```
+### Cancel all duplicates for named jobs
+
+Cancels all duplicated runs for all jobs that match specified regular expression.
+Note that it does not take into account the branch of the runs. It will cancel all duplicates with
+the same match for jobs, no matter what branch originated it.
+
+This is useful in case of job names generated dynamically.
+
+In the case below, for all the runs that have job names generated containing Branch/Repo/Event combination
+that have the same match, the duplicates will get cancelled leaving only the most recent run for each exact
+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.
+
+```yaml
+on:
+ push:
+ workflow_run:
+ workflows: ['CI']
+ types: ['requested']
+
+jobs:
+ cancel-self-failed-runs:
+ name: "Cancel the self workflow run"
+ runs-on: ubuntu-latest
+ steps:
+ - uses: potiuk/cancel-workflow-runs@master
+ name: "Cancel past CI runs"
+ with:
+ cancelMode: allDuplicatedNamedJobs
+ token: ${{ secrets.GITHUB_TOKEN }}
+ jobNameRegexps: '["Branch: .* Repo: .* Event: .* "]'
+ notifyPRCancel: true
+```
+
## Repositories that do not use Pull Requests from forks
@@ -582,7 +620,7 @@
name: "Cancel duplicate workflow runs"
runs-on: ubuntu-latest
steps:
- - uses: potiuk/cancel-workflow-runs@v4_1
+ - uses: potiuk/cancel-workflow-runs@master
name: "Cancel duplicate workflow runs"
with:
cancelMode: duplicates
@@ -606,7 +644,7 @@
runs-on: ubuntu-latest
steps:
- name: "Cancel the self workflow run"
- uses: potiuk/cancel-workflow-runs@v4_1
+ uses: potiuk/cancel-workflow-runs@master
with:
cancelMode: self
token: ${{ secrets.GITHUB_TOKEN }}
@@ -632,7 +670,7 @@
name: "Cancel failed runs"
runs-on: ubuntu-latest
steps:
- - uses: potiuk/cancel-workflow-runs@v4_1
+ - uses: potiuk/cancel-workflow-runs@master
name: "Cancel failed runs"
with:
cancelMode: failedJobs
@@ -664,7 +702,7 @@
name: "Cancel the self workflow run"
runs-on: ubuntu-latest
steps:
- - uses: potiuk/cancel-workflow-runs@v4_1
+ - uses: potiuk/cancel-workflow-runs@master
name: "Cancel past CI runs"
with:
cancelMode: namedJobs
diff --git a/dist/index.js b/dist/index.js
index 6f047aa..2a806db 100644
--- a/dist/index.js
+++ b/dist/index.js
@@ -1485,15 +1485,17 @@
CancelMode["SELF"] = "self";
CancelMode["FAILED_JOBS"] = "failedJobs";
CancelMode["NAMED_JOBS"] = "namedJobs";
+ CancelMode["ALL_DUPLICATED_NAMED_JOBS"] = "allDuplicatedNamedJobs";
})(CancelMode || (CancelMode = {}));
/**
* Converts the source of a run object into a string that can be used as map key in maps where we keep
* arrays of runs per source group
- * @param sourceGroup the object identifying the source of the run (Pull Request, Master Push)
+ * @param triggeringRunInfo the object identifying the triggering workflow
* @returns the unique string id for the source group
*/
-function getSourceGroupId(sourceGroup) {
- return `:${sourceGroup.workflowId}:${sourceGroup.headRepo}:${sourceGroup.headBranch}:${sourceGroup.eventName}`;
+function getSourceGroupId(triggeringRunInfo) {
+ return (`:${triggeringRunInfo.workflowId}:${triggeringRunInfo.headRepo}` +
+ `:${triggeringRunInfo.headBranch}:${triggeringRunInfo.eventName}`);
}
/**
* Creates query parameters selecting all runs that share the same source group as we have. This can
@@ -1501,18 +1503,18 @@
*
* @param repositoryInfo - information about the repository used
* @param status - status of the run that we are querying for
- * @param mySourceGroup - source group of the originating run
+ * @param triggeringRunInfo - information about the workflow that triggered the run
* @return query parameters merged with the listWorkflowRuns criteria
*/
-function createListRunsQueryRunsSameSource(repositoryInfo, status, mySourceGroup) {
+function createListRunsQueryRunsSameSource(repositoryInfo, status, triggeringRunInfo) {
const request = {
owner: repositoryInfo.owner,
repo: repositoryInfo.repo,
// eslint-disable-next-line @typescript-eslint/camelcase
- workflow_id: mySourceGroup.workflowId,
+ workflow_id: triggeringRunInfo.workflowId,
status,
- branch: mySourceGroup.headBranch,
- event: mySourceGroup.eventName
+ branch: triggeringRunInfo.headBranch,
+ event: triggeringRunInfo.eventName
};
return repositoryInfo.octokit.actions.listWorkflowRuns.endpoint.merge(request);
}
@@ -1520,19 +1522,18 @@
* Creates query parameters selecting only specific run Id.
* @param repositoryInfo - information about the repository used
* @param status - status of the run that we are querying for
- * @param workflowId - Id of the workflow to retrieve
- * @param runId - run Id to retrieve
+ * @param triggeringRunInfo - information about the workflow that triggered the run
* @return query parameters merged with the listWorkflowRuns criteria
*/
-function createListRunsQuerySpecificRunId(repositoryInfo, status, workflowId, runId) {
+function createListRunsQuerySpecificRunId(repositoryInfo, status, triggeringRunInfo) {
const request = {
owner: repositoryInfo.owner,
repo: repositoryInfo.repo,
// eslint-disable-next-line @typescript-eslint/camelcase
- workflow_id: workflowId,
+ workflow_id: triggeringRunInfo.workflowId,
status,
// eslint-disable-next-line @typescript-eslint/camelcase
- run_id: runId.toString()
+ run_id: triggeringRunInfo.runId.toString()
};
return repositoryInfo.octokit.actions.listWorkflowRuns.endpoint.merge(request);
}
@@ -1572,15 +1573,33 @@
* Returns true if the string matches any of the regexps in array of regexps
* @param stringToMatch string to match
* @param regexps array of regexp to match the string against
- * @return true if there is a match
+ * @return array of [matched (boolean), [array of matched strings]]
*/
function matchInArray(stringToMatch, regexps) {
+ let matched = false;
+ const allMatches = [];
for (const regexp of regexps) {
- if (stringToMatch.match(regexp)) {
- return true;
+ const match = stringToMatch.match(regexp);
+ if (match) {
+ matched = true;
+ allMatches.push(match[0]);
}
}
- return false;
+ return [matched, allMatches];
+}
+/**
+ * Adds workflow run to the array in the map indexed by key.
+ * @param key key to use
+ * @param runItem run Item to add
+ * @param mapOfWorkflowRunCandidates map of workflow runs to add the run item to
+ */
+function addWorkflowRunToMap(key, runItem, mapOfWorkflowRunCandidates) {
+ let arrayOfRuns = mapOfWorkflowRunCandidates.get(key);
+ if (arrayOfRuns === undefined) {
+ arrayOfRuns = [];
+ mapOfWorkflowRunCandidates.set(key, arrayOfRuns);
+ }
+ arrayOfRuns.push(runItem);
}
/**
* Returns true if the runId specified has jobs matching the regexp and optionally checks if those
@@ -1591,7 +1610,7 @@
* @param runId - Id of the run to retrieve jobs for
* @param jobNameRegexps - array of job name regexps
* @param checkIfFailed - whether to check the 'failed' status of matched jobs
- * @return true if there is a match
+ * @return array of [matched (boolean), array of matches]
*/
function jobsMatchingNames(repositoryInfo, runId, jobNameRegexps, checkIfFailed) {
var e_1, _a;
@@ -1603,18 +1622,22 @@
else {
core.info(`\nChecking if runId ${runId} has job names matching any of the ${jobNameRegexps}\n`);
}
+ const allMatches = [];
+ let matched = false;
try {
for (var _b = __asyncValues(repositoryInfo.octokit.paginate.iterator(listJobs)), _c; _c = yield _b.next(), !_c.done;) {
const item = _c.value;
- for (const job of item.data.jobs) {
+ for (const job of item.data) {
core.info(` The job name: ${job.name}, Conclusion: ${job.conclusion}`);
- if (matchInArray(job.name, jobNameRegexps)) {
+ const [jobMatched, jobMatches] = matchInArray(job.name, jobNameRegexps);
+ if (jobMatched) {
+ allMatches.push(...jobMatches);
+ matched = true;
if (checkIfFailed) {
// Only fail the build if one of the matching jobs fail
if (job.conclusion === 'failure') {
core.info(` The Job ${job.name} matches one of the ${jobNameRegexps} regexps and it failed.` +
- ` Cancelling run.`);
- return true;
+ ` It will be added to the candidates.`);
}
else {
core.info(` The Job ${job.name} matches one of the ${jobNameRegexps} regexps but it did not fail. ` +
@@ -1623,8 +1646,8 @@
}
else {
// Fail the build if any of the job names match
- core.info(` The Job ${job.name} matches one of the ${jobNameRegexps} regexps. Cancelling run.`);
- return true;
+ core.info(` The Job ${job.name} matches one of the ${jobNameRegexps} regexps. ` +
+ `It will be added to the candidates.`);
}
}
}
@@ -1637,7 +1660,7 @@
}
finally { if (e_1) throw e_1.error; }
}
- return false;
+ return [matched, allMatches];
});
}
/**
@@ -1711,67 +1734,74 @@
/**
* True if the request is candidate for cancelling in case of duplicate deletion
* @param runItem item to check
- * @param headRepo head Repo that we are checking against
* @param cancelFutureDuplicates whether future duplicates are being cancelled
- * @param sourceRunId the source Run Id that originates the request
+ * @param triggeringRunInfo - information about the workflow that triggered the run
+ * @param mapOfWorkflowRunCandidates - map of the workflow runs to add candidates to
* @return true if we determine that the run Id should be cancelled
*/
-function isCandidateForCancellingDuplicate(runItem, headRepo, cancelFutureDuplicates, sourceRunId) {
+function checkCandidateForCancellingDuplicate(runItem, cancelFutureDuplicates, triggeringRunInfo, mapOfWorkflowRunCandidates) {
const runHeadRepo = runItem.head_repository.full_name;
- if (headRepo !== undefined && runHeadRepo !== headRepo) {
+ if (triggeringRunInfo.headRepo !== undefined &&
+ runHeadRepo !== triggeringRunInfo.headRepo) {
core.info(`\nThe run ${runItem.id} is from a different ` +
- `repo: ${runHeadRepo} (expected ${headRepo}). Not cancelling it\n`);
- return false;
+ `repo: ${runHeadRepo} (expected ${triggeringRunInfo.headRepo}). Not adding as candidate to cancel.\n`);
}
if (cancelFutureDuplicates) {
core.info(`\nCancel Future Duplicates: Returning run id that might be duplicate or my own run: ${runItem.id}.\n`);
- return true;
+ addWorkflowRunToMap(getSourceGroupId(triggeringRunInfo), runItem, mapOfWorkflowRunCandidates);
}
else {
- if (runItem.id === sourceRunId) {
+ if (runItem.id === triggeringRunInfo.runId) {
core.info(`\nThis is my own run ${runItem.id}. Not returning myself!\n`);
- return false;
}
- else if (runItem.id > sourceRunId) {
- core.info(`\nThe run ${runItem.id} is started later than my own run ${sourceRunId}. Not returning it\n`);
- return false;
+ else if (runItem.id > triggeringRunInfo.runId) {
+ core.info(`\nThe run ${runItem.id} is started later than my own ` +
+ `run ${triggeringRunInfo.runId}. Not returning it\n`);
}
core.info(`\nFound duplicate of my own run: ${runItem.id}.\n`);
- return true;
}
}
/**
- * Should the run is candidate for cancelling in SELF cancelling mode?
+ * Should the run be candidate for cancelling in SELF cancelling mode?
* @param runItem run item
- * @param sourceRunId source run id
- * @return true if the run item is self
+ * @param triggeringRunInfo - information about the workflow that triggered the run
+ * @param mapOfWorkflowRunCandidates - map of the workflow runs to add candidates to
*/
-function isCandidateForCancellingSelf(runItem, sourceRunId) {
- if (runItem.id === sourceRunId) {
- core.info(`\nReturning the "source" run: ${runItem.id}.\n`);
- return true;
+function checkCandidateForCancellingSelf(runItem, triggeringRunInfo, mapOfWorkflowRunCandidates) {
+ if (runItem.id === triggeringRunInfo.runId) {
+ core.info(`\nAdding the "source" run: ${runItem.id} to candidates.\n`);
+ addWorkflowRunToMap(getSourceGroupId(triggeringRunInfo), runItem, mapOfWorkflowRunCandidates);
}
- else {
- return false;
- }
+}
+/**
+ * Should the run be candidate for cancelling of all duplicates
+ * @param runItem run item
+ * @param triggeringRunInfo - information about the workflow that triggered the run
+ * @param mapOfWorkflowRunCandidates - map of the workflow runs to add candidates to
+ */
+function checkCandidateForAllDuplicates(runItem, triggeringRunInfo, mapOfWorkflowRunCandidates) {
+ core.info(`\nAdding the run: ${runItem.id} to candidates.\n`);
+ addWorkflowRunToMap(getSourceGroupId(triggeringRunInfo), runItem, mapOfWorkflowRunCandidates);
}
/**
* Should the run is candidate for cancelling in naming job cancelling mode?
* @param repositoryInfo - information about the repository used
* @param runItem run item
* @param jobNamesRegexps array of regexps to match job names against
+ * @param triggeringRunInfo - information about the workflow that triggered the run
+ * @param mapOfWorkflowRunCandidates - map of the workflow runs to add candidates to
* @return true if the run item contains jobs with names matching the pattern
*/
-function isCandidateForCancellingNamedJobs(repositoryInfo, runItem, jobNamesRegexps) {
+function checkCandidateForCancellingNamedJobs(repositoryInfo, runItem, jobNamesRegexps, triggeringRunInfo, mapOfWorkflowRunCandidates) {
return __awaiter(this, void 0, void 0, function* () {
// Cancel all jobs that have failed jobs (no matter when started)
- if (yield jobsMatchingNames(repositoryInfo, runItem.id, jobNamesRegexps, false)) {
- core.info(`\nSome jobs have matching names in ${runItem.id} . Returning it.\n`);
- return true;
+ const [matched, allMatches] = yield jobsMatchingNames(repositoryInfo, runItem.id, jobNamesRegexps, false);
+ if (matched) {
+ core.info(`\nSome jobs have matching names in ${runItem.id}: ${allMatches}. Adding it candidates.\n`);
+ addWorkflowRunToMap(getSourceGroupId(triggeringRunInfo), runItem, mapOfWorkflowRunCandidates);
}
else {
- core.info(`\nNone of the jobs match name in ${runItem.id}. Returning it.\n`);
- return false;
+ core.info(`\nNone of the jobs match name in ${runItem.id}.\n`);
}
});
}
@@ -1780,67 +1810,92 @@
* @param repositoryInfo - information about the repository used
* @param runItem run item
* @param jobNamesRegexps array of regexps to match job names against
+ * @param triggeringRunInfo - information about the workflow that triggered the run
+ * @param mapOfWorkflowRunCandidates - map of the workflow runs to add candidates to
* @return true if the run item contains failed jobs with names matching the pattern
*/
-function isCandidateForCancellingFailedJobs(repositoryInfo, runItem, jobNamesRegexps) {
+function checkCandidateForCancellingFailedJobs(repositoryInfo, runItem, jobNamesRegexps, triggeringRunInfo, mapOfWorkflowRunCandidates) {
return __awaiter(this, void 0, void 0, function* () {
// Cancel all jobs that have failed jobs (no matter when started)
- if (yield jobsMatchingNames(repositoryInfo, runItem.id, jobNamesRegexps, true)) {
- core.info(`\nSome matching named jobs failed in ${runItem.id} . Cancelling it.\n`);
- return true;
+ const [matched, allMatches] = yield jobsMatchingNames(repositoryInfo, runItem.id, jobNamesRegexps, true);
+ if (matched) {
+ core.info(`\nSome matching named jobs failed in ${runItem.id}: ${allMatches}. Adding it to candidates.\n`);
+ addWorkflowRunToMap(getSourceGroupId(triggeringRunInfo), runItem, mapOfWorkflowRunCandidates);
}
else {
- core.info(`\nNone of the matching jobs failed in ${runItem.id}. Not cancelling it.\n`);
- return false;
+ core.info(`\nNone of the matching jobs failed in ${runItem.id}. Not adding as candidate to cancel.\n`);
}
});
}
/**
- * Determines whether the run is candidate to be cancelled depending on the mode used
+ * Checks if the run is candidate for duplicate cancelling of named jobs. It adds it to the map
+ * including the match as a key for duplicate detection.
* @param repositoryInfo - information about the repository used
* @param runItem - run item
- * @param headRepo - head repository
+ * @param jobNamesRegexps - array of regexps to match job names against
+ * @param mapOfWorkflowRunCandidates - map of runs to update
+ */
+function checkCandidateForDuplicateNamedJobs(repositoryInfo, runItem, jobNamesRegexps, mapOfWorkflowRunCandidates) {
+ return __awaiter(this, void 0, void 0, function* () {
+ const [matched, allMatches] = yield jobsMatchingNames(repositoryInfo, runItem.id, jobNamesRegexps, false);
+ if (matched) {
+ for (const match of allMatches) {
+ // This is the only case where we are not using source group to cancelling candidates but
+ // the match of job names
+ addWorkflowRunToMap(match, runItem, mapOfWorkflowRunCandidates);
+ }
+ }
+ });
+}
+/**
+ * Determines whether the run is candidate to be cancelled depending on the mode used and add it to the map
+ * of workflow names if it is.
+ * @param repositoryInfo - information about the repository used
+ * @param runItem - run item
+ * @param triggeringRunInfo - information about the workflow that triggered the run
* @param cancelMode - cancel mode
* @param cancelFutureDuplicates - whether to cancel future duplicates
- * @param sourceRunId - what is the source run id
* @param jobNamesRegexps - what are the regexps for job names
* @param skipEventTypes - which events should be skipped
- * @return true if the run id is candidate for cancelling
+ * @param mapOfWorkflowRunCandidates - map of workflow runs to add candidates to
*/
-function isCandidateForCancelling(repositoryInfo, runItem, headRepo, cancelMode, cancelFutureDuplicates, sourceRunId, jobNamesRegexps, skipEventTypes) {
+function checkCandidateForCancelling(repositoryInfo, runItem, triggeringRunInfo, cancelMode, cancelFutureDuplicates, jobNamesRegexps, skipEventTypes, mapOfWorkflowRunCandidates) {
return __awaiter(this, void 0, void 0, function* () {
if ('completed' === runItem.status.toString()) {
- core.info(`\nThe run ${runItem.id} is completed. Not cancelling it.\n`);
- return false;
+ core.info(`\nThe run ${runItem.id} is completed. Not adding as candidate to cancel.\n`);
+ return;
}
if (!CANCELLABLE_EVENT_TYPES.includes(runItem.event.toString())) {
core.info(`\nThe run ${runItem.id} is (${runItem.event} event - not ` +
- `in ${CANCELLABLE_EVENT_TYPES}). Not cancelling it.\n`);
- return false;
+ `in ${CANCELLABLE_EVENT_TYPES}). Not adding as candidate to cancel.\n`);
+ return;
}
if (skipEventTypes.includes(runItem.event.toString())) {
core.info(`\nThe run ${runItem.id} is (${runItem.event} event - ` +
- `it is in skipEventTypes ${skipEventTypes}). Not cancelling it.\n`);
- return false;
+ `it is in skipEventTypes ${skipEventTypes}). Not adding as candidate to cancel.\n`);
+ return;
}
if (cancelMode === CancelMode.FAILED_JOBS) {
- return yield isCandidateForCancellingFailedJobs(repositoryInfo, runItem, jobNamesRegexps);
+ yield checkCandidateForCancellingFailedJobs(repositoryInfo, runItem, jobNamesRegexps, triggeringRunInfo, mapOfWorkflowRunCandidates);
}
else if (cancelMode === CancelMode.NAMED_JOBS) {
- return yield isCandidateForCancellingNamedJobs(repositoryInfo, runItem, jobNamesRegexps);
+ yield checkCandidateForCancellingNamedJobs(repositoryInfo, runItem, jobNamesRegexps, triggeringRunInfo, mapOfWorkflowRunCandidates);
}
else if (cancelMode === CancelMode.SELF) {
- return isCandidateForCancellingSelf(runItem, sourceRunId);
+ checkCandidateForCancellingSelf(runItem, triggeringRunInfo, mapOfWorkflowRunCandidates);
}
else if (cancelMode === CancelMode.DUPLICATES) {
- return isCandidateForCancellingDuplicate(runItem, headRepo, cancelFutureDuplicates, sourceRunId);
+ checkCandidateForCancellingDuplicate(runItem, cancelFutureDuplicates, triggeringRunInfo, mapOfWorkflowRunCandidates);
}
else if (cancelMode === CancelMode.ALL_DUPLICATES) {
- core.info(`Returning candidate ${runItem.id} for "all_duplicates" cancelling.`);
- return true;
+ checkCandidateForAllDuplicates(runItem, triggeringRunInfo, mapOfWorkflowRunCandidates);
+ }
+ else if (cancelMode === CancelMode.ALL_DUPLICATED_NAMED_JOBS) {
+ yield checkCandidateForDuplicateNamedJobs(repositoryInfo, runItem, jobNamesRegexps, mapOfWorkflowRunCandidates);
+ return;
}
else {
- throw Error(`\nWrong cancel mode ${cancelMode}! This should never happen.\n`);
+ throw Error(`\nWrong cancel mode ${cancelMode}! Please correct it!.\n`);
}
});
}
@@ -1871,35 +1926,35 @@
* @param repositoryInfo - information about the repository used
* @param statusValues - status values we want to check
* @param cancelMode - cancel mode to use
- * @param sourceWorkflowId - source workflow id
- * @param sourceRunId - source run id
- * @param sourceEventName - name of the source event
- * @param mySourceGroup - source of the run that originated it
+ * @param triggeringRunInfo - information about the workflow that triggered the run
* @return map of the run items matching grouped by workflow run id
*/
-function getWorkflowRunsMatchingCriteria(repositoryInfo, statusValues, cancelMode, sourceWorkflowId, sourceRunId, sourceEventName, mySourceGroup) {
+function getWorkflowRunsMatchingCriteria(repositoryInfo, statusValues, cancelMode, triggeringRunInfo) {
return __awaiter(this, void 0, void 0, function* () {
return yield getWorkflowRuns(repositoryInfo, statusValues, cancelMode, function (status) {
if (cancelMode === CancelMode.SELF) {
core.info(`\nFinding runs for my own run: Owner: ${repositoryInfo.owner}, Repo: ${repositoryInfo.repo}, ` +
- `Workflow ID:${sourceWorkflowId}, Source Run id: ${sourceRunId}\n`);
- return createListRunsQuerySpecificRunId(repositoryInfo, status, sourceWorkflowId, sourceRunId);
+ `Workflow ID:${triggeringRunInfo.workflowId},` +
+ `Source Run id: ${triggeringRunInfo.runId}\n`);
+ return createListRunsQuerySpecificRunId(repositoryInfo, status, triggeringRunInfo);
}
else if (cancelMode === CancelMode.FAILED_JOBS ||
cancelMode === CancelMode.NAMED_JOBS ||
- cancelMode === CancelMode.ALL_DUPLICATES) {
+ cancelMode === CancelMode.ALL_DUPLICATES ||
+ cancelMode === CancelMode.ALL_DUPLICATED_NAMED_JOBS) {
core.info(`\nFinding runs for all runs: Owner: ${repositoryInfo.owner}, Repo: ${repositoryInfo.repo}, ` +
- `Status: ${status} Workflow ID:${sourceWorkflowId}\n`);
- return createListRunsQueryAllRuns(repositoryInfo, status, sourceWorkflowId);
+ `Status: ${status} Workflow ID:${triggeringRunInfo.workflowId}\n`);
+ return createListRunsQueryAllRuns(repositoryInfo, status, triggeringRunInfo.workflowId);
}
else if (cancelMode === CancelMode.DUPLICATES) {
core.info(`\nFinding duplicate runs: Owner: ${repositoryInfo.owner}, Repo: ${repositoryInfo.repo}, ` +
- `Status: ${status} Workflow ID:${sourceWorkflowId}, Head Branch: ${mySourceGroup.headBranch},` +
- `Event name: ${sourceEventName}\n`);
- return createListRunsQueryRunsSameSource(repositoryInfo, status, mySourceGroup);
+ `Status: ${status} Workflow ID:${triggeringRunInfo.workflowId}, ` +
+ `Head Branch: ${triggeringRunInfo.headBranch},` +
+ `Event name: ${triggeringRunInfo.eventName}\n`);
+ return createListRunsQueryRunsSameSource(repositoryInfo, status, triggeringRunInfo);
}
else {
- throw Error(`\nWrong cancel mode ${cancelMode}! This should never happen.\n`);
+ throw Error(`\nWrong cancel mode ${cancelMode}! Please correct it.\n`);
}
});
});
@@ -1948,42 +2003,28 @@
});
}
/**
- * Maps found workflow runs into groups - filters out the workflows that are not eligible for canceling
+ * Maps found workflow runs into groups - filters out the workflows that are not eligible for cancelling
* (depends on cancel Mode) and assigns each workflow to groups - where workflow runs from the
* same group are put together in one array - in a map indexed by the source group id.
*
* @param repositoryInfo - information about the repository used
- * @param headRepo - head repository the event comes from
+ * @param triggeringRunInfo - information about the workflow that triggered the run
* @param cancelMode - cancel mode to use
* @param cancelFutureDuplicates - whether to cancel future duplicates
- * @param sourceRunId - source run id for the run
* @param jobNameRegexps - regexps for job names
* @param skipEventTypes - array of event names to skip
* @param workflowRuns - map of workflow runs found
- * @parm map where key is the source group id and value is array of workflow run item candidates to cancel
+ * @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, headRepo, cancelMode, cancelFutureDuplicates, sourceRunId, jobNameRegexps, skipEventTypes, workflowRuns) {
+function filterAndMapWorkflowRunsToGroups(repositoryInfo, triggeringRunInfo, cancelMode, cancelFutureDuplicates, jobNameRegexps, skipEventTypes, 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},` +
+ core.info(`\nChecking run number: ${key} RunId: ${runItem.id} Url: ${runItem.url} Status ${runItem.status}` +
` Created at ${runItem.created_at}\n`);
- if (yield isCandidateForCancelling(repositoryInfo, runItem, headRepo, cancelMode, cancelFutureDuplicates, sourceRunId, jobNameRegexps, skipEventTypes)) {
- const candidateSourceGroup = {
- workflowId: retrieveWorkflowIdFromUrl(runItem.workflow_url),
- headBranch: runItem.head_branch,
- headRepo: runItem.head_repository.full_name,
- eventName: runItem.event
- };
- const sourceGroupId = getSourceGroupId(candidateSourceGroup);
- let workflowRunArray = mapOfWorkflowRunCandidates.get(sourceGroupId);
- if (workflowRunArray === undefined) {
- workflowRunArray = [];
- mapOfWorkflowRunCandidates.set(sourceGroupId, workflowRunArray);
- }
- core.info(`The candidate ${runItem.id} has been added to ${sourceGroupId} group of candidates`);
- workflowRunArray.push(runItem);
- }
+ yield checkCandidateForCancelling(repositoryInfo, runItem, triggeringRunInfo, cancelMode, cancelFutureDuplicates, jobNameRegexps, skipEventTypes, mapOfWorkflowRunCandidates);
}
return mapOfWorkflowRunCandidates;
});
@@ -2011,7 +2052,7 @@
* @param repositoryInfo - information about the repository used
* @param selfRunId - my own run id
* @param pullRequestNumber - number of pull request
- * @param reason reason for canceling
+ * @param reason reason for cancelling
*/
function notifyPR(repositoryInfo, selfRunId, pullRequestNumber, reason) {
return __awaiter(this, void 0, void 0, function* () {
@@ -2027,9 +2068,9 @@
* @param notifyPRCancel - whether to notify the PR when cancelling
* @param selfRunId - what is the self run id
* @param sourceGroupId - what is the source group id
- * @param reason - reason for canceling
+ * @param reason - reason for cancelling
*/
-function cancelAllRunsInTheSourceGroup(repositoryInfo, sortedRunItems, notifyPRCancel, selfRunId, sourceGroupId, reason) {
+function cancelAllRunsInTheGroup(repositoryInfo, sortedRunItems, notifyPRCancel, selfRunId, sourceGroupId, reason) {
return __awaiter(this, void 0, void 0, function* () {
core.info(`\n###### Cancelling runs for ${sourceGroupId} starting from the most recent ##########\n` +
`\n Number of runs to cancel: ${sortedRunItems.length}\n`);
@@ -2051,39 +2092,40 @@
});
}
/**
- * Cancels found runs in a smart way. It takes all the found runs group by the source group, sorts them
- * descending according to create date in each of the source groups and cancels them in that order -
- * optionally skipping the first found run in each source group in case of duplicates.
+ * Cancels found runs per group. It takes all the found groups, sorts them
+ * descending according to create date in each of the groups and cancels them in that order -
+ * optionally skipping the first found run in each source group in case of duplicate cancelling.
*
* @param repositoryInfo - information about the repository used
- * @param mapOfWorkflowRunsCandidatesToCancel map of all workflow run candidates
+ * @param mapOfWorkflowRunCandidatesCandidatesToCancel map of all workflow run candidates
* @param cancelMode - cancel mode
* @param cancelFutureDuplicates - whether to cancel future duplicates
* @param notifyPRCancel - whether to notify PRs with comments
* @param selfRunId - self run Id
- * @param reason - reason for canceling
+ * @param reason - reason for cancelling
*/
-function cancelTheRunsPerGroup(repositoryInfo, mapOfWorkflowRunsCandidatesToCancel, cancelMode, cancelFutureDuplicates, notifyPRCancel, selfRunId, reason) {
+function cancelTheRunsPerGroup(repositoryInfo, mapOfWorkflowRunCandidatesCandidatesToCancel, cancelMode, cancelFutureDuplicates, notifyPRCancel, selfRunId, reason) {
return __awaiter(this, void 0, void 0, function* () {
const cancelledRuns = [];
- for (const [sourceGroupId, candidatesArray] of mapOfWorkflowRunsCandidatesToCancel) {
+ for (const [groupId, candidatesArray] of mapOfWorkflowRunCandidatesCandidatesToCancel) {
// Sort from most recent date - this way we always kill current one at the end (if we kill it at all)
const sortedRunItems = candidatesArray.sort((runItem1, runItem2) => runItem2.created_at.localeCompare(runItem1.created_at));
if (sortedRunItems.length > 0) {
if ((cancelMode === CancelMode.DUPLICATES && cancelFutureDuplicates) ||
- cancelMode === CancelMode.ALL_DUPLICATES) {
+ cancelMode === CancelMode.ALL_DUPLICATES ||
+ cancelMode === CancelMode.ALL_DUPLICATED_NAMED_JOBS) {
core.info(`\nSkipping the first run (${sortedRunItems[0].id}) of all the matching ` +
- `duplicates for '${sourceGroupId}'. This one we are going to leave in peace!\n`);
+ `duplicates for '${groupId}'. This one we are going to leave in peace!\n`);
sortedRunItems.shift();
}
if (sortedRunItems.length === 0) {
- core.info(`\nNo duplicates to cancel for ${sourceGroupId}!\n`);
+ core.info(`\nNo duplicates to cancel for ${groupId}!\n`);
continue;
}
- cancelledRuns.push(...(yield cancelAllRunsInTheSourceGroup(repositoryInfo, sortedRunItems, notifyPRCancel, selfRunId, sourceGroupId, reason)));
+ cancelledRuns.push(...(yield cancelAllRunsInTheGroup(repositoryInfo, sortedRunItems, notifyPRCancel, selfRunId, groupId, reason)));
}
else {
- core.info(`\n###### There are no runs to cancel for ${sourceGroupId} ##########\n`);
+ core.info(`\n###### There are no runs to cancel for ${groupId} ##########\n`);
}
}
return cancelledRuns;
@@ -2093,14 +2135,7 @@
* Find and cancels runs based on the criteria chosen.
* @param repositoryInfo - information about the repository used
* @param selfRunId - number of own run id
- * @param sourceWorkflowId - source workflow id that triggered the workflow
- * (might be different than self for workflow_run)
- * @param sourceRunId - source run id that triggered the workflow
- * (might be different than self for workflow_run)
- * @param headRepo - head repository that triggered the workflow (repo from which PR came)
- * @param headBranch - branch of the PR that triggered the workflow (when it is triggered by PR)
- * @param sourceEventName - name of the event that triggered the workflow
- * (different than self event name for workflow_run)
+ * @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
@@ -2110,18 +2145,12 @@
* @param reason - reason for cancelling
* @return array of canceled workflow run ids
*/
-function findAndCancelRuns(repositoryInfo, selfRunId, sourceWorkflowId, sourceRunId, headRepo, headBranch, sourceEventName, cancelMode, cancelFutureDuplicates, notifyPRCancel, notifyPRMessageStart, jobNameRegexps, skipEventTypes, reason) {
+function findAndCancelRuns(repositoryInfo, selfRunId, triggeringRunInfo, cancelMode, cancelFutureDuplicates, notifyPRCancel, notifyPRMessageStart, jobNameRegexps, skipEventTypes, reason) {
return __awaiter(this, void 0, void 0, function* () {
const statusValues = ['queued', 'in_progress'];
- const mySourceGroup = {
- headBranch,
- headRepo,
- eventName: sourceEventName,
- workflowId: sourceWorkflowId
- };
- const workflowRuns = yield getWorkflowRunsMatchingCriteria(repositoryInfo, statusValues, cancelMode, sourceWorkflowId, sourceRunId, sourceEventName, mySourceGroup);
- const mapOfWorkflowRunsCandidatesToCancel = yield filterAndMapWorkflowRunsToGroups(repositoryInfo, headRepo, cancelMode, cancelFutureDuplicates, sourceRunId, jobNameRegexps, skipEventTypes, workflowRuns);
- return yield cancelTheRunsPerGroup(repositoryInfo, mapOfWorkflowRunsCandidatesToCancel, cancelMode, cancelFutureDuplicates, notifyPRCancel, selfRunId, reason);
+ const workflowRuns = yield getWorkflowRunsMatchingCriteria(repositoryInfo, statusValues, cancelMode, triggeringRunInfo);
+ const mapOfWorkflowRunCandidatesCandidatesToCancel = yield filterAndMapWorkflowRunsToGroups(repositoryInfo, triggeringRunInfo, cancelMode, cancelFutureDuplicates, jobNameRegexps, skipEventTypes, workflowRuns);
+ return yield cancelTheRunsPerGroup(repositoryInfo, mapOfWorkflowRunCandidatesCandidatesToCancel, cancelMode, cancelFutureDuplicates, notifyPRCancel, selfRunId, reason);
});
}
/**
@@ -2138,12 +2167,12 @@
return value;
}
/**
- * Gets origin of the runId - if this is a workflow run, it returns the information about the source run
+ * Gets source run using of the runId - if this is a workflow run, it returns the information about the source run
* @param repositoryInfo - information about the repository used
* @param runId - run id of the run to check
- * @return information about the triggering workflow
+ * @return information about the triggering run
*/
-function getOrigin(repositoryInfo, runId) {
+function getTriggeringRunInfo(repositoryInfo, runId) {
return __awaiter(this, void 0, void 0, function* () {
const reply = yield repositoryInfo.octokit.actions.getWorkflowRun({
owner: repositoryInfo.owner,
@@ -2160,6 +2189,8 @@
pullRequest = yield findPullRequest(repositoryInfo, sourceRun.head_repository.owner.login, sourceRun.head_branch, sourceRun.head_sha);
}
return {
+ workflowId: retrieveWorkflowIdFromUrl(reply.data.workflow_url),
+ runId,
headRepo: reply.data.head_repository.full_name,
headBranch: reply.data.head_branch,
headSha: reply.data.head_sha,
@@ -2174,14 +2205,7 @@
*
* @param repositoryInfo - information about the repository used
* @param selfRunId - number of own run id
- * @param sourceWorkflowId - source workflow id that triggered the workflow
- * (might be different than self for workflow_run)
- * @param sourceRunId - source run id that triggered the workflow
- * (might be different than self for workflow_run)
- * @param headRepo - head repository that triggered the workflow (repo from which PR came)
- * @param headBranch - branch of the PR that triggered the workflow (when it is triggered by PR)
- * @param sourceEventName - name of the event that triggered the workflow
- * (different than self event name for workflow_run)
+ * @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
@@ -2191,41 +2215,51 @@
* @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
*/
-function performCancelJob(repositoryInfo, selfRunId, sourceWorkflowId, sourceRunId, headRepo, headBranch, sourceEventName, cancelMode, notifyPRCancel, notifyPRCancelMessage, notifyPRMessageStart, jobNameRegexps, skipEventTypes, cancelFutureDuplicates) {
+function performCancelJob(repositoryInfo, selfRunId, triggeringRunInfo, cancelMode, notifyPRCancel, notifyPRCancelMessage, notifyPRMessageStart, jobNameRegexps, skipEventTypes, cancelFutureDuplicates) {
return __awaiter(this, void 0, void 0, function* () {
core.info('\n###################################################################################\n');
- core.info(`All parameters: owner: ${repositoryInfo.owner}, repo: ${repositoryInfo.repo}, run id: ${sourceRunId}, ` +
- `head repo ${headRepo}, headBranch: ${headBranch}, ` +
- `sourceEventName: ${sourceEventName}, cancelMode: ${cancelMode}, jobNames: ${jobNameRegexps}`);
+ core.info(`All parameters: owner: ${repositoryInfo.owner}, repo: ${repositoryInfo.repo}, ` +
+ `run id: ${triggeringRunInfo.runId}, ` +
+ `head repo ${triggeringRunInfo.headRepo}, headBranch: ${triggeringRunInfo.headBranch}, ` +
+ `sourceEventName: ${triggeringRunInfo.eventName}, ` +
+ `cancelMode: ${cancelMode}, jobNames: ${jobNameRegexps}`);
core.info('\n###################################################################################\n');
let reason = '';
if (cancelMode === CancelMode.SELF) {
- core.info(`# Cancelling source run: ${sourceRunId} for workflow ${sourceWorkflowId}.`);
+ core.info(`# Cancelling source run: ${triggeringRunInfo.runId} ` +
+ `for workflow ${triggeringRunInfo.workflowId}.`);
reason = notifyPRCancelMessage
? notifyPRCancelMessage
: `The job has been cancelled by another workflow.`;
}
else if (cancelMode === CancelMode.FAILED_JOBS) {
- core.info(`# Cancel all runs for workflow ${sourceWorkflowId} where job names matching ${jobNameRegexps} failed.`);
+ core.info(`# Cancel all runs for workflow ${triggeringRunInfo.workflowId} ` +
+ `where job names matching ${jobNameRegexps} failed.`);
reason = `It has some failed jobs matching ${jobNameRegexps}.`;
}
else if (cancelMode === CancelMode.NAMED_JOBS) {
- core.info(`# Cancel all runs for workflow ${sourceWorkflowId} have job names matching ${jobNameRegexps}.`);
+ core.info(`# Cancel all runs for workflow ${triggeringRunInfo.workflowId} ` +
+ `have job names matching ${jobNameRegexps}.`);
reason = `It has jobs matching ${jobNameRegexps}.`;
}
else if (cancelMode === CancelMode.DUPLICATES) {
- core.info(`# Cancel duplicate runs for workflow ${sourceWorkflowId} for same triggering branch as own run Id.`);
- reason = `It is an earlier duplicate of ${sourceWorkflowId} run.`;
+ core.info(`# Cancel duplicate runs for workflow ${triggeringRunInfo.workflowId} ` +
+ `for same triggering branch as own run Id.`);
+ reason = `It is an earlier duplicate of ${triggeringRunInfo.workflowId} run.`;
}
else if (cancelMode === CancelMode.ALL_DUPLICATES) {
- core.info(`# Cancel all duplicates runs started for workflow ${sourceWorkflowId}.`);
- reason = `It is an earlier duplicate of ${sourceWorkflowId} run.`;
+ core.info(`# Cancel all duplicates runs started for workflow ${triggeringRunInfo.workflowId}.`);
+ reason = `It is an earlier duplicate of ${triggeringRunInfo.workflowId} run.`;
+ }
+ else if (cancelMode === CancelMode.ALL_DUPLICATED_NAMED_JOBS) {
+ core.info(`# Cancel all duplicate named jobs matching the patterns ${jobNameRegexps}.`);
+ reason = `It is an earlier duplicate of ${triggeringRunInfo.workflowId} run.`;
}
else {
- throw Error(`Wrong cancel mode ${cancelMode}! This should never happen.`);
+ throw Error(`Wrong cancel mode ${cancelMode}! Please correct it.`);
}
core.info('\n###################################################################################\n');
- return yield findAndCancelRuns(repositoryInfo, selfRunId, sourceWorkflowId, sourceRunId, headRepo, headBranch, sourceEventName, cancelMode, cancelFutureDuplicates, notifyPRCancel, notifyPRMessageStart, jobNameRegexps, skipEventTypes, reason);
+ return yield findAndCancelRuns(repositoryInfo, selfRunId, triggeringRunInfo, cancelMode, cancelFutureDuplicates, notifyPRCancel, notifyPRMessageStart, jobNameRegexps, skipEventTypes, reason);
});
}
/**
@@ -2235,27 +2269,27 @@
*
* @param repositoryInfo - information about the repository used
* @param workflowFileName - optional workflow file name
- * @param sourceRunId - source run id of the workfow
+ * @param runId - run id of the workflow
* @param selfRunId - self run id
* @return workflow id that is associate with the workflow we are going to act on.
*/
-function retrieveWorkflowId(repositoryInfo, workflowFileName, sourceRunId, selfRunId) {
+function retrieveWorkflowId(repositoryInfo, workflowFileName, runId, selfRunId) {
return __awaiter(this, void 0, void 0, function* () {
- let sourceWorkflowId;
+ let workflowId;
if (workflowFileName) {
- sourceWorkflowId = workflowFileName;
- core.info(`\nFinding runs for another workflow found by ${workflowFileName} name: ${sourceWorkflowId}\n`);
+ workflowId = workflowFileName;
+ core.info(`\nFinding runs for another workflow found by ${workflowFileName} name: ${workflowId}\n`);
}
else {
- sourceWorkflowId = yield getWorkflowId(repositoryInfo, sourceRunId);
- if (sourceRunId === selfRunId) {
- core.info(`\nFinding runs for my own workflow ${sourceWorkflowId}\n`);
+ workflowId = yield getWorkflowId(repositoryInfo, runId);
+ if (runId === selfRunId) {
+ core.info(`\nFinding runs for my own workflow ${workflowId}\n`);
}
else {
- core.info(`\nFinding runs for source workflow ${sourceWorkflowId}\n`);
+ core.info(`\nFinding runs for source workflow ${workflowId}\n`);
}
}
- return sourceWorkflowId;
+ return workflowId;
});
}
/**
@@ -2273,15 +2307,15 @@
* are verified and in case od unexpected combination found, approrpriate error is raised.
*
* @param eventName - name of the event to act on
- * @param sourceRunId - run id of the triggering event
+ * @param runId - run id of the triggering event
* @param selfRunId - our own run id
* @param cancelMode - cancel mode used
* @param cancelFutureDuplicates - whether future duplicate cancelling is enabled
* @param jobNameRegexps - array of regular expression of job names
*/
-function performSanityChecks(eventName, sourceRunId, selfRunId, cancelMode, cancelFutureDuplicates, jobNameRegexps) {
+function performSanityChecks(eventName, runId, selfRunId, cancelMode, cancelFutureDuplicates, jobNameRegexps) {
if (eventName === 'workflow_run' &&
- sourceRunId === selfRunId &&
+ runId === selfRunId &&
cancelMode === CancelMode.DUPLICATES) {
throw Error(`You cannot run "workflow_run" in ${cancelMode} cancelMode without "sourceId" input.` +
'It will likely not work as you intended - it will cancel runs which are not duplicates!' +
@@ -2293,9 +2327,19 @@
CancelMode.SELF,
CancelMode.ALL_DUPLICATES
].includes(cancelMode)) {
- throw Error(`You cannot specify jobNames on ${cancelMode} cancelMode.`);
+ throw Error(`You cannot specify jobNamesRegexps on ${cancelMode} cancelMode.`);
}
- if (cancelMode === CancelMode.ALL_DUPLICATES && !cancelFutureDuplicates) {
+ if (jobNameRegexps.length === 0 &&
+ [
+ CancelMode.NAMED_JOBS,
+ CancelMode.FAILED_JOBS,
+ CancelMode.ALL_DUPLICATED_NAMED_JOBS
+ ].includes(cancelMode)) {
+ throw Error(`You must specify jobNamesRegexps on ${cancelMode} cancelMode.`);
+ }
+ if ((cancelMode === CancelMode.ALL_DUPLICATES ||
+ cancelMode === CancelMode.ALL_DUPLICATED_NAMED_JOBS) &&
+ !cancelFutureDuplicates) {
throw Error(`The ${cancelMode} cancelMode has to have cancelFutureDuplicates set to true.`);
}
}
@@ -2303,42 +2347,39 @@
* Produces basic outputs for the action. This does not include cancelled workflow run id - those are
* set after cancelling is done.
*
- * @param triggeringWorkflowInfo
+ * @param triggeringRunInfo
*/
-function produceBasicOutputs(triggeringWorkflowInfo) {
- verboseOutput('sourceHeadRepo', triggeringWorkflowInfo.headRepo);
- verboseOutput('sourceHeadBranch', triggeringWorkflowInfo.headBranch);
- verboseOutput('sourceHeadSha', triggeringWorkflowInfo.headSha);
- verboseOutput('sourceEvent', triggeringWorkflowInfo.eventName);
- verboseOutput('pullRequestNumber', triggeringWorkflowInfo.pullRequest
- ? triggeringWorkflowInfo.pullRequest.number.toString()
+function produceBasicOutputs(triggeringRunInfo) {
+ verboseOutput('sourceHeadRepo', triggeringRunInfo.headRepo);
+ verboseOutput('sourceHeadBranch', triggeringRunInfo.headBranch);
+ verboseOutput('sourceHeadSha', triggeringRunInfo.headSha);
+ verboseOutput('sourceEvent', triggeringRunInfo.eventName);
+ verboseOutput('pullRequestNumber', triggeringRunInfo.pullRequest
+ ? triggeringRunInfo.pullRequest.number.toString()
: '');
- verboseOutput('mergeCommitSha', triggeringWorkflowInfo.mergeCommitSha
- ? triggeringWorkflowInfo.mergeCommitSha
- : '');
- verboseOutput('targetCommitSha', triggeringWorkflowInfo.mergeCommitSha
- ? triggeringWorkflowInfo.mergeCommitSha
- : triggeringWorkflowInfo.headSha);
+ verboseOutput('mergeCommitSha', triggeringRunInfo.mergeCommitSha ? triggeringRunInfo.mergeCommitSha : '');
+ verboseOutput('targetCommitSha', triggeringRunInfo.mergeCommitSha
+ ? triggeringRunInfo.mergeCommitSha
+ : triggeringRunInfo.headSha);
}
/**
* Notifies the PR that the action has started.
*
* @param repositoryInfo information about the repository
- * @param triggeringWorkflowInfo information about the triggering workflow
- * @param sourceRunId run id of the source workflow
+ * @param triggeringRunInfo information about the triggering workflow
* @param selfRunId self run id
* @param notifyPRMessageStart whether to notify about the start of the action
*/
-function notifyActionStart(repositoryInfo, triggeringWorkflowInfo, sourceRunId, selfRunId, notifyPRMessageStart) {
+function notifyActionStart(repositoryInfo, triggeringRunInfo, selfRunId, notifyPRMessageStart) {
return __awaiter(this, void 0, void 0, function* () {
- if (notifyPRMessageStart && triggeringWorkflowInfo.pullRequest) {
+ if (notifyPRMessageStart && triggeringRunInfo.pullRequest) {
const selfWorkflowRunUrl = `https://github.com/${repositoryInfo.owner}/${repositoryInfo.repo}` +
`/actions/runs/${selfRunId}`;
yield repositoryInfo.octokit.issues.createComment({
owner: repositoryInfo.owner,
repo: repositoryInfo.repo,
// eslint-disable-next-line @typescript-eslint/camelcase
- issue_number: triggeringWorkflowInfo.pullRequest.number,
+ issue_number: triggeringRunInfo.pullRequest.number,
body: `${notifyPRMessageStart} [The workflow run](${selfWorkflowRunUrl})`
});
}
@@ -2383,10 +2424,10 @@
`Event name: ${eventName}, CancelMode: ${cancelMode}, ` +
`sourceWorkflowId: ${sourceWorkflowId}, sourceRunId: ${sourceRunId}, selfRunId: ${selfRunId}, ` +
`jobNames: ${jobNameRegexps}`);
- const triggeringWorkflowInfo = yield getOrigin(repositoryInfo, sourceRunId);
- produceBasicOutputs(triggeringWorkflowInfo);
- yield notifyActionStart(repositoryInfo, triggeringWorkflowInfo, sourceRunId, selfRunId, notifyPRMessageStart);
- const cancelledRuns = yield performCancelJob(repositoryInfo, selfRunId, sourceWorkflowId, sourceRunId, triggeringWorkflowInfo.headRepo, triggeringWorkflowInfo.headBranch, triggeringWorkflowInfo.eventName, cancelMode, notifyPRCancel, notifyPRCancelMessage, notifyPRMessageStart, jobNameRegexps, skipEventTypes, cancelFutureDuplicates);
+ 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);
verboseOutput('cancelledRuns', JSON.stringify(cancelledRuns));
});
}
diff --git a/src/main.ts b/src/main.ts
index 19b00b3..abf39f1 100644
--- a/src/main.ts
+++ b/src/main.ts
@@ -21,21 +21,8 @@
ALL_DUPLICATES = 'allDuplicates',
SELF = 'self',
FAILED_JOBS = 'failedJobs',
- NAMED_JOBS = 'namedJobs'
-}
-
-/**
- * Stores information uniquely identifying the run by it's triggering source:
- * It is the workflow id, Head Repo and Branch the change originates from and event name that originates it.
- *
- * All Runs coming from the same Pull Request share the same Source Group. Also all pushes to master
- * share the same Source Group
- */
-interface WorkflowRunSourceGroup {
- workflowId: number | string
- headRepo: string
- headBranch: string
- eventName: string
+ NAMED_JOBS = 'namedJobs',
+ ALL_DUPLICATED_NAMED_JOBS = 'allDuplicatedNamedJobs'
}
/**
@@ -51,7 +38,9 @@
/**
* Stores information about the workflow info that triggered the current workflow.
*/
-interface TriggeringWorkflowInfo {
+interface TriggeringRunInfo {
+ workflowId: number
+ runId: number
headRepo: string
headBranch: string
headSha: string
@@ -63,11 +52,14 @@
/**
* Converts the source of a run object into a string that can be used as map key in maps where we keep
* arrays of runs per source group
- * @param sourceGroup the object identifying the source of the run (Pull Request, Master Push)
+ * @param triggeringRunInfo the object identifying the triggering workflow
* @returns the unique string id for the source group
*/
-function getSourceGroupId(sourceGroup: WorkflowRunSourceGroup): string {
- return `:${sourceGroup.workflowId}:${sourceGroup.headRepo}:${sourceGroup.headBranch}:${sourceGroup.eventName}`
+function getSourceGroupId(triggeringRunInfo: TriggeringRunInfo): string {
+ return (
+ `:${triggeringRunInfo.workflowId}:${triggeringRunInfo.headRepo}` +
+ `:${triggeringRunInfo.headBranch}:${triggeringRunInfo.eventName}`
+ )
}
/**
@@ -76,22 +68,22 @@
*
* @param repositoryInfo - information about the repository used
* @param status - status of the run that we are querying for
- * @param mySourceGroup - source group of the originating run
+ * @param triggeringRunInfo - information about the workflow that triggered the run
* @return query parameters merged with the listWorkflowRuns criteria
*/
function createListRunsQueryRunsSameSource(
repositoryInfo: RepositoryInfo,
status: string,
- mySourceGroup: WorkflowRunSourceGroup
+ triggeringRunInfo: TriggeringRunInfo
): rest.RequestOptions {
const request = {
owner: repositoryInfo.owner,
repo: repositoryInfo.repo,
// eslint-disable-next-line @typescript-eslint/camelcase
- workflow_id: mySourceGroup.workflowId,
+ workflow_id: triggeringRunInfo.workflowId,
status,
- branch: mySourceGroup.headBranch,
- event: mySourceGroup.eventName
+ branch: triggeringRunInfo.headBranch,
+ event: triggeringRunInfo.eventName
}
return repositoryInfo.octokit.actions.listWorkflowRuns.endpoint.merge(request)
}
@@ -99,24 +91,22 @@
* Creates query parameters selecting only specific run Id.
* @param repositoryInfo - information about the repository used
* @param status - status of the run that we are querying for
- * @param workflowId - Id of the workflow to retrieve
- * @param runId - run Id to retrieve
+ * @param triggeringRunInfo - information about the workflow that triggered the run
* @return query parameters merged with the listWorkflowRuns criteria
*/
function createListRunsQuerySpecificRunId(
repositoryInfo: RepositoryInfo,
status: string,
- workflowId: number | string,
- runId: number
+ triggeringRunInfo: TriggeringRunInfo
): rest.RequestOptions {
const request = {
owner: repositoryInfo.owner,
repo: repositoryInfo.repo,
// eslint-disable-next-line @typescript-eslint/camelcase
- workflow_id: workflowId,
+ workflow_id: triggeringRunInfo.workflowId,
status,
// eslint-disable-next-line @typescript-eslint/camelcase
- run_id: runId.toString()
+ run_id: triggeringRunInfo.runId.toString()
}
return repositoryInfo.octokit.actions.listWorkflowRuns.endpoint.merge(request)
}
@@ -168,15 +158,44 @@
* Returns true if the string matches any of the regexps in array of regexps
* @param stringToMatch string to match
* @param regexps array of regexp to match the string against
- * @return true if there is a match
+ * @return array of [matched (boolean), [array of matched strings]]
*/
-function matchInArray(stringToMatch: string, regexps: string[]): boolean {
+function matchInArray(
+ stringToMatch: string,
+ regexps: string[]
+): [boolean, string[]] {
+ let matched = false
+ const allMatches: string[] = []
for (const regexp of regexps) {
- if (stringToMatch.match(regexp)) {
- return true
+ const match = stringToMatch.match(regexp)
+ if (match) {
+ matched = true
+ allMatches.push(match[0])
}
}
- return false
+ return [matched, allMatches]
+}
+
+/**
+ * Adds workflow run to the array in the map indexed by key.
+ * @param key key to use
+ * @param runItem run Item to add
+ * @param mapOfWorkflowRunCandidates map of workflow runs to add the run item to
+ */
+function addWorkflowRunToMap(
+ key: string,
+ runItem: rest.ActionsListWorkflowRunsResponseWorkflowRunsItem,
+ mapOfWorkflowRunCandidates: Map<
+ string,
+ rest.ActionsListWorkflowRunsResponseWorkflowRunsItem[]
+ >
+): void {
+ let arrayOfRuns = mapOfWorkflowRunCandidates.get(key)
+ if (arrayOfRuns === undefined) {
+ arrayOfRuns = []
+ mapOfWorkflowRunCandidates.set(key, arrayOfRuns)
+ }
+ arrayOfRuns.push(runItem)
}
/**
@@ -188,14 +207,14 @@
* @param runId - Id of the run to retrieve jobs for
* @param jobNameRegexps - array of job name regexps
* @param checkIfFailed - whether to check the 'failed' status of matched jobs
- * @return true if there is a match
+ * @return array of [matched (boolean), array of matches]
*/
async function jobsMatchingNames(
repositoryInfo: RepositoryInfo,
runId: number,
jobNameRegexps: string[],
checkIfFailed: boolean
-): Promise<boolean> {
+): Promise<[boolean, string[]]> {
const listJobs = createJobsForWorkflowRunQuery(repositoryInfo, runId)
if (checkIfFailed) {
core.info(
@@ -206,18 +225,22 @@
`\nChecking if runId ${runId} has job names matching any of the ${jobNameRegexps}\n`
)
}
+ const allMatches: string[] = []
+ let matched = false
for await (const item of repositoryInfo.octokit.paginate.iterator(listJobs)) {
- for (const job of item.data.jobs) {
+ for (const job of item.data) {
core.info(` The job name: ${job.name}, Conclusion: ${job.conclusion}`)
- if (matchInArray(job.name, jobNameRegexps)) {
+ const [jobMatched, jobMatches] = matchInArray(job.name, jobNameRegexps)
+ if (jobMatched) {
+ allMatches.push(...jobMatches)
+ matched = true
if (checkIfFailed) {
// Only fail the build if one of the matching jobs fail
if (job.conclusion === 'failure') {
core.info(
` The Job ${job.name} matches one of the ${jobNameRegexps} regexps and it failed.` +
- ` Cancelling run.`
+ ` It will be added to the candidates.`
)
- return true
} else {
core.info(
` The Job ${job.name} matches one of the ${jobNameRegexps} regexps but it did not fail. ` +
@@ -227,14 +250,14 @@
} else {
// Fail the build if any of the job names match
core.info(
- ` The Job ${job.name} matches one of the ${jobNameRegexps} regexps. Cancelling run.`
+ ` The Job ${job.name} matches one of the ${jobNameRegexps} regexps. ` +
+ `It will be added to the candidates.`
)
- return true
}
}
}
}
- return false
+ return [matched, allMatches]
}
/**
@@ -310,86 +333,135 @@
/**
* True if the request is candidate for cancelling in case of duplicate deletion
* @param runItem item to check
- * @param headRepo head Repo that we are checking against
* @param cancelFutureDuplicates whether future duplicates are being cancelled
- * @param sourceRunId the source Run Id that originates the request
+ * @param triggeringRunInfo - information about the workflow that triggered the run
+ * @param mapOfWorkflowRunCandidates - map of the workflow runs to add candidates to
* @return true if we determine that the run Id should be cancelled
*/
-function isCandidateForCancellingDuplicate(
+function checkCandidateForCancellingDuplicate(
runItem: rest.ActionsListWorkflowRunsResponseWorkflowRunsItem,
- headRepo: string,
cancelFutureDuplicates: boolean,
- sourceRunId: number
-): boolean {
+ triggeringRunInfo: TriggeringRunInfo,
+ mapOfWorkflowRunCandidates: Map<
+ string,
+ rest.ActionsListWorkflowRunsResponseWorkflowRunsItem[]
+ >
+): void {
const runHeadRepo = runItem.head_repository.full_name
- if (headRepo !== undefined && runHeadRepo !== headRepo) {
+ if (
+ triggeringRunInfo.headRepo !== undefined &&
+ runHeadRepo !== triggeringRunInfo.headRepo
+ ) {
core.info(
`\nThe run ${runItem.id} is from a different ` +
- `repo: ${runHeadRepo} (expected ${headRepo}). Not cancelling it\n`
+ `repo: ${runHeadRepo} (expected ${triggeringRunInfo.headRepo}). Not adding as candidate to cancel.\n`
)
- return false
}
if (cancelFutureDuplicates) {
core.info(
`\nCancel Future Duplicates: Returning run id that might be duplicate or my own run: ${runItem.id}.\n`
)
- return true
+ addWorkflowRunToMap(
+ getSourceGroupId(triggeringRunInfo),
+ runItem,
+ mapOfWorkflowRunCandidates
+ )
} else {
- if (runItem.id === sourceRunId) {
+ if (runItem.id === triggeringRunInfo.runId) {
core.info(`\nThis is my own run ${runItem.id}. Not returning myself!\n`)
- return false
- } else if (runItem.id > sourceRunId) {
+ } else if (runItem.id > triggeringRunInfo.runId) {
core.info(
- `\nThe run ${runItem.id} is started later than my own run ${sourceRunId}. Not returning it\n`
+ `\nThe run ${runItem.id} is started later than my own ` +
+ `run ${triggeringRunInfo.runId}. Not returning it\n`
)
- return false
}
core.info(`\nFound duplicate of my own run: ${runItem.id}.\n`)
- return true
}
}
/**
- * Should the run is candidate for cancelling in SELF cancelling mode?
+ * Should the run be candidate for cancelling in SELF cancelling mode?
* @param runItem run item
- * @param sourceRunId source run id
- * @return true if the run item is self
+ * @param triggeringRunInfo - information about the workflow that triggered the run
+ * @param mapOfWorkflowRunCandidates - map of the workflow runs to add candidates to
*/
-function isCandidateForCancellingSelf(
+function checkCandidateForCancellingSelf(
runItem: rest.ActionsListWorkflowRunsResponseWorkflowRunsItem,
- sourceRunId: number
-): boolean {
- if (runItem.id === sourceRunId) {
- core.info(`\nReturning the "source" run: ${runItem.id}.\n`)
- return true
- } else {
- return false
+ triggeringRunInfo: TriggeringRunInfo,
+ mapOfWorkflowRunCandidates: Map<
+ string,
+ rest.ActionsListWorkflowRunsResponseWorkflowRunsItem[]
+ >
+): void {
+ if (runItem.id === triggeringRunInfo.runId) {
+ core.info(`\nAdding the "source" run: ${runItem.id} to candidates.\n`)
+ addWorkflowRunToMap(
+ getSourceGroupId(triggeringRunInfo),
+ runItem,
+ mapOfWorkflowRunCandidates
+ )
}
}
/**
+ * Should the run be candidate for cancelling of all duplicates
+ * @param runItem run item
+ * @param triggeringRunInfo - information about the workflow that triggered the run
+ * @param mapOfWorkflowRunCandidates - map of the workflow runs to add candidates to
+ */
+function checkCandidateForAllDuplicates(
+ runItem: rest.ActionsListWorkflowRunsResponseWorkflowRunsItem,
+ triggeringRunInfo: TriggeringRunInfo,
+ mapOfWorkflowRunCandidates: Map<
+ string,
+ rest.ActionsListWorkflowRunsResponseWorkflowRunsItem[]
+ >
+): void {
+ core.info(`\nAdding the run: ${runItem.id} to candidates.\n`)
+ addWorkflowRunToMap(
+ getSourceGroupId(triggeringRunInfo),
+ runItem,
+ mapOfWorkflowRunCandidates
+ )
+}
+
+/**
* Should the run is candidate for cancelling in naming job cancelling mode?
* @param repositoryInfo - information about the repository used
* @param runItem run item
* @param jobNamesRegexps array of regexps to match job names against
+ * @param triggeringRunInfo - information about the workflow that triggered the run
+ * @param mapOfWorkflowRunCandidates - map of the workflow runs to add candidates to
* @return true if the run item contains jobs with names matching the pattern
*/
-async function isCandidateForCancellingNamedJobs(
+async function checkCandidateForCancellingNamedJobs(
repositoryInfo: RepositoryInfo,
runItem: rest.ActionsListWorkflowRunsResponseWorkflowRunsItem,
- jobNamesRegexps: string[]
-): Promise<boolean> {
+ jobNamesRegexps: string[],
+ triggeringRunInfo: TriggeringRunInfo,
+ mapOfWorkflowRunCandidates: Map<
+ string,
+ rest.ActionsListWorkflowRunsResponseWorkflowRunsItem[]
+ >
+): Promise<void> {
// Cancel all jobs that have failed jobs (no matter when started)
- if (
- await jobsMatchingNames(repositoryInfo, runItem.id, jobNamesRegexps, false)
- ) {
+ const [matched, allMatches] = await jobsMatchingNames(
+ repositoryInfo,
+ runItem.id,
+ jobNamesRegexps,
+ false
+ )
+ if (matched) {
core.info(
- `\nSome jobs have matching names in ${runItem.id} . Returning it.\n`
+ `\nSome jobs have matching names in ${runItem.id}: ${allMatches}. Adding it candidates.\n`
)
- return true
+ addWorkflowRunToMap(
+ getSourceGroupId(triggeringRunInfo),
+ runItem,
+ mapOfWorkflowRunCandidates
+ )
} else {
- core.info(`\nNone of the jobs match name in ${runItem.id}. Returning it.\n`)
- return false
+ core.info(`\nNone of the jobs match name in ${runItem.id}.\n`)
}
}
@@ -398,99 +470,165 @@
* @param repositoryInfo - information about the repository used
* @param runItem run item
* @param jobNamesRegexps array of regexps to match job names against
+ * @param triggeringRunInfo - information about the workflow that triggered the run
+ * @param mapOfWorkflowRunCandidates - map of the workflow runs to add candidates to
* @return true if the run item contains failed jobs with names matching the pattern
*/
-async function isCandidateForCancellingFailedJobs(
+async function checkCandidateForCancellingFailedJobs(
repositoryInfo: RepositoryInfo,
runItem: rest.ActionsListWorkflowRunsResponseWorkflowRunsItem,
- jobNamesRegexps: string[]
-): Promise<boolean> {
+ jobNamesRegexps: string[],
+ triggeringRunInfo: TriggeringRunInfo,
+ mapOfWorkflowRunCandidates: Map<
+ string,
+ rest.ActionsListWorkflowRunsResponseWorkflowRunsItem[]
+ >
+): Promise<void> {
// Cancel all jobs that have failed jobs (no matter when started)
- if (
- await jobsMatchingNames(repositoryInfo, runItem.id, jobNamesRegexps, true)
- ) {
+ const [matched, allMatches] = await jobsMatchingNames(
+ repositoryInfo,
+ runItem.id,
+ jobNamesRegexps,
+ true
+ )
+ if (matched) {
core.info(
- `\nSome matching named jobs failed in ${runItem.id} . Cancelling it.\n`
+ `\nSome matching named jobs failed in ${runItem.id}: ${allMatches}. Adding it to candidates.\n`
)
- return true
+ addWorkflowRunToMap(
+ getSourceGroupId(triggeringRunInfo),
+ runItem,
+ mapOfWorkflowRunCandidates
+ )
} else {
core.info(
- `\nNone of the matching jobs failed in ${runItem.id}. Not cancelling it.\n`
+ `\nNone of the matching jobs failed in ${runItem.id}. Not adding as candidate to cancel.\n`
)
- return false
}
}
/**
- * Determines whether the run is candidate to be cancelled depending on the mode used
+ * Checks if the run is candidate for duplicate cancelling of named jobs. It adds it to the map
+ * including the match as a key for duplicate detection.
* @param repositoryInfo - information about the repository used
* @param runItem - run item
- * @param headRepo - head repository
- * @param cancelMode - cancel mode
- * @param cancelFutureDuplicates - whether to cancel future duplicates
- * @param sourceRunId - what is the source run id
- * @param jobNamesRegexps - what are the regexps for job names
- * @param skipEventTypes - which events should be skipped
- * @return true if the run id is candidate for cancelling
+ * @param jobNamesRegexps - array of regexps to match job names against
+ * @param mapOfWorkflowRunCandidates - map of runs to update
*/
-async function isCandidateForCancelling(
+async function checkCandidateForDuplicateNamedJobs(
repositoryInfo: RepositoryInfo,
runItem: rest.ActionsListWorkflowRunsResponseWorkflowRunsItem,
- headRepo: string,
+ jobNamesRegexps: string[],
+ mapOfWorkflowRunCandidates: Map<
+ string,
+ rest.ActionsListWorkflowRunsResponseWorkflowRunsItem[]
+ >
+): Promise<void> {
+ const [matched, allMatches] = await jobsMatchingNames(
+ repositoryInfo,
+ runItem.id,
+ jobNamesRegexps,
+ false
+ )
+ if (matched) {
+ for (const match of allMatches) {
+ // This is the only case where we are not using source group to cancelling candidates but
+ // the match of job names
+ addWorkflowRunToMap(match, runItem, mapOfWorkflowRunCandidates)
+ }
+ }
+}
+
+/**
+ * Determines whether the run is candidate to be cancelled depending on the mode used and add it to the map
+ * of workflow names if it is.
+ * @param repositoryInfo - information about the repository used
+ * @param runItem - run item
+ * @param triggeringRunInfo - information about the workflow that triggered the run
+ * @param cancelMode - cancel mode
+ * @param cancelFutureDuplicates - whether to cancel future duplicates
+ * @param jobNamesRegexps - what are the regexps for job names
+ * @param skipEventTypes - which events should be skipped
+ * @param mapOfWorkflowRunCandidates - map of workflow runs to add candidates to
+ */
+async function checkCandidateForCancelling(
+ repositoryInfo: RepositoryInfo,
+ runItem: rest.ActionsListWorkflowRunsResponseWorkflowRunsItem,
+ triggeringRunInfo: TriggeringRunInfo,
cancelMode: CancelMode,
cancelFutureDuplicates: boolean,
- sourceRunId: number,
jobNamesRegexps: string[],
- skipEventTypes: string[]
-): Promise<boolean> {
+ skipEventTypes: string[],
+ mapOfWorkflowRunCandidates: Map<
+ string,
+ rest.ActionsListWorkflowRunsResponseWorkflowRunsItem[]
+ >
+): Promise<void> {
if ('completed' === runItem.status.toString()) {
- core.info(`\nThe run ${runItem.id} is completed. Not cancelling it.\n`)
- return false
+ core.info(
+ `\nThe run ${runItem.id} is completed. Not adding as candidate to cancel.\n`
+ )
+ return
}
if (!CANCELLABLE_EVENT_TYPES.includes(runItem.event.toString())) {
core.info(
`\nThe run ${runItem.id} is (${runItem.event} event - not ` +
- `in ${CANCELLABLE_EVENT_TYPES}). Not cancelling it.\n`
+ `in ${CANCELLABLE_EVENT_TYPES}). Not adding as candidate to cancel.\n`
)
- return false
+ return
}
if (skipEventTypes.includes(runItem.event.toString())) {
core.info(
`\nThe run ${runItem.id} is (${runItem.event} event - ` +
- `it is in skipEventTypes ${skipEventTypes}). Not cancelling it.\n`
+ `it is in skipEventTypes ${skipEventTypes}). Not adding as candidate to cancel.\n`
)
- return false
+ return
}
if (cancelMode === CancelMode.FAILED_JOBS) {
- return await isCandidateForCancellingFailedJobs(
+ await checkCandidateForCancellingFailedJobs(
repositoryInfo,
runItem,
- jobNamesRegexps
+ jobNamesRegexps,
+ triggeringRunInfo,
+ mapOfWorkflowRunCandidates
)
} else if (cancelMode === CancelMode.NAMED_JOBS) {
- return await isCandidateForCancellingNamedJobs(
+ await checkCandidateForCancellingNamedJobs(
repositoryInfo,
runItem,
- jobNamesRegexps
+ jobNamesRegexps,
+ triggeringRunInfo,
+ mapOfWorkflowRunCandidates
)
} else if (cancelMode === CancelMode.SELF) {
- return isCandidateForCancellingSelf(runItem, sourceRunId)
- } else if (cancelMode === CancelMode.DUPLICATES) {
- return isCandidateForCancellingDuplicate(
+ checkCandidateForCancellingSelf(
runItem,
- headRepo,
+ triggeringRunInfo,
+ mapOfWorkflowRunCandidates
+ )
+ } else if (cancelMode === CancelMode.DUPLICATES) {
+ checkCandidateForCancellingDuplicate(
+ runItem,
cancelFutureDuplicates,
- sourceRunId
+ triggeringRunInfo,
+ mapOfWorkflowRunCandidates
)
} else if (cancelMode === CancelMode.ALL_DUPLICATES) {
- core.info(
- `Returning candidate ${runItem.id} for "all_duplicates" cancelling.`
+ checkCandidateForAllDuplicates(
+ runItem,
+ triggeringRunInfo,
+ mapOfWorkflowRunCandidates
)
- return true
+ } else if (cancelMode === CancelMode.ALL_DUPLICATED_NAMED_JOBS) {
+ await checkCandidateForDuplicateNamedJobs(
+ repositoryInfo,
+ runItem,
+ jobNamesRegexps,
+ mapOfWorkflowRunCandidates
+ )
+ return
} else {
- throw Error(
- `\nWrong cancel mode ${cancelMode}! This should never happen.\n`
- )
+ throw Error(`\nWrong cancel mode ${cancelMode}! Please correct it!.\n`)
}
}
@@ -524,25 +662,14 @@
* @param repositoryInfo - information about the repository used
* @param statusValues - status values we want to check
* @param cancelMode - cancel mode to use
- * @param sourceWorkflowId - source workflow id
- * @param sourceRunId - source run id
- * @param sourceEventName - name of the source event
- * @param mySourceGroup - source of the run that originated it
+ * @param triggeringRunInfo - information about the workflow that triggered the run
* @return map of the run items matching grouped by workflow run id
*/
async function getWorkflowRunsMatchingCriteria(
repositoryInfo: RepositoryInfo,
statusValues: string[],
- cancelMode:
- | CancelMode
- | CancelMode.DUPLICATES
- | CancelMode.ALL_DUPLICATES
- | CancelMode.FAILED_JOBS
- | CancelMode.NAMED_JOBS,
- sourceWorkflowId: number | string,
- sourceRunId: number,
- sourceEventName: string,
- mySourceGroup: WorkflowRunSourceGroup
+ cancelMode: CancelMode,
+ triggeringRunInfo: TriggeringRunInfo
): Promise<Map<number, rest.ActionsListWorkflowRunsResponseWorkflowRunsItem>> {
return await getWorkflowRuns(
repositoryInfo,
@@ -552,43 +679,43 @@
if (cancelMode === CancelMode.SELF) {
core.info(
`\nFinding runs for my own run: Owner: ${repositoryInfo.owner}, Repo: ${repositoryInfo.repo}, ` +
- `Workflow ID:${sourceWorkflowId}, Source Run id: ${sourceRunId}\n`
+ `Workflow ID:${triggeringRunInfo.workflowId},` +
+ `Source Run id: ${triggeringRunInfo.runId}\n`
)
return createListRunsQuerySpecificRunId(
repositoryInfo,
status,
- sourceWorkflowId,
- sourceRunId
+ triggeringRunInfo
)
} else if (
cancelMode === CancelMode.FAILED_JOBS ||
cancelMode === CancelMode.NAMED_JOBS ||
- cancelMode === CancelMode.ALL_DUPLICATES
+ cancelMode === CancelMode.ALL_DUPLICATES ||
+ cancelMode === CancelMode.ALL_DUPLICATED_NAMED_JOBS
) {
core.info(
`\nFinding runs for all runs: Owner: ${repositoryInfo.owner}, Repo: ${repositoryInfo.repo}, ` +
- `Status: ${status} Workflow ID:${sourceWorkflowId}\n`
+ `Status: ${status} Workflow ID:${triggeringRunInfo.workflowId}\n`
)
return createListRunsQueryAllRuns(
repositoryInfo,
status,
- sourceWorkflowId
+ triggeringRunInfo.workflowId
)
} else if (cancelMode === CancelMode.DUPLICATES) {
core.info(
`\nFinding duplicate runs: Owner: ${repositoryInfo.owner}, Repo: ${repositoryInfo.repo}, ` +
- `Status: ${status} Workflow ID:${sourceWorkflowId}, Head Branch: ${mySourceGroup.headBranch},` +
- `Event name: ${sourceEventName}\n`
+ `Status: ${status} Workflow ID:${triggeringRunInfo.workflowId}, ` +
+ `Head Branch: ${triggeringRunInfo.headBranch},` +
+ `Event name: ${triggeringRunInfo.eventName}\n`
)
return createListRunsQueryRunsSameSource(
repositoryInfo,
status,
- mySourceGroup
+ triggeringRunInfo
)
} else {
- throw Error(
- `\nWrong cancel mode ${cancelMode}! This should never happen.\n`
- )
+ throw Error(`\nWrong cancel mode ${cancelMode}! Please correct it.\n`)
}
}
)
@@ -653,26 +780,26 @@
}
/**
- * Maps found workflow runs into groups - filters out the workflows that are not eligible for canceling
+ * Maps found workflow runs into groups - filters out the workflows that are not eligible for cancelling
* (depends on cancel Mode) and assigns each workflow to groups - where workflow runs from the
* same group are put together in one array - in a map indexed by the source group id.
*
* @param repositoryInfo - information about the repository used
- * @param headRepo - head repository the event comes from
+ * @param triggeringRunInfo - information about the workflow that triggered the run
* @param cancelMode - cancel mode to use
* @param cancelFutureDuplicates - whether to cancel future duplicates
- * @param sourceRunId - source run id for the run
* @param jobNameRegexps - regexps for job names
* @param skipEventTypes - array of event names to skip
* @param workflowRuns - map of workflow runs found
- * @parm map where key is the source group id and value is array of workflow run item candidates to cancel
+ * @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)
*/
async function filterAndMapWorkflowRunsToGroups(
repositoryInfo: RepositoryInfo,
- headRepo: string,
+ triggeringRunInfo: TriggeringRunInfo,
cancelMode: CancelMode,
cancelFutureDuplicates: boolean,
- sourceRunId: number,
jobNameRegexps: string[],
skipEventTypes: string[],
workflowRuns: Map<
@@ -685,40 +812,19 @@
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},` +
+ `\nChecking run number: ${key} RunId: ${runItem.id} Url: ${runItem.url} Status ${runItem.status}` +
` Created at ${runItem.created_at}\n`
)
- if (
- await isCandidateForCancelling(
- repositoryInfo,
- runItem,
- headRepo,
- cancelMode,
- cancelFutureDuplicates,
- sourceRunId,
- jobNameRegexps,
- skipEventTypes
- )
- ) {
- const candidateSourceGroup: WorkflowRunSourceGroup = {
- workflowId: retrieveWorkflowIdFromUrl(runItem.workflow_url),
- headBranch: runItem.head_branch,
- headRepo: runItem.head_repository.full_name,
- eventName: runItem.event
- }
- const sourceGroupId = getSourceGroupId(candidateSourceGroup)
- let workflowRunArray:
- | rest.ActionsListWorkflowRunsResponseWorkflowRunsItem[]
- | undefined = mapOfWorkflowRunCandidates.get(sourceGroupId)
- if (workflowRunArray === undefined) {
- workflowRunArray = []
- mapOfWorkflowRunCandidates.set(sourceGroupId, workflowRunArray)
- }
- core.info(
- `The candidate ${runItem.id} has been added to ${sourceGroupId} group of candidates`
- )
- workflowRunArray.push(runItem)
- }
+ await checkCandidateForCancelling(
+ repositoryInfo,
+ runItem,
+ triggeringRunInfo,
+ cancelMode,
+ cancelFutureDuplicates,
+ jobNameRegexps,
+ skipEventTypes,
+ mapOfWorkflowRunCandidates
+ )
}
return mapOfWorkflowRunCandidates
}
@@ -749,7 +855,7 @@
* @param repositoryInfo - information about the repository used
* @param selfRunId - my own run id
* @param pullRequestNumber - number of pull request
- * @param reason reason for canceling
+ * @param reason reason for cancelling
*/
async function notifyPR(
repositoryInfo: RepositoryInfo,
@@ -774,9 +880,9 @@
* @param notifyPRCancel - whether to notify the PR when cancelling
* @param selfRunId - what is the self run id
* @param sourceGroupId - what is the source group id
- * @param reason - reason for canceling
+ * @param reason - reason for cancelling
*/
-async function cancelAllRunsInTheSourceGroup(
+async function cancelAllRunsInTheGroup(
repositoryInfo: RepositoryInfo,
sortedRunItems: rest.ActionsListWorkflowRunsResponseWorkflowRunsItem[],
notifyPRCancel: boolean,
@@ -813,21 +919,21 @@
}
/**
- * Cancels found runs in a smart way. It takes all the found runs group by the source group, sorts them
- * descending according to create date in each of the source groups and cancels them in that order -
- * optionally skipping the first found run in each source group in case of duplicates.
+ * Cancels found runs per group. It takes all the found groups, sorts them
+ * descending according to create date in each of the groups and cancels them in that order -
+ * optionally skipping the first found run in each source group in case of duplicate cancelling.
*
* @param repositoryInfo - information about the repository used
- * @param mapOfWorkflowRunsCandidatesToCancel map of all workflow run candidates
+ * @param mapOfWorkflowRunCandidatesCandidatesToCancel map of all workflow run candidates
* @param cancelMode - cancel mode
* @param cancelFutureDuplicates - whether to cancel future duplicates
* @param notifyPRCancel - whether to notify PRs with comments
* @param selfRunId - self run Id
- * @param reason - reason for canceling
+ * @param reason - reason for cancelling
*/
async function cancelTheRunsPerGroup(
repositoryInfo: RepositoryInfo,
- mapOfWorkflowRunsCandidatesToCancel: Map<
+ mapOfWorkflowRunCandidatesCandidatesToCancel: Map<
string,
rest.ActionsListWorkflowRunsResponseWorkflowRunsItem[]
>,
@@ -839,9 +945,9 @@
): Promise<number[]> {
const cancelledRuns: number[] = []
for (const [
- sourceGroupId,
+ groupId,
candidatesArray
- ] of mapOfWorkflowRunsCandidatesToCancel) {
+ ] of mapOfWorkflowRunCandidatesCandidatesToCancel) {
// Sort from most recent date - this way we always kill current one at the end (if we kill it at all)
const sortedRunItems = candidatesArray.sort((runItem1, runItem2) =>
runItem2.created_at.localeCompare(runItem1.created_at)
@@ -849,31 +955,32 @@
if (sortedRunItems.length > 0) {
if (
(cancelMode === CancelMode.DUPLICATES && cancelFutureDuplicates) ||
- cancelMode === CancelMode.ALL_DUPLICATES
+ cancelMode === CancelMode.ALL_DUPLICATES ||
+ cancelMode === CancelMode.ALL_DUPLICATED_NAMED_JOBS
) {
core.info(
`\nSkipping the first run (${sortedRunItems[0].id}) of all the matching ` +
- `duplicates for '${sourceGroupId}'. This one we are going to leave in peace!\n`
+ `duplicates for '${groupId}'. This one we are going to leave in peace!\n`
)
sortedRunItems.shift()
}
if (sortedRunItems.length === 0) {
- core.info(`\nNo duplicates to cancel for ${sourceGroupId}!\n`)
+ core.info(`\nNo duplicates to cancel for ${groupId}!\n`)
continue
}
cancelledRuns.push(
- ...(await cancelAllRunsInTheSourceGroup(
+ ...(await cancelAllRunsInTheGroup(
repositoryInfo,
sortedRunItems,
notifyPRCancel,
selfRunId,
- sourceGroupId,
+ groupId,
reason
))
)
} else {
core.info(
- `\n###### There are no runs to cancel for ${sourceGroupId} ##########\n`
+ `\n###### There are no runs to cancel for ${groupId} ##########\n`
)
}
}
@@ -884,14 +991,7 @@
* Find and cancels runs based on the criteria chosen.
* @param repositoryInfo - information about the repository used
* @param selfRunId - number of own run id
- * @param sourceWorkflowId - source workflow id that triggered the workflow
- * (might be different than self for workflow_run)
- * @param sourceRunId - source run id that triggered the workflow
- * (might be different than self for workflow_run)
- * @param headRepo - head repository that triggered the workflow (repo from which PR came)
- * @param headBranch - branch of the PR that triggered the workflow (when it is triggered by PR)
- * @param sourceEventName - name of the event that triggered the workflow
- * (different than self event name for workflow_run)
+ * @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
@@ -904,11 +1004,7 @@
async function findAndCancelRuns(
repositoryInfo: RepositoryInfo,
selfRunId: number,
- sourceWorkflowId: number | string,
- sourceRunId: number,
- headRepo: string,
- headBranch: string,
- sourceEventName: string,
+ triggeringRunInfo: TriggeringRunInfo,
cancelMode: CancelMode,
cancelFutureDuplicates: boolean,
notifyPRCancel: boolean,
@@ -918,34 +1014,24 @@
reason: string
): Promise<number[]> {
const statusValues = ['queued', 'in_progress']
- const mySourceGroup: WorkflowRunSourceGroup = {
- headBranch,
- headRepo,
- eventName: sourceEventName,
- workflowId: sourceWorkflowId
- }
const workflowRuns = await getWorkflowRunsMatchingCriteria(
repositoryInfo,
statusValues,
cancelMode,
- sourceWorkflowId,
- sourceRunId,
- sourceEventName,
- mySourceGroup
+ triggeringRunInfo
)
- const mapOfWorkflowRunsCandidatesToCancel = await filterAndMapWorkflowRunsToGroups(
+ const mapOfWorkflowRunCandidatesCandidatesToCancel = await filterAndMapWorkflowRunsToGroups(
repositoryInfo,
- headRepo,
+ triggeringRunInfo,
cancelMode,
cancelFutureDuplicates,
- sourceRunId,
jobNameRegexps,
skipEventTypes,
workflowRuns
)
return await cancelTheRunsPerGroup(
repositoryInfo,
- mapOfWorkflowRunsCandidatesToCancel,
+ mapOfWorkflowRunCandidatesCandidatesToCancel,
cancelMode,
cancelFutureDuplicates,
notifyPRCancel,
@@ -969,15 +1055,15 @@
}
/**
- * Gets origin of the runId - if this is a workflow run, it returns the information about the source run
+ * Gets source run using of the runId - if this is a workflow run, it returns the information about the source run
* @param repositoryInfo - information about the repository used
* @param runId - run id of the run to check
- * @return information about the triggering workflow
+ * @return information about the triggering run
*/
-async function getOrigin(
+async function getTriggeringRunInfo(
repositoryInfo: RepositoryInfo,
runId: number
-): Promise<TriggeringWorkflowInfo> {
+): Promise<TriggeringRunInfo> {
const reply = await repositoryInfo.octokit.actions.getWorkflowRun({
owner: repositoryInfo.owner,
repo: repositoryInfo.repo,
@@ -1001,6 +1087,8 @@
}
return {
+ workflowId: retrieveWorkflowIdFromUrl(reply.data.workflow_url),
+ runId,
headRepo: reply.data.head_repository.full_name,
headBranch: reply.data.head_branch,
headSha: reply.data.head_sha,
@@ -1015,14 +1103,7 @@
*
* @param repositoryInfo - information about the repository used
* @param selfRunId - number of own run id
- * @param sourceWorkflowId - source workflow id that triggered the workflow
- * (might be different than self for workflow_run)
- * @param sourceRunId - source run id that triggered the workflow
- * (might be different than self for workflow_run)
- * @param headRepo - head repository that triggered the workflow (repo from which PR came)
- * @param headBranch - branch of the PR that triggered the workflow (when it is triggered by PR)
- * @param sourceEventName - name of the event that triggered the workflow
- * (different than self event name for workflow_run)
+ * @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
@@ -1035,11 +1116,7 @@
async function performCancelJob(
repositoryInfo: RepositoryInfo,
selfRunId: number,
- sourceWorkflowId: number | string,
- sourceRunId: number,
- headRepo: string,
- headBranch: string,
- sourceEventName: string,
+ triggeringRunInfo: TriggeringRunInfo,
cancelMode: CancelMode,
notifyPRCancel: boolean,
notifyPRCancelMessage: string,
@@ -1052,9 +1129,11 @@
'\n###################################################################################\n'
)
core.info(
- `All parameters: owner: ${repositoryInfo.owner}, repo: ${repositoryInfo.repo}, run id: ${sourceRunId}, ` +
- `head repo ${headRepo}, headBranch: ${headBranch}, ` +
- `sourceEventName: ${sourceEventName}, cancelMode: ${cancelMode}, jobNames: ${jobNameRegexps}`
+ `All parameters: owner: ${repositoryInfo.owner}, repo: ${repositoryInfo.repo}, ` +
+ `run id: ${triggeringRunInfo.runId}, ` +
+ `head repo ${triggeringRunInfo.headRepo}, headBranch: ${triggeringRunInfo.headBranch}, ` +
+ `sourceEventName: ${triggeringRunInfo.eventName}, ` +
+ `cancelMode: ${cancelMode}, jobNames: ${jobNameRegexps}`
)
core.info(
'\n###################################################################################\n'
@@ -1062,33 +1141,42 @@
let reason = ''
if (cancelMode === CancelMode.SELF) {
core.info(
- `# Cancelling source run: ${sourceRunId} for workflow ${sourceWorkflowId}.`
+ `# Cancelling source run: ${triggeringRunInfo.runId} ` +
+ `for workflow ${triggeringRunInfo.workflowId}.`
)
reason = notifyPRCancelMessage
? notifyPRCancelMessage
: `The job has been cancelled by another workflow.`
} else if (cancelMode === CancelMode.FAILED_JOBS) {
core.info(
- `# Cancel all runs for workflow ${sourceWorkflowId} where job names matching ${jobNameRegexps} failed.`
+ `# Cancel all runs for workflow ${triggeringRunInfo.workflowId} ` +
+ `where job names matching ${jobNameRegexps} failed.`
)
reason = `It has some failed jobs matching ${jobNameRegexps}.`
} else if (cancelMode === CancelMode.NAMED_JOBS) {
core.info(
- `# Cancel all runs for workflow ${sourceWorkflowId} have job names matching ${jobNameRegexps}.`
+ `# Cancel all runs for workflow ${triggeringRunInfo.workflowId} ` +
+ `have job names matching ${jobNameRegexps}.`
)
reason = `It has jobs matching ${jobNameRegexps}.`
} else if (cancelMode === CancelMode.DUPLICATES) {
core.info(
- `# Cancel duplicate runs for workflow ${sourceWorkflowId} for same triggering branch as own run Id.`
+ `# Cancel duplicate runs for workflow ${triggeringRunInfo.workflowId} ` +
+ `for same triggering branch as own run Id.`
)
- reason = `It is an earlier duplicate of ${sourceWorkflowId} run.`
+ reason = `It is an earlier duplicate of ${triggeringRunInfo.workflowId} run.`
} else if (cancelMode === CancelMode.ALL_DUPLICATES) {
core.info(
- `# Cancel all duplicates runs started for workflow ${sourceWorkflowId}.`
+ `# Cancel all duplicates runs started for workflow ${triggeringRunInfo.workflowId}.`
)
- reason = `It is an earlier duplicate of ${sourceWorkflowId} run.`
+ reason = `It is an earlier duplicate of ${triggeringRunInfo.workflowId} run.`
+ } else if (cancelMode === CancelMode.ALL_DUPLICATED_NAMED_JOBS) {
+ core.info(
+ `# Cancel all duplicate named jobs matching the patterns ${jobNameRegexps}.`
+ )
+ reason = `It is an earlier duplicate of ${triggeringRunInfo.workflowId} run.`
} else {
- throw Error(`Wrong cancel mode ${cancelMode}! This should never happen.`)
+ throw Error(`Wrong cancel mode ${cancelMode}! Please correct it.`)
}
core.info(
'\n###################################################################################\n'
@@ -1097,11 +1185,7 @@
return await findAndCancelRuns(
repositoryInfo,
selfRunId,
- sourceWorkflowId,
- sourceRunId,
- headRepo,
- headBranch,
- sourceEventName,
+ triggeringRunInfo,
cancelMode,
cancelFutureDuplicates,
notifyPRCancel,
@@ -1119,31 +1203,31 @@
*
* @param repositoryInfo - information about the repository used
* @param workflowFileName - optional workflow file name
- * @param sourceRunId - source run id of the workfow
+ * @param runId - run id of the workflow
* @param selfRunId - self run id
* @return workflow id that is associate with the workflow we are going to act on.
*/
async function retrieveWorkflowId(
repositoryInfo: RepositoryInfo,
workflowFileName: string | null,
- sourceRunId: number,
+ runId: number,
selfRunId: number
): Promise<string | number> {
- let sourceWorkflowId
+ let workflowId
if (workflowFileName) {
- sourceWorkflowId = workflowFileName
+ workflowId = workflowFileName
core.info(
- `\nFinding runs for another workflow found by ${workflowFileName} name: ${sourceWorkflowId}\n`
+ `\nFinding runs for another workflow found by ${workflowFileName} name: ${workflowId}\n`
)
} else {
- sourceWorkflowId = await getWorkflowId(repositoryInfo, sourceRunId)
- if (sourceRunId === selfRunId) {
- core.info(`\nFinding runs for my own workflow ${sourceWorkflowId}\n`)
+ workflowId = await getWorkflowId(repositoryInfo, runId)
+ if (runId === selfRunId) {
+ core.info(`\nFinding runs for my own workflow ${workflowId}\n`)
} else {
- core.info(`\nFinding runs for source workflow ${sourceWorkflowId}\n`)
+ core.info(`\nFinding runs for source workflow ${workflowId}\n`)
}
}
- return sourceWorkflowId
+ return workflowId
}
/**
* Sets output but also prints the output value in the logs.
@@ -1161,7 +1245,7 @@
* are verified and in case od unexpected combination found, approrpriate error is raised.
*
* @param eventName - name of the event to act on
- * @param sourceRunId - run id of the triggering event
+ * @param runId - run id of the triggering event
* @param selfRunId - our own run id
* @param cancelMode - cancel mode used
* @param cancelFutureDuplicates - whether future duplicate cancelling is enabled
@@ -1169,7 +1253,7 @@
*/
function performSanityChecks(
eventName: string,
- sourceRunId: number,
+ runId: number,
selfRunId: number,
cancelMode: CancelMode,
cancelFutureDuplicates: boolean,
@@ -1177,7 +1261,7 @@
): void {
if (
eventName === 'workflow_run' &&
- sourceRunId === selfRunId &&
+ runId === selfRunId &&
cancelMode === CancelMode.DUPLICATES
) {
throw Error(
@@ -1195,10 +1279,27 @@
CancelMode.ALL_DUPLICATES
].includes(cancelMode)
) {
- throw Error(`You cannot specify jobNames on ${cancelMode} cancelMode.`)
+ throw Error(
+ `You cannot specify jobNamesRegexps on ${cancelMode} cancelMode.`
+ )
}
- if (cancelMode === CancelMode.ALL_DUPLICATES && !cancelFutureDuplicates) {
+ if (
+ jobNameRegexps.length === 0 &&
+ [
+ CancelMode.NAMED_JOBS,
+ CancelMode.FAILED_JOBS,
+ CancelMode.ALL_DUPLICATED_NAMED_JOBS
+ ].includes(cancelMode)
+ ) {
+ throw Error(`You must specify jobNamesRegexps on ${cancelMode} cancelMode.`)
+ }
+
+ if (
+ (cancelMode === CancelMode.ALL_DUPLICATES ||
+ cancelMode === CancelMode.ALL_DUPLICATED_NAMED_JOBS) &&
+ !cancelFutureDuplicates
+ ) {
throw Error(
`The ${cancelMode} cancelMode has to have cancelFutureDuplicates set to true.`
)
@@ -1209,32 +1310,28 @@
* Produces basic outputs for the action. This does not include cancelled workflow run id - those are
* set after cancelling is done.
*
- * @param triggeringWorkflowInfo
+ * @param triggeringRunInfo
*/
-function produceBasicOutputs(
- triggeringWorkflowInfo: TriggeringWorkflowInfo
-): void {
- verboseOutput('sourceHeadRepo', triggeringWorkflowInfo.headRepo)
- verboseOutput('sourceHeadBranch', triggeringWorkflowInfo.headBranch)
- verboseOutput('sourceHeadSha', triggeringWorkflowInfo.headSha)
- verboseOutput('sourceEvent', triggeringWorkflowInfo.eventName)
+function produceBasicOutputs(triggeringRunInfo: TriggeringRunInfo): void {
+ verboseOutput('sourceHeadRepo', triggeringRunInfo.headRepo)
+ verboseOutput('sourceHeadBranch', triggeringRunInfo.headBranch)
+ verboseOutput('sourceHeadSha', triggeringRunInfo.headSha)
+ verboseOutput('sourceEvent', triggeringRunInfo.eventName)
verboseOutput(
'pullRequestNumber',
- triggeringWorkflowInfo.pullRequest
- ? triggeringWorkflowInfo.pullRequest.number.toString()
+ triggeringRunInfo.pullRequest
+ ? triggeringRunInfo.pullRequest.number.toString()
: ''
)
verboseOutput(
'mergeCommitSha',
- triggeringWorkflowInfo.mergeCommitSha
- ? triggeringWorkflowInfo.mergeCommitSha
- : ''
+ triggeringRunInfo.mergeCommitSha ? triggeringRunInfo.mergeCommitSha : ''
)
verboseOutput(
'targetCommitSha',
- triggeringWorkflowInfo.mergeCommitSha
- ? triggeringWorkflowInfo.mergeCommitSha
- : triggeringWorkflowInfo.headSha
+ triggeringRunInfo.mergeCommitSha
+ ? triggeringRunInfo.mergeCommitSha
+ : triggeringRunInfo.headSha
)
}
@@ -1242,19 +1339,17 @@
* Notifies the PR that the action has started.
*
* @param repositoryInfo information about the repository
- * @param triggeringWorkflowInfo information about the triggering workflow
- * @param sourceRunId run id of the source workflow
+ * @param triggeringRunInfo information about the triggering workflow
* @param selfRunId self run id
* @param notifyPRMessageStart whether to notify about the start of the action
*/
async function notifyActionStart(
repositoryInfo: RepositoryInfo,
- triggeringWorkflowInfo: TriggeringWorkflowInfo,
- sourceRunId: number,
+ triggeringRunInfo: TriggeringRunInfo,
selfRunId: number,
notifyPRMessageStart: string
): Promise<void> {
- if (notifyPRMessageStart && triggeringWorkflowInfo.pullRequest) {
+ if (notifyPRMessageStart && triggeringRunInfo.pullRequest) {
const selfWorkflowRunUrl =
`https://github.com/${repositoryInfo.owner}/${repositoryInfo.repo}` +
`/actions/runs/${selfRunId}`
@@ -1262,7 +1357,7 @@
owner: repositoryInfo.owner,
repo: repositoryInfo.repo,
// eslint-disable-next-line @typescript-eslint/camelcase
- issue_number: triggeringWorkflowInfo.pullRequest.number,
+ issue_number: triggeringRunInfo.pullRequest.number,
body: `${notifyPRMessageStart} [The workflow run](${selfWorkflowRunUrl})`
})
}
@@ -1331,13 +1426,15 @@
`jobNames: ${jobNameRegexps}`
)
- const triggeringWorkflowInfo = await getOrigin(repositoryInfo, sourceRunId)
- produceBasicOutputs(triggeringWorkflowInfo)
+ const triggeringRunInfo = await getTriggeringRunInfo(
+ repositoryInfo,
+ sourceRunId
+ )
+ produceBasicOutputs(triggeringRunInfo)
await notifyActionStart(
repositoryInfo,
- triggeringWorkflowInfo,
- sourceRunId,
+ triggeringRunInfo,
selfRunId,
notifyPRMessageStart
)
@@ -1345,11 +1442,7 @@
const cancelledRuns = await performCancelJob(
repositoryInfo,
selfRunId,
- sourceWorkflowId,
- sourceRunId,
- triggeringWorkflowInfo.headRepo,
- triggeringWorkflowInfo.headBranch,
- triggeringWorkflowInfo.eventName,
+ triggeringRunInfo,
cancelMode,
notifyPRCancel,
notifyPRCancelMessage,