Repository navigation
Harden Action release outputs against stdout-scraping (silent-empty tags/has-changes) #594
Description
Problem
The releasekit Action advertises release-mode outputs — tags, has-changes, version-output, release-output (see action.yml). scripts/run-action.mjs derives them by parsing the CLI's stdout:
function writeReleaseOutputs(input, stdout) {
const parsed = parseReleaseOutput(stdout, ...);
const tags = parsed?.versionOutput?.tags;
setOutput('tags', Array.isArray(tags) ? tags.join(',') : ''); // '' when stdout didn't parse
setOutput('has-changes', !!parsed?.versionOutput?.updates?.length ? 'true' : 'false');
}If the CLI's stdout comes back without a parseable .versionOutput — a rare/transient capture hiccup, the same failure mode that stranded the v0.41.1 action dist (#593) — every release output silently degrades to empty / false, with a green check.
A user relying on the advertised contract:
- uses: goosewobbler/releasekit@vX
id: release
with: { mode: release }
- if: steps.release.outputs.tags != ''
run: ./promote "${{ steps.release.outputs.tags }}"then silently skips their downstream step even though the release actually tagged and published. This is a user-facing footgun on an output contract people build automation on.
Proposed hardening (two tiers)
Tier 1 — git-derive tags + verify (targeted, no CLI change).
In writeReleaseOutputs (and the standing-pr-publish output path), after the release/publish has run in the checkout, source the tag authoritatively from git — git tag --points-at HEAD filtered to version tags — cross-checked against the parsed stdout. If the CLI reported changes but git shows no version tag, warn / set a tag-verification-failed output (optionally exit non-zero under a strict flag). Hardens the single most load-bearing output.
Tier 2 — file-based structured output (comprehensive).
tags can come from git, but version-output / release-output need the CLI's structured result, which today only travels via stdout. Add --output <file> (or a temp file) to release / standing-pr publish; run-action.mjs reads the file instead of capturing stdout. Removes stdout-scraping fragility from all release outputs.
Relationship to #593
#593 fixed the same failure class in releasekit's own release workflow (_release.reusable.yml + the action-dist mechanism). This issue is the product counterpart: the outputs that users consume from the Action share the fragility.
Note (correcting an earlier assumption): releasekit's own publish runs the CLI directly inside _release.reusable.yml — not via the Action — because the OIDC job_workflow_ref must match the npm trusted-publisher entry (one workflow path per package; see the comment at _release.reusable.yml L176-177). So the self-workflow can't just consume steps.releasekit.outputs.tags, and #593's workflow-level git-derivation + fail-loud stay. Tier 2's file output could still let the self-workflow drop its stdout capture later.
Acceptance
outputs.tagsreflects the real pushed tag(s) even when stdout parsing fails.- A release that reports changes but produces no git tag surfaces (warn / output / strict-fail), never silent-empty.
- (Tier 2) release outputs no longer depend on capturing CLI stdout.
Activity
fix(action): recover the release `tags` output from git when stdout c…
feat(action): read release outputs from a --output file, not scraped …
fix(action): read release outputs from a --output file, not scraped s…
fix(action): read release outputs from a --output file, not scraped s…