Skip to content

Commit 34ca88d

Browse files
ilopezlunaclaude
andauthored
ci(release): keep image release on its own v* track (#1004)
* ci(release): keep image release on its own v* track The release.yml resolver picked the newest release repo-wide via `gh release list --limit 1`, with no prefix filter. When the separate standalone-dmr track published `dmr-v0.1.0` (newest release overall), the container-image release grabbed it and ran it through the v* auto-bump logic. `${LATEST_TAG#v}` only strips a leading `v`, so `dmr-v0.1.0` was mangled into the invalid tag `vdmr-v0.1.1`, which was then baked into the server image, pushed as a git tag, and propagated downstream — breaking verify-docker-ce (server `vdmr-v0.1.1` vs CE client `v1.2.5`). Restrict the resolver to this workflow's own strict-semver (vX.Y.Z) scheme: - filter the GitHub release lookup to `^v[0-9]+\.[0-9]+\.[0-9]+$` - filter the git-tag fallback the same way (the `v*` glob also matched stray tags like `vdmr-v0.1.1`) - add a post-resolve guard that aborts prepare if RELEASE_TAG is not valid vX.Y.Z, so a malformed tag can never reach the image build or downstream release triggers The standalone `dmr` binary continues to release independently via release-dmr.yml on `dmr-v*` tags. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * ci(release): centralize semver pattern, harden release lookup Address PR review feedback: - Centralize the strict-semver pattern in a single RELEASE_TAG_PATTERN variable, reused by the release lookup, git-tag fallback, explicit-tag validation, and the final guard, so the rule can't drift between them. - Make the git-tag fallback trigger on "no semver match found" (not just "no releases exist"), covering the case where the newest 100 releases are all off-track (e.g. a burst of dmr-v*) and the true latest vX.Y.Z release falls outside the release-list window. git tag is unbounded, so it always finds the real latest semver tag. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * ci(release): surface gh failures and fix pipefail in tag fallback Address second-pass review feedback: - Stop swallowing `gh release list` stderr with `2>/dev/null`. Capture it and, on failure, emit a `::warning::` before falling back to git tags, so a broken release lookup is visible in the logs instead of silently degrading. - Document why `--limit 100` is sufficient (the unbounded git-tag fallback backstops the window). - Fix a pipefail hazard the previous commit introduced: under the default `bash -eo pipefail` shell, a no-match `grep` in the git-tag fallback exits non-zero and would abort the step (breaking the legitimate "fresh repo, no tags -> start at v0.1.0" path). Wrap it in `{ grep ... || true; }`. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent 362c7f6 commit 34ca88d

1 file changed

Lines changed: 45 additions & 11 deletions

File tree

.github/workflows/release.yml

Lines changed: 45 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -63,25 +63,52 @@ jobs:
6363
BUMP_TYPE: ${{ inputs.bumpType || 'patch' }}
6464
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
6565
run: |
66-
# --- Find the latest published GitHub Release tag ---
67-
# gh release list defaults to --order desc (newest first by creation
68-
# date), so --limit 1 gives us the most recent release.
69-
LATEST_RELEASE_TAG=$(gh release list --limit 1 --json tagName \
70-
--jq '.[0].tagName // ""' \
71-
2>/dev/null || echo "")
72-
66+
# Strict semver (vX.Y.Z) pattern for this workflow's release track.
67+
# Defined once and reused for release discovery, explicit-tag
68+
# validation, and the final guard so the rule can't drift.
69+
RELEASE_TAG_PATTERN='^v[0-9]+\.[0-9]+\.[0-9]+$'
70+
71+
# --- Find the latest published GitHub Release tag ---
72+
# This workflow owns the container-image / docker-model-plugin track,
73+
# which uses strict semver tags (vX.Y.Z). The standalone `dmr` binary
74+
# is released separately via release-dmr.yml on `dmr-v*` tags. Filter
75+
# to our own scheme so we never pick up the other track's release
76+
# (gh release list is newest-first by creation, so `first` = newest).
77+
# 100 comfortably covers this repo's release cadence; if the latest
78+
# vX.Y.Z ever falls outside that window (e.g. a burst of off-track
79+
# dmr-v* releases), the git-tag fallback below still finds it.
80+
LATEST_RELEASE_TAG=""
81+
GH_ERR=$(mktemp)
82+
if RELEASES_JSON=$(gh release list --limit 100 --json tagName 2>"$GH_ERR"); then
83+
LATEST_RELEASE_TAG=$(printf '%s' "$RELEASES_JSON" \
84+
| jq -r --arg pat "$RELEASE_TAG_PATTERN" \
85+
'map(.tagName) | map(select(test($pat))) | first // ""')
86+
else
87+
# Surface the failure instead of silently degrading, then fall back.
88+
echo "::warning::gh release list failed; falling back to git tags: $(cat "$GH_ERR")"
89+
fi
90+
rm -f "$GH_ERR"
91+
92+
# Fall back to git tags when the release lookup found no semver match —
93+
# because it failed, because no GitHub Releases exist yet, or because
94+
# the newest 100 releases were all off-track and the true latest
95+
# vX.Y.Z release fell outside that window. `git tag` is unbounded, so
96+
# this always finds the real latest semver tag. Match only strict
97+
# semver — the 'v*' glob would also match stray tags like
98+
# 'vdmr-v0.1.1'. `|| true` keeps a no-match grep from tripping
99+
# pipefail (a fresh repo with no semver tags is valid — see below).
73100
if [ -n "$LATEST_RELEASE_TAG" ]; then
74101
LATEST_TAG="$LATEST_RELEASE_TAG"
75102
echo "Latest published GitHub Release tag: $LATEST_TAG"
76103
else
77-
# Fallback to git tags when no GitHub Releases exist yet
78-
LATEST_TAG=$(git tag --list 'v*' --sort=-v:refname | head -1)
79-
echo "No GitHub Releases found, falling back to latest git tag: ${LATEST_TAG:-<none>}"
104+
LATEST_TAG=$(git tag --list 'v*' --sort=-v:refname \
105+
| { grep -E "$RELEASE_TAG_PATTERN" || true; } | head -1)
106+
echo "No matching GitHub Release found, falling back to latest git tag: ${LATEST_TAG:-<none>}"
80107
fi
81108
82109
if [ -n "$EXPLICIT_TAG" ]; then
83110
# Validate explicit tag format
84-
if ! echo "$EXPLICIT_TAG" | grep -qE '^v[0-9]+\.[0-9]+\.[0-9]+$'; then
111+
if ! echo "$EXPLICIT_TAG" | grep -qE "$RELEASE_TAG_PATTERN"; then
85112
echo "::error::Invalid release tag format: '$EXPLICIT_TAG'. Expected format: v<MAJOR>.<MINOR>.<PATCH> (e.g., v1.2.3)"
86113
exit 1
87114
fi
@@ -127,6 +154,13 @@ jobs:
127154
fi
128155
fi
129156
157+
# Guard: never let a malformed tag reach the image build or the
158+
# downstream release triggers. Must be strict semver (vX.Y.Z).
159+
if ! echo "$RELEASE_TAG" | grep -qE "$RELEASE_TAG_PATTERN"; then
160+
echo "::error::Resolved release tag '$RELEASE_TAG' is not valid vX.Y.Z. Aborting."
161+
exit 1
162+
fi
163+
130164
echo "release_tag=$RELEASE_TAG" >> "$GITHUB_OUTPUT"
131165
echo "version=$VERSION" >> "$GITHUB_OUTPUT"
132166
echo "previous_tag=${PREVIOUS_TAG:-}" >> "$GITHUB_OUTPUT"

0 commit comments

Comments
 (0)