From 1963c8c01407565df5db615692473c67e5f93a32 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Tue, 8 Sep 2026 03:59:19 +0900 Subject: [PATCH 1/5] fix(ci): coalesce superseded Docker PR image validations Bandit already has an owner PR. Docker publish still queued a new image-validation run for every PR event with no workflow-level group. Cancel only pull_request runs so tag publication is not discarded. Signed-off-by: Seongho Bae --- .github/workflows/docker-publish.yml | 8 ++++++++ backend/tests/test_release_governance.py | 9 +++++++++ 2 files changed, 17 insertions(+) diff --git a/.github/workflows/docker-publish.yml b/.github/workflows/docker-publish.yml index fc7058413..d57074e93 100644 --- a/.github/workflows/docker-publish.yml +++ b/.github/workflows/docker-publish.yml @@ -13,6 +13,14 @@ on: permissions: contents: read +concurrency: + # Workflow-level admission so superseded PR image validations coalesce + # before they occupy an org job slot. Tag publication is merge/release + # work: cancel-in-progress stays false on push so an in-flight v* publish + # is not discarded. + group: docker-publish-${{ github.repository }}-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: ${{ github.event_name == 'pull_request' }} + env: REGISTRY: ghcr.io # Keep the explicit opt-in as belt-and-suspenders. The real warning removal diff --git a/backend/tests/test_release_governance.py b/backend/tests/test_release_governance.py index a23c70746..3b2eb610c 100644 --- a/backend/tests/test_release_governance.py +++ b/backend/tests/test_release_governance.py @@ -738,6 +738,15 @@ def test_docker_publish_validates_pr_images_and_publishes_semver_images_only_on_ assert ( "needs.deploy_preflight.outputs.aks_kubeconfig_configured == 'true'" in workflow ) + assert "concurrency:" in workflow + assert ( + "docker-publish-${{ github.repository }}-${{ github.event.pull_request.number || github.ref }}" + in workflow + ) + assert ( + "cancel-in-progress: ${{ github.event_name == 'pull_request' }}" in workflow + ) + assert "cancel-in-progress: true" not in workflow.split("jobs:", 1)[0] def test_frontend_dockerfile_builds_and_starts_production_artifact() -> None: From 8ef19470fa2af62b05f4920ecc4db0a9e3cf1bf6 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Tue, 8 Sep 2026 04:47:39 +0900 Subject: [PATCH 2/5] test(ci): reproduce Docker rerun cancellation race --- .../tests/test_docker_workflow_concurrency.py | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 backend/tests/test_docker_workflow_concurrency.py diff --git a/backend/tests/test_docker_workflow_concurrency.py b/backend/tests/test_docker_workflow_concurrency.py new file mode 100644 index 000000000..908631f98 --- /dev/null +++ b/backend/tests/test_docker_workflow_concurrency.py @@ -0,0 +1,24 @@ +"""Regression coverage for Docker workflow concurrency identity.""" + +from pathlib import Path + + +REPO_ROOT = Path(__file__).resolve().parents[2] + + +def test_docker_pr_concurrency_isolates_reruns_from_first_attempts() -> None: + """Keep manual reruns out of the first-attempt PR cancellation group.""" + workflow = (REPO_ROOT / ".github/workflows/docker-publish.yml").read_text( + encoding="utf-8" + ) + header = workflow.split("jobs:", 1)[0] + + assert ( + "docker-publish-${{ github.repository }}-${{ github.event_name == 'pull_request' && github.run_attempt == 1 && github.event.pull_request.number || github.run_id }}" + in header + ) + assert ( + "cancel-in-progress: ${{ github.event_name == 'pull_request' && github.run_attempt == 1 }}" + in header + ) + assert "github.event.pull_request.number || github.ref" not in header From 54b09513908b07cbbb72628d0057eaa6afb8bb6e Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Tue, 8 Sep 2026 04:50:10 +0900 Subject: [PATCH 3/5] fix(ci): isolate Docker reruns from PR supersession --- .github/workflows/docker-publish.yml | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/.github/workflows/docker-publish.yml b/.github/workflows/docker-publish.yml index d57074e93..7b6e7950f 100644 --- a/.github/workflows/docker-publish.yml +++ b/.github/workflows/docker-publish.yml @@ -14,12 +14,11 @@ permissions: contents: read concurrency: - # Workflow-level admission so superseded PR image validations coalesce - # before they occupy an org job slot. Tag publication is merge/release - # work: cancel-in-progress stays false on push so an in-flight v* publish - # is not discarded. - group: docker-publish-${{ github.repository }}-${{ github.event.pull_request.number || github.ref }} - cancel-in-progress: ${{ github.event_name == 'pull_request' }} + # Supersede only first-attempt validations for the same PR. A manual rerun + # keeps its run_id so a later PR event cannot cancel an intentional retry. + # Tag publication also keeps a unique run identity and is never cancelled. + group: docker-publish-${{ github.repository }}-${{ github.event_name == 'pull_request' && github.run_attempt == 1 && github.event.pull_request.number || github.run_id }} + cancel-in-progress: ${{ github.event_name == 'pull_request' && github.run_attempt == 1 }} env: REGISTRY: ghcr.io From 145e8b2134d3b6a5c642cb76a18858583337da79 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Tue, 8 Sep 2026 04:53:19 +0900 Subject: [PATCH 4/5] test(ci): pin Docker concurrency generation identity --- .../tests/test_docker_workflow_concurrency.py | 20 +++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/backend/tests/test_docker_workflow_concurrency.py b/backend/tests/test_docker_workflow_concurrency.py index 908631f98..d90ee5786 100644 --- a/backend/tests/test_docker_workflow_concurrency.py +++ b/backend/tests/test_docker_workflow_concurrency.py @@ -12,13 +12,17 @@ def test_docker_pr_concurrency_isolates_reruns_from_first_attempts() -> None: encoding="utf-8" ) header = workflow.split("jobs:", 1)[0] - - assert ( - "docker-publish-${{ github.repository }}-${{ github.event_name == 'pull_request' && github.run_attempt == 1 && github.event.pull_request.number || github.run_id }}" - in header + expected_group = ( + "group: docker-publish-${{ github.repository }}-" + "${{ github.event.pull_request.number || github.ref }}-" + "${{ github.event_name == 'pull_request' && github.run_attempt == 1 " + "&& 'first-attempt' || github.run_id }}" ) - assert ( - "cancel-in-progress: ${{ github.event_name == 'pull_request' && github.run_attempt == 1 }}" - in header + bare_group = ( + "group: docker-publish-${{ github.repository }}-" + "${{ github.event.pull_request.number || github.ref }}" ) - assert "github.event.pull_request.number || github.ref" not in header + + assert expected_group in header + assert bare_group not in header.splitlines() + assert "cancel-in-progress: ${{ github.event_name == 'pull_request' }}" in header From b2ee42b6e9286aac4b908a644f472c71d9ccc2a6 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Tue, 8 Sep 2026 04:54:16 +0900 Subject: [PATCH 5/5] fix(ci): separate Docker PR rerun generations --- .github/workflows/docker-publish.yml | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/.github/workflows/docker-publish.yml b/.github/workflows/docker-publish.yml index 7b6e7950f..f72a542be 100644 --- a/.github/workflows/docker-publish.yml +++ b/.github/workflows/docker-publish.yml @@ -14,11 +14,12 @@ permissions: contents: read concurrency: - # Supersede only first-attempt validations for the same PR. A manual rerun - # keeps its run_id so a later PR event cannot cancel an intentional retry. - # Tag publication also keeps a unique run identity and is never cancelled. - group: docker-publish-${{ github.repository }}-${{ github.event_name == 'pull_request' && github.run_attempt == 1 && github.event.pull_request.number || github.run_id }} - cancel-in-progress: ${{ github.event_name == 'pull_request' && github.run_attempt == 1 }} + # First-attempt PR validations share a generation key so a newer event + # supersedes only another first attempt. Manual reruns add their stable + # run_id and therefore cannot cancel, or be cancelled by, a newer PR event. + # Tag publication is unique per run and cancel-in-progress remains false. + group: docker-publish-${{ github.repository }}-${{ github.event.pull_request.number || github.ref }}-${{ github.event_name == 'pull_request' && github.run_attempt == 1 && 'first-attempt' || github.run_id }} + cancel-in-progress: ${{ github.event_name == 'pull_request' }} env: REGISTRY: ghcr.io