Skip to content

fix(release): build valid create-tree request in cleanup step#10

Merged
sammccord merged 1 commit into
mainfrom
fix/release-cleanup-tree-json
Jun 3, 2026
Merged

fix(release): build valid create-tree request in cleanup step#10
sammccord merged 1 commit into
mainfrom
fix/release-cleanup-tree-json

Conversation

@sammccord

Copy link
Copy Markdown
Contributor

What changed? Why?

The release workflow failed at the Cleanup staging and determine tag target step with:

unexpected end of JSON input

Root cause

For a release where .release/<version>/ is the only staging dir, the step took the else branch and sent a bare JSON array to POST /git/trees:

NEW_TREE_SHA=$(echo "${TREE_JSON}" | gh api ".../git/trees" --input - --jq ".sha")

That endpoint requires an object {"tree":[...]}, not a bare [...]. GitHub rejected the body, and gh api --jq ".sha" then ran jq over an empty/error response → unexpected end of JSON input.

The other branch (when additional .release/* dirs exist) was also broken: it combined gh api --input - with -f base_tree=..., which gh api refuses (a stdin body is mutually exclusive with -f/-F fields). Both branches additionally rebuilt the entire top-level tree by hand via string concatenation — fragile around file modes, executable bits, LFS pointers, and paths with spaces.

Fix

Replace both branches with a single base_tree + null-deletion path:

  • Collect every blob path under .release/<version>/ with git ls-tree -r --name-only.
  • Build a proper request body with jq: {base_tree, tree:[{path,mode,type,sha:null}, ...]}.
  • POST it via --input - (no -f mixing). sha:null deletes each blob; base_tree inherits everything else, so any other .release/* dirs are preserved automatically and the emptied .release/<version>/ (plus .release/ itself if it was the only version) drops out — git has no empty trees.

Signed-commit creation (POST /git/commits, author/committer omitted → GitHub-signed) and the main fast-forward are unchanged. Cleanup remains hard-fail: any API error still aborts before tagging.

Net: +33 / −83 lines; deletes the brittle dual-branch string concatenation.

Verification

  • bash -n on the extracted step body — syntax OK.
  • Confirmed the jq pipeline emits valid {base_tree, tree:[...]} JSON with sha:null preserved, including paths containing spaces.

Checklist

  • Required documentation (e.g. README) has been updated.
  • I have read and agree to the CONTRIBUTING guide.

type=routine
risk=low
impact=sev5

The 'Cleanup staging and determine tag target' step failed with
'unexpected end of JSON input'. The else-branch sent a bare JSON array
to POST /git/trees (which requires {"tree":[...]}), and the if-branch
illegally combined 'gh api --input -' with '-f base_tree='. Both also
rebuilt the whole top-level tree by hand via string concatenation.

Replace both branches with a single base_tree + null-deletion path:
collect blob paths under .release/<version>/, build a proper request
body with jq, and POST it. base_tree inherits everything else, so other
.release/* dirs are preserved and the emptied dir drops automatically.
Signed-commit creation and main fast-forward are unchanged.
@cb-heimdall

cb-heimdall commented Jun 3, 2026

Copy link
Copy Markdown
Collaborator

✅ Heimdall Review Status

Requirement Status More Info
Reviews 1/1
Denominator calculation
Show calculation
1 if user is bot 0
1 if user is external 0
2 if repo is sensitive 0
From .codeflow.yml 1
Additional review requirements
Show calculation
Max 0
0
From CODEOWNERS 0
Global minimum 0
Max 1
1
1 if commit is unverified 0
Sum 1

@sammccord sammccord merged commit 1373d33 into main Jun 3, 2026
4 checks passed
@sammccord sammccord deleted the fix/release-cleanup-tree-json branch June 3, 2026 15:21
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

3 participants