Skip to content

fix(release): publish cannot run gh release create --verify-tag without a checkout - #152

Merged
ChelseaKR merged 2 commits into
mainfrom
fix/publish-cannot-verify-tag-without-a-checkout
Sep 7, 2026
Merged

fix(release): publish cannot run gh release create --verify-tag without a checkout#152
ChelseaKR merged 2 commits into
mainfrom
fix/publish-cannot-verify-tag-without-a-checkout

Conversation

@ChelseaKR

Copy link
Copy Markdown
Owner

The first release this workflow ever performed failed here. authorize succeeded, build succeeded, and publication died with:

failed to run git: fatal: not a git repository (or any of the parent directories): .git

gh release create --verify-tag shells out to git to confirm the tag exists locally. The publish job deliberately never checks out code — that separation is the point of splitting it from build, since it holds the only contents: write authority. So the flag was asking a question the job is designed to be unable to answer.

Nothing is lost by removing it

The two lines immediately above the call already do something strictly stronger:

LIVE_TAG_OBJECT="$(gh api "repos/${GITHUB_REPOSITORY}/git/ref/tags/${RELEASE_TAG}" --jq .object.sha)"
test "${LIVE_TAG_OBJECT}" = "${TAG_OBJECT_SHA}"

TAG_OBJECT_SHA comes from authorize, which produced it after verifying the tag's SSH signature against the committed allowed-signers file. So that is an identity check against the live repository. --verify-tag is only an existence check — and it was being made against a working tree that is not there.

The alternative I did not take

Adding actions/checkout to the publish job would also make the flag work, and would be the wrong fix: it puts a working tree in the one job holding write authority, to satisfy a weaker version of a check that already passes. The regression test pins all three facts together — publish still creates the release, does not pass --verify-tag, and does not check out code — so both regressions fail, and the second one fails with an explanation of why it is refused.

Verified

21 tests pass. Negative control: re-adding --verify-tag fails exactly test_the_publish_job_does_not_ask_git_a_question_it_cannot_answer and nothing else. Restoring afterward initially left the file matching origin/main rather than my edit — git checkout -- restores to the branch point, not to uncommitted work — so the fix was re-applied and confirmed byte-identical to the tested state by git hash-object before committing.

Why this was never found before

This defect is reachable only by running the workflow, and this workflow had never run. It could not: it called a reusable workflow from a private repo, which a public repo cannot read, so every dispatch failed at parse time. #151 fixed that; this is the next thing standing between v0.1.0 and a published release.

Prepared with AI assistance; reviewed before submission.

…hout a checkout

The first release this workflow ever performed failed here. `authorize` and
`build` both succeeded; publication died with

  failed to run git: fatal: not a git repository (or any of the parent
  directories): .git

`--verify-tag` makes `gh` shell out to git to confirm the tag exists locally,
and the publish job deliberately never checks out code. That separation is the
point of splitting publish from build -- it holds the only `contents: write`
authority -- so the flag was asking a question the job is designed to be unable
to answer.

Nothing is lost by removing it. The two lines above the call already fetch the
live tag ref through the API and assert it equals the object SHA `authorize`
produced after verifying the tag's SSH signature against the committed
allowed-signers file. That is an identity check; `--verify-tag` is only an
existence check, and it was being made against a repository that is not there.

A regression test pins all three facts together: publish still creates the
release, does not pass `--verify-tag`, and does not check out code -- so
re-adding the flag and adding a checkout to satisfy it both fail, and the
second is the one worth refusing.

This defect was reachable only by running the workflow. It had never run.
@ChelseaKR
ChelseaKR merged commit 9ab4c6a into main Sep 7, 2026
12 checks passed
@ChelseaKR
ChelseaKR deleted the fix/publish-cannot-verify-tag-without-a-checkout branch September 7, 2026 21:44
ChelseaKR added a commit that referenced this pull request Sep 8, 2026
…infer one from (#153)

Removing `--verify-tag` in #152 was **necessary and not sufficient**. Run `34164224595` failed at exactly the same step, with exactly the same message, after that flag was already gone:

```
failed to run git: fatal: not a git repository (or any of the parent directories): .git
```

### Why

`gh` determines which repository to act on from the **git remote**. The publish job deliberately never checks out code — it holds the only `contents: write` authority, which is why it is split from build — so there is no remote, and `gh release create` shells out to git looking for one before it does anything else.

`GITHUB_REPOSITORY` is set in the environment, but **`gh` does not read it**. The variable it reads is `GH_REPO`.

### This portfolio had already solved it

`self-osint-monitor` #25 is literally *"fix(ci): give `gh release create` the repo via `GH_REPO`"*. I rediscovered the same defect from scratch by running into it. That is worth recording in itself: the fix existed, in a sibling repo, and nothing surfaced it — which is an argument for the shared release workflow this repo now calls, not against it.

### Honest accounting of my own diagnosis

I attributed the first failure entirely to `--verify-tag`. That flag *is* broken here for the same underlying reason, and removing it was right, but I stated a complete cause when I had only found one contributor. The test now pins `GH_REPO` alongside the no-checkout and no-`--verify-tag` assertions, so the incomplete version of this fix cannot be mistaken for the complete one again.

### Verified

21 tests pass. **Negative control:** stripping the `GH_REPO` line fails exactly `test_the_publish_job_does_not_ask_git_a_question_it_cannot_answer` and nothing else. The first restore left the file differing from the tested state — I had reinserted the line without its comment block — so it was rebuilt from the branch point and re-applied, and `git hash-object` confirms the committed file is the one the tests ran against.

*Prepared with AI assistance; reviewed before submission.*
@ChelseaKR

Copy link
Copy Markdown
Owner Author

Correcting this PR after the fact: its stated cause was wrong, and it removed a check that was never broken.

This PR removed --verify-tag on the theory that it shells out to git and so cannot work in a job with no checkout. It does not. gh release create --help is explicit — --verify-tag aborts "in case the git tag doesn't already exist in the remote repository". It queries the API like every other gh call. A sibling repo in this portfolio passes it today from a publish job with four steps and no checkout, and releases successfully.

The evidence against my theory was already in hand when I wrote it: run 34164224595 failed with the identical fatal: not a git repository after the flag was gone. I read that as a second, separate defect rather than as a refutation of the first diagnosis.

The single cause of both failures is that gh resolves which repository to act on from the git remote. With no working tree there is none, so it shells out to git before doing anything else. GITHUB_REPOSITORY is set in the environment and gh does not read it; the variable it reads is GH_REPO.

#153 restores --verify-tag and adds GH_REPO. The regression test now asserts both are present, so the flag cannot be dropped again on the same mistaken theory, and its docstring records why. Both controls fire.

This was caught by another agent reading gh's own help instead of taking my explanation at face value, which is the right instinct.

Prepared with AI assistance; reviewed before submission.

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