Commit graph build-cran-binaries/scripts
Author SHA1 Message Date
cfa552f54e
test(verify): gate on regressions against the generic slot, not fallback rate
A source-fallback percentage stopped being a readiness signal once
bincraft learned to keep a matching-minor generic binary out of a
fallback's shadow. What survives now is the case where the generic
binary was built under a different minor: unsafe for this client anyway,
so serving source is correct, just slow. Failing on that share would
block slots that are in fact ready - amd64/noble sits at 53% for 4.5 and
4.6 while regressing nobody.

Gate on the thing that decides it instead: packages this client would
receive as source through per-minor routing while the generic slot holds
a binary built under its own minor. That is strictly worse than not
routing, and must be zero. Fallback share is still printed, as context
rather than a verdict.

Measured zero across every reindexed slot and minor.
2026-08-31 09:35:28 +00:00
93d720a9e6
test(verify): fail a slot whose per-minor entries are mostly source fallbacks
A Path: target returning 200 is not the same as a per-minor binary
existing. Roughly 53% of steered entries are source fallbacks: they
resolve and install correctly, but they compile on the client, which is
not the binary service we advertise.

Entries without a Built: field are source fallbacks, so this is readable
straight from the index with no downloads.
2026-08-31 08:44:34 +00:00
771d3a7768
feat(edge): send unsupported R minors to CRAN instead of serving them
Falling back to the flat index for an excluded minor is the silent case:
risky packages there are built under another minor and fail at load time,
far from the cause. Send those clients to CRAN for sources instead, which
is what the router already does for an unidentifiable distro.

The whole interaction has to move, not just the index. R resolves tarball
URLs against the repo it was configured with, so serving the index from
CRAN and tarballs from here would hand R a binary where it expects a
source tarball.

A client reporting no R minor at all is not excluded: mirror scripts and
image builds keep getting the flat slot.

- Declare the supported window once in cdn.tf as local.rpkgs_supported_minors
  and set KNOWN_MINORS from it on both zones, so the router cannot drift
  from build-env-images' LATEST/PREV1/PREV2 unnoticed.
- Split the verification script into supported and excluded minors: the
  former must have published indexes, the latter must have none and must
  redirect to CRAN under --live.
2026-08-30 15:44:14 +00:00
4c1e9b773c
feat(edge): gate per-minor routing on published minors and add a staging zone
Enabling UNION_SLOTS today would break every client on an R minor we do
not publish. contribPath() redirects on any minor the User-Agent carries,
without checking that the target exists and without a fallback, and only
4.4, 4.5 and 4.6 are published: a 4.3 client would be sent to a 404 and
see no packages at all.

- Gate routing on KNOWN_MINORS, falling back to the flat index otherwise.
- Honour EXTRA_PUBLIC_HOSTS so the same script can run on a staging zone
  and redirect within itself instead of into production.
- Add the cran-rpkgs-test pull zone with UNION_SLOTS pre-enabled, served
  on the bunny default hostname so it needs no DNS record.
- Add scripts/verify-r-minor-routing.sh, covering all 16 slots: index
  reachability, the union property against flat, Path: target
  resolution, coverage parity across minors, and (--live) real
  User-Agent routing.
