Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

allDuplicates cancel mode does not cancel previous run #15

Open
ennarto opened this issue May 18, 2021 · 8 comments
Open

allDuplicates cancel mode does not cancel previous run #15

ennarto opened this issue May 18, 2021 · 8 comments

Comments

@ennarto
Copy link

ennarto commented May 18, 2021

I am trying to get my head around why you are skipping the first duplicate in allDuplicates mode.
This line here

cancelMode === CancelMode.ALL_DUPLICATES ||

skips the first candidate, but the candidates list does not seem to have the running workflow.
As a result, the previous duplicate job is skipped and not cancelled.

If I run the same workflow from different tags, the previous one is not cancelled, but that's exactly what I would expect.

Why is that? Is there anyway to avoid this behavior?

My use case is that duplicate version tags may be pushed by human error, so I want to avoid being charged for the first run and just run the second. So I included your action as first step.

In any case, thanks for your work, very useful!

@potiuk
Copy link
Owner

potiuk commented May 18, 2021

How it works:

The 'all duplicates" is an optimization so that whenever the "Cancel workflow" runs it can cancel ALL POSSIBLE duplicate runs from all actions pushed, no matter what branch they came from. This is because in "queue strain" situation, the cancel workflows can be queued themselves and I want to aggressively cancel all duplicates from All PRs when just single "cancel" action runs.

