Skip to content

fix: scope apt regen to dispatched channel (stop cross-channel index clobber) — #3218#5

Merged
bpamiri merged 1 commit into
mainfrom
peter/fix-cross-channel-index-clobber
Jun 19, 2026
Merged

fix: scope apt regen to dispatched channel (stop cross-channel index clobber) — #3218#5
bpamiri merged 1 commit into
mainfrom
peter/fix-cross-channel-index-clobber

Conversation

@bpamiri

@bpamiri bpamiri commented Jun 19, 2026

Copy link
Copy Markdown
Contributor

Problem

apt install wheels on the stable channel fails with "Unable to locate package wheels" — the stable Packages index is empty (0 bytes), reported in wheels-dev/wheels#3218 (a recurrence of #2838). The .deb is present in the R2 pool (pool/stable/w/wheels/wheels_4.0.5_all.deb → HTTP 200); only the dists/stable/.../Packages index is empty.

Root cause (cross-channel clobber)

Confirmed from the live run timeline:

Time (UTC) Run Stable index
12:12 Publish 4.0.5 (stable) ✅ populated
12:53 Publish 4.0.6-snapshot.2205 (bleeding-edge) ❌ clobbered to 0 bytes
14:13 Publish 4.0.6-snapshot.2208 (bleeding-edge) ❌ still empty

Three-step interaction:

  1. Sync step pulls only pool/${CHANNEL}/ from R2 (channel-filtered).
  2. regenerate-apt-metadata.sh hardcoded DISTRIBUTIONS="stable bleeding-edge" and regenerated both channels every run. The non-dispatched channel's local pool is empty (only mkdir -p'd), so apt-ftparchive emits an empty Packages.
  3. Upload step does find dists -type f and pushes all dists to R2 → the empty index overwrites the other channel's good one.

So every bleeding-edge snapshot publish (several per day) wipes the stable index minutes after each stable release. The per-channel regen that #2838 was meant to introduce is absent from the live script (only the arm64 fix from #4 is present).

Fix

  • regenerate-apt-metadata.sh: DISTRIBUTIONS="${CHANNELS:-stable bleeding-edge}" — default unchanged for manual full rebuilds; the workflow now scopes it.
  • wheels-released.yml: regen step passes CHANNELS=<dispatched channel>. A publish now regenerates and uploads only its own channel's dists; the other channel's R2 index is untouched.

CHANNELS is passed via env: and the channel value is already validated against a strict stable|bleeding-edge allowlist in the Resolve inputs step — no injection surface.

After merge

Dispatch channel=stable, version=4.0.5 (workflow_dispatch) to repopulate the stable index, then verify apt install wheels resolves on both amd64 and arm64.

Refs: wheels-dev/wheels#3218, wheels-dev/wheels#2838

…x clobber

The publish workflow syncs only pool/<dispatched-channel>/ from R2, but
regenerate-apt-metadata.sh hardcoded DISTRIBUTIONS="stable bleeding-edge"
and regenerated BOTH channels every run. The non-dispatched channel had an
empty local pool, so apt-ftparchive emitted an empty Packages, and the
upload step's `find dists` pushed that empty index to R2 — clobbering the
other channel's good index.

In practice every bleeding-edge snapshot publish wiped the stable index
minutes after each stable release (the .deb stays in the R2 pool; only the
dists Packages index dies). This is the root cause of #3218 (a recurrence
of #2838). The per-channel regen that #2838 was meant to introduce was
absent from the live script.

Fix: regen reads CHANNELS (default both, for manual full rebuilds when both
pools are present locally); the workflow passes CHANNELS=<dispatched channel>
so a publish touches only its own channel's dists. The other channel's R2
index is left untouched.

