Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -113,5 +113,4 @@ jobs:
gh release create "${RELEASE_TAG}" \
--title "${RELEASE_TAG}" \
--notes-file release-assets/release-notes.md \
--verify-tag \
release-assets/dist/*
59 changes: 59 additions & 0 deletions tests/test_release_preflight.py
Original file line number Diff line number Diff line change
Expand Up @@ -489,3 +489,62 @@ def test_the_preflight_agrees_with_the_repository_it_ships_in() -> None:
f"the preflight reports more than the expected pre-first-release state: {done.stderr}"
)
assert f"no '## [{version}]' section" in done.stderr, done.stderr


def _job_body(workflow: str, name: str) -> str:
"""The lines of one top-level job, heading excluded.

Written here rather than reused: the only other job reader in this file is
specific to the build job's steps, and a release job's shape is exactly
what these assertions are about.
"""
lines = workflow.splitlines()
opening = f" {name}:"
start = next(index for index, line in enumerate(lines) if line == opening)
end = next(
(index for index in range(start + 1, len(lines)) if line_is_job(lines[index])),
len(lines),
)
return "\n".join(lines[start + 1 : end])


def line_is_job(line: str) -> bool:
"""A top-level job heading: exactly two spaces of indent, then `name:`."""
return bool(re.match(r"^ [A-Za-z0-9_-]+:\s*$", line))


def test_the_publish_job_does_not_ask_git_a_question_it_cannot_answer() -> None:
"""`gh release create --verify-tag` shells out to git, and publish has no checkout.

The publish job holds the only write authority and deliberately never
checks out code -- that separation is the reason it is split from the
build. `--verify-tag` makes `gh` run git to confirm the tag exists locally,
so in a job with no working tree it dies with `fatal: not a git repository`
*after* the artifacts have already been built.

Not hypothetical: run 34163784808, the first release this workflow ever
performed, failed exactly there. `authorize` and `build` both succeeded and
publication died on the flag.

Nothing is lost by dropping it. The two lines above the call 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.
"""
workflow = RELEASE_WORKFLOW.read_text(encoding="utf-8")
publish = _job_body(workflow, "publish")

assert _runs(publish, "gh release create"), "the publish job no longer creates the release"
assert not _runs(publish, "--verify-tag"), (
"publish runs `gh release create --verify-tag`, which shells out to git, in a job "
"that never checks out code. It fails with `fatal: not a git repository` after the "
"build has already succeeded. The API recheck above it verifies tag identity, which "
"is strictly stronger."
)
assert not _runs(publish, "actions/checkout"), (
"the publish job now checks out code. It holds the only `contents: write` authority, "
"and keeping a working tree out of it is why --verify-tag was removed rather than "
"satisfied."
)
Loading