admin管理员组

文章数量:1390924

I'm using concurrency in my GitHub Actions to limit all workflows to one simultaneous run per workflow, per ref.

This ensures that only the latest push to a branch or pull request runs per workflow, saving both time and money.

This setup worked perfectly, until I tried using workflow_call. The issue arises because my concurrency group naming, which relies on ${{ github.workflow }}-${{ github.ref }}, doesn't work as expected with workflow_call events.

The problem is that ${{ github.workflow }} in the callee workflow inherits the name of the caller workflow (similar to how permissions work). This leads to a naming conflict, causing the callee to fail immediately. I can't seem to find a way to get the filename inside a workflow called by workflow_call, since according to the docs the callee inherits everything from the caller.

I need a solution that keeps the concurrency group label dynamic. ${{ github.workflow }} was ideal because workflow filenames are unique in the workflows folder, ensuring no accidental duplicates from other team members. I want to maintain this level of "idiot-proofing" while resolving the conflict with workflow_call.

Here is my setup:

deploy.web.yaml

name: "deploy / web"

on:
  workflow_dispatch:
  workflow_call:

concurrency:
  group: ${{ github.workflow }}-${{ github.ref }}
  cancel-in-progress: true

jobs:
  staging:
    runs-on: ubuntu-latest
    steps:
      - run: echo "Web deployment to staging not configured yet"

release.yaml

name: "release"

on:
  push:
    branches: [main]

concurrency:
  group: ${{ github.workflow }}-${{ github.ref }}
  cancel-in-progress: true

jobs:
  metadata:
    runs-on: ubuntu-latest
    steps:
      - uses: googleapis/release-please-action@v4
  web:
    needs: [metadata]
    uses: ./.github/workflows/deploy.web.yaml

EXPECTED

The release.yaml workflow should and call deploy.web.yaml successfully. If release.yaml is then called again while the previous call is still running it should cancel the oldest one and proceed with the newest.

ACTUALITY

deploy.web.yaml fails immediately on the first call with this error:

Canceling since a deadlock for concurrency group 'release-refs/heads/main' was detected between 'top level workflow' and 'api'

NOTE

Before answering this please be know that:

  • I am aware that I can just name the workflows hard coded, e.g. deploy-web-${{ github.ref }}, but this would go against the "idiot-proofing" I talked about before. Not to mention it would be very annoying having every concurrency group be dynamic except for the ones with workflow_call, which would always need to be hard coded.

  • I am also aware I can set the concurrency at the job level in the release.yaml workflow, like this:

jobs:
  web:
    concurrency:
      group: ${{ github.workflow }}-web-${{ github.ref }}
      cancel-in-progress: true
    uses: ./.github/workflows/deploy.web.yaml

This however, does not define concurrency at the workflow level and would mean If I were to call the workflow by another event, say workflow_dispatch, it would not have concurrency protection. Not to mention we also be hard coding the name for the group once again.

Hopefully there is a reasonable solution for this within the context of what I am looking for!

I'm using concurrency in my GitHub Actions to limit all workflows to one simultaneous run per workflow, per ref.

This ensures that only the latest push to a branch or pull request runs per workflow, saving both time and money.

This setup worked perfectly, until I tried using workflow_call. The issue arises because my concurrency group naming, which relies on ${{ github.workflow }}-${{ github.ref }}, doesn't work as expected with workflow_call events.

The problem is that ${{ github.workflow }} in the callee workflow inherits the name of the caller workflow (similar to how permissions work). This leads to a naming conflict, causing the callee to fail immediately. I can't seem to find a way to get the filename inside a workflow called by workflow_call, since according to the docs the callee inherits everything from the caller.

I need a solution that keeps the concurrency group label dynamic. ${{ github.workflow }} was ideal because workflow filenames are unique in the workflows folder, ensuring no accidental duplicates from other team members. I want to maintain this level of "idiot-proofing" while resolving the conflict with workflow_call.

Here is my setup:

deploy.web.yaml

name: "deploy / web"

on:
  workflow_dispatch:
  workflow_call:

concurrency:
  group: ${{ github.workflow }}-${{ github.ref }}
  cancel-in-progress: true

jobs:
  staging:
    runs-on: ubuntu-latest
    steps:
      - run: echo "Web deployment to staging not configured yet"

release.yaml

name: "release"

on:
  push:
    branches: [main]

concurrency:
  group: ${{ github.workflow }}-${{ github.ref }}
  cancel-in-progress: true

jobs:
  metadata:
    runs-on: ubuntu-latest
    steps:
      - uses: googleapis/release-please-action@v4
  web:
    needs: [metadata]
    uses: ./.github/workflows/deploy.web.yaml

