Skip to content

Fix four reproducible bugs in the release pipeline#89

Merged
Yan Xue (yanxue06) merged 4 commits into
mainfrom
fix/release-pipeline-bugs
Jun 11, 2026
Merged

Fix four reproducible bugs in the release pipeline#89
Yan Xue (yanxue06) merged 4 commits into
mainfrom
fix/release-pipeline-bugs

Conversation

@yanxue06

@yanxue06 Yan Xue (yanxue06) commented Jun 9, 2026

Copy link
Copy Markdown
Member

Four bugs in the release workflows, each verifiable by reading the code, each fixed with the smallest change that restores the documented intent. Per fix: what breaks, why, and why the fix can't change anything that works today.

1. update-skills.yaml — releases with large diffs kill the job (E2BIG)

gather-changes built the release diff with DIFF=$(git diff ...) and then ran export DIFF before invoking python3. On Linux a single environment variable over ~128 KiB makes execve() fail with E2BIG ("Argument list too long"), so any release whose diff passed that limit failed the job — the 100k-char truncation lived inside the Python process that never got to start.

81a89f4 fixed exactly this in update-docs.yaml ("write diff to file"); update-skills.yaml is a copy of that code and kept the broken version. This ports the fix verbatim: write the diff to ${RUNNER_TEMP}/changes.diff, pass the path through the environment, read and truncate in Python.

Can't-regress argument: for diffs that worked before (under 128 KiB) the output is byte-identical apart from a trailing newline; diffs that crashed the job now succeed.

2. go-build — the goos: windows path never worked

The build step runs go build -o "<name>" and the package step runs mv "<name>.exe" when goos=windows. go build -o uses the output name verbatim — it does not append .exe — so the file mv looks for has never existed, and every windows invocation fails at the package step. The build step now appends the same .exe suffix the package step expects.

Can't-regress argument: for darwin/linux, SUFFIX="" and the command is character-for-character what it was. No current caller builds windows (go-service-release runs darwin/arm64 and linux/amd64), so nothing live changes at all; the windows path goes from provably broken to consistent with packaging.

3. typescript-monorepo-release.yaml — a second same-day release overwrites the first

The github-release job picks its tag by counting existing release/<date>.* tags and using the count as the .N suffix. Its checkout is the default shallow one, which fetches no tags, so git tag -l always returned nothing, N was always 0, and a second release on the same day reused release/<date>.0. softprops/action-gh-release updates an existing release when the tag exists, so run #2 silently replaced run #1's release notes.

Fix: fetch-depth: 0 on that checkout (fetches all branches and tags). bump-versions and npm-publish in the same workflow already check out with fetch-depth: 0.

Can't-regress argument: the only behavior change is the counter seeing real tags — the documented purpose of the suffix.

4. update-docs.yaml + update-skills.yaml — one CODEOWNERS team entry disables all reviewer requests

The reviewer extraction comment says "not @org/team", but grep -oE '@[a-zA-Z0-9_-]+' stops at the slash and emits the bare @org out of every team entry. GitHub's request-reviewers call is all-or-nothing: one invalid login rejects the whole request, and the gh pr edit runs under || true, so a single team entry in a caller repo's CODEOWNERS silently turned reviewer assignment off for every reviewer. The pattern now matches the full token and drops anything containing a slash — GitHub logins can't contain /, so every dropped entry was already guaranteed-invalid.

The grep is also wrapped in || true: these steps run under pipefail, and a CODEOWNERS with no @ entries at all made grep exit 1 and fail the whole job.

Can't-regress argument: every login the new filter emits, the old one emitted too; the removed entries are ones the API rejects categorically.

Test plan

  • yaml.safe_load passes on all 42 workflow/block files after the edits.
  • bash -n passes on every edited run: block; the embedded Python heredoc compiles.
  • A cross-reference check (declared inputs/outputs/step ids/needs across all workflows and blocks, including workflow→block with: keys) passes.
  • Not exercised end-to-end: a real release run. Fix 1 mirrors code that already shipped for update-docs in 81a89f4; fixes 2–4 only touch paths that are dead or wrong today.