- Cover the fallback in the edge test suite.
2026-08-30 15:24:43 +00:00
9bded261ee fix(cdn): restore Alliance pull-zone hostname (#166)
All checks were successful
ci/crow/cron/process-updates/13 Pipeline was successful
ci/crow/cron/process-updates/10 Pipeline was successful
ci/crow/cron/process-updates/15 Pipeline was successful
ci/crow/cron/process-updates/14 Pipeline was successful
ci/crow/cron/process-updates/4 Pipeline was successful
ci/crow/cron/process-updates/16 Pipeline was successful
ci/crow/cron/process-updates/18 Pipeline was successful
ci/crow/cron/process-updates/11 Pipeline was successful
ci/crow/cron/process-updates/17 Pipeline was successful
ci/crow/cron/process-updates/12 Pipeline was successful
ci/crow/cron/process-updates/6 Pipeline was successful
ci/crow/cron/process-updates/5 Pipeline was successful
ci/crow/cron/process-updates/1 Pipeline was successful
ci/crow/cron/process-updates/2 Pipeline was successful
ci/crow/cron/process-updates/7 Pipeline was successful
ci/crow/cron/process-updates/8 Pipeline was successful
ci/crow/cron/process-updates/9 Pipeline was successful
ci/crow/cron/process-updates/3 Pipeline was successful
## Motivation

Applying #165 recreated the Alliance SwissPass pull zone without its custom hostname because the hostname association was not represented in OpenTofu.
The recreated zone also received a new numeric ID, making the weekly purge configuration stale.

## Changes

- Manage `cran.allianceswisspass.devxy.io` as a pull-zone hostname with TLS and forced HTTPS.
- Resolve the Alliance pull-zone ID from its hostname before purging instead of persisting a replaceable numeric ID.
- Install `jq` in the purge step for the Bunny API lookup.

## Verification

- Targeted `prek` hooks pass.
- `tofu validate` passes.
- `crow lint .crow/` passes.
- `just edge-test` passes all 14 routing steps.
- `bash -n scripts/purge_cdn_zone.sh` passes.

## Deployment

Run `tofu apply` to restore the Alliance hostname on the recreated pull zone.

Reviewed-on: #166
2026-08-13 14:13:04 +00:00
a1c1f5e78f fix(cdn): align repository routing across pull zones (#165)
## Motivation

`cran.rpkgs.com` and `cran.allianceswisspass.devxy.io` serve the same B2 repository through separate Bunny pull zones, but only the first zone was managed and purged after weekly reindexing.
This allowed the Alliance endpoint to retain stale repository metadata and left locked `renv` restores unable to retrieve versions whose binary archive object was absent.

## Changes

- Adopt the Alliance SwissPass pull zone `3265648` into OpenTofu and configure it with the shared B2 origin and middleware script.
- Purge both Bunny pull zones after the weekly rebuild reindex.
- Preserve the requested public hostname in middleware redirects.
- Redirect missing archived binaries to the corresponding CRAN source package, checking whether the version is archived or still current.
- Cover the existing archived-binary passthrough behavior in the edge routing matrix.

## Verification

- `prek run -a`
- `just edge-test`
- `crow lint .crow/`
- `tofu validate`
- `bash -n scripts/purge_cdn_zone.sh`

## Deployment

Run `tofu apply` to adopt pull zone `3265648`, publish the middleware release, and align both pull zones.
After the apply, rerun the Alliance SwissPass CI restore that requested `cli 3.6.5` and `AzureStor 3.7.1`.

Reviewed-on: #165
2026-08-13 14:08:10 +00:00
01b8ab43df fix(rebuild): re-index and purge the CDN after a rebuild (#160)
Some checks failed
ci/crow/cron/process-updates/10 Pipeline was successful
ci/crow/cron/process-updates/4 Pipeline was successful
ci/crow/cron/process-updates/13 Pipeline was successful
ci/crow/cron/process-updates/14 Pipeline was successful
ci/crow/cron/process-updates/15 Pipeline was successful
ci/crow/cron/process-updates/16 Pipeline was successful
ci/crow/cron/process-updates/11 Pipeline is running
ci/crow/cron/process-updates/18 Pipeline was successful
ci/crow/cron/process-updates/17 Pipeline was successful
ci/crow/cron/process-updates/12 Pipeline was successful
ci/crow/manual/weekly-audit-missing/6 Pipeline was successful
ci/crow/manual/weekly-audit-missing/5 Pipeline was successful
ci/crow/cron/process-updates/6 Pipeline failed
ci/crow/cron/process-updates/5 Pipeline was successful
ci/crow/cron/process-updates/1 Pipeline was successful
ci/crow/cron/process-updates/2 Pipeline was successful
ci/crow/cron/process-updates/7 Pipeline was successful
ci/crow/cron/process-updates/8 Pipeline was successful
ci/crow/cron/process-updates/9 Pipeline was successful
ci/crow/cron/process-updates/3 Pipeline was successful
ci/crow/manual/weekly-rebuild-missing/5 Pipeline was canceled
## Problem

The rebuild now works — `AATtools 0.0.3` was detected as a source fallback, built, and published:

```
ℹ `upload_single_binary()`: Replacing the CRAN source published for AATtools 0.0.3 … with the binary.
✔ Successfully uploaded package AATtools with tag 0.0.3.
```

But clients still get the source, and will for about a year:

```
$ curl -sI .../src/contrib/AATtools_0.0.3.tar.gz
etag: "ea8127d953ca6a2f118ea49441772af6"   # CRAN's source MD5
cdn-cache: HIT
cdn-cachedat: 08/09/2026 16:43:32          # predates the 18:12 upload
```

Two causes, both specific to a rebuild:

1. **The slot is never re-indexed.** `weekly-rebuild-missing` has no `upload_package_index` step, so the index keeps the old MD5 and — for anything that had been served from source — no `Built` stamp. This one self-heals at the next `process-updates` run.
2. **The tarball URL is never purged.** A normal update publishes new packages at *new* URLs, so `purge_cdn_cache.sh` only needs the five index files. A rebuild replaces an object *in place*, and the zone caches tarballs for `cache_expiration_time = 31919000` (~370 days). This does not self-heal.

Nothing about a stale package looks wrong from the outside, which is what makes it worth fixing rather than documenting.

## What this changes

**Re-index at the end of a rebuild**, flat and per-minor, mirroring the tail of `process-updates`. The codename is detected from the image's `/etc/os-release` (as `local/packages-to-build.R` already does) rather than adding `OS_ID` to all 18 matrix rows.

**Purge the zone afterwards**, via a new `scripts/purge_cdn_zone.sh`. One call to `POST /pullzone/{id}/purgeCache` covers every replaced object, and all three hostnames — `cran.devxy.io`, `cran.allianceswisspass.devxy.io`, `cran.rpkgs.com` — share pull zone `3857050`, confirmed from the `cdn-pullzone` response header.

Purging per URL was the alternative and is worse here: ~13.5k rate-limited calls per arch, where a single missed call leaves a package silently stale. The cost of the zone purge is a cold cache for everything else, which is why it stays out of the daily update path — `purge_cdn_cache.sh` is untouched.

The purge runs on failure too (`when: status: [success, failure]`): a rebuild that died part-way still replaced objects, and those are exactly the ones a stale edge keeps hiding.

## Verification

`crow lint .crow/` reports all ten configs valid; `bash -n` on the new script passes; prek hooks pass.

Not yet exercised against Bunny — it needs `BUNNYNET_API_KEY`, which is a CI secret. The failure mode is explicit rather than silent: any status other than 200/204 prints the response body and exits non-zero.

Reviewed-on: #160
2026-08-10 06:30:35 +00:00
228459c0ba fix(ci): actually purge BunnyCDN cache in purge_cdn_cache.sh (#78)
All checks were successful
ci/crow/cron/process-updates-alpine-323-arm64 Pipeline was successful
ci/crow/cron/process-updates-redhat-9-arm64 Pipeline was successful
ci/crow/cron/process-updates-ubuntu-2204-amd64 Pipeline was successful
ci/crow/cron/process-updates-ubuntu-2204-arm64 Pipeline was successful
ci/crow/cron/process-updates-ubuntu-2404-amd64 Pipeline was successful
ci/crow/cron/process-updates-ubuntu-2404-arm64 Pipeline was successful
ci/crow/cron/process-updates-redhat-10-amd64 Pipeline was successful
ci/crow/cron/process-updates-redhat-10-arm64 Pipeline was successful
ci/crow/cron/process-updates-alpine-322-amd64 Pipeline was successful
ci/crow/cron/process-updates-alpine-322-arm64 Pipeline was successful
ci/crow/cron/process-updates-redhat-9-amd64 Pipeline was successful
ci/crow/cron/process-updates-alpine-323-amd64 Pipeline was successful
ci/crow/cron/process-updates-redhat-8-arm64 Pipeline was successful
ci/crow/cron/process-updates-redhat-8-amd64 Pipeline was successful
## Summary

While cleaning this up I noticed the script **defined** `purge_cdn_cache()` but never **called** it. Every CI run only declared the helper and exited cleanly without issuing a single curl. The PACKAGES freshness on cran.devxy.io / cran.rpkgs.com has been carried entirely by R's `Cache-Control: no-cache` header on the index files.

Fixes in one shot:

1. **Actually run the purge.** The shifted args (`api_key`, `arch`, `os_id`, `domain…`) are now consumed inline and a curl POST is issued per (domain × resource).
2. **Drop `set -x`** — leaked the `AccessKey:` header into job logs.
3. **Drop the Python URL-encoder.** `curl -G --data-urlencode "url=…" --data "async=false" https://api.bunny.net/purge` does the same with no Python. The workflow purge steps can later switch from `alpine:3.23 + apk add bash curl` to a slimmer curl-only image.
4. **Add `src/contrib/Meta/archive.rds`** to the purged resource list. It's rewritten on every `process_cran_updates` run (see README) and was being served stale.

## Risks

- This is the **first time** the script actually purges anything. If anything else (e.g. a downstream service) relied on the no-op behavior, this PR is the moment it stops being silent. I don't see any such caller.
- Edge cache miss right after a purge means an origin S3 fetch — minor latency uptick on the first request per region per resource.

Reviewed-on: #78
2026-06-08 08:31:03 +00:00
b769276679
fix: bincraftr url for install
All checks were successful
ci/crow/cron/process-updates-alpine-320-arm64 Pipeline was successful
ci/crow/cron/process-updates-redhat-8-arm64 Pipeline was successful
ci/crow/cron/process-updates-redhat-8-amd64 Pipeline was successful
ci/crow/cron/process-updates-alpine-320-amd64 Pipeline was successful
ci/crow/cron/process-updates-ubuntu-2404-amd64 Pipeline was successful
ci/crow/cron/process-updates-redhat-9-arm64 Pipeline was successful
ci/crow/cron/process-updates-alpine-321-amd64 Pipeline was successful
ci/crow/cron/process-updates-ubuntu-2204-amd64 Pipeline was successful
ci/crow/cron/process-updates-redhat-9-amd64 Pipeline was successful
ci/crow/cron/process-updates-ubuntu-2404-arm64 Pipeline was successful
2025-05-29 17:15:05 +02:00
4dbe4daa4b
transparent purging 2025-03-26 15:57:42 +01:00
3e55e81a0e
feat: build-single for alpine321 2025-03-26 15:25:53 +01:00