Duplicates are based on "repo + branch" the workflow comes from. Whenever two workflows have the same "branch + repo", they are considered as duplicates (for example when someone pushed two commits in one branch very quickly that creates two workflow runs from the same "branch + repo"

Example:

Imagine you have three PRs pushed by 3 different people to the same repp from their forks and every person pushed several commits.

Person A, PR X: Commit 1, Commit 2, commit 3
Person B, PR Y: Commit 4, Commit 5, commit 6
Person C, PR Z: Commit 7, Commit 8, commit 9

They were all pushed and 9 worklfow are eligible to start (either running or queued), Commits 3, 6, 9 are the most recent commits for PRS X, Y, Z respectively. So the desired situation is that Comits 1,.2, 4, 5, 7, 8 are cancelled (as duplicates), Commits 3, 6, 9 continue.

Now single "Cancel" action runs with ALL_DUPLICATES and sees the following (it operates "per PR" - so firtst groups all the wokflows "per PR" and runs check for candidates on all commits belonging to the same PR. The commits are retrieved in reverse order (most recent is first)

PR X: -> here cancel action sees 3, 2, 1, skips 3, candidates for canceling: 2,1
PR Y: -> here cancel action sees 6, 5, 4 , skips 6, candidates for canceling: 4, 5
PR Z: -> here cancel action sees 9, 8, 7 , skips 9, candidates for canceling: 8, 7

Now, the action cancels 2,1 4, 5, 8, 7. Remaining ones: 3, 6, 9. Which is what we wanted.

@ennarto
Copy link
Author

ennarto commented May 18, 2021

Thank you for the thorough explanation.

I think the reason why I am observing this behavior is because I run the cancel step Within the PR action.
So, in your example it skips 3,6,9 because it is itself, and it also skips 2,5,8 because of the behavior in discussion.

So, as a result it skips 2 runs instead of one. If on the other hand the cancel step is on a different workflow, it should work as you explained, which makes perfect sense.

I hope I understood this correctly, apologies if I didn't. If so, do you think my scenario is worth supporting?

The reason why I have the cancel step embedded in the PR action is because I felt this is cleaner for my case. "Before you start building etc, cancel everyone else and continue. If someone else in the future runs, they will cancel you respectively"

@potiuk
Copy link
Owner

potiuk commented May 18, 2021

Well, yes and no. ALL_DUPLICATES is only useful when you want to handle PRs "run from fork" scenario which is why the action is needed in the first place.

Here you can find the scenario description: https://github.com/potiuk/cancel-workflow-runs#repositories-that-use-pull-requests-from-forks

In this scenario the action is ALWAYS run in a different workflow - "Cancelling workflow" (because workflows which are run from PR do not have permission to cancel the workflow as they are read-only). In this case you need to create a separate "Cancelling" "workflow_run" triggered by the "PR Workflow" which always run from the master of the "target" repository. And it always cancels different workflow (the "PR Workflow" that triggered it).

In your case, you should use "duplicates" mode. This is enough when you run the PRs yourself only from your repo. In this case your own PR workflow has enough permissions to cancel the duplicates and it is by far simpler.

See https://github.com/potiuk/cancel-workflow-runs#repositories-that-do-not-use-pull-requests-from-forks

If you look there, you will see that "All Duplicates" is only useful for the "forks" scenario.

@potiuk
Copy link
Owner

potiuk commented May 18, 2021

This is the exact case that describe your scenario: https://github.com/potiuk/cancel-workflow-runs#cancel-duplicate-runs-for-self-workflow

@potiuk
Copy link
Owner

potiuk commented May 18, 2021

And yes. I know it is overly complex, but this so complex to handle the complexity introduced by Github Actions (which in turn is introduced to provide security for the case when you have PR actions triggered by PRs coming from forks.

@ghost
Copy link

ghost commented May 24, 2021

Thanks for building this tool and for all the documentation! After reading through this issue I am still struggling with the problem initially posted: When a new action is started it doesn't cancel the previous because it always skips the first one found.

In my case we use private repos and always push to pull requests, so I am following Cancel duplicate runs for "self" workflow, but with pull_request as the trigger action.

Below is an example of the github action I have set up, and some example output which I have scrubbed a bit. What I am finding is that after pushing a commit to a PR where my_example_action is already running, it doesn't cancel my_example_action because it is the first match found. In my example output what I would have expected is for run 9999999 to be cancelled. Can you please advise on how I need to configure this differently? I'm really excited to get this working!

name: Example Pull Request Action
on: [pull_request]

jobs:
  cancel-duplicate-workflow-runs:
    name: "Cancel duplicate workflow runs"
    runs-on: ubuntu-latest
    steps:
      - uses: potiuk/cancel-workflow-runs@master
        name: "Cancel duplicate workflow runs"
        with:
          cancelMode: duplicates
          cancelFutureDuplicates: true
          notifyPRCancel: true
          token: ${{ secrets.GITHUB_TOKEN }}

  my_example_action:
    name: "My Example Action"
    runs-on: "ubuntu-latest"
    steps:
      - uses: actions/checkout@v2
      - name: Do Something
        run: echo "something"

And here is some example output:

###################################################################################

All parameters: owner: MyGithubOrg, repo: MyGithubRepo, run id: 8888888, Source workflow id: 22223333, head repo MyGithubOrg/MyGithubRepo, headBranch: cancel_dupes, sourceEventName: pull_request, cancelMode: duplicates, jobNames: 

###################################################################################

# Cancel duplicate runs for workflow 22223333 for same triggering branch as own run Id.

###################################################################################


Finding duplicate runs: Owner: MyGithubOrg, Repo: MyGithubRepo, Status: queued Workflow ID:22223333, Head Branch: cancel_dupes,Event name: pull_request


Finding duplicate runs: Owner: MyGithubOrg, Repo: MyGithubRepo, Status: in_progress Workflow ID:22223333, Head Branch: cancel_dupes,Event name: pull_request


Found runs: 1111,2222


Checking run number: 1111 RunId: 8888888 Url: https://api.github.com/repos/MyGithubOrg/MyGithubRepo/actions/runs/8888888 Status in_progress Created at 2021-05-24T18:59:45Z


I have self-preservation built in. I refuse to cancel myself :)


Checking run number: 2222 RunId: 9999999 Url: https://api.github.com/repos/MyGithubOrg/MyGithubRepo/actions/runs/9999999 Status in_progress Created at 2021-05-24T18:59:08Z


Cancel Future Duplicates: Returning run id that might be duplicate or my own run: 9999999.


Adding the run: 9999999 to candidates with :22223333:MyGithubOrg/MyGithubRepo:cancel_dupes:pull_request key.


Skipping the first run (9999999) of all the matching duplicates for ':22223333:MyGithubOrg/MyGithubRepo:cancel_dupes:pull_request'. This one we are going to leave in peace!


No duplicates to cancel for :22223333:MyGithubOrg/MyGithubRepo:cancel_dupes:pull_request!

Setting output: cancelledRuns: []

############### Cancel complete ##################

@potiuk
Copy link
Owner

potiuk commented May 24, 2021

Actually. I am going to - most likely - deprecate this action. Recently (21st of April) Github introduced a new feature "concurrency" that is targeted to solve the problem of cancelling duplicate workflows. It's much easier to use https://github.blog/changelog/2021-04-19-github-actions-limit-workflow-run-or-job-concurrency/ (documentation here: https://docs.github.com/en/actions/reference/workflow-syntax-for-github-actions#concurrency)

@ghost
Copy link

ghost commented May 24, 2021

Thanks for the quick reply, and for pointing out the new concurrency features! I will check it out. Thanks again!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants