Skip to content

fix(release): run tests before mutating, restore files on abort - #151

Merged
adnaan merged 1 commit into
mainfrom
fix/release-script-restore-trap
Jul 20, 2026
Merged

fix(release): run tests before mutating, restore files on abort#151
adnaan merged 1 commit into
mainfrom
fix/release-script-restore-trap

Conversation

@adnaan

@adnaan adnaan commented Jul 19, 2026

Copy link
Copy Markdown
Contributor

Ports the fix that landed in the core library's release.sh (livetemplate#499) to the client, where it had never been applied.

Two problems, both hit while releasing v0.19.1

1. Files were mutated before tests ran. update_versions and generate_changelog ran ahead of build_and_test, so a failing test left VERSION, package.json, package-lock.json and CHANGELOG.md already rewritten. main() refuses to start on a dirty tree — so the next attempt fails for a reason unrelated to why the first one did.

Tests and the build now run first, on the pre-bump tree.

2. No recovery for post-write failures. Reordering can't help with anything that fails after the write — verify_build, npm pack, gh release create. An EXIT trap now restores the four release-managed files when the run aborts before committing, and stands down once commit_and_tag has run.

Ordering constraint the client has and core doesn't

verify_build asserts package.json's version equals the new one, so it genuinely must run post-bump. Moving the build ahead of the bump was only safe after confirming the dist bundle embeds no version string — otherwise dist would ship stamped with the old version. Verified: no __VERSION__ substitution, no package.json import in the TS sources. Final order:

build_and_test          # tests + build, pre-bump — fails before touching anything
update_versions         # sets release_files_written
generate_changelog
verify_build            # asserts package.json == new version, so must be post-bump
verify_package_contents
commit_and_tag          # sets release_committed — trap stands down
publish_github

Two subtleties carried over from the core fix

  • Restore from HEAD, not the index. commit_and_tag stages the files before committing, so if the commit itself fails a plain git checkout -- would restore them from the index — copying the modified versions back over themselves.
  • One path per invocation. git checkout HEAD -- a b c resolves the whole pathspec first and bails before touching the worktree if any entry is missing from HEAD, so a single call would restore none of them when one is untracked.

Verification

Both directions exercised against a real worktree:

# pre-commit abort
--- dirty before abort: 3 files
WARN: Release aborted before committing — restoring version and changelog files
--- after trap: 1 dirty files      (only scripts/release.sh, the actual edit)

# post-commit abort (release_committed=true)
--- VERSION after: 9.9.9           (untouched, correctly — it's in history now)

The second case matters as much as the first: a trap that fired after the commit would discard the release commit's content.

bash -n clean; --dry-run reaches the same decision points as before.

🤖 Generated with Claude Code

https://claude.ai/code/session_01Ui2cwpeGkrUfRt8rh2FgGG

Ports the fix that landed in the core library's release.sh
(livetemplate/livetemplate#499) to the client, where it hadn't been.

Two related problems, both hit while releasing v0.19.1:

update_versions and generate_changelog ran before build_and_test, so a
failing test left VERSION, package.json, package-lock.json and
CHANGELOG.md already rewritten. main() refuses to start on a dirty tree,
so the next attempt would fail for a reason unrelated to why the first
one did. Tests and the build now run first, on the pre-bump tree. The
dist bundle embeds no version string, so building ahead of the bump is
equivalent; verify_build still runs after, since it asserts package.json
carries the new version.

Reordering can't help with failures that only happen after the write —
verify_build, npm pack, gh release create — so an EXIT trap restores the
four release-managed files when the run aborts before committing, and
stands down once commit_and_tag has run.

Restores from HEAD rather than the index (commit_and_tag stages the files
first, so a plain checkout would copy the modified versions back over
themselves), and one path per invocation (a combined pathspec bails
before touching anything if any entry is missing from HEAD, restoring
none of them).

Verified both directions: a simulated pre-commit abort restores VERSION
and CHANGELOG.md, and a simulated post-commit abort leaves them alone.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ui2cwpeGkrUfRt8rh2FgGG
@adnaan
adnaan merged commit 769f77e into main Jul 20, 2026
6 of 7 checks passed
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.

1 participant