Skip to content

feat(release): add tag/version validation + previous-tag scripts (epic 005 T005) - #42

Merged
rsenna merged 2 commits into
mainfrom
005-t005-t006-version-buildid
Aug 1, 2026
Merged

feat(release): add tag/version validation + previous-tag scripts (epic 005 T005)#42
rsenna merged 2 commits into
mainfrom
005-t005-t006-version-buildid

Conversation

@owkwo-bot

@owkwo-bot owkwo-bot commented Jul 30, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • .github/scripts/validate-release-tag.sh: validates a release tag against Cargo.toml's [workspace.package].version (FR-010), fails naming both values on mismatch (SC-006).
  • .github/scripts/previous-release-tag.sh: resolves the nearest prior SemVer tag (FR-011), empty output (not an error) for the first-release case (FR-009).
  • Fixture tests for both, written test-first per Constitution I, run against a throwaway git repo/Cargo.toml fixture rather than this repo's own tags.
  • Marks epic 005's T005 done in tasks.md.

Despite the branch name (leftover from initial scoping), this PR is T005 only — T006 (build-identifier) is a separate, upcoming PR to keep one concern per PR.

Why

First real Phase 2 "Foundational" task for epic 005 — these scripts are what release.yml (T008, not yet built) will call.

Scope note

T005's own description mentions asserting "re-publishing an existing tag is rejected" — that check isn't in these scripts. It requires querying the GitHub Release API (does a release already exist for this tag?), which these are deliberately offline/git-only and can't determine. That check is T012's job in release.yml, where it's already documented.

Test plan

  • bash .github/scripts/tests/test-validate-release-tag.sh — 8/8 pass
  • bash .github/scripts/tests/test-previous-release-tag.sh — 4/4 pass
  • Manually verified against the real repo's Cargo.toml (v0.1.0 passes, v9.9.9 fails with both values named)
  • cargo build --workspace unaffected (no Rust files touched)

🧙 Built with WOZCODE

Summary by Sourcery

Add release tag validation and previous-tag resolution scripts backed by fixture-based tests, and mark CI release versioning task T005 as complete.

New Features:

  • Add validate-release-tag.sh to enforce SemVer tag format and match release tags against the workspace version in Cargo.toml.
  • Add previous-release-tag.sh to resolve the nearest prior SemVer release tag, returning empty output for first-release cases.

Documentation:

  • Update epic 005 CI release versioning tasks documentation to record completion of T005 and clarify that existing-release rejection will be handled later in release.yml (T012).

Tests:

  • Introduce fixture-based bash tests for validate-release-tag.sh using a temporary Cargo.toml workspace setup.
  • Introduce fixture-based bash tests for previous-release-tag.sh using a temporary git repository with a controlled tag history.

Summary by cubic

Adds two Bash scripts to validate a release tag against [workspace.package].version and to resolve the previous SemVer tag, with fixture tests. Completes epic 005 T005 and prepares inputs for release.yml.

  • New Features

    • .github/scripts/validate-release-tag.sh: Enforces vMAJOR.MINOR.PATCH and exact match to Cargo.toml [workspace.package].version; on mismatch, error names both values.
    • .github/scripts/previous-release-tag.sh: Finds the nearest previous strict SemVer v* tag; prints nothing for the first release. Re-publish check is deferred to T012 in release.yml.
  • Bug Fixes

    • previous-release-tag.sh: Skips prerelease tags matched by git describe --match "v[0-9]*" by walking back to the nearest strict vMAJOR.MINOR.PATCH; regression test added.
    • validate-release-tag.sh: Accepts leading whitespace in TOML when reading [workspace.package].version; tests now use mktemp for safe temp files.

Written for commit 239f119. Summary will update on new commits.

Review in cubic

Summary by CodeRabbit

  • New Features

    • Added strict validation of release tags against the workspace version.
    • Improved previous-release tag resolution to return the last fully valid SemVer release.
  • Tests

    • Expanded test coverage with a fixture-based harness for tag validation scenarios.
    • Updated previous-tag tests to ensure intermediate prerelease tags aren’t treated as previous releases.
  • Documentation

    • Updated CI release versioning documentation to reflect completed work and clarify how republishing existing tags is handled outside these git-only scripts.

…c 005 T005)

