feat: publish releases through stoke CLI #55
No reviewers
Labels
No labels
attention
blocked
blocker:ci-red
blocker:conflict
blocker:drill-pending
blocker:unrequested
bug
claimed
documentation
enhancement
epic
merge-next
needs-ruling
needs-triage
offsite
post-merge
ready
release
scope:ci
scope:cli
scope:docs
scope:manifests
scope:packaging
stale
state:addressing
state:bots-reviewing
state:building
state:needs-human
No milestone
No project
No assignees
4 participants
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference: heavy-duty/stoke#55
Loading…
Reference in a new issue
No description provided.
Delete branch "build/54-publish-release"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Refs #54
Summary
Worklog
Acceptance criteria
.forgejo/workflows/release.ymlnpm testpasses, including both new test filesscripts/changelog-section.shextracts 1.4.0 and rejects missing 1.5.0--token-file, never argvRound log
Round at
ee88d7d3Round passed with no written reply.
feat: publish releases through stoke CLIto WIP: feat: publish releases through stoke CLI📣 round answered at head
ee88d7d395WIP: feat: publish releases through stoke CLIto feat: publish releases through stoke CLI🔎 reviewing head
ee88d7d395All six of #54's acceptance criteria settled at this head. Verdict: approve
(two non-blocking nits in the body).
🔎 reviewing head
ee88d7d395ee88d7d395(2026-09-02T09:41:51Z); worktree removed🔎 reviewing head
ee88d7d395Approve at
ee88d7d395b840d8ac9593f27d6656414fba79cb.Reviewed in a detached worktree at the head with
npm cirun first. Every one of #54's six acceptance criteria holds, the vendored extractor is byte-identical to its stated source, and both branches of the get-or-create path work against the realsrc/cli.js, not just the stub. Two non-blocking nits below.Acceptance criteria
curl, noAuthorization: tokenin.forgejo/workflows/git grep -n curl .forgejo/workflows/release.yml;git grep -rn "curl|Authorization" .forgejo/workflows/npm testpasses incl. both new filesnpm test1.4.0→ 21 lines exit 0;1.5.0→ empty stdout, reason on stderr, exit 1no section for '1.5.0'/ exit 1 ✅--token-file, never argv--token-file "$TOKEN_FILE"→--token "$RELEASE_TOKEN"ifto always-createRefs #54, close stays with triageclose[sd]?/fix(e[sd])?/resolve[sd]?near any#N#54occurrences areRefs #54, worklog prose, and "this PR only references #54" ✅What I ran beyond the criteria
The stubbed tests never exercise the real CLI, so I closed that gap: I stood up a localhost HTTP stub of the Forgejo release API and drove the real
scripts/publish-release.sh→ realnode src/cli.js→ stub, using the repo's realCHANGELOG.md.releases/tags/v1.4.0):GET /api/v1/user→GET .../releases/tags/v1.4.0(404) →POST .../releases→POST .../releases/100/assets?name=stoke_1.4.0_all.deb. Exit 0. The create'sbodyfield carried the full 21-line1.4.0section — the empty-notes-pane defect #54 calls out is genuinely closed, not just plumbed.GET /user→ twoGET .../releases/tags/v1.4.0→POST .../releases/63/assets. NoPOST /releases. Exit 0./tmp/e2e/stoke-release.XXXXXX/config.json, never$HOME— §1.2 satisfied for real.Authorization: token …appeared only in the header, and the temp dir was gone afterwards.The workflow
run:line is executed by no CI job, so I ran it directly: parsedrelease.ymlwith a YAML loader, substituted${{ github.ref_name }}/${{ github.repository_owner }}, and executed it against a fake workspace. The nested"$(node -p "require('./package.json').version")"quoting resolves correctly — 5 args,[v1.4.0] [1.4.0] [dist/stoke_1.4.0_all.deb] [heavy-duty] [stoke]. Withdist/empty it fails loudly (${3:?usage…}, exit 1) rather than passing a shifted argument list.Vendoring fidelity. Fetched
lib/changelog.shandbin/changelog-sectionat ceremony tag0.6.3and diffed:changelog_sectionandchangelog_section_problemare byte-identical to upstream. The header comment records the tag and both source paths, as §2 requires. The one wrapper divergence — the diagnosis says "no publishable section" instead of upstream's message pointing atchangelog-assemble— is right, since stoke has no assembler to point at.Failure cases actually fail. Six mutations, five of which reddened the intended test (details in the nit below for the sixth):
--token-file→--token "$RELEASE_TOKEN"authentication uses a 0600 token file…release viewbranch)existing release uploads…+missing release creates it…if (found) exitextraction stops before the next version headingmissing changelog section aborts before any release commandheading without a list entry is rejected as emptyConventions and gates.
shellcheckclean on both new scripts (and on all ofscripts/).npm run check:governance→4 identities resolved; 5 scope rows valid.test/*.test.jsis in the ruled residue allowlist attest/governance.test.js:218, so the two new test files don't trip #48's coverage gate.changelog.d/54.mdmatches the sibling fragments' shape and ends with the terminal(#54).citation. Both scripts followpublish-deb.sh's idiom —#!/usr/bin/env bash,set -euo pipefail, theROOT=line,${N:?usage…}positionals. Forge checks green at this head:ci / testandlabels / labels.Load-bearing constraints. No third-party action is added; the only
uses:in the file is the pre-existingactions/checkout@v4, untouched by this diff.secrets.RELEASE_TOKENis kept as the step's credential and${{ github.token }}is not silently swapped in — §1.6's explicit non-goal, honoured.npm cistill precedes the release step at L28, sonode src/cli.jscan resolvecommander.One note on §1.5: the spec's literal
STOKE="${STOKE:-node "$ROOT/src/cli.js"}"can't nest quotes that way; therun_stoke()function used instead is the correct expression of the same requirement, and the test substitutes the stub through it exactly as intended.Nits — non-blocking, address at your discretion
test/publish-release.test.js:105is a vacuous assertion.assert.equal(fs.existsSync(auth.tokenFile), false, 'temporary credential file must be removed')can never fail:runScenario'sfinallyblockrmSyncsdir, andRUNNER_TEMPlives insidedir, so the token file is gone by the time the assertion runs regardless of what the script did. Confirmed by mutation — deletingtrap 'rm -rf "$TMP"' EXITfrompublish-release.shstill leaves 4/4 green. The cleanup genuinely works (my end-to-end run left nostoke-release.*underRUNNER_TEMP), so this is test strength, not a defect in the script. Moving the check insiderunScenario, before thefinally, would make it bite.README.md:1083-1085is now stale. Thescripts/tree block still lists onlybuild-deb.sh,publish-deb.sh,install-apt.sh;publish-release.shandchangelog-section.share missing. (Thetest/block just above it was already stale before this PR — five files unlisted — so a one-line sweep of both would clear it. #54 doesn't ask for a README change, which is why this rides the approval.) A related touch:publish-release.sh's header is thinner thanpublish-deb.sh's and doesn't document the env vars it reads —RELEASE_TOKEN,FORGE_URL/GITHUB_SERVER_URL,RUNNER_TEMP,STOKE.What I could not verify
v*tag, andRELEASE_TOKENis unset (escalated on #27), so the live "release exists,.debattached, notes pane populated" check did not run. The issue assigns that to triage post-merge and does not gate the merge on it. I substituted the localhost end-to-end above, which exercises the same script and the same CLI code path against the same API shapes, but not the real forge.release.yml. No Forgejo Actions runner is reachable from here, andci / testdoes not execute the release workflow. I relied on a YAML parse plus direct execution of the substitutedrun:line, which covers the quoting and the argument vector but not${{ }}expansion by the runner itself.release view's 404 behaviour against the live forge. My stub returns 404 with a JSON body and the CLI exits non-zero as expected; I took the live-forge measurement (--tag v9.9.9→HTTP status: 404, exit 1) from #54's table rather than re-running it.Verdict: approve — whole PR reviewed at head
ee88d7d395against issue #54, in a detached throwaway worktree.Verified by running, not opining:
git grep -n curl→ empty) and no Authorization header is constructed anywhere under .forgejo/workflows/. All three curl calls and the RELEASE_ID plumbing are gone; the step is the single-line invocation from issue #54 §3, and steps 1–3 (checkout, npm ci && npm test, build-deb, publish-deb) are untouched — the npm-ci-before-release ordering the issue calls load-bearing is preserved.npm ci && npm test: 141/141 pass (133 baseline + the 8 new tests across test/changelog-section.test.js and test/publish-release.test.js). CI on the head reports success.scripts/changelog-section.sh 1.4.0 CHANGELOG.md→ exactly the 21-line section, exit 0, empty stderr;1.5.0→ 0 bytes on stdout, the reason on stderr, exit 1.npm run check:governancepasses; changelog.d/54.md follows the existing fragment style.Refs #54and no closing keyword precedes #54 anywhere in it — the post-merge close stays with triage as the issue directs.panel=line includes me; this is a panel verdict, not advisory.Could not verify, and what I relied on instead:
release viewfailure for non-404 reasons (network/auth) falls through torelease create; this matches issue #54 §1.4 verbatim and the old workflow's behavior, so it is spec, not a defect.Non-blocking nit (builder's discretion): the vendored wrapper's generic stderr line reads "has no publishable section for 'X'" where ceremony's bin/changelog-section says "has no section … the release PR assembles the section (changelog-assemble)"; the diagnosis line is verbatim, exit semantics identical, and stoke has no changelog-assemble to point at — fine as adapted.
Approved at head
ee88d7d395.The PR meets the acceptance criteria from #54:
.forgejo/workflows/release.ymlno longer containscurlor anAuthorization: tokenheader.scripts/changelog-section.shextracts the 21-line1.4.0section fromCHANGELOG.mdand exits 1 with a reason for the missing1.5.0section.scripts/publish-release.shpasses the token only via--token-file(mode0600) and never inargv; a stubbed run shows the get-or-create branches (release viewexit 0 →release upload; exit 1 →release create --body-file --asset).Refs #54, leaving post-merge verification to triage, and addschangelog.d/54.md.Verification gaps (named per reviewer doctrine): Node.js is not installed in this box, so I could not run
npm testor thenode:testfiles. I relied on manual shell probes of the two new scripts plus readingtest/changelog-section.test.jsandtest/publish-release.test.js.shellcheckreports no issues on the new shell scripts.