Description
When a post_version hook (replace, patch, or regexp) in .github/version.yml references a file that doesn't exist, the resulting Deno.errors.NotFound propagates uncaught out of postVersionHook (src/hooks/post.ts), through the set command handler, and the semver process exits non-zero.
This aborts the entire version-set operation, even though:
- The
VERSION file write already happened.
- Any earlier hooks in the same
on.post list already ran successfully.
Callers that shell out to semver set (e.g. our internal release-automation webhook) then treat the whole operation as failed and roll it back / never commit, even though the only real problem is one misconfigured hook referencing a file that no longer exists in that particular repo.
Repro
.github/version.yml contains a regexp hook pointing at a file that was since removed:
on:
post:
- kind: regexp
file: .github/README.md
pattern: '\[\d+\.\d+\.\d+\]'
Running semver set 1.17.0 when .github/README.md doesn't exist produces:
Invoking post_version hook...
replacing [1.16.0] -> 1.17.0 in .github/plugin.json
replacing [1.16.0] -> 1.17.0 in .github/.codex-plugin/plugin.json
replacing [1.16.0] -> 1.17.0 in .github/.claude-plugin/plugin.json
replacing [1.16.0] -> 1.17.0 in .github/plugin/marketplace.json
replacing [1.16.0] -> 1.17.0 in .claude-plugin/marketplace.json
then exits non-zero with:
NotFound: No such file or directory (os error 2): readfile '.github/README.md'
at async Object.readTextFile (ext:deno_fs/30_fs.js:1:10081)
at async Object.regexp (file:///.../src/hooks/regexp.ts:13:20)
at async postVersionHook (file:///.../src/hooks/post.ts:40:11)
at async Object.handler (file:///.../src/commands/set.ts:29:5) {
code: "ENOENT"
}
Note that four of the five replace hooks above already succeeded before the failing regexp hook — but because the whole process exits non-zero, a caller has no way to keep any of that work.
Requested behavior
Treat a missing hook target file as a recoverable warning rather than a fatal error:
- Continue running the remaining configured hooks instead of aborting the whole
on.post list.
- Exit 0 overall (the version was still set correctly).
- Surface which hook(s) failed and why in a way callers can detect programmatically — e.g. via the existing
GITHUB_OUTPUT mechanism already used for version/dotnet/docker outputs (writeGithubOutput in src/util/version.ts), as a new hook_warnings field.
Other hook error types (unknown kind, invalid YAML, etc.) should stay fail-fast as-is — this is specifically about a hook's file not existing.
I'll follow up with a PR implementing this.
Description
When a
post_versionhook (replace,patch, orregexp) in.github/version.ymlreferences afilethat doesn't exist, the resultingDeno.errors.NotFoundpropagates uncaught out ofpostVersionHook(src/hooks/post.ts), through thesetcommand handler, and thesemverprocess exits non-zero.This aborts the entire version-set operation, even though:
VERSIONfile write already happened.on.postlist already ran successfully.Callers that shell out to
semver set(e.g. our internal release-automation webhook) then treat the whole operation as failed and roll it back / never commit, even though the only real problem is one misconfigured hook referencing a file that no longer exists in that particular repo.Repro
.github/version.ymlcontains aregexphook pointing at a file that was since removed:Running
semver set 1.17.0when.github/README.mddoesn't exist produces:then exits non-zero with:
Note that four of the five
replacehooks above already succeeded before the failingregexphook — but because the whole process exits non-zero, a caller has no way to keep any of that work.Requested behavior
Treat a missing hook target file as a recoverable warning rather than a fatal error:
on.postlist.GITHUB_OUTPUTmechanism already used forversion/dotnet/dockeroutputs (writeGithubOutputinsrc/util/version.ts), as a newhook_warningsfield.Other hook error types (unknown
kind, invalid YAML, etc.) should stay fail-fast as-is — this is specifically about a hook'sfilenot existing.I'll follow up with a PR implementing this.