Skip to content

ci: point the dl cache at the directory buildroot actually uses - #2355

Merged
widgetii merged 2 commits into
masterfrom
ci/dl-cache-real-directory
Sep 2, 2026
Merged

widgetii merged 2 commits into
masterfrom
ci/dl-cache-real-directory

Conversation

@widgetii

@widgetii widgetii commented Sep 2, 2026

Copy link
Copy Markdown
Member

Problem

build.yml and build-one.yml cache output/dl and prune moving-ref tarballs from it. Buildroot
has never written there. DL_DIR defaults to $(TOPDIR)/dl, and TOPDIR is the source tree the
Makefile hands to -C, not O=:

$ make -C output/buildroot-2024.02.10 O=$PWD/output BR2_EXTERNAL=$PWD/general -p | grep '^DL_DIR'
DL_DIR := /home/dima/git/firmware/output/buildroot-2024.02.10/dl

So output/dl never existed. Two things followed, both silent:

  • actions/cache saved nothing. It only archives paths that exist, so the dl-<month> key was
    never populated and never restored. Every board has been re-downloading every source tarball,
    every run.
  • The refresh step matched nothing. find output/dl … 2>/dev/null || true swallowed the missing
    directory, so the moving-ref protection it exists to provide has never once run.

The two cancel out to "CI is correct but pays a full download for every board on every run" — which
is also why CI escaped the stale rolling tarball that bit local from-source builds in #2352. Correct
by accident, and only while both halves stayed broken.

What this does

Sets BR2_DL_DIR in the environment so buildroot writes to the directory the cache already keys on,
rather than teaching three more call sites to spell output/buildroot-$(BR_VER)/dl and keep it in
step with BR_VER. Buildroot reads it ahead of .config by design — "To make sure that the
environment variable overrides the .config option, set this before including .config"
— and the
Makefile's expiry from #2352 already prefers $(BR2_DL_DIR) when set:

$ BR2_DL_DIR=$PWD/output/dl make -C output/buildroot-2024.02.10 O=$PWD/output ... -p | grep '^DL_DIR'
DL_DIR := /home/dima/git/firmware/output/dl

Both workflows are fixed; build-one.yml carries the same two steps and the same bug.

Why turning the cache on is safe

Restoring the cache also makes the refresh step live for the first time, and that is the half that
keeps it safe — neither should be restored without the other. Its regex covers the moving-ref
class this tree actually produces:

form example matched
_VERSION = HEAD (24 packages) ipctool-HEAD.tar.gz yes
majestic-webui rolling asset majestic-webui-dist.tar.gz yes
majestic S3 tarball majestic.hi3516cv500.lite.master.tar.bz2 yes
vendor SDKs hisilicon-opensdk-<sha>.tar.gz immutable
everything else semver-named immutable

Measured against a real 201-file dl from local builds: 9 files are moving-ref and get re-fetched,
192 are content-addressed and cache correctly. No _VERSION in the tree pins a branch outside
HEAD/master/main/dist, so nothing moving escapes the regex.

The suppression goes too

2>/dev/null || true is how this hid for months. A cold cache is a legitimate state and now says
so out loud; anything else is a real error that reaches the log.

Verification

$ python3 .github/scripts/lint-workflow-shell.py
checked 56 run block(s)
all run blocks parse clean

$ python3 .github/scripts/build-summary.py --self-test
build-summary: self-test ok (4 grammars, 8 annotations in build.yml, 99 fixture boards)

The DL_DIR values above are the real check: buildroot itself, asked before and after, reporting the
directory it will use.

Scope

CI plumbing. ci-matrix.py --stdin selects the smoke set for build.yml; no image content changes —
the same sources are built, they are merely fetched once instead of every run.

build.yml and build-one.yml cache `output/dl` and prune moving-ref tarballs
from it. Buildroot never wrote there. DL_DIR defaults to $(TOPDIR)/dl, and
TOPDIR is the source tree the Makefile passes to -C, so downloads landed in
output/buildroot-$(BR_VER)/dl:

  $ make -C output/buildroot-2024.02.10 O=$PWD/output -p | grep '^DL_DIR'
  DL_DIR := /home/dima/git/firmware/output/buildroot-2024.02.10/dl

output/dl therefore never existed. actions/cache only archives paths that
exist, so the cache saved nothing and restored nothing, and the refresh find
matched nothing -- both silently, the find because of its own
`2>/dev/null || true`. The two failures cancelled to "every board re-downloads
every tarball, every run", which is also why CI never hit the stale rolling
tarball that #2352 fixed for local builds.

Setting BR2_DL_DIR in the environment moves buildroot to the directory the
cache already keys on, rather than teaching three more places to spell
output/buildroot-$(BR_VER)/dl. Buildroot reads it ahead of .config by design,
and the Makefile's expiry from #2352 already prefers $(BR2_DL_DIR):

  $ BR2_DL_DIR=$PWD/output/dl make -C ... -p | grep '^DL_DIR'
  DL_DIR := /home/dima/git/firmware/output/dl

Turning the cache on makes the refresh live for the first time, which is the
half that keeps it safe -- do not restore one without the other. Its regex
covers the moving-ref class this tree actually produces: 24 packages pin
_VERSION = HEAD and download as <pkg>-HEAD.tar.gz, majestic-webui as
-dist.tar.gz, majestic's S3 tarball as .master.tar.bz2. Everything else in a
real 201-file dl is semver- or SHA-named and immutable.

The suppression goes with it. A cold cache is legitimate and now says so;
anything else is an error that reaches the log.
@qodo-free-for-open-source-projects

Copy link
Copy Markdown

PR Summary by Qodo

Fix Buildroot download caching in CI workflows

🐞 Bug fix ⚙️ Configuration changes 🕐 10-20 Minutes

Grey Divider

AI Description

• Directs Buildroot downloads into output/dl so GitHub Actions caches real artifacts.
• Refreshes moving-reference archives while reporting cold caches and surfacing unexpected failures.
• Applies consistent cache behavior across matrix and single-board firmware workflows.
Diagram

graph TD
  A["Prepare firmware"] --> B["Set BR2_DL_DIR"] --> C["Restore dl cache"] --> D{"Cache exists?"}
  D -->|Yes| E["Prune moving refs"] --> F["Buildroot build"]
  D -->|No| G["Log cold cache"] --> F
Loading
High-Level Assessment

The following are alternative approaches to this PR:

1. Cache Buildroot's default versioned directory
  • ➕ Avoids overriding Buildroot's download directory through the environment.
  • ➕ Keeps downloads in Buildroot's default source-tree location.
  • ➖ Couples workflow cache and pruning paths to BR_VER.
  • ➖ Requires synchronized path changes across multiple workflow call sites.
  • ➖ Makes sharing archives across Buildroot version directories less direct.

Recommendation: Keep the PR's BR2_DL_DIR approach. It uses Buildroot's supported override mechanism, preserves the existing stable cache path, and avoids duplicating version-sensitive directory construction across workflows. Caching the default directory would work but introduces unnecessary coupling to BR_VER.

Files changed (2) +42 / -6

Bug fix (2) +42 / -6
build-one.ymlConnect single-board builds to the real download cache +14/-3

Connect single-board builds to the real download cache

• Exports 'BR2_DL_DIR' so Buildroot downloads into the cached 'output/dl' directory. The refresh step now handles cold caches explicitly and surfaces unexpected 'find' failures instead of suppressing them.

.github/workflows/build-one.yml

build.ymlActivate download caching for matrix firmware builds +28/-3

Activate download caching for matrix firmware builds

• Points Buildroot at 'output/dl', aligning its download location with the GitHub Actions cache. Moving-reference pruning now uses the configured path, reports cold caches, and no longer hides operational errors.

.github/workflows/build.yml

@qodo-free-for-open-source-projects

qodo-free-for-open-source-projects Bot commented Sep 2, 2026 •

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (1) 📘 Rule violations (0) 📎 Requirement gaps (0) 🎨 UX issues (0) 🔗 Cross-repo conflicts (0) 📜 Skill insights (0)

