Skip to content

fix(ci): defer fromJson(release.outputs.pr) into a run block#51

Merged
StefanSteiner merged 1 commit into
tableau:mainfrom
StefanSteiner:ssteiner/fix-release-please-fromjson-eval
May 27, 2026
Merged

fix(ci): defer fromJson(release.outputs.pr) into a run block#51
StefanSteiner merged 1 commit into
tableau:mainfrom
StefanSteiner:ssteiner/fix-release-please-fromjson-eval

Conversation

@StefanSteiner
Copy link
Copy Markdown
Contributor

Summary

Follow-up to PR #50. The release-please workflow added in #50 references fromJson(steps.release.outputs.pr).headBranchName directly in two YAML expression slots:

- name: Checkout release-please branch
  if: ${{ steps.release.outputs.pr != '' }}
  uses: actions/checkout@v6
  with:
    ref: ${{ fromJson(steps.release.outputs.pr).headBranchName }}  # ← evaluated at job-load time
    ...
- name: Commit Cargo.lock if changed
  ...
  env:
    BRANCH: ${{ fromJson(steps.release.outputs.pr).headBranchName }}  # ← evaluated at job-load time

GitHub Actions evaluates every ${{ }} expression in the workflow at job-load time, BEFORE per-step if: gates short-circuit. When release-please runs on a push that doesn't produce a release PR (pr output is the empty string), fromJson('') errors with:

##[error]The template is not valid. .github/workflows/release-please.yml
(Line: 203, Col: 19): Error reading JToken from JsonReader.
Path '', line 0, position 0.

This blocks release-please from opening the next release PR. Observed in run 26494686197 on the merge of PR #50 itself.

Fix

Extract the branch name in a new Resolve release-please branch name step (id=branch) that does the parse via jq inside a run: block. That block only runs when the step's if: gate passes, so the parse is never attempted on empty input. The downstream actions/checkout ref: and the final git push env: BRANCH: both reference steps.branch.outputs.name, which evaluates safely to the empty string when the resolver step was skipped.

Also adds a doc comment explaining the GHA expression-evaluation timing trap so the next person reading the workflow doesn't re-introduce the same pattern.

Why this didn't surface in PR #50's review

The expression-evaluation timing was correctly identified during local development and the fix landed in the working tree. It then failed to ride the squash-merge — the amended commit was force-pushed to the fork after PR #50 was opened, and the merge picked up the earlier head. A timing/ordering mistake on my side; the workflow change in #50 was incomplete.

Test plan

  • Workflow YAML lints (validated locally with ruby -ryaml).
  • After this PR merges, release-please's run on the fix(ci): ... push completes successfully (no JsonReader error).
  • The release-please run opens a v0.2.2 release PR (it didn't fire this on the merge of fix: clean version stamps on release builds (no -dirty markers) #50 because the workflow crashed before reaching outputPRs; the next push to main re-runs release-please which then opens the PR).
  • The follow-up "Resolve release-please branch name" step appears in the workflow logs and emits the branch name as a step output.
  • The "Commit Cargo.lock if changed" step pushes a chore: sync Cargo.lock with bumped workspace versions commit on top of release-please's bot commit.
  • The released v0.2.2 binary stamps 0.2.2.r<hash> with no -dirty-... suffix.

The follow-up steps added in PR tableau#50 reference
`fromJson(steps.release.outputs.pr).headBranchName` directly in `ref:`
(checkout) and `env:` (push) blocks. GitHub Actions evaluates every
`${{ }}` expression in the workflow at job-load time, BEFORE per-step
`if:` gates short-circuit. When release-please runs on a push that
doesn't produce a release PR (e.g. a PR merge whose conventional-
commit was already covered by an earlier release), `pr` is the empty
string, `fromJson('')` errors out, and the entire job fails with:

  ##[error]The template is not valid. .../release-please.yml
  (Line: 203, Col: 19): Error reading JToken from JsonReader.
  Path '', line 0, position 0.

This blocks release-please from ever opening the next release PR.
Observed in run 26494686197 on the merge of PR tableau#50 itself.

Fix: extract the branch name in a new "Resolve release-please branch
name" step (id=`branch`) that does the parse via `jq` inside a `run:`
block — that block only runs when the step's `if:` gate passes, so
the parse is never attempted on empty input. The downstream
`actions/checkout` `ref:` and the final `git push` `env: BRANCH:`
both reference `steps.branch.outputs.name`, which evaluates safely
to the empty string when the step was skipped.

Add a doc comment explaining the GHA expression-evaluation timing
trap so the next person reading the workflow doesn't accidentally
re-introduce the same pattern.
@StefanSteiner StefanSteiner merged commit dd78df9 into tableau:main May 27, 2026
11 checks passed
This was referenced May 27, 2026
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