Risk / rollback

The four fixes are independent — each is its own commit touching disjoint hunks; revert any single commit to roll one back. No inputs, outputs, or interfaces change.

🤖 Generated with Claude Code


View with Codesmith Autofix with Codesmith
Need help on this PR? Tag /codesmith with what you need. Autofix is disabled.

Summary by CodeRabbit

  • Bug Fixes
    • Windows binary artifacts now include the correct .exe file extension
    • Improved release process to prevent tag conflicts when deploying multiple versions on the same day
    • Enhanced CI/CD workflow stability for contributor acknowledgment and release documentation handling

Yan Xue (yanxue06) and others added 4 commits June 9, 2026 15:32
…expects

go build -o writes the output name verbatim — it never appends .exe — so
the goos=windows path always failed at `mv <name>.exe`. Build with the
same suffix the Package step moves. darwin/linux commands are unchanged,
and no current caller builds windows, so nothing live changes.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…counter works

The github-release job picks release/<date>.N by counting existing tags,
but its shallow checkout fetched no tags, so N was always 0 and a second
same-day release reused release/<date>.0 — action-gh-release then updates
the existing release instead of creating a new one. bump-versions and
npm-publish in this workflow already check out with fetch-depth: 0.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…eviewer requests

The extraction regex matched the bare @org prefix of @org/team entries
(the comment says "not @org/team" but the pattern stops at the slash).
GitHub's request-reviewers call is all-or-nothing — one invalid login
rejects the whole request — and the gh call runs under `|| true`, so a
single team entry silently disabled reviewer assignment entirely. Match
the full token and drop anything with a slash; logins cannot contain '/',
so every dropped entry was already guaranteed-invalid. Also wrap the grep
in `|| true`: these steps run with pipefail, and a CODEOWNERS with no
@entries made grep exit 1 and fail the job.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…onment

Ports 81a89f4 (the update-docs fix) to the identical code here: export
DIFF put the untruncated git diff into the environment, and a single env
var over ~128 KiB makes execve() fail with E2BIG ("Argument list too
long"), so any release whose diff exceeded that killed gather-changes
before the Python truncation ever ran. The diff now goes through
${RUNNER_TEMP}/changes.diff exactly like update-docs.yaml.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Jun 9, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

This pull request makes four targeted improvements to GitHub Actions workflows and composite actions. The Go build action gains Windows-aware artifact naming, the release workflow fixes tag enumeration via full repository fetch, reviewer extraction is refined in two workflows to exclude team identifiers, and release diff generation is moved to file-based handling to avoid environment size constraints.

Changes

CI/CD Infrastructure Improvements

Layer / File(s) Summary
Go build Windows artifact suffix
.github/blocks/go-build/action.yaml
Composite action introduces a SUFFIX variable set to .exe for Windows targets, applied to the go build -o output path, ensuring the binary filename matches downstream expectations.
Release tag enumeration via full repository fetch
.github/workflows/typescript-monorepo-release.yaml
The github-release job Checkout step now uses fetch-depth: 0 to enable full tag history, fixing the tag counter for release/<date>.N versioning to reset correctly on same-day releases.
CODEOWNERS reviewer extraction refinement
.github/workflows/update-docs.yaml, .github/workflows/update-skills.yaml
Both workflows now filter CODEOWNERS entries to exclude @org/team patterns, ensuring only individual usernames are emitted for GitHub review assignment.
Release diff file-based handling
.github/workflows/update-skills.yaml
The "Extract release info" step now writes git diff output to a temporary file and reads it in Python, rather than through environment variables, to avoid shell size limits while preserving truncation logic.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Poem

🐰 Four fixes hopped into place today,
Windows binaries now know their way,
Tags count right, reviewers clean,
And diffs avoid the space machine!
CI flows smooth—what a view! 🚀

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Fix four reproducible bugs in the release pipeline' accurately summarizes the main change—addressing four independent bugs across GitHub Actions workflows in the release pipeline.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/release-pipeline-bugs

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions

github-actions Bot commented Jun 9, 2026

Copy link
Copy Markdown

📄 README may need an update

This PR introduces changes that might not be reflected in README.md.

Reason: README.md does not document the new use-blacksmith workflow input now exposed by typescript-monorepo-release, update-docs, and update-skills, so it is missing a user-facing configuration option.

This is an automated check powered by AI. If the README is intentionally unchanged, feel free to ignore this.

@yanxue06 Yan Xue (yanxue06) marked this pull request as ready for review June 10, 2026 01:25
Copilot AI review requested due to automatic review settings June 10, 2026 01:25

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.github/workflows/typescript-monorepo-release.yaml:
- Around line 154-158: In the github-release job update the actions/checkout
step (the one that currently sets fetch-depth: 0) to add persist-credentials:
false so credentials are not retained for that checkout; locate the checkout
step inside the github-release job and add the persist-credentials: false key
alongside fetch-depth: 0 to limit token exposure (this affects the checkout
action used prior to git tag -l and softprops/action-gh-release).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: ef7813ad-9fbe-47b2-b4ff-f996f60ecb0e

📥 Commits

Reviewing files that changed from the base of the PR and between 81a89f4 and e970914.

📒 Files selected for processing (4)
  • .github/blocks/go-build/action.yaml
  • .github/workflows/typescript-monorepo-release.yaml
  • .github/workflows/update-docs.yaml
  • .github/workflows/update-skills.yaml
📜 Review details
🧰 Additional context used
🧠 Learnings (3)
📚 Learning: 2026-04-05T00:07:12.194Z
Learnt from: qwerzl
Repo: photon-hq/buildspace PR: 62
File: .github/workflows/update-docs.yaml:164-167
Timestamp: 2026-04-05T00:07:12.194Z
Learning: In this repo (photon-hq/buildspace), reusable workflow blocks referenced under photon-hq/buildspace (e.g., `photon-hq/buildspace/.github/blocks/<block>main`) are intentionally pinned to `main` because the blocks and workflows ship together and are kept in sync. During review, do not flag these references as version-pinning/supply-chain issues just because they use `main`; only require pinning to a release tag if there’s a separate reason (e.g., referencing an external repo or a non-controlled branch).

Applied to files:

  • .github/workflows/typescript-monorepo-release.yaml
  • .github/workflows/update-skills.yaml
  • .github/workflows/update-docs.yaml
📚 Learning: 2026-04-27T01:30:22.893Z
Learnt from: yanxue06
Repo: photon-hq/buildspace PR: 73
File: .github/workflows/check-readme.yaml:18-18
Timestamp: 2026-04-27T01:30:22.893Z
Learning: When reviewing this repo’s GitHub Actions workflows, treat Blacksmith runner labels like `blacksmith-4vcpu-ubuntu-2404` and other `blacksmith-*vcpu-ubuntu-*` values as valid/intentional third-party runner labels (Blacksmith: blacksmith.sh). Do not flag them as unknown or non-standard runner labels—these are an intentional drop-in replacement for GitHub-hosted runners.

Applied to files:

  • .github/workflows/typescript-monorepo-release.yaml
  • .github/workflows/update-skills.yaml
  • .github/workflows/update-docs.yaml
📚 Learning: 2026-04-27T01:30:22.893Z
Learnt from: yanxue06
Repo: photon-hq/buildspace PR: 73
File: .github/workflows/check-readme.yaml:18-18
Timestamp: 2026-04-27T01:30:22.893Z
Learning: In photon-hq/buildspace GitHub workflow YAML files, runner labels that match `blacksmith-*vcpu-ubuntu-*` (e.g., `blacksmith-4vcpu-ubuntu-2404`) are intentionally managed by the Blacksmith CI service (blacksmith.sh) and serve as drop-in replacements for GitHub-hosted ephemeral microVM runners. Do not flag these labels as unknown/non-standard, supply-chain concerns, or “unrecognized runner” issues; any linter/actionlint warnings about these specific labels are expected false positives.

Applied to files:

  • .github/workflows/typescript-monorepo-release.yaml
  • .github/workflows/update-skills.yaml
  • .github/workflows/update-docs.yaml
🪛 zizmor (1.25.2)
.github/workflows/typescript-monorepo-release.yaml

[warning] 152-158: credential persistence through GitHub Actions artifacts (artipacked): does not set persist-credentials: false

(artipacked)


[error] 153-153: unpinned action reference (unpinned-uses): action is not pinned to a hash (required by blanket policy)

(unpinned-uses)

🔇 Additional comments (3)
.github/blocks/go-build/action.yaml (1)

50-56: LGTM!

.github/workflows/update-docs.yaml (1)

99-104: LGTM!

.github/workflows/update-skills.yaml (1)

132-137: LGTM!

Also applies to: 182-182, 185-185, 189-190, 197-203

Comment on lines +154 to +158
with:
# The release/<date>.N counter below counts existing tags; the default
# shallow checkout fetches no tags, so N was always 0 and a second
# same-day release reused (and overwrote) the first one's tag.
fetch-depth: 0

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Verify whether github-release job actually needs persisted checkout credentials.
# Expected: no `git push`/authenticated write commands in this job, making persist-credentials unnecessary.

awk '
  /github-release:/ {in_job=1}
  in_job && /^[^[:space:]]/ && $0 !~ /^github-release:/ {in_job=0}
  in_job {print NR ":" $0}
' .github/workflows/typescript-monorepo-release.yaml | rg -n 'git push|gh release|gh api|persist-credentials'

Repository: photon-hq/buildspace

Length of output: 46


🏁 Script executed:

#!/bin/bash
set -euo pipefail

WF=".github/workflows/typescript-monorepo-release.yaml"

# Extract the github-release job block (from "github-release:" up to the next top-level job key)
awk '
  /^  [A-Za-z0-9_-]+:/ { # top-level keys under "jobs:"
    if ($1 ~ /^[A-Za-z0-9_-]+:$/) {
      job=$1
    }
  }
  /^  github-release:/ {in=1; print; next}
  in && /^  [A-Za-z0-9_-]+:/ {in=0}
  in {print}
' "$WF" > /tmp/github-release-job.yaml

# Print the extracted block with line numbers but keep it concise
nl -ba /tmp/github-release-job.yaml | sed -n '1,220p'

# Search within the job for any signs that persisted git creds might be needed
rg -n --hidden --no-ignore -S \
  'git push|git tag|git commit|git reflog|gh release|gh api|actions/checkout|persist-credentials|GITHUB_TOKEN|token:' \
  /tmp/github-release-job.yaml || true

Repository: photon-hq/buildspace

Length of output: 182


🏁 Script executed:

#!/bin/bash
set -euo pipefail

WF=".github/workflows/typescript-monorepo-release.yaml"
OUT="/tmp/github-release-job.yaml"

# Extract the github-release job block under jobs:
awk -v out="$OUT" '
  BEGIN {injob=0}
  /^jobs:/ {next}
  # Start when we hit "  github-release:"
  /^  github-release:/ {
    injob=1
  }
  # Stop when we hit the next top-level job key "  <name>:"
  injob && /^  [A-Za-z0-9_-]+:/ && $0 !~ /^  github-release:/ {
    injob=0
  }
  injob {print}
' "$WF" > "$OUT"

echo "=== Extracted github-release job (first ~220 lines) ==="
nl -ba "$OUT" | sed -n '1,220p'

echo "=== Search for credential-requiring operations ==="
rg -n --hidden --no-ignore -S \
  'git push|git tag|git commit|persist-credentials|gh release|gh api|actions/checkout|GITHUB_TOKEN|token[:=]' \
  "$OUT" || true

Repository: photon-hq/buildspace

Length of output: 182


🏁 Script executed:

#!/bin/bash
set -euo pipefail

WF=".github/workflows/typescript-monorepo-release.yaml"

echo "=== Lines around github-release ==="
rg -n "github-release:" "$WF" || true
start=$(rg -n "github-release:" "$WF" | head -n1 | cut -d: -f1 || echo 140)
# print a window around it
lo=$((start-40))
hi=$((start+220))
sed -n "${lo},${hi}p" "$WF"

echo "=== Steps inside github-release that might need auth/push ==="
# Roughly limit to the github-release region by matching from github-release: down until next job key
awk '
  /^  github-release:/ {injob=1}
  injob {print}
  injob && /^  [A-Za-z0-9_-]+:/ && $0 !~ /^  github-release:/ {exit}
' "$WF" | rg -n -S 'git push|git tag|git commit|persist-credentials|gh release|gh api|actions/checkout|GITHUB_TOKEN|token[:=]' || true

Repository: photon-hq/buildspace

Length of output: 4333


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Locate the create-github-release block implementation in this repo
root=".github/blocks"

echo "=== Find create-github-release files ==="
fd -a -H "create-github-release" "$root" || true
fd -a -H "*create-github-release*" "$root" || true

# If there are multiple matches, list them
mapfile -t files < <(fd -a -H "create-github-release" "$root" -t f 2>/dev/null || true)
if [ "${`#files`[@]}" -eq 0 ]; then
  # fallback: grab anything containing the name
  mapfile -t files < <(fd -a -H "*create-github-release*" "$root" -t f 2>/dev/null || true)
fi

echo "=== Candidate files ==="
printf '%s\n' "${files[@]:0:30}"

# Search for any git push / checkout / credential persistence usage
for f in "${files[@]:0:30}"; do
  echo "--- Searching: $f"
  rg -n -S 'git push|git commit|git tag|persist-credentials|actions/checkout|gh api|gh release|GITHUB_TOKEN|token' "$f" || true
done

Repository: photon-hq/buildspace

Length of output: 541


🏁 Script executed:

#!/bin/bash
set -euo pipefail

DIR=".github/blocks/create-github-release"

echo "=== ls $DIR (depth-limited) ==="
find "$DIR" -maxdepth 3 -type f | head -n 200

echo "=== Search for credential-requiring git operations ==="
# Search in all files under the directory for git/credential/push usage
rg -n -S --hidden --no-ignore \
  'git push|git commit|git tag\b|git fetch|git remote|persist-credentials|checkout@|GITHUB_TOKEN|actions/checkout|gh release|gh api' \
  "$DIR" || true

echo "=== Search for token usage patterns ==="
rg -n -S --hidden --no-ignore \
  'app-private-key|app-id|github-token|token[:=]|JWT|octokit|`@actions/github`|request\.headers|Authorization' \
  "$DIR" || true

Repository: photon-hq/buildspace

Length of output: 941


🏁 Script executed:

#!/bin/bash
set -euo pipefail
FILE=".github/blocks/create-github-release/action.yaml"
wc -l "$FILE"
sed -n '1,140p' "$FILE"

Repository: photon-hq/buildspace

Length of output: 2682


Set persist-credentials: false on the github-release checkout.

The github-release job only enumerates existing tags (git tag -l) and creates the GitHub Release via API (softprops/action-gh-release using the app/GitHub token), so persisting checkout credentials is unnecessary and increases token exposure surface.

Suggested patch
       - name: Checkout
         uses: actions/checkout@v5
         with:
           # The release/<date>.N counter below counts existing tags; the default
           # shallow checkout fetches no tags, so N was always 0 and a second
           # same-day release reused (and overwrote) the first one's tag.
           fetch-depth: 0
+          persist-credentials: false
🧰 Tools
🪛 zizmor (1.25.2)

[warning] 152-158: credential persistence through GitHub Actions artifacts (artipacked): does not set persist-credentials: false

(artipacked)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/typescript-monorepo-release.yaml around lines 154 - 158,
In the github-release job update the actions/checkout step (the one that
currently sets fetch-depth: 0) to add persist-credentials: false so credentials
are not retained for that checkout; locate the checkout step inside the
github-release job and add the persist-credentials: false key alongside
fetch-depth: 0 to limit token exposure (this affects the checkout action used
prior to git tag -l and softprops/action-gh-release).

Source: Linters/SAST tools

@yanxue06 Yan Xue (yanxue06) merged commit 5e56f0d into main Jun 11, 2026
3 checks passed
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.

2 participants