forked from heavy-duty/ceremony
Both findings are @codex's on !193 (#1583), and both are real. The asset name travels as a QUERY VALUE, and the artifact-hook contract permits any file the consumer drops in RELEASE_ASSETS_DIR. Raw interpolation meant `release asset.tgz` made curl reject the URL outright (exit 3), and '&', '#', '+', '%' silently changed the name or the query's shape. `gh release create` handled all of those, so a 1:1 port had to. Encoded through one boundary — jq's @uri, since jq is already a hard dependency of this backend and a hand-rolled sed class is how the next unescaped character gets through. Six backend cases cover it: the encoder on a space and on the delimiters, uploads under both names, the created release id in the path, and the multipart attachment. Mutation-checked: dropping the encoder fails exactly the two name assertions. docs/CONSUMERS.md's artifact-hook recovery still told operators to "run `gh release create` by hand" and described the hook as running "before `gh release create`" — on a Forgejo runner that is precisely the failure this PR fixes. It now names the forge-neutral tag-door recovery first and shows both clients for the manual path, without regressing the GitHub guidance. 1035 assertions, 22 suites, shellcheck-all and actionlint clean. Refs #191 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|---|---|---|
| .. | ||
| 188.md | ||
| 191.md | ||
| README.md | ||
| shape | ||
changelog.d/ — the next release's section, one fragment per issue
Machine-assembled by bin/changelog-assemble (#112): every PR that changes
behavior writes one file here — <issue>.md, the exact prose that will be
published, nothing else — and the release PR folds them all into the next
## X.Y.Z — DATE section of CHANGELOG.md, consuming them. Distinct
filenames never conflict, which is this directory's whole reason to exist.
This README is the marker that keeps the directory tracked when it holds no
fragments (#112 D1) — changelog-armed refuses a tree without it; do not
delete it. The shape sentinel beside it declares the set's shape —
grouped here, so every fragment carries ### headings (#182).