Skip to content

Commit 2221f17

Browse files
authored
Merge pull request #1005 from docker/harden-release-idempotency
ci(release): make downstream release triggers idempotent and race-free
2 parents 34ca88d + 786ed6a commit 2221f17

1 file changed

Lines changed: 77 additions & 34 deletions

File tree

.github/workflows/release.yml

Lines changed: 77 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -485,43 +485,75 @@ jobs:
485485
permissions:
486486
contents: read
487487
steps:
488+
# Idempotency guard: a re-run of the release for a tag that was already
489+
# published must NOT re-trigger the downstream release. The desktop
490+
# release re-pushes docker/docker-model-cli-desktop-module:<tag>, and
491+
# pushing an already-existing tag fails with "403 denied". Detect the
492+
# already-published tag on Docker Hub and skip the trigger instead.
493+
- name: Log in to DockerHub
494+
uses: docker/login-action@650006c6eb7dba73a995cc03b0b2d7f5ca915bee
495+
with:
496+
username: "docker"
497+
password: ${{ secrets.ORG_ACCESS_TOKEN }}
498+
499+
- name: Check whether desktop module is already published
500+
id: published
501+
env:
502+
RELEASE_TAG: ${{ needs.prepare.outputs.release_tag }}
503+
run: |
504+
if docker manifest inspect \
505+
"docker/docker-model-cli-desktop-module:$RELEASE_TAG" >/dev/null 2>&1; then
506+
echo "already=true" >> "$GITHUB_OUTPUT"
507+
echo "✅ desktop-module:$RELEASE_TAG already exists — skipping trigger"
508+
else
509+
echo "already=false" >> "$GITHUB_OUTPUT"
510+
fi
511+
488512
- name: Trigger Desktop CLI release and wait for completion
513+
if: steps.published.outputs.already != 'true'
489514
env:
490515
GH_TOKEN: ${{ secrets.CLI_RELEASE_PAT }}
491516
RELEASE_TAG: ${{ needs.prepare.outputs.release_tag }}
492517
VERSION: ${{ needs.prepare.outputs.version }}
493518
run: |
494519
echo "🚀 Triggering Desktop CLI release workflow"
495520
496-
# gh workflow run returns run URL on stdout (gh v2.87.0+)
497-
OUTPUT=$(gh workflow run release-cli-dd.yml \
521+
# Record the newest existing run BEFORE dispatch so we can identify
522+
# the run WE trigger. `gh workflow run` prints nothing usable on
523+
# stdout, and "query the latest run" races: it can return a run
524+
# started by someone else, or a stale completed run (which is how a
525+
# trigger job can "succeed" while watching the wrong run). Run IDs are
526+
# monotonically increasing, so the first run with a strictly greater
527+
# ID than BEFORE_ID is ours.
528+
BEFORE_ID=$(gh run list \
498529
--repo docker/inference-engine-llama.cpp \
499-
-f model-cli-ref="$RELEASE_TAG" \
500-
-f tag="v$VERSION")
530+
--workflow release-cli-dd.yml \
531+
--limit 1 --json databaseId --jq '.[0].databaseId // 0')
501532
502-
RUN_URL=$(echo "$OUTPUT" \
503-
| grep -o 'https://github.com/[^ ]*/actions/runs/[0-9]*') || true
533+
gh workflow run release-cli-dd.yml \
534+
--repo docker/inference-engine-llama.cpp \
535+
-f model-cli-ref="$RELEASE_TAG" \
536+
-f tag="v$VERSION"
504537
505-
if [ -z "$RUN_URL" ]; then
506-
echo "⚠️ Could not extract run URL from gh output, querying latest run..."
507-
sleep 5
508-
RUN_ID=$(gh run list \
538+
RUN_ID=""
539+
for _ in $(seq 1 30); do
540+
sleep 3
541+
CANDIDATE=$(gh run list \
509542
--repo docker/inference-engine-llama.cpp \
510543
--workflow release-cli-dd.yml \
511-
--limit 1 \
512-
--json databaseId \
513-
--jq '.[0].databaseId')
514-
else
515-
RUN_ID=$(echo "$RUN_URL" | grep -o '[0-9]*$')
516-
fi
544+
--limit 1 --json databaseId --jq '.[0].databaseId // 0')
545+
if [ "$CANDIDATE" -gt "$BEFORE_ID" ]; then
546+
RUN_ID="$CANDIDATE"
547+
break
548+
fi
549+
done
517550
518551
if [ -z "$RUN_ID" ]; then
519-
echo "::error::Failed to determine workflow run ID"
552+
echo "::error::Failed to determine Desktop CLI release run ID"
520553
exit 1
521554
fi
522555
523556
echo "::add-mask::$RUN_ID"
524-
echo "::add-mask::$RUN_URL"
525557
echo "✅ Desktop CLI release workflow triggered"
526558
527559
echo "⏳ Waiting for Desktop CLI release to complete..."
@@ -556,34 +588,39 @@ jobs:
556588
run: |
557589
echo "📦 Triggering packaging workflow"
558590
559-
# gh workflow run returns run URL on stdout (gh v2.87.0+)
560-
OUTPUT=$(gh workflow run release-model.yml \
591+
# Identify the run WE trigger by its ID, not by "the latest run".
592+
# `gh workflow run` prints nothing usable on stdout and querying the
593+
# newest run right after dispatch races with concurrent/stale runs.
594+
# Run IDs increase monotonically, so the first run with an ID greater
595+
# than the pre-dispatch newest is ours. (See release-cli-desktop.)
596+
BEFORE_ID=$(gh run list \
561597
--repo docker/packaging \
562-
-f ref="$RELEASE_TAG")
598+
--workflow release-model.yml \
599+
--limit 1 --json databaseId --jq '.[0].databaseId // 0')
563600
564-
RUN_URL=$(echo "$OUTPUT" \
565-
| grep -o 'https://github.com/[^ ]*/actions/runs/[0-9]*') || true
601+
gh workflow run release-model.yml \
602+
--repo docker/packaging \
603+
-f ref="$RELEASE_TAG"
566604
567-
if [ -z "$RUN_URL" ]; then
568-
echo "⚠️ Could not extract run URL from gh output, querying latest run..."
569-
sleep 5
570-
RUN_ID=$(gh run list \
605+
RUN_ID=""
606+
for _ in $(seq 1 30); do
607+
sleep 3
608+
CANDIDATE=$(gh run list \
571609
--repo docker/packaging \
572610
--workflow release-model.yml \
573-
--limit 1 \
574-
--json databaseId \
575-
--jq '.[0].databaseId')
576-
else
577-
RUN_ID=$(echo "$RUN_URL" | grep -o '[0-9]*$')
578-
fi
611+
--limit 1 --json databaseId --jq '.[0].databaseId // 0')
612+
if [ "$CANDIDATE" -gt "$BEFORE_ID" ]; then
613+
RUN_ID="$CANDIDATE"
614+
break
615+
fi
616+
done
579617
580618
if [ -z "$RUN_ID" ]; then
581619
echo "::error::Failed to determine packaging workflow run ID"
582620
exit 1
583621
fi
584622
585623
echo "::add-mask::$RUN_ID"
586-
echo "::add-mask::$RUN_URL"
587624
echo "✅ Packaging workflow triggered"
588625
589626
echo "⏳ Waiting for packaging to complete..."
@@ -662,6 +699,12 @@ jobs:
662699
RELEASE_TAG: ${{ needs.prepare.outputs.release_tag }}
663700
VERSION: ${{ needs.prepare.outputs.version }}
664701
run: |
702+
# Idempotency: a re-run for an already-released tag must not fail here.
703+
if gh release view "$RELEASE_TAG" --repo docker/model-runner >/dev/null 2>&1; then
704+
echo "✅ GitHub Release $RELEASE_TAG already exists — skipping creation"
705+
exit 0
706+
fi
707+
665708
echo "Creating GitHub Release for $RELEASE_TAG"
666709
gh release create "$RELEASE_TAG" \
667710
--repo docker/model-runner \

0 commit comments

Comments
 (0)