EXPECTED

The release.yaml workflow should and call deploy.web.yaml successfully. If release.yaml is then called again while the previous call is still running it should cancel the oldest one and proceed with the newest.

ACTUALITY

deploy.web.yaml fails immediately on the first call with this error:

Canceling since a deadlock for concurrency group 'release-refs/heads/main' was detected between 'top level workflow' and 'api'

NOTE

Before answering this please be know that:

  • I am aware that I can just name the workflows hard coded, e.g. deploy-web-${{ github.ref }}, but this would go against the "idiot-proofing" I talked about before. Not to mention it would be very annoying having every concurrency group be dynamic except for the ones with workflow_call, which would always need to be hard coded.

  • I am also aware I can set the concurrency at the job level in the release.yaml workflow, like this:

jobs:
  web:
    concurrency:
      group: ${{ github.workflow }}-web-${{ github.ref }}
      cancel-in-progress: true
    uses: ./.github/workflows/deploy.web.yaml

This however, does not define concurrency at the workflow level and would mean If I were to call the workflow by another event, say workflow_dispatch, it would not have concurrency protection. Not to mention we also be hard coding the name for the group once again.

Hopefully there is a reasonable solution for this within the context of what I am looking for!

Share Improve this question edited Mar 16 at 0:18 dy0gu asked Mar 15 at 23:48 dy0gudy0gu 4321 gold badge4 silver badges15 bronze badges 5
  • I think you can just drop the concurrency object in the calling workflow, no? – Benjamin W. Commented Mar 15 at 23:48
  • Fot to mention that the calling workflows does some stuff before calling other workflows, so it also needs to have a concurrency limit. I'll edit the question to include that information, thanks for your comment! – dy0gu Commented Mar 16 at 0:21
  • What if you keep only the concurrency object in the caller then? – Benjamin W. Commented Mar 16 at 3:59
  • Thanks you @SzymonMaszke! This seems to be exactly what I was looking for. Only thing I still need is for it to not get overwritten by the caller workflow in workflow_call. Do you know what the behavior is in that case? I can't seem to find it in the documentation, and I also can't test it right now unfortunately. If it works with workflow_call it's the perfect solution! – dy0gu Commented Mar 17 at 1:20
  • @dy0gu Unfortunately there is no way to do that AFAIK, that's why I originally didn't post an answer. Did leave it below with other references and pointers, though, hopefully of use to you or someone else. Also made the thing about github.workflow_ref more precise as my comment above is misleading after reading it again (that's why I deleted it). – Szymon Maszke Commented Mar 17 at 13:04
Add a comment  | 

1 Answer 1

Reset to default 1

TLDR

  • Unfortunately (AFAICT) hardcoded concurrency groups is the only way
  • ${{ github.workflow_ref }}-${{ github.ref }} seems to be the most error-proof combination I've found

Uniqueness

${{ github.workflow }} was ideal because workflow filenames are unique in the workflows folder

As per documentation (emphasis mine):

The name of the workflow. If the workflow file doesn't specify a name, the value of this property is the full path of the workflow file in the repository.

So, if anyone names their workflow name: A and another name: A it would be a collision. On the other hand ${{ github.workflow_ref }} has this property, as it's a path, no matter the name.

Additionally (unlike what might have seemed in my comment), you would need ${{ github.ref }} as it refers to the ref tirggering the worklow (it might be refs/heads/<branch_name> if this is the trigger). ${{ github.workflow_ref }} ref refers to the one which hosts the file (usually main).

In your case it shouldn't matter as the workflow is set to main, but in some other branch-based workflow it might (so I'd deem it less "automatic" as per your jargon).

Caller vs callee

This seems to be exactly what I was looking for. Only thing I still need is for it to not get overwritten by the caller workflow in workflow_call

Unfortunately it isn't as there is no way to differentiate between the two via available contexts to the concurrency field.

There seems to be a way to distinguish between caller and the calle as described here but for OIDC claim tokens, for example:

{
    "workflow_ref": "/repo/.github/workflows/top-level-workflow.yml@refs/heads/main",
    "job_workflow_ref": "2/repo2/.github/workflows/reusable-workflow-3.yml@v1",
}

where workflow_ref is akin to the one mentioned above and job_workflow_ref is the callee reference at the end of the chain (which would suffice in your case).

Other things tried

Just to be sure, I've also checked in a separate repo specifying the reusable workflow via "local" (as yours ./.github/workflows/deploy.web.yaml) and "global" references (octo-/example-repo/.github/workflows/reusable-workflow.yml@main), no difference

本文标签: