name: prepare-release description: Prepare a new release by finalizing CHANGELOG, tagging, and verifying builds. Covers everything before pushing the tag that triggers CI.
Skill: Prepare a Release
When to use
When [Unreleased] in CHANGELOG.md has accumulated features/fixes and you're ready to cut a new version.
Prerequisites
- All intended PRs are merged to
main cargo testandcargo clippy -- -D warningspass onmain- You know the next version number (follow Semantic Versioning)
Steps
1. Determine the version number
Check the current version in Cargo.toml and the latest git tag:
# Windows
Select-String '^version = ' Cargo.toml
git --no-pager tag --sort=-v:refname | Select-Object -First 5
Also check the GitHub Releases page (https://github.com/lqdev/podcast-tui/releases) to confirm the latest published release — the Cargo.toml version may lag behind due to the post-release workflow.
Increment based on changes in [Unreleased]:
- Major (X.0.0): Breaking changes to config format, storage schema, or CLI behavior
- Minor (X.Y.0): New features, commands, or keybindings
- Patch (X.Y.Z): Bug fixes only
2. Finalize CHANGELOG.md
Convert [Unreleased] into a versioned release section with today's date:
## [Unreleased]
---
## [X.Y.Z] - YYYY-MM-DD
### Fixed
<content from [Unreleased] Fixed sections>
### Added
<content from [Unreleased] Added sections>
Rules:
- Add a fresh empty
## [Unreleased]section at the top (above the new versioned section) - Keep the
---separator between the new section and the previous versioned entry - Preserve all existing content — just move it from
[Unreleased]to[X.Y.Z] Fixedsections conventionally come beforeAddedwithin a release entry- Remove the subtitle from the
[Unreleased]heading (e.g.- Playlist Enhancements (post-v1.6.0)→ just## [Unreleased])
3. Update Cargo.toml version
Edit the version = "..." line in Cargo.toml to the new version (top-level [package] only):
version = "X.Y.Z"
Note: The
post-release-version-sync.ymlworkflow also updates this after publishing, but setting it locally ensurescargo build --versionoutput is correct for verification.
Then sync Cargo.lock so its podcast-tui entry matches (the lockfile is tracked in git and must never drift from Cargo.toml):
cargo update -p podcast-tui --precise X.Y.Z
Why this matters:
release.ymlkeys its build cache onhashFiles('**/Cargo.lock'). If the lockfile version never changes, the cache key is identical across releases and a stale cross-versiontarget/can be served, embedding the wrongCARGO_PKG_VERSIONinto the binary. Always commit the updatedCargo.lockalongsideCargo.toml.
4. Run full verification
cargo fmt --check
cargo clippy -- -D warnings
cargo test
All three must pass cleanly before tagging. The test_opml_local_file integration test has a pre-existing failure (missing local fixture file) — this is expected and can be ignored.
cargo build --releaseis optional on Windows since CI runs the official release builds. Run it if you want extra confidence (it takes ~5 minutes).
5. Commit the release prep
git add Cargo.toml Cargo.lock CHANGELOG.md
git commit -m "chore: prepare release vX.Y.Z
- Finalize CHANGELOG.md for vX.Y.Z
- Update Cargo.toml and Cargo.lock version to X.Y.Z
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>"
6. Tag and push
git tag vX.Y.Z
git push origin main
git push origin vX.Y.Z
Pushing the tag triggers the release.yml workflow which:
- Builds Linux (x86_64, aarch64) and Windows (x86_64, aarch64) binaries
- Creates and publishes a GitHub Release with all artifacts and auto-generated release notes
7. Verify the release workflow
After pushing the tag:
gh run list --workflow release.yml --limit 3
After CI completes (~10 minutes):
- Check Actions — all jobs green
- Check Releases — new release with 4 artifacts (Linux x86_64, Linux aarch64, Windows x86_64, Windows aarch64)
8. Post-release workflow
The post-release-version-sync.yml workflow triggers automatically when the GitHub Release is published (triggered by release: types: [published], not by the tag push directly). It:
- Updates
Cargo.tomlversion onmain(no-op if you already set it in step 3) - Syncs
Cargo.lock'spodcast-tuientry to match (no-op if already in sync) - Generates winget manifests under
manifests/l/lqdev/PodcastTUI/<version>/ - Commits and pushes all three to
main
Monitor it:
gh run list --workflow post-release-version-sync.yml --limit 3
After it completes:
git pull origin main # pick up the post-release commit
Verify:
Cargo.tomlonmainhas the new versionmanifests/l/lqdev/PodcastTUI/<version>/directory exists
Workflow trigger chain
git push origin vX.Y.Z
└─► release.yml (tag push: v*)
└─► builds binaries + creates GitHub Release (published)
└─► post-release-version-sync.yml (release: published)
└─► updates Cargo.toml + Cargo.lock + generates winget manifests
What NOT to do
- Don't update winget manifests manually — the post-release workflow handles it
- Don't create the GitHub Release manually —
release.ymlusessoftprops/action-gh-releasewith auto-generated notes - Don't skip
cargo clippy -- -D warnings— it's required to pass before tagging
Naming convention for tags
Tags use clean semver with v prefix: v1.6.0, v1.7.0, v1.8.0, v1.9.0, etc.
Artifact naming follows: podcast-tui-v{VERSION}-{os}-{arch}.{ext}
podcast-tui-v1.9.0-linux-x86_64.tar.gzpodcast-tui-v1.9.0-linux-aarch64.tar.gzpodcast-tui-v1.9.0-windows-x86_64.zippodcast-tui-v1.9.0-windows-aarch64.zip