Skip to content

ci(release): keep image release on its own v* track - #1004

Merged
ilopezluna merged 3 commits into
mainfrom
fix/release-resolver-vtrack-filter
Jul 9, 2026
Merged

ci(release): keep image release on its own v* track#1004
ilopezluna merged 3 commits into
mainfrom
fix/release-resolver-vtrack-filter

Conversation

@ilopezluna

Copy link
Copy Markdown
Contributor

Problem

Release run 28956315525 failed at verify-docker-ce:

✗ Error: Expected client version vdmr-v0.1.1, got 'v1.2.5'
✓ Server version matches expected vdmr-v0.1.1

The malformed version vdmr-v0.1.1 is the real bug — the client (v1.2.5, the Docker CE plugin) was fine.

Root cause

There are two independent release tracks:

Track Workflow Trigger Scheme
Container image + docker-model-plugin release.yml v* v1.2.x
Standalone dmr binary release-dmr.yml dmr-v* dmr-v0.1.x

release.yml's resolver found "latest release" with gh release list --limit 1no prefix filter — so it grabbed the newest release repo-wide, which was dmr-v0.1.0 from the other track. The v* auto-bump then mangled it: ${LATEST_TAG#v} strips only a leading v, so dmr-v0.1.0vdmr-v0.1.1. That corrupted tag was baked into the server image, pushed as a git tag, and propagated downstream.

Fix

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 isn't valid vX.Y.Z, so a malformed tag can never again reach the image build or downstream triggers.

The standalone dmr binary continues to release independently via release-dmr.yml.

Verification

  • Dry-run harness of the resolver logic: 9/9 cases pass — picks v1.2.5 (ignoring dmr-v0.1.0 / vdmr-v0.1.1), bumps to v1.2.6, guard aborts on vdmr-v0.1.1, explicit dmr- tags rejected, normal minor/major bumps unaffected.
  • Against the live release list, the fixed filter returns v1.2.5 (old logic returned dmr-v0.1.0).

Follow-up (not in this PR)

  • Cleanup: delete the vdmr-v0.1.1 git tag + the 7 vdmr-v0.1.1* Docker Hub tags; check the downstream docker/inference-engine-llama.cpp, docker/packaging, docker/release-repo for stray vdmr-v0.1.1 artifacts.
  • Re-release: re-run the Release workflow (resolves v1.2.6, restores latest*).

🤖 Generated with Claude Code

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>
@gemini-code-assist

Copy link
Copy Markdown
Contributor

Note

Gemini is unable to generate a review for this pull request due to the file types involved not being currently supported.

@ilopezluna
ilopezluna marked this pull request as ready for review July 9, 2026 07:15
@ilopezluna
ilopezluna requested a review from a team July 9, 2026 07:15

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - I've found 2 issues

Prompt for AI Agents
Please address the comments from this code review:

## Individual Comments

### Comment 1
<location path=".github/workflows/release.yml" line_range="72-73" />
<code_context>
+          # is released separately via release-dmr.yml on `dmr-v*` tags. Filter
+          # to our own scheme so we never pick up the other track's release
+          # (gh release list is newest-first by creation, so `first` = newest).
+          LATEST_RELEASE_TAG=$(gh release list --limit 100 --json tagName \
+            --jq 'map(.tagName) | map(select(test("^v[0-9]+\\.[0-9]+\\.[0-9]+$"))) | first // ""' \
             2>/dev/null || echo "")

</code_context>
<issue_to_address>
**issue:** Potentially missing the latest semver release if there are many non-matching tags in the newest 100 releases

Because `gh release list` is limited to the newest 100 releases and you filter semver tags client-side, you only ever pick the newest `vX.Y.Z` tag within that 100-release window. If many non-`vX.Y.Z` tags (like `dmr-v*`) are created, the actual latest semver tag may fall outside that window and be skipped. If this is realistic for this repo, consider a larger limit or a different approach (e.g. filtering by tag pattern via `gh api`, or falling back to `git tag` when no semver match is found) so you don’t miss the true latest semver release.
</issue_to_address>

### Comment 2
<location path=".github/workflows/release.yml" line_range="138-139" />
<code_context>

+          # Guard: never let a malformed tag reach the image build or the
+          # downstream release triggers. Must be strict semver (vX.Y.Z).
+          if ! echo "$RELEASE_TAG" | grep -qE '^v[0-9]+\.[0-9]+\.[0-9]+$'; then
+            echo "::error::Resolved release tag '$RELEASE_TAG' is not valid vX.Y.Z. Aborting."
+            exit 1
+          fi
</code_context>
<issue_to_address>
**suggestion:** Regex for tag validation is duplicated and could be centralized

This strict semver pattern (`^v[0-9]+\.[0-9]+\.[0-9]+$`) is now repeated (here and in release discovery). To avoid inconsistencies if the rule changes (e.g., allowing build metadata), centralize it in a shared variable or helper so all validations use the same definition.

Suggested implementation:

```
            RELEASE_TAG_PATTERN='^v[0-9]+\.[0-9]+\.[0-9]+$'
            LATEST_TAG=$(git tag --list 'v*' --sort=-v:refname \
              | grep -E "$RELEASE_TAG_PATTERN" | head -1)
            echo "No GitHub Releases found, falling back to latest git tag: ${LATEST_TAG:-<none>}"
          fi

```

```
          # Guard: never let a malformed tag reach the image build or the
          # downstream release triggers. Must be strict semver (vX.Y.Z).
          if ! echo "$RELEASE_TAG" | grep -qE "$RELEASE_TAG_PATTERN"; then
            echo "::error::Resolved release tag '$RELEASE_TAG' is not valid vX.Y.Z. Aborting."
            exit 1
          fi

```

- Ensure `RELEASE_TAG_PATTERN` is defined once within the same `run:` script block that uses both `LATEST_TAG` discovery and `RELEASE_TAG` validation. If those snippets are in different steps, extract the pattern definition into each relevant step or into a shared reusable workflow/action.
- Search the rest of `.github/workflows/release.yml` (and any related workflows) for other hard-coded uses of `'^v[0-9]+\.[0-9]+\.[0-9]+$'` and replace them with `$RELEASE_TAG_PATTERN` to keep all validations consistent.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment thread .github/workflows/release.yml Outdated
Comment thread .github/workflows/release.yml Outdated
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>
@ilopezluna

Copy link
Copy Markdown
Contributor Author

@sourcery-ai review

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - I've left some high level feedback:

  • The gh release list --limit 100 window introduces a subtle edge case where older but still relevant vX.Y.Z releases could be missed; consider either paginating through all releases or using --limit with a clearer justification or comment about why 100 is sufficient.
  • Swallowing all gh release list stderr output with 2>/dev/null makes diagnosing future resolver issues harder; it may be safer to allow errors to surface or explicitly handle known failure modes with targeted messaging.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- The `gh release list --limit 100` window introduces a subtle edge case where older but still relevant `vX.Y.Z` releases could be missed; consider either paginating through all releases or using `--limit` with a clearer justification or comment about why 100 is sufficient.
- Swallowing all `gh release list` stderr output with `2>/dev/null` makes diagnosing future resolver issues harder; it may be safer to allow errors to surface or explicitly handle known failure modes with targeted messaging.

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

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>
@ilopezluna

Copy link
Copy Markdown
Contributor Author

Thanks @sourcery-ai — both high-level points addressed in 1d1d870:

1. --limit 100 window. Added a comment justifying the limit. We intentionally keep the bounded lookup rather than paginate all releases: gh release list is newest-first, so the first vX.Y.Z match is the newest one, and the only failure mode (the latest vX.Y.Z pushed outside the 100-window by a burst of off-track dmr-v* releases) is already backstopped by the unbounded git tag fallback. Pagination would add API calls for a case the fallback already covers.

2. Swallowing stderr. Good call. gh release list stderr is no longer discarded — it's captured, and on failure we emit a ::warning:: with the error before falling back to git tags, so a broken lookup is visible in the logs instead of silently degrading.

Also fixed a related pipefail hazard while here: under the default bash -eo pipefail shell, a no-match grep in the git-tag fallback would abort the step (breaking the legitimate "fresh repo → start at v0.1.0" path); wrapped it in { grep … || true; }. All resolver paths verified under set -eo pipefail.

@ilopezluna
ilopezluna merged commit 34ca88d into main Jul 9, 2026
14 checks passed
@ilopezluna
ilopezluna deleted the fix/release-resolver-vtrack-filter branch July 9, 2026 08:21
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants