One-line fbuild setup for consumer CI pipelines. Installs fbuild, wires actions/cache@v5 with sensible defaults, and exports FBUILD_CACHE_DIR plus ZCCACHE_DIR so subsequent steps Just Work.
For the underlying cache design (what's cached, why, and how to tune the key for your project) see ../../docs/CI_CACHING.md.
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: FastLED/fbuild/.github/actions/setup@main
with:
cache-key-extra: ${{ hashFiles('platformio.ini') }}
- run: fbuild build examples/Blink -e esp32devjobs:
build:
strategy:
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
board: [uno, esp32dev, esp32s3, teensy41]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v4
- uses: FastLED/fbuild/.github/actions/setup@main
id: fbuild
with:
cache-key-extra: ${{ matrix.board }}-${{ hashFiles('platformio.ini') }}
- run: fbuild build examples/Blink -e ${{ matrix.board }}
- name: Second build should be a near-no-op (smoke test cache warmth)
shell: bash
run: |
start=$SECONDS
fbuild build examples/Blink -e ${{ matrix.board }}
elapsed=$((SECONDS - start))
echo "second-build elapsed: ${elapsed}s"
test "$elapsed" -lt 15 || {
echo "::error::cache restore did not produce a warm build (took ${elapsed}s)"
exit 1
}For large board matrices, split mode keeps packages shared per platform family and build payloads per board (#1433). It needs an fbuild release with fbuild install.
- uses: FastLED/fbuild/.github/actions/setup@main
id: fbuild
with:
cache-mode: split
environments: ${{ matrix.board }}
cache-key-extra: ${{ hashFiles('platformio.ini') }}
save: ${{ github.event_name != 'pull_request' }}
- run: fbuild build examples/Blink -e ${{ matrix.board }}In split mode the action:
- Runs
fbuild install --dry-run --jsonto learn each environment's platform, without touching the network. - Restores the packages cache (
toolchains,platforms,packages,libraries,archives,installed,index.sqlite) by the prefixfbuild-pkgs-<cache-version>-<os>-<arch>-<family>-. - Runs
fbuild installas its own step, then saves the packages cache under that prefix plus thepackages_hash. It skips the save when the restored key already matches. - Restores the build-payload cache (
core,framework-libs,library-selection, the zccache store) keyed by fbuild hash, board andcache-key-extra, without falling back to other boards. It is saved at the end of the job unlesssaveisfalse.
The built-in cache: true wiring covers the fbuild package/tool cache rooted at FBUILD_CACHE_DIR. If you also want cross-run reuse of zccache's object store, add your own actions/cache@v5 step for the resolved zccache directory and your project build outputs:
- name: Setup fbuild
id: fbuild-setup
uses: FastLED/fbuild/.github/actions/setup@main
with:
cache-key-extra: ${{ matrix.board }}-${{ hashFiles('platformio.ini') }}
- name: Restore zccache store + project build outputs
uses: actions/cache@v5
with:
path: |
${{ steps.fbuild-setup.outputs.zccache-store-path }}
.fbuild/build
key: zccache-v1-${{ runner.os }}-${{ runner.arch }}-${{ steps.fbuild-setup.outputs.fbuild-hash }}-${{ matrix.board }}-${{ hashFiles('platformio.ini', 'rust-toolchain.toml') }}
restore-keys: |
zccache-v1-${{ runner.os }}-${{ runner.arch }}-${{ steps.fbuild-setup.outputs.fbuild-hash }}-${{ matrix.board }}-
zccache-v1-${{ runner.os }}-${{ runner.arch }}-${{ steps.fbuild-setup.outputs.fbuild-hash }}-
zccache-v1-${{ runner.os }}-${{ runner.arch }}-
- run: fbuild build examples/Blink -e ${{ matrix.board }}Use steps.<id>.outputs.zccache-store-path inside workflow expressions. The same path is also exported as ZCCACHE_DIR via $GITHUB_ENV, which is convenient for later shell steps:
- name: Print resolved cache paths
shell: bash
run: |
echo "fbuild cache: $FBUILD_CACHE_DIR"
echo "zccache store: $ZCCACHE_DIR"| Input | Default | Description |
|---|---|---|
fbuild-version |
latest |
PyPI version spec. Pin to an exact version (2.1.16) for reproducible CI. |
python-version |
3.10 |
Python used to install fbuild. Must be >= 3.10. |
cache |
true |
Set to false to install fbuild without wiring actions/cache. |
cache-mode |
combined |
combined keeps one FBUILD_CACHE_DIR entry per key. split restores a packages cache shared per platform family and a per-board build-payload cache, and runs fbuild install as its own step. |
save |
true |
false restores caches without saving; use it on pull requests. |
project-dir |
. |
Split mode: project passed to fbuild install. |
environments |
"" |
Split mode: space-separated environments to provision. Required in split mode. |
board |
"" |
Split mode: name in the build-payload cache key. Defaults to environments joined with _. |
cache-key-extra |
"" |
String baked into the cache key. Use hashFiles(...) over your graph inputs so edits invalidate stale artifacts. |
cache-version |
v1 |
Manual cache bump. Increment when you want to force-invalidate across your matrix. |
cache-dir |
$RUNNER_TEMP/fbuild-cache |
Override if you need a different cache root. |
install-cache-key-extra |
"" |
Extra string for the install cache (separate from the build artifact cache). Pass hashFiles('pyproject.toml') so a fbuild pin bump invalidates the cached binary. The install cache is intentionally board/platform-independent so every job in a matrix shares it. |
| Output | Description |
|---|---|
cache-hit |
Combined mode: true if the cache was restored from an exact key match, false otherwise. |
platform-family |
Split mode: the platform family the packages cache is shared across. |
packages-hash |
Split mode: packages_hash from fbuild install --json. |
packages-cache-hit |
Split mode: the restored packages cache key, empty on a miss. |
build-cache-hit |
Split mode: true if the build-payload cache was restored from an exact key match. |
cache-dir |
Resolved cache directory path. Useful for diagnostic steps. |
fbuild-hash |
sha256 prefix (16 hex chars) of the installed fbuild wheel's RECORD file. Baked into the cache key so any fbuild change, including a re-released wheel at the same version, invalidates stale cache artifacts. |
zccache-store-path |
Resolved zccache object-store directory. The same path is exported to later steps as ZCCACHE_DIR, so consumer-managed actions/cache@v5 blocks can reuse it without guessing platform-specific defaults. |
- Sets up Python.
- Resolves a stable fbuild cache directory under
$RUNNER_TEMP(survives across steps of the same job; not a surprise$HOMEpath) and the zccache store path the job will use for compiler-object caching. - Exports
FBUILD_CACHE_DIRandZCCACHE_DIRvia$GITHUB_ENVso every later step inherits them, and exposes the zccache location as thezccache-store-pathoutput. - Restores the install cache (binary + dist-info) into
$RUNNER_TEMP/fbuild-installkeyed by os/arch/python/pip-spec/install-cache-key-extra. On hit, the install step is skipped entirely. The key intentionally omits anything matrix-specific (board, OS-tag, etc.) so every job in a repo's matrix shares the cached binary. - Installs fbuild from PyPI at the requested version (skipped on install-cache hit). Install uses
pip install --target=$RUNNER_TEMP/fbuild-installso the cached directory is the entire install surface. - Activates the install dir by appending
bin/(POSIX) andScripts/(Windows) to$GITHUB_PATHand prependingPYTHONPATH. - Computes the installed fbuild's content hash (sha256 of its dist-info
RECORD) and bakes it into the build artifact cache key. This guarantees the artifact cache is tied to the exact fbuild you're running, not just the PyPI version string, solatestis safe and a re-released wheel won't poison the cache. - Restores (and on job-end, saves) the fbuild build artifact cache via
actions/cache@v5. Withsave: falseit only restores. Incache-mode: splitthis step is replaced by the packages and build-payload caches described in Split caches.
The fbuild cache stores toolchains, frameworks, and build outputs whose internal layout is tied to fbuild's own fingerprint format, response-file generation, and path embedding. If fbuild itself changes without the cache key changing, the restored cache can encode stale paths or obsolete fingerprints: silent cache poisoning.
Baking the wheel's RECORD hash into the key means:
fbuild-version: latestis reproducible-by-construction: a new release produces a new hash, which rolls the cache.- A re-uploaded wheel (same version, different contents) invalidates correctly.
- Per-platform wheel differences (manylinux vs. macOS vs. Windows) produce different hashes and do not cross-pollute caches.
It does not cache ~/.fbuild/*/daemon/ - that's ephemeral runtime state and restoring it across runs causes broken daemon discovery on the next client call. The action sidesteps the whole ~/.fbuild/ tree by redirecting fbuild's cache to $RUNNER_TEMP/fbuild-cache via FBUILD_CACHE_DIR.
It also does not automatically cache zccache's object store for you. That store lives at ZCCACHE_DIR / zccache-store-path, and consumers who want cross-run per-TU reuse should add their own actions/cache@v5 step as shown above.
For reproducible CI, reference a release tag rather than @main:
- uses: FastLED/fbuild/.github/actions/setup@v1At time of writing there is no v1 tag - use @main and pin fbuild-version to an explicit PyPI version if reproducibility matters.
docs/CI_CACHING.md- detailed design of the underlying cache, plus rawactions/cache@v5snippets for consumers who prefer to avoid the action dependency.- #101 - the issue that tracked creation of this action.