Refs: wheels-dev/wheels#3218, wheels-dev/wheels#2838

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: Peter Amiri <peter@alurium.com>
@bpamiri bpamiri merged commit a441a31 into main Jun 19, 2026
1 check passed
@bpamiri bpamiri deleted the peter/fix-cross-channel-index-clobber branch June 19, 2026 19:50
bpamiri added a commit that referenced this pull request Jun 19, 2026
…218 follow-up) (#6)

The cross-channel clobber (apt-wheels#5) was only half the #3218 story. The
other half is CDN caching: Cloudflare auto-caches by file extension, so a
published Packages.gz got edge-cached and kept being served after the next
publish rewrote it. Its hash no longer matched the freshly-regenerated Release,
so `apt-get update` rejected it with "File has unexpected size (29 != 1404)" —
exactly the symptom users keep reporting. The uncompressed Packages/Release are
extension-less (served DYNAMIC, never cached), which is why only the .gz broke
and why a cache-buster query masked it (different cache key -> origin).

Fix:
- Upload apt metadata (Release, Release.gpg, InRelease, Packages, Packages.gz)
  with `Cache-Control: no-store` so the edge never caches a stale index again.
  This alone prevents recurrence regardless of purge permissions.
- Upload .deb pool files with `public, max-age=31536000, immutable` — they're
  version-stamped and never change, so long edge caching is correct and fast.
- Add a best-effort Cloudflare purge step that evicts the just-published URLs,
  to unstick copies cached BEFORE no-store existed (the current legacy entry).
  Resolves the wheels.dev zone at runtime; warns and continues (does not fail
  the publish) if the token lacks Zone.Cache-Purge scope.

Refs: wheels-dev/wheels#3218, #5

Signed-off-by: Peter Amiri <peter@alurium.com>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
bpamiri added a commit to wheels-dev/wheels that referenced this pull request Jun 19, 2026
…gression guard) (#3234)

* ci: add index-integrity probe to distribution install smoke (#3218 regression guard)

The daily install-smoke installs the CLI per channel and asserts the version,
but that only samples once at 14:00 UTC. The #3218 cross-channel clobber empties
the stable apt index for most of the day (a bleeding-edge snapshot wipes it
minutes after each stable publish; it's only briefly populated), so a clobber
can land outside the install legs' sample window and slip past them — and when
they do fail, "apt install failed" doesn't name the cause.

Add a fast, container-free `index-integrity` job that probes the PUBLISHED apt
and yum dist indexes directly: stable must be non-empty and name the current GA
(per arch for apt), and bleeding-edge must stay non-empty (the clobber was
bidirectional — apt-wheels#5 scopes regen per-channel to prevent both
directions). An empty/missing/stale index fails with a message that names the
regression.

Reliability: fetches use `curl --retry ... --retry-all-errors` so a transient
blip can't false-red the guardian, and all grep checks feed from a here-string
(`grep -q PAT <<<"$body"`) rather than `printf | grep -q` — under `set -o
pipefail` the latter false-fails when grep -q early-exits and SIGPIPEs the
upstream printf of a 100KB index. Verified: 3/3 green against the live indexes,
and a negative test (bogus version) correctly fails.

Refs: #3218, wheels-dev/apt-wheels#5

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: Peter Amiri <petera@pai.com>

* ci: probe plain (edge) URLs + hash-consistency, mirroring apt/dnf exactly

Two corrections to the index-integrity job after finding the real #3218
failure mode is CDN edge-cache staleness, not just the origin clobber:

1. Drop the `?cb=` cache-buster. apt/dnf fetch the plain URLs that hit
   Cloudflare's edge; a cache-buster hit R2 origin instead, so the probe went
   green while real `apt install` failed on a stale edge-cached Packages.gz.
   Test the SAME plain URLs clients use.

2. Verify hash-consistency, exactly what the package managers verify: parse the
   SHA256 the (plain) Release records for each binary-<arch>/Packages.gz, fetch
   the (plain) Packages.gz, and compare — this is precisely the "File has
   unexpected size" check that failed in #3218. yum: verify the served
   primary.xml.gz matches the hash repomd references. Then gunzip and confirm
   the content lists the GA (catches an empty-but-internally-consistent index).

Binary correctness: gzip blobs are fetched to a temp file and hashed/gunzipped
from the file — never captured into a shell variable, since `$(...)` is
text-only and silently corrupts binary (strips trailing newlines, drops NULs),
which would compute a bogus hash.

Paired with apt-wheels#6 / yum-wheels#6 (set `no-store` on metadata so the edge
stops caching it). Verified against the live indexes: correctly RED on the
currently-stale apt stable amd64 edge, GREEN on arm64 / bleeding-edge / yum.

Refs: #3218, wheels-dev/apt-wheels#6, wheels-dev/yum-wheels#6

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: Peter Amiri <petera@pai.com>

---------

Signed-off-by: Peter Amiri <petera@pai.com>
Co-authored-by: Peter Amiri <petera@pai.com>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
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