fix(release): publish real notes, and stop shipping old releases' installers
🏗️ Build and Test JellyTau / Run Tests (pull_request) Successful in 15m1s
🏗️ Build and Test JellyTau / Supply Chain (pull_request) Successful in 42s
Traceability Validation / Check Requirement Traces (pull_request) Successful in 10s
🏗️ Build and Test JellyTau / Android Compile Check (pull_request) Successful in 4m6s
🏗️ Build and Test JellyTau / Run Tests (pull_request) Successful in 15m1s
🏗️ Build and Test JellyTau / Supply Chain (pull_request) Successful in 42s
Traceability Validation / Check Requirement Traces (pull_request) Successful in 10s
🏗️ Build and Test JellyTau / Android Compile Check (pull_request) Successful in 4m6s
Two defects found while preparing v0.9.2, both of which had been shipping for months without anything to notice them by. **Every release note was the same 1,050 bytes.** All 35 releases from v0.0.1 to v0.9.1 published identical generic install instructions whose "What's New" section read "See CHANGELOG.md" -- a link that does not resolve from a release page. A reader learned nothing about what changed in any release the project has ever made. The body now comes from the `## <version>` section of CHANGELOG.md, and a missing section fails the release: notes that say nothing are worse than a build that waits for a maintainer to write two sentences. The 35 published bodies have been backfilled from the changelog via the tea CLI. This also corrects something introduced two commits ago. That change generated the body from `bun run release:notes`, which CLAUDE.md is explicit about -- its output is "a reviewed draft, not a final changelog". Publishing it unreviewed proved the point immediately: the v0.9.1..HEAD range contains a repo-wide prettier sweep, so every file in src/ counted as changed, their TRACES resolved to nearly the whole matrix, and the draft claimed the release had added the entire application. The script now skips cosmetic commits (chore(format), chore(deps), style) and reports how many rather than silently returning a smaller set, but it stays a local drafting tool. **Every release from v0.1.0 to v0.8.2 shipped every Windows installer ever built.** src-tauri/target/*/release/bundle/ is not versioned, cargo never cleans it, and the runner reuses the target directory -- so the copy step's bundle/**/*-setup.exe glob collected the lot. v0.8.2 carried sixteen installers, thirteen of them stale; v0.5.0 offered users a download list going back to 0.1.0. Eight months, and nothing to notice it by: the upload loop reported success, the files were real, and the page looked busy rather than wrong. It stopped only because an unrelated cargo cache change wiped the runner's target dir, so it was dormant, not fixed. Both desktop builds now remove the bundle directory before building, so a stale file cannot exist to be copied. Filtering the copy by version would have hidden it instead. The Linux job gets the same treatment: it was never hit only because Linux packaging is newer, and the glob is identical. scripts/check-release-artifacts.sh is the backstop for whatever reintroduces one by a route nobody predicted. It runs before the SBOM, the checksums and the upload -- all of which describe the file set, so a stale artifact has to be caught before it is hashed and published as part of the release. Verified against a reconstruction of the real v0.8.2 accumulation. DR-219, DR-220, UT-210.
This commit is contained in:
@@ -146,6 +146,15 @@ jobs:
|
||||
# If TAURI_SIGNING_PRIVATE_KEY is ever absent the build fails loudly rather
|
||||
# than quietly shipping an unsigned release that no client will accept --
|
||||
# which is the behaviour we want.
|
||||
# Same hazard as the Windows job: the bundle directory is never cleaned by
|
||||
# cargo and the runner reuses src-tauri/target, while the copy step below
|
||||
# globs bundle/deb/*.deb and friends. Windows is where this actually bit
|
||||
# (v0.8.2 shipped thirteen stale installers), but only because Linux
|
||||
# packaging is newer -- the glob is identical. Remove the directory so a
|
||||
# stale artifact cannot exist to be copied.
|
||||
- name: Clear previous bundle output
|
||||
run: rm -rf src-tauri/target/release/bundle
|
||||
|
||||
- name: Build for Linux
|
||||
run: bun run tauri build
|
||||
env:
|
||||
@@ -413,6 +422,19 @@ jobs:
|
||||
name: jellytau-android
|
||||
path: artifacts/android/
|
||||
|
||||
# Runs before the SBOM, the checksums and the upload -- everything
|
||||
# downstream describes this set of files, so a stale artifact must be
|
||||
# caught before it gets hashed into SHA256SUMS and published as though it
|
||||
# belonged to this release.
|
||||
#
|
||||
# See the script for the eight months of releases that shipped their
|
||||
# predecessors' Windows installers.
|
||||
- name: Verify artifacts belong to this release
|
||||
run: |
|
||||
./scripts/check-release-artifacts.sh \
|
||||
"${{ steps.tag_name.outputs.VERSION }}" \
|
||||
artifacts/linux artifacts/windows artifacts/android
|
||||
|
||||
# Software Bill of Materials, one per half of the app. Without it there is
|
||||
# no answer to "does this release contain <vulnerable crate>?" other than
|
||||
# rebuilding the tag and re-resolving it. cargo-cyclonedx is in the builder
|
||||
@@ -482,9 +504,11 @@ jobs:
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Release notes for the update prompt come from the traceability graph,
|
||||
# same source as the release body.
|
||||
NOTES="$(bun run release:notes 2>/dev/null | head -c 4000 || echo "See the release page for details.")"
|
||||
# What the in-app update prompt shows. Same reviewed source as the
|
||||
# release body -- the CHANGELOG section for this version, not the
|
||||
# traceability draft.
|
||||
NOTES="$(awk -v ver="## $VERSION" '$0==ver{f=1;next} /^## /{if(f)exit} f' CHANGELOG.md | head -c 4000)"
|
||||
[ -n "$NOTES" ] || NOTES="See the release page for details."
|
||||
|
||||
jq -n \
|
||||
--arg version "$PLAIN" \
|
||||
@@ -549,24 +573,37 @@ jobs:
|
||||
# release rather than shipping and failing for users.
|
||||
sha256sum -c SHA256SUMS
|
||||
|
||||
# Release notes come from the traceability graph, not from a hardcoded
|
||||
# heredoc. scripts/release-notes.ts resolves the commit range's changed
|
||||
# files to their TRACES ids and then to requirement descriptions, grouping
|
||||
# UR into Features and DR/IR into Improvements -- which is what CLAUDE.md
|
||||
# has asked for all along, while this workflow pasted a fixed block of
|
||||
# install instructions and a line saying "see CHANGELOG.md for detailed
|
||||
# changes". It also linked "GitHub Issues" on a Gitea-hosted project.
|
||||
# The published body is the hand-written CHANGELOG.md section for this
|
||||
# version. `bun run release:notes` is printed into the job log as a
|
||||
# drafting aid, but is NOT published: CLAUDE.md is explicit that its
|
||||
# output is "a reviewed draft, not a final changelog", and publishing it
|
||||
# unreviewed proved the point -- a range containing a repo-wide prettier
|
||||
# sweep resolved to nearly the whole requirement matrix and produced notes
|
||||
# claiming one release had added the entire application.
|
||||
#
|
||||
# A missing CHANGELOG section fails the release. A release whose notes say
|
||||
# nothing is worse than one that waits for a maintainer to write two
|
||||
# sentences, and the checklist already requires that entry.
|
||||
- name: Prepare release notes
|
||||
id: release_notes
|
||||
run: |
|
||||
set -e
|
||||
VERSION="${{ steps.tag_name.outputs.VERSION }}"
|
||||
|
||||
echo "📋 Traceability draft (for reference; not published):"
|
||||
bun run release:notes 2>/dev/null || echo "(could not derive a draft)"
|
||||
echo ""
|
||||
|
||||
# The section between this version's heading and the next one.
|
||||
CHANGES=$(awk -v ver="## $VERSION" '$0==ver{f=1;next} /^## /{if(f)exit} f' CHANGELOG.md)
|
||||
if [ -z "$(echo "$CHANGES" | tr -d '[:space:]')" ]; then
|
||||
echo "::error::CHANGELOG.md has no '## $VERSION' section."
|
||||
echo "::error::Add the entry for this version and re-tag; see docs/release-checklist.md."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
{
|
||||
echo "## JellyTau $VERSION"
|
||||
echo ""
|
||||
# A generated summary of what actually changed; falls back to a
|
||||
# pointer rather than failing the release if the range is odd.
|
||||
bun run release:notes 2>/dev/null || echo "See the commit log for changes in this release."
|
||||
echo "$CHANGES"
|
||||
echo ""
|
||||
echo "### Downloads"
|
||||
echo ""
|
||||
@@ -578,8 +615,8 @@ jobs:
|
||||
echo "| Windows | \`*-setup.exe\` (NSIS). Unsigned — SmartScreen may warn on first run. |"
|
||||
echo "| Android | \`*.apk\` sideload, or \`*.aab\` for Play Console |"
|
||||
echo ""
|
||||
echo "Desktop builds update themselves from here on: JellyTau checks this"
|
||||
echo "release feed and can install a new version in place."
|
||||
echo "Desktop builds check for updates from here and can install a new"
|
||||
echo "version in place, verifying its signature first."
|
||||
echo ""
|
||||
echo "### Verifying your download"
|
||||
echo ""
|
||||
@@ -599,6 +636,7 @@ jobs:
|
||||
echo "---"
|
||||
echo "Report a problem: ${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}/issues"
|
||||
} > release_notes.md
|
||||
|
||||
echo "📝 Release notes:"
|
||||
cat release_notes.md
|
||||
|
||||
|
||||
Reference in New Issue
Block a user