ci: verify BundledVersion.value matches changelog and release tag#76
ci: verify BundledVersion.value matches changelog and release tag#76
Conversation
Make Sources/swift-section/Version.swift the single source of truth for the CLI version, so source builds (including Homebrew's source path) pick up the correct version without relying on CI injection. - Version.swift: drop the "-dev" placeholder; set to plain "0.10.0". - release.yml: replace the heredoc inject step with a verification step that fails when BundledVersion.value != github.ref_name. - version-check.yml (new): on PR and push-to-main, extract BundledVersion.value and fail when Changelogs/<value>.md is missing.
There was a problem hiding this comment.
Pull request overview
Aligns the CLI versioning and release metadata so Sources/swift-section/Version.swift becomes the canonical version for all builds, while CI enforces consistency between Version.swift, Changelogs/<version>.md, and release tags.
Changes:
- Update
BundledVersion.valueto a real release version and document the bump workflow inVersion.swift. - Add a PR/push CI workflow that verifies a matching
Changelogs/<BundledVersion.value>.mdexists. - Replace the release workflow’s version “injection” step with a verification step that fails if
BundledVersion.valuedoesn’t match the pushed tag.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 3 comments.
| File | Description |
|---|---|
| Sources/swift-section/Version.swift | Makes the CLI version a checked-in source of truth instead of a CI-injected placeholder. |
| .github/workflows/version-check.yml | New workflow enforcing Version.swift ↔ changelog presence on PRs and pushes to main. |
| .github/workflows/release.yml | Validates Version.swift matches the release tag before building/publishing. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| set -euo pipefail | ||
| file="Sources/swift-section/Version.swift" | ||
| value=$(grep -E 'static let value = "[^"]+"' "$file" | sed -E 's/.*"([^"]+)".*/\1/') | ||
| if [ -z "$value" ]; then | ||
| echo "::error file=$file::Failed to extract BundledVersion.value" | ||
| exit 1 | ||
| fi |
There was a problem hiding this comment.
With set -euo pipefail, the value=$(grep ... | sed ...) assignment will cause the step to exit immediately if grep finds no match, so the subsequent if [ -z "$value" ] block (and its ::error message) will never run. Consider capturing the extraction in a conditional (e.g., if ! value=$(...); then ...) and/or appending || true so you can reliably emit the intended error message. Also, the current grep pattern requires exact spacing around =; making it whitespace-tolerant (and limiting to a single match) will reduce CI breakage from harmless formatting changes.
| value=$(grep -E 'static let value = "[^"]+"' "$file" | sed -E 's/.*"([^"]+)".*/\1/') | ||
| if [ -z "$value" ]; then |
There was a problem hiding this comment.
Same issue here: set -euo pipefail combined with value=$(grep ... | sed ...) means the script will exit before your if [ -z "$value" ] branch can emit the custom ::error when the pattern isn't found. Wrap the extraction in a conditional so you can fail with the intended message (and consider making the regex whitespace-tolerant / single-match).
| value=$(grep -E 'static let value = "[^"]+"' "$file" | sed -E 's/.*"([^"]+)".*/\1/') | |
| if [ -z "$value" ]; then | |
| if value=$(grep -m1 -E 'static[[:space:]]+let[[:space:]]+value[[:space:]]*=[[:space:]]*"[^"]+"' "$file" | sed -E 's/.*"([^"]+)".*/\1/'); then | |
| : | |
| else |
| file="Sources/swift-section/Version.swift" | ||
| value=$(grep -E 'static let value = "[^"]+"' "$file" | sed -E 's/.*"([^"]+)".*/\1/') | ||
| if [ -z "$value" ]; then |
There was a problem hiding this comment.
The BundledVersion extraction logic is duplicated between version-check.yml and release.yml. To avoid the two workflows drifting over time, consider factoring this into a shared script (e.g., under Scripts/) or a small composite action that both workflows call.
Address review feedback on #76: - `|| true` absorbs pipefail so the custom `::error` branch for unmatched input is actually reachable (previously `set -euo pipefail` would exit the step before the `[ -z "$value" ]` check ran). - Whitespace-tolerant regex (`[[:space:]]+`) plus `grep -m1` so that reformatting Version.swift (e.g. extra spaces, formatter changes) cannot silently break the CI extraction.
Summary
Sources/swift-section/Version.swiftthe single source of truth for the CLI version so source builds (local checkouts, Homebrew'sswift buildinstall path) get the real version string instead of the0.10.0-devplaceholder.release.yml"Inject release version" heredoc step with a verification step that fails the tag build whenBundledVersion.valueand the pushed tag disagree.version-check.yml(PR / push-to-main) that extractsBundledVersion.valueand fails whenChangelogs/<value>.mdis missing.Together these keep
Version.swift ↔ Changelogs/*.md ↔ git tagin lockstep, and the Homebrew bottle / source compile path will finally print the correct version.Context
Previously only the CI Release build produced a binary with the real version —
release.ymloverwroteVersion.swiftwith the tag name before compiling. Anything built from the source tarball (includingbrew install swift-section, which runsswift buildon the archive) was shipping0.10.0-dev. MakingVersion.swiftthe source of truth fixes that, and the new CI guards keep it honest.Test plan
Version Check / Verify version metadatapasses on this PR (BundledVersion.value == "0.10.0"andChangelogs/0.10.0.mdexists).Version.swiftto e.g."0.11.0"without creatingChangelogs/0.11.0.md— confirm the check goes red, then revert.Verify Version.swift matches tagstep should fail ifVersion.swiftwas not bumped before tagging.Verify version metadataas a required status check in branch protection so this PR's promise ("can't merge if version is wrong") actually holds.