OpenIPC/firmware/.github/workflows/build.yml has a `output/dl` cache
step that mirrors the Buildroot download directory across runs. This
saves both runner-minutes and — importantly — masks transient upstream
mirror failures: once any cron has cached the canonical tarball for a
package, every subsequent run reuses it regardless of whether the
primary URL is healthy that day.
builder's master.yml + build-one.yml had no equivalent. Each cron run
fully re-downloads the entire dl/ tree, so every package's upstream
must be healthy in every run. Hit hard 2026-05-24 by
wireguard-tools-1.0.20210914.tar.xz: it's served from a cgit
`snapshot/` URL on git.zx2c4.com which regenerates the tarball
per-request with shifting sha256, and `sources.buildroot.net` also
served drifted bytes — see kaeru
'wireguard-zx2c4-tarball-regeneration-hash-drift'. Result: random
matrix entries fail with hash mismatches each cron, with selection
roulette-style (93 boards enable wireguard; on any given run a few
roll snake-eyes on the download).
Mirror firmware's pattern with one wrinkle: builder.sh does
`rm -rf openipc` at the top of every run to force a clean re-clone of
firmware, which would obliterate `openipc/output/dl/` if we cached the
default path. Cache `/tmp/builder-dl` instead (outside the openipc
tree) and point Buildroot at it via BR2_DL_DIR env var, honored per
Buildroot manual section "Location of source tarballs". Same restore +
save lifecycle as firmware's, same monthly key shape.
Effect:
- First cron after merge: full download as usual; warms /tmp/builder-dl.
- Subsequent crons: instant cache hit for unchanged packages; only
re-download for version bumps. Removes the wireguard-roulette class
of failure entirely once cache is populated.
build-one.yml gets the same treatment so single-board dispatches share
the cache pool.
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Mirror the OpenIPC/firmware nightly redesign (PRs #2111-#2129) to
builder. This commit is PR-Bld-A: the build.yml-side changes only.
Subsequent PRs add the manifest aggregator + retention sweep
(PR-Bld-B) and the build-one.yml commit input (PR-Bld-C).
Changes:
1. **Preflight SHA gate**. New `preflight` job reads `sha=` from the
rolling `nightly` release body. On schedule events it short-
circuits the matrix when HEAD matches the previously-published
SHA; PRs and workflow_dispatch always build. Mirrors firmware's
#2111.
2. **Three release tags per build**:
- `nightly-YYYYMMDD-<short>` (prerelease, content-addressable,
the unit of history — 90-day retention via PR-Bld-B's
cleanup.yml).
- `nightly` (rolling channel pointer; body carries sha/short/
built_at).
- `latest` (legacy alias, kept one cycle so existing sysupgrade
clients hitting the old hardcoded URL don't break).
3. **`BUILD_ID` / `BUILD_SHA` / `BUILD_PLATFORM` env** exported at
the job level from preflight outputs. firmware's rootfs_script.sh
(which builder.sh reuses via its clone of firmware) picks BUILD_ID
+ BUILD_SHA into /etc/os-release automatically. BUILD_PLATFORM is
set to the matrix entry (e.g. `gk7205v200_fpv_caddx-fly`) and is
forward-compatible with Phase 3 of the mirror plan (the firmware-
side rootfs_script.sh update that emits BUILD_PLATFORM as an
os-release field).
4. **Retry budget** around `bash builder.sh`. Matches firmware's
`30 60 120 300 600 1200` ladder, covering toolchain/CDN flakes
(#2115 + #2129 in firmware).
5. **Telegram caption** leads with `Build: <build_id>`; obsolete
`Tag: ${TAG_NAME}` line gone; workflow-level `env: TAG_NAME=
latest` removed.
No on-device firmware changes; builder cameras keep their existing
sysupgrade legacy URL until Phase 2 of the mirror (sysupgrade variant
routing) lands in OpenIPC/firmware.
See ~/.claude/plans/mirror-nightly-redesign-to-builder.md.
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Rationale:
End users might not know exactly what hardware they have, they just want to find RubyFPV firmware options, so in the release page, in the list of released firmware packages, it makes sense to have all RubyFPV related configs in a single place in the list (so all start with the same RubyFPV prefix).