Grey Divider


Remediation recommended

1. Matrix cache freezes partial downloads 🐞 Bug ➹ Performance
Description
Every board now writes downloads into a cache using the same monthly key, so concurrent cold-cache
jobs race to create one immutable archive containing only the winning board's dependencies. Packages
required only by other boards are then downloaded again on every build for the rest of the month
because exact-key cache hits cannot save their additions.
Code

.github/workflows/build.yml[302]

+          echo "BR2_DL_DIR=${GITHUB_WORKSPACE}/output/dl" >> ${GITHUB_ENV}
Evidence
The build job fans out over a board matrix, exports one shared BR2_DL_DIR, and later invokes
make with a different matrix platform in each job. All those jobs use the board-independent dl-
key, while the workflow explicitly documents that the resulting snapshot is frozen because the cache
saves only on a key miss; build-one.yml uses that same key as well.

.github/workflows/build.yml[274-302]
.github/workflows/build.yml[318-339]
.github/workflows/build.yml[413-423]
.github/workflows/build-one.yml[54-67]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Activating `BR2_DL_DIR` makes every matrix job use the same save-capable monthly download cache. The first completed job freezes a board-specific subset under that key, preventing other boards from persisting their additional dependencies.
## Issue Context
The workflow itself documents that `actions/cache` saves only on a key miss and leaves the snapshot frozen. `build-one.yml` also shares this key namespace, creating additional contention.
## Fix Focus Areas
- .github/workflows/build.yml[302-332]
- .github/workflows/build-one.yml[54-67]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

Tip of the day
💡 Did you know, you can turn on the rule miner and Qodo learns your standards from review history

More tips ↗ | Customize Qodo ↗ | Qodo docs ↗

Grey Divider

Qodo Logo

Comment thread .github/workflows/build.yml
Review on #2355: `dl-<month>` is a single key shared by every board, and
actions/cache saves only on a key miss, so the first job to finish freezes that
month's snapshot. build-one.yml builds ONE board on manual dispatch. Letting it
win that race would pin the shared cache to one board's dependency set for the
rest of the month, which is a worse outcome than the partial snapshot the
matrix itself produces.

Restore-only there. It consumes the cache; it does not get to define it.

The matrix keeps the one shared key on purpose. Per-board dl caches would be
the obvious alternative and are the wrong trade here: the repo already holds
197 active caches totalling 15.3 GB, because ccache is keyed per board, and
those entries buy far more build time than a per-board download set would. A
second per-board series would evict them.
@widgetii

widgetii commented Sep 2, 2026

Copy link
Copy Markdown
Member Author

Confirmed on this PR's own run

From Firmware (hi3516ev200_lite) in run 33591336839:

BR2_DL_DIR: /home/runner/work/firmware/firmware/output/dl
Cache restored from key: hi3516ev200_lite-09          <- ccache, per-board, working
Cache not found for input keys: dl-09, dl-            <- dl cache genuinely empty
dl cache is cold (/home/runner/work/firmware/firmware/output/dl absent) -- nothing to refresh.
...
cd .../output/build/host-tar-1.34 && gzip -d -c .../output/dl/tar/tar-1.34.cpio.gz | cpio -i ...

Four things this shows:

  1. BR2_DL_DIR reaches the build.
  2. Cache not found for input keys: dl-09, dl- — the dl cache really has never been written, which is
    the bug this PR fixes. The ccache beside it restores fine, so the cache machinery itself is healthy.
  3. dl cache is cold ... nothing to refresh — the de-silenced branch saying out loud what
    2>/dev/null || true used to swallow.
  4. Buildroot now downloads into output/dl — the last line is it unpacking output/dl/tar/…. The
    redirect works end to end.

The cache is restore-only on pull requests by design, so this run does not populate it; the nightly
(a push/schedule event) is what will save the first snapshot.

@widgetii
widgetii merged commit 5b67a07 into master Sep 2, 2026
32 of 33 checks passed
@widgetii
widgetii deleted the ci/dl-cache-real-directory branch September 2, 2026 04:44
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