From 174746e2f72fcd684d29c3260d36e9ad26634159 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Tue, 8 Sep 2026 15:05:16 +0900 Subject: [PATCH] fix(ci): serialize queued Docker releases without cancelling reruns --- .github/workflows/docker-publish.yml | 5 +++-- .../tests/test_docker_workflow_concurrency.py | 21 ++++++++++++++++--- backend/tests/test_release_governance.py | 2 +- 3 files changed, 22 insertions(+), 6 deletions(-) diff --git a/.github/workflows/docker-publish.yml b/.github/workflows/docker-publish.yml index f72a542be..070f338ce 100644 --- a/.github/workflows/docker-publish.yml +++ b/.github/workflows/docker-publish.yml @@ -17,9 +17,10 @@ concurrency: # 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 }} + # Releases share the GHCR/AKS destination and retain up to 100 pending runs. + group: docker-publish-${{ github.repository }}-${{ github.event_name == 'pull_request' && format('{0}-{1}', github.event.pull_request.number, github.run_attempt == 1 && 'first-attempt' || github.run_id) || 'release-ghcr-aks' }} cancel-in-progress: ${{ github.event_name == 'pull_request' }} + queue: ${{ github.event_name == 'pull_request' && 'single' || 'max' }} env: REGISTRY: ghcr.io diff --git a/backend/tests/test_docker_workflow_concurrency.py b/backend/tests/test_docker_workflow_concurrency.py index d90ee5786..0b447e21c 100644 --- a/backend/tests/test_docker_workflow_concurrency.py +++ b/backend/tests/test_docker_workflow_concurrency.py @@ -2,10 +2,25 @@ from pathlib import Path +import yaml + REPO_ROOT = Path(__file__).resolve().parents[2] +def test_docker_releases_keep_pending_runs_in_a_shared_destination() -> None: + """Do not silently restore the single-pending default for release writers.""" + workflow = yaml.safe_load( + (REPO_ROOT / ".github/workflows/docker-publish.yml").read_text(encoding="utf-8") + ) + concurrency = workflow["concurrency"] + assert concurrency.get("queue") == ( + "${{ github.event_name == 'pull_request' && 'single' || 'max' }}" + ) + assert "|| 'release-ghcr-aks' }}" in concurrency["group"] + assert concurrency["cancel-in-progress"] == "${{ github.event_name == 'pull_request' }}" + + 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( @@ -14,9 +29,9 @@ def test_docker_pr_concurrency_isolates_reruns_from_first_attempts() -> None: header = workflow.split("jobs:", 1)[0] 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 }}" + "${{ github.event_name == 'pull_request' && format('{0}-{1}', " + "github.event.pull_request.number, github.run_attempt == 1 " + "&& 'first-attempt' || github.run_id) || 'release-ghcr-aks' }}" ) bare_group = ( "group: docker-publish-${{ github.repository }}-" diff --git a/backend/tests/test_release_governance.py b/backend/tests/test_release_governance.py index 3b2eb610c..cf37ebc42 100644 --- a/backend/tests/test_release_governance.py +++ b/backend/tests/test_release_governance.py @@ -740,7 +740,7 @@ def test_docker_publish_validates_pr_images_and_publishes_semver_images_only_on_ ) assert "concurrency:" in workflow assert ( - "docker-publish-${{ github.repository }}-${{ github.event.pull_request.number || github.ref }}" + "docker-publish-${{ github.repository }}-${{ github.event_name == 'pull_request' && format('{0}-{1}', github.event.pull_request.number," in workflow ) assert (