fix(bundle): publish to a bundles branch, and unbreak the stale check #13

Open
wtclaude wants to merge 1 commits from ci/bundle-require-aarch64 into main
Member

What & why

Three things, all surfaced by the first compose run that ever had a bundle to write (task 542).

1. main is protected, so the bundle could never land

remote: error: Internal Server Error (no message for end users)
 ! [remote rejected] HEAD -> main (pre-receive hook declined)

Both attempts — the retry rebases and pushes to the same protected branch. Everything upstream of it was fine: credentials present, all four assets checksum-verified, both gates green, bundle composed. It was then thrown away.

Bundles now publish to a bundles branch of their own, at its root. That needs no protection exception and keeps everything the original choice was for: a reviewable diff, a git history of the compat matrix, plain anonymous raw URLs, no credentials on the shard host. The header's claim that this push "needs no new branch-protection exception" was simply false — it assumed release.yml already pushed to main, which it never has (see #3 below and the release.yml PR).

The published bundles are materialized into a worktree at published/, so the .2 suffix scan and the idempotence check read what is actually published rather than a stale copy on main. The branch is created from an empty-tree root commit on first use, so it carries no history that has nothing to do with the compat matrix.

It is already seeded with bundle-2026.08.04.json + current.json, because every bundle is kept forever so --bundle <tag> stays reproducible, and the move must not lose the one that exists. Verified anonymously readable at the new URL.

2. A ${{ }} in a comment had silently disabled the stale check

Unable to interpolate expression 'format('…', , steps.resolve.outputs.link_tag, …)': Failed to parse: unexpected token ","

The runner scans a step's script for template expressions before running it, fails to parse the empty one written in a comment, and then skips the step without failing the job. So the dispatch that fires a component's release workflow when it has unreleased work has never run once. Reworded, with a warning not to write that token in a comment again.

link/release.yml and this repo's release.yml carry the identical bug in their bump-and-tag step — which is why link's Cargo.toml still says 0.1.0 after six releases. Handled in their own PRs, because fixing the comment makes those steps start pushing to a protected main.

3. linux-aarch64 is now a required key

Step 3 of PLAN.md §5.2, which was waiting on link publishing one. v1.1.1 does, so from here a dropped target reddens this job instead of vanishing from every bundle.

bundles/*.json is deleted from main: it is now a stale copy of data that lives elsewhere, and a wrong "current" is worse than none. The README stays and documents the branch.

How it was tested

The whole job was extracted and run in a container against a bare repo standing in for the remote, so the push is real (only the auth-URL rewrite is stubbed, as there is no server):

Run Result
1 — branch absent bundles branch does not exist yetcomposed bundle 2026.08.05* [new branch] bundles -> bundles → both files published
2 — branch present bundles branch: 2 published bundle(s)identical to the published current.json — nothing to publish
3 — again identical, nothing pushed

Final state: one publish commit on top of the root commit, bundle-2026.08.05.json + current.json, and link.assets carrying all three keys (linux-aarch64, linux-x86_64, windows-x86_64). The stale check now runs, reporting both components as having nothing releasable.

One re-entrancy fix came out of that: rm -rf published leaves the worktree registered, so worktree add refuses the path on a second run in the same checkout. CI checks out fresh, but git worktree prune makes driving the job by hand — which is how it is tested — work.

After this merges

A dispatch or the nightly cron publishes 2026.08.05 (link v1.1.1 + overlay v0.2.0, protocol 3). The installer's own BUNDLE_BASE still points at raw/branch/main/bundles/ and is updated on edge in a companion PR; nothing is released from edge, so there is no window where a shipped binary reads the wrong URL.

Checklist

  • I have read CONTRIBUTING.md.
  • The change builds and existing tests/checks pass locally.
  • I have added or updated tests/docs where it makes sense.
  • My commits are reasonably scoped with clear messages.

AI-assisted contributions (required)

  • No AI tools were used to produce this contribution.
  • AI tools were used. Tool(s): Claude Code (Opus 5). I have reviewed and understand every change, and take responsibility for it. AI-authored commits are marked with a Co-Authored-By trailer.

License

  • I agree that my contribution is licensed under this project's license (GNU GPL v3.0 or later), and I have the right to contribute it.
## What & why Three things, all surfaced by the first compose run that ever had a bundle to write (task 542). ### 1. `main` is protected, so the bundle could never land ``` remote: error: Internal Server Error (no message for end users) ! [remote rejected] HEAD -> main (pre-receive hook declined) ``` Both attempts — the retry rebases and pushes to the same protected branch. Everything upstream of it was fine: credentials present, all four assets checksum-verified, both gates green, bundle composed. It was then thrown away. Bundles now publish to a **`bundles` branch** of their own, at its root. That needs no protection exception and keeps everything the original choice was for: a reviewable diff, a git history of the compat matrix, plain anonymous raw URLs, no credentials on the shard host. The header's claim that this push *"needs no new branch-protection exception"* was simply false — it assumed release.yml already pushed to `main`, which it never has (see #3 below and the release.yml PR). The published bundles are materialized into a worktree at `published/`, so the `.2` suffix scan and the idempotence check read **what is actually published** rather than a stale copy on `main`. The branch is created from an empty-tree root commit on first use, so it carries no history that has nothing to do with the compat matrix. **It is already seeded** with `bundle-2026.08.04.json` + `current.json`, because every bundle is kept forever so `--bundle <tag>` stays reproducible, and the move must not lose the one that exists. Verified anonymously readable at the new URL. ### 2. A `${{ }}` in a comment had silently disabled the stale check ``` Unable to interpolate expression 'format('…', , steps.resolve.outputs.link_tag, …)': Failed to parse: unexpected token "," ``` The runner scans a step's script for template expressions **before** running it, fails to parse the empty one written in a comment, and then **skips the step without failing the job**. So the dispatch that fires a component's release workflow when it has unreleased work has never run once. Reworded, with a warning not to write that token in a comment again. `link/release.yml` and this repo's `release.yml` carry the identical bug in their bump-and-tag step — which is why `link`'s `Cargo.toml` still says `0.1.0` after six releases. Handled in their own PRs, because fixing the comment makes those steps *start* pushing to a protected `main`. ### 3. `linux-aarch64` is now a required key Step 3 of [PLAN.md §5.2](https://gitea.whitlocktech.com/RunicGateway/docs/src/branch/main/installer/PLAN.md), which was waiting on link publishing one. `v1.1.1` does, so from here a dropped target reddens this job instead of vanishing from every bundle. `bundles/*.json` is deleted from `main`: it is now a stale copy of data that lives elsewhere, and a wrong "current" is worse than none. The README stays and documents the branch. ## How it was tested The whole job was extracted and run in a container against a **bare repo standing in for the remote**, so the push is real (only the auth-URL rewrite is stubbed, as there is no server): | Run | Result | |---|---| | 1 — branch absent | `bundles branch does not exist yet` → `composed bundle 2026.08.05` → `* [new branch] bundles -> bundles` → both files published | | 2 — branch present | `bundles branch: 2 published bundle(s)` → `identical to the published current.json — nothing to publish` | | 3 — again | identical, nothing pushed | Final state: one publish commit on top of the root commit, `bundle-2026.08.05.json` + `current.json`, and `link.assets` carrying **all three** keys (`linux-aarch64`, `linux-x86_64`, `windows-x86_64`). The stale check now runs, reporting both components as having nothing releasable. One re-entrancy fix came out of that: `rm -rf published` leaves the worktree registered, so `worktree add` refuses the path on a second run in the same checkout. CI checks out fresh, but `git worktree prune` makes driving the job by hand — which is how it is tested — work. ## After this merges A dispatch or the nightly cron publishes `2026.08.05` (link `v1.1.1` + overlay `v0.2.0`, protocol 3). The installer's own `BUNDLE_BASE` still points at `raw/branch/main/bundles/` and is updated on `edge` in a companion PR; nothing is released from `edge`, so there is no window where a shipped binary reads the wrong URL. ## Checklist - [x] I have read [CONTRIBUTING.md](CONTRIBUTING.md). - [x] The change builds and existing tests/checks pass locally. - [x] I have added or updated tests/docs where it makes sense. - [x] My commits are reasonably scoped with clear messages. ## AI-assisted contributions (required) - [ ] No AI tools were used to produce this contribution. - [x] AI tools were used. Tool(s): `Claude Code (Opus 5)`. I have reviewed and understand every change, and take responsibility for it. AI-authored commits are marked with a `Co-Authored-By` trailer. ## License - [x] I agree that my contribution is licensed under this project's license (**GNU GPL v3.0 or later**), and I have the right to contribute it.
wtclaude added 1 commit 2026-08-05 22:15:30 +00:00
fix(bundle): publish to a bundles branch, and unbreak the stale check
All checks were successful
PR Checks / rust-gates (pull_request) Successful in 6s
7db58031c7
Three things, all found by the first compose run that ever had a bundle
to write.

1. `main` is protected, so the push was declined by the pre-receive
   hook -- twice, since the retry rebases and pushes to the same place.
   Every bundle since v1.1.1 has been composed correctly and thrown
   away. Bundles now go to a `bundles` branch of their own, at its
   root, which needs no protection exception and keeps everything the
   original choice was for: a reviewable diff, a git history of the
   compat matrix, plain anonymous raw URLs, no credentials on the shard
   host. The header's claim that this push "needs no new
   branch-protection exception" was simply false.

2. A `${{ }}` written literally in a shell comment silently disabled
   the entire stale-component check. The runner scans a step's script
   for template expressions before running it, fails to parse the empty
   one, and skips the step WITHOUT failing the job -- so the dispatch
   that is supposed to fire a component's release workflow has never
   run once. Reworded, with a warning not to write that token in a
   comment again. (link/release.yml and this repo's release.yml carry
   the same bug in their bump-and-tag step; handled separately.)

3. linux-aarch64 is now a REQUIRED platform key, which was step 3 of
   PLAN.md §5.2 and was waiting on link publishing one. v1.1.1 does, so
   from here a dropped target reddens this job instead of vanishing
   from every bundle.

The published bundles are materialized into a worktree at `published/`,
so the ".2 suffix" scan and the idempotence check read what is actually
published rather than a stale copy on main. The branch is created from
an empty-tree root commit on first use, so it carries no history that
has nothing to do with the compat matrix; it has been seeded already
with bundle 2026.08.04, because every bundle is kept forever and the
move must not lose the one that exists.

bundles/*.json is deleted from main -- it is now a stale copy of data
that lives elsewhere, and a wrong "current" is worse than none. The
README stays and documents the branch.

Verified by running the whole job in a container against a bare repo
standing in for the remote: first run creates the branch and publishes
both files with all three asset keys, second and third runs report
"identical to the published current.json -- nothing to publish" and
push nothing, and the stale check now runs and reports both components
as having nothing releasable.

Co-Authored-By: Claude <noreply@anthropic.com>
All checks were successful
PR Checks / rust-gates (pull_request) Successful in 6s
Required
Details
This pull request doesn't have enough required approvals yet. 0 of 1 approvals granted from users or teams on the allowlist.
You are not authorized to merge this pull request.
View command line instructions

Checkout

From your project repository, check out a new branch and test the changes.
git fetch -u origin ci/bundle-require-aarch64:ci/bundle-require-aarch64
git checkout ci/bundle-require-aarch64
Sign in to join this conversation.
No description provided.