Commit Graph

3 Commits

Author SHA1 Message Date
7db58031c7 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
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>
2026-08-05 17:14:57 -05:00
fd59a74912 ci(bundle): recognize a linux-aarch64 link asset
All checks were successful
PR Checks / rust-gates (pull_request) Successful in 5s
Step 1 of PLAN.md §5.2's four, and it has to be first. Two rules in this
job are strict in opposite directions: an unrecognized link asset name
fails the run, and a missing REQUIRED platform key fails it too. So the
name must be taught before the release that carries it, and the key can
only be required after one exists -- requiring it first would fail every
bundle for as long as the gap lasts.

This is therefore the mapping only. linux-aarch64 is not in REQUIRED
yet; step 3 promotes it once a link release actually ships the binary,
after which a dropped target reddens CI instead of vanishing silently
from every bundle.

The compose step needed no change: it builds the asset map from the
platform TSV, so a third key costs it nothing.

Edited on `main` and deliberately not on `edge`. The compose job runs
from `main`, and leaving `edge`'s copy untouched means the eventual
cutover merge has nothing to conflict over.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-08-05 05:22:59 -05:00
c100d4a2d7 ci(bundle): compose and publish the bundle manifest
All checks were successful
PR Checks / rust-gates (pull_request) Successful in 5s
Phase 0 item 3 of docs/installer/PLAN.md (§7.1-§7.3). The installer resolves
what to install *from* the bundle, so this has to exist before Phase 1 code is
useful. Both components it composes now have releases, which is what unblocked
it.

Adds .gitea/workflows/bundle.yml — resolve both components' latest releases,
run the two compose-time gates, and publish bundles/current.json — plus the
first real bundle (2026.08.04: link v1.1.0 + overlay v0.1.1, protocol 3).

Bundles are COMMITTED under bundles/, not published as releases. This repo's own
releases are the installer binaries, and /releases/latest returns whichever
release is newest regardless of kind, so interleaving bundle releases would make
"latest" intermittently resolve to a release carrying no installer binary. The
push to main needs no new branch-protection exception: release.yml's version-bump
commit already requires it.

Gate 1 (protocol agreement) reads the sidecar's PROTOCOL_VERSION from
sidecar/src/main.rs at the release tag, not from the binary. --print-config
would answer, but only for releases from v1.1.0 on, and --bundle <tag> has to be
able to recompose an older bundle. It also avoids executing a downloaded
artifact and provisioning a throwaway config whose auth token would land in a CI
log. The overlay half comes from manifest.json inside the tarball, which is the
only statement of that version that exists.

Gate 2 (assets) downloads every asset and verifies it against the SHA256SUMS its
publishing repo shipped, then records the hash it computed itself. These
artifacts are deliberately unsigned, so a hash copied from a file nobody checked
would make the whole chain decorative. An asset with no SHA256SUMS entry is
caught separately, since `sha256sum -c` passes right over it.

Release reads are ANONYMOUS on purpose: they are exactly the requests the shipped
installer makes on a host with no Gitea credentials, so a repo flipped to private
fails here rather than on an operator's machine.

Stale components (§7.3) are dispatched, never awaited — Gitea's dispatch endpoint
returns no run handle. "Ahead of its release" counts only releasable commits and
excludes merge commits, whose subject quotes the feat/fix title and would
otherwise re-dispatch a workflow that correctly declines to run, every night.

A run that finds nothing changed writes nothing, comparing everything except
`bundle` and `generated` — that is what makes the nightly cron free rather than a
dated duplicate every morning.

Verified by running the workflow's exact compose steps in a Linux container
against the live releases: both gates pass, the composed bundle is the file
committed here, a re-run reports changed=false, and the stale-detection filter
scores 1 releasable commit for link v1.0.0..main (excluding the merge that quotes
it) and 0 for a docs-only range.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-08-04 11:10:51 -05:00