Test-first per Constitution I: fixture tests written alongside each
script, run against a throwaway git repo (not this repo's own tags).

- validate-release-tag.sh: tag format (vMAJOR.MINOR.PATCH) + match
  against Cargo.toml's [workspace.package].version (FR-010), error
  names both values on mismatch (SC-006).
- previous-release-tag.sh: nearest reachable prior SemVer tag via
  git describe (FR-011); empty output (not an error) when there is none,
  for FR-009's first-release fallback to key off.

Re-publishing-an-existing-tag rejection is deliberately not here -- it
needs the GitHub Release API to check for an existing release, which is
T012's concern in release.yml, not something an offline git-only script
can determine.
@gemini-code-assist

Copy link
Copy Markdown

Caution

The consumer version of Gemini Code Assist on GitHub has been sunset. All code review activity has officially ceased.

@sourcery-ai

sourcery-ai Bot commented Jul 30, 2026

Copy link
Copy Markdown

Reviewer's Guide

Adds two bash scripts for release tag/version validation and previous-tag resolution, with fixture-based tests and task tracking updates for epic 005 T005.

Sequence diagram for validate-release-tag.sh tag/version validation

sequenceDiagram
  actor Release_workflow
  participant validate_release_tag_sh
  participant Cargo_toml

  Release_workflow->>validate_release_tag_sh: invoke with tag, cargo_toml_path
  validate_release_tag_sh-->>Release_workflow: usage (missing args)
  note over validate_release_tag_sh: usage is only called on invalid invocation

  validate_release_tag_sh->>Cargo_toml: check file exists
  validate_release_tag_sh-->>Release_workflow: error (file not found)

  validate_release_tag_sh->>validate_release_tag_sh: validate tag regex ^vMAJOR.MINOR.PATCH$
  validate_release_tag_sh-->>Release_workflow: error (tag not SemVer)

  validate_release_tag_sh->>Cargo_toml: awk read [workspace.package].version
  validate_release_tag_sh-->>Release_workflow: error (version not found)

  validate_release_tag_sh->>validate_release_tag_sh: build expected_tag from workspace_version
  validate_release_tag_sh-->>Release_workflow: error (tag/version mismatch)
  validate_release_tag_sh-->>Release_workflow: ok (tag matches workspace version)
Loading

Sequence diagram for previous-release-tag.sh previous tag resolution

sequenceDiagram
  actor Release_workflow
  participant previous_release_tag_sh
  participant Git_repo

  Release_workflow->>previous_release_tag_sh: invoke with current_tag
  previous_release_tag_sh-->>Release_workflow: usage (missing args)
  note over previous_release_tag_sh: usage is only called on invalid invocation

  previous_release_tag_sh->>Git_repo: git rev-parse --verify refs/tags/current_tag
  previous_release_tag_sh-->>Release_workflow: error (tag does not exist)

  previous_release_tag_sh->>Git_repo: git describe --tags --match v[0-9]* --abbrev=0 current_tag^
  Git_repo-->>previous_release_tag_sh: previous_tag or no match
  previous_release_tag_sh-->>Release_workflow: previous_tag on stdout
  previous_release_tag_sh-->>Release_workflow: empty stdout if first release
Loading

File-Level Changes

Change Details Files
Implement a script that validates a release tag against the workspace version in Cargo.toml and enforces SemVer and naming rules.
  • Introduce validate-release-tag.sh that accepts a tag and optional Cargo.toml path, with usage and error handling.
  • Validate tag format as vMAJOR.MINOR.PATCH and reject non-SemVer, missing v-prefix, or pre-release suffix tags.
  • Parse [workspace.package].version from Cargo.toml using awk, failing if the version cannot be found.
  • Compare the provided tag to the expected v<workspace_version> and emit an SC-006-compliant error message naming both values on mismatch.
  • Print a success message when the tag matches the workspace version.
.github/scripts/validate-release-tag.sh
Implement a script that resolves the nearest prior SemVer release tag for a given current tag, handling the first-release case.
  • Introduce previous-release-tag.sh that accepts a current tag with usage and error handling.
  • Verify that the current tag exists in the repository via git rev-parse and fail with an error if it does not.
  • Use git describe with SemVer tag matching to find the nearest prior tag from current_tag^, suppressing errors and returning empty output on first release.
  • Ensure the script exits 0 whether or not a previous tag exists, leaving FR-009 handling to callers.
.github/scripts/previous-release-tag.sh
Add fixture-based tests for both scripts using temporary git repositories and temporary Cargo.toml files.
  • Create test-validate-release-tag.sh that writes a temporary Cargo.toml workspace, runs the validator with various tags, and counts pass/fail results.
  • Cover success and failure scenarios including matching tag, version mismatches, invalid formats, missing Cargo.toml, and SC-006 error message content.
  • Create test-previous-release-tag.sh that builds a temporary git repo with lightweight SemVer tags and runs the previous-tag script for several current tags.
  • Verify previous-tag resolution for intermediate and latest tags, assert empty output for the first release, and ensure nonexistent tags cause failure.
.github/scripts/tests/test-validate-release-tag.sh
.github/scripts/tests/test-previous-release-tag.sh
Update task tracking documentation to mark T005 as complete and clarify scope boundaries around re-publishing checks.
  • Mark T005 as done in tasks.md and add a completion note describing the implemented scripts and fixture-based tests.
  • Clarify that re-publishing-an-existing-tag rejection is not handled by these scripts and is deferred to T012 in release.yml using GitHub Release API checks.
  • Leave T006 (build-identifier computation) as not started.
specs/005-ci-release-versioning/tasks.md

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@greptile-apps greptile-apps Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

owkwo-bot has reached the 50-credit limit for trial accounts. To continue receiving code reviews, upgrade your plan.

@coderabbitai

coderabbitai Bot commented Jul 30, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Adds release-tag validation and previous-release selection scripts, fixture-based Bash tests, and updated T005 completion notes. Previous-tag lookup now skips prerelease tags while validating strict SemVer releases.

Changes

Release versioning checks

Layer / File(s) Summary
Release tag validation
.github/scripts/validate-release-tag.sh, .github/scripts/tests/test-validate-release-tag.sh
Validates vMAJOR.MINOR.PATCH tags against [workspace.package].version in Cargo.toml, tolerates leading whitespace in TOML keys, and tests valid, mismatched, malformed, unsupported, and missing-file cases.
Previous release lookup
.github/scripts/previous-release-tag.sh, .github/scripts/tests/test-previous-release-tag.sh, specs/005-ci-release-versioning/tasks.md
Backtracks through Git history to select the nearest strict SemVer release while skipping prerelease tags, tests missing-tag and first-release cases, and marks T005 complete with its scope documented.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Possibly related PRs

  • rsenna/iklo#37: Updates the Epic 005 T005 release-versioning specifications.
  • rsenna/iklo#38: Adds the CI release-versioning gate and related plan documentation.
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main release-tag validation and previous-tag scripting changes.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch 005-t005-t006-version-buildid

Comment @coderabbitai help to get the list of available commands.

@codacy-production

Copy link
Copy Markdown

Up to standards ✅

🟢 Issues 0 issues

Results:
0 new issues

View in Codacy

AI Reviewer: first review requested successfully. AI can make mistakes. Always validate suggestions.

Run reviewer

TIP This summary will be updated as you push new changes.

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown

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 header comment in validate-release-tag.sh still claims it "resolves the previous release tag" even though that logic now lives in previous-release-tag.sh; consider updating the description to avoid confusion about script responsibilities.
  • previous-release-tag.sh uses git describe --match "v[0-9]*", which can match non-SemVer tags like v1; if you truly only want SemVer tags, tightening this to a pattern that enforces MAJOR.MINOR.PATCH would better align with the semantics implied by the validate script.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- The header comment in validate-release-tag.sh still claims it "resolves the previous release tag" even though that logic now lives in previous-release-tag.sh; consider updating the description to avoid confusion about script responsibilities.
- previous-release-tag.sh uses git describe --match "v[0-9]*", which can match non-SemVer tags like v1; if you truly only want SemVer tags, tightening this to a pattern that enforces MAJOR.MINOR.PATCH would better align with the semantics implied by the validate script.

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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
specs/005-ci-release-versioning/tasks.md (1)

89-107: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Align T005’s acceptance criteria with its completion note.

T005 still says it must test and reject republishing an existing tag, while the Done note says that behavior is deferred to T012. Remove that requirement from T005 or leave T005 incomplete; otherwise the task status claims completion of work that is intentionally not included.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@specs/005-ci-release-versioning/tasks.md` around lines 89 - 107, Update
T005’s acceptance criteria and completion note to remove the requirement to test
or reject republishing an existing tag, since that behavior belongs to T012 in
release.yml. Keep T005 focused on Cargo.toml version/tag validation and
previous_release_tag selection, and retain its completed status only for that
scope.
🧹 Nitpick comments (1)
.github/scripts/tests/test-previous-release-tag.sh (1)

48-69: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Add a regression case for non-release-format tags.

Insert a tag such as v1.1.0-rc1 between v1.1.0 and v2.0.0, then assert that the previous tag for v2.0.0 remains v1.1.0. The current fixture would pass even with the broad git describe filter.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/scripts/tests/test-previous-release-tag.sh around lines 48 - 69,
Extend the fixture around the existing tag setup by adding a non-release-format
tag such as v1.1.0-rc1 between v1.1.0 and v2.0.0, then retain or update the
v2.0.0 assertion to verify it still returns v1.1.0 rather than the prerelease
tag.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.github/scripts/tests/test-validate-release-tag.sh:
- Around line 16-26: Update assert_ok and assert_fail to create unpredictable
capture files with mktemp inside the existing private $tmp directory instead of
using /tmp/out.$$. Store each generated filename, redirect command output to it,
read it on failure, and remove it afterward.

---

Outside diff comments:
In `@specs/005-ci-release-versioning/tasks.md`:
- Around line 89-107: Update T005’s acceptance criteria and completion note to
remove the requirement to test or reject republishing an existing tag, since
that behavior belongs to T012 in release.yml. Keep T005 focused on Cargo.toml
version/tag validation and previous_release_tag selection, and retain its
completed status only for that scope.

---

Nitpick comments:
In @.github/scripts/tests/test-previous-release-tag.sh:
- Around line 48-69: Extend the fixture around the existing tag setup by adding
a non-release-format tag such as v1.1.0-rc1 between v1.1.0 and v2.0.0, then
retain or update the v2.0.0 assertion to verify it still returns v1.1.0 rather
than the prerelease tag.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 46566e32-8f12-41fa-a8ab-3ed028d696a5

📥 Commits

Reviewing files that changed from the base of the PR and between c37d4a2 and 883a6d7.

📒 Files selected for processing (5)
  • .github/scripts/previous-release-tag.sh
  • .github/scripts/tests/test-previous-release-tag.sh
  • .github/scripts/tests/test-validate-release-tag.sh
  • .github/scripts/validate-release-tag.sh
  • specs/005-ci-release-versioning/tasks.md

Comment thread .github/scripts/tests/test-validate-release-tag.sh

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

All reported issues were addressed across 5 files

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

Comment thread .github/scripts/validate-release-tag.sh Outdated
Comment thread .github/scripts/tests/test-validate-release-tag.sh Outdated
Comment thread .github/scripts/validate-release-tag.sh
…s, header

- previous-release-tag.sh: git describe's --match "v[0-9]*" is a glob, not
  a regex -- it also matches non-release tags like v1.1.0-rc1. Walk back
  past any non-strict-SemVer match instead of returning the first hit.
  Verified the bug empirically before fixing (a throwaway repo with
  v1.0.0/v1.1.0/v1.1.0-rc1/v2.0.0 returned v1.1.0-rc1 as v2.0.0's
  "previous release" before this fix); added the same fixture as a
  permanent regression test.
- validate-release-tag.sh: awk pattern required version/[workspace.package]
  to start at column 0, which TOML doesn't require; allow leading
  whitespace. Also removed the header's inaccurate FR-011 claim -- that
  logic lives in previous-release-tag.sh, not here.
- test-validate-release-tag.sh: replaced predictable/racy /tmp/out.$$
  capture files with mktemp under the test's own private tmpdir.
@owkwo-bot

Copy link
Copy Markdown
Collaborator Author

Re: PR-level review comments (no thread to reply on directly):

sourcery (review 4819125782) — both points confirmed valid, fixed in 239f119:

  • Header claim about FR-011 in validate-release-tag.sh was inaccurate — removed.
  • git describe --match "v[0-9]*" is a glob, not a regex, so it can match non-SemVer tags like v1.1.0-rc1. Verified this empirically with a throwaway repo before fixing (a v1.0.0/v1.1.0/v1.1.0-rc1/v2.0.0 history returned v1.1.0-rc1 as v2.0.0's "previous release" — wrong). Fixed by walking back past any non-strict-SemVer match in previous-release-tag.sh.

coderabbitai (review 4819214130, prompt to extend the fixture) — implemented exactly as suggested: added v1.1.0-rc1 between v1.1.0 and v2.0.0 in test-previous-release-tag.sh, asserting v2.0.0's previous tag is still v1.1.0. This test would have failed before the fix above and passes now.

greptile — hit its trial credit limit again, no actual review content; nothing to action.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
.github/scripts/validate-release-tag.sh (1)

22-25: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Enforce strict SemVer numeric identifiers

Both regexes still accept v01.2.3-style tags; replace each [0-9]+ with 0|[1-9][0-9]* in .github/scripts/validate-release-tag.sh and .github/scripts/previous-release-tag.sh.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/scripts/validate-release-tag.sh around lines 22 - 25, Update the
release-tag regex in validate-release-tag.sh at lines 22-25 and
previous-release-tag.sh at lines 27-28, replacing each numeric [0-9]+ component
with 0|[1-9][0-9]*. Preserve the vMAJOR.MINOR.PATCH structure while rejecting
identifiers with leading zeroes.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In @.github/scripts/validate-release-tag.sh:
- Around line 22-25: Update the release-tag regex in validate-release-tag.sh at
lines 22-25 and previous-release-tag.sh at lines 27-28, replacing each numeric
[0-9]+ component with 0|[1-9][0-9]*. Preserve the vMAJOR.MINOR.PATCH structure
while rejecting identifiers with leading zeroes.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 424dbb11-74c6-4cda-9f23-2a849626b022

📥 Commits

Reviewing files that changed from the base of the PR and between 883a6d7 and 239f119.

📒 Files selected for processing (4)
  • .github/scripts/previous-release-tag.sh
  • .github/scripts/tests/test-previous-release-tag.sh
  • .github/scripts/tests/test-validate-release-tag.sh
  • .github/scripts/validate-release-tag.sh

@owkwo-bot

Copy link
Copy Markdown
Collaborator Author

While independently implementing T005 in a separate session (before spotting this branch already existed), I noticed a gap worth flagging here rather than opening a competing PR.

Missing: duplicate-release rejection. T005's acceptance criteria (and the spec's edge cases) call for rejecting re-publishing an existing tag. Neither validate-release-tag.sh nor previous-release-tag.sh in this PR checks that — both are silent on whether a release already exists for the tag.

Worth noting why a local git-tag-existence check won't work here: by the time release.yml runs, the pushed tag that triggered the run is already a local git ref in the checkout. A check like git rev-parse --verify refs/tags/$tag would therefore reject every single release, not just re-publishes. The check needs to ask GitHub (gh release view <tag>) whether a Release already exists for that tag — not whether the git ref exists.

In my scratch version I made that check injectable via an env var (default gh release view, overridable for offline fixture tests) so it stays testable without hitting the network. Happy to share the diff if useful, but since this branch already has fixture coverage and a cleaner split (previous-release-tag.sh correctly walks past intervening prerelease tags like v1.1.0-rc1, which my version didn't handle), it's probably easiest to just add the duplicate-check as a small addition here rather than reconcile two implementations.

🧙 Noted via WOZCODE

@owkwo-bot

Copy link
Copy Markdown
Collaborator Author

Follow-up on my own comment above: no code change needed here after all.

tasks.md's T005 "Done" note already explains this scripting choice explicitly — duplicate-release rejection is deliberately not in validate-release-tag.sh/previous-release-tag.sh because it needs the real GitHub Release API (does a release already exist for this tag?), not something these offline, git-only fixture-tested scripts can determine. T012 (Phase 4, US2, in release.yml) already lists "reject re-publishing an existing tag/version" as in scope.

So this branch already has the right design — same reasoning I gave above (git-ref presence can't distinguish "just-pushed tag" from "already-published release"), just correctly deferred to the task that actually owns release.yml. Nothing to add to this PR.

@rsenna
rsenna merged commit 4ecc5b6 into main Aug 1, 2026
4 checks passed
@rsenna
rsenna deleted the 005-t005-t006-version-buildid branch August 1, 2026 01:10
rsenna added a commit that referenced this pull request Aug 1, 2026
# Conflicts:
#	specs/005-ci-release-versioning/tasks.md
rsenna added a commit that referenced this pull request Aug 1, 2026
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