This is the smallest useful GitHub Actions shape for a repository that wants to pilot GloriousFlywheel today.
It proves cache-backed local or CI execution on shared capability classes. It does not prove Bazel remote execution.
Before copying the workflow, decide these inputs with the operator:
| Input | Required | Notes |
|---|---|---|
| Shared runner label | yes | Use a capability class such as your-org-nix; do not mint a repo-specific label |
ATTIC_PUBLIC_KEY variable |
when using Nix cache trust | Public key only; token stays in secrets |
ATTIC_TOKEN secret |
only for trusted cache publication | Needed only when default-branch jobs should push Nix results |
| Front-door kit | when using Bazel | Import justfile.flywheel and .bazelrc.flywheel; ordinary pilots use the cache-backed mode |
| Bazel remote-cache endpoint | yes for Bazel cache pilots | Shared capability-class runners should receive it from bootstrap; hosted runners are not countable runner adoption |
| Bazel executor endpoint | only for approved RBE candidates | Separate from the cache endpoint; do not set it in the ordinary pilot workflow |
name: GloriousFlywheel Pilot
on:
push:
pull_request:
permissions:
contents: read
jobs:
nix:
runs-on: your-org-nix
steps:
- uses: actions/checkout@v6
- uses: tinyland-inc/GloriousFlywheel/.github/actions/nix-job@main
env:
ATTIC_TOKEN: ${{ secrets.ATTIC_TOKEN || '' }}
with:
attic-public-key: ${{ vars.ATTIC_PUBLIC_KEY || '' }}
command: nix flake check
push-cache: ${{ github.event_name == 'push' && github.ref == 'refs/heads/main' && secrets.ATTIC_TOKEN != '' && 'true' || 'false' }}
bazel:
runs-on: your-org-nix
steps:
- uses: actions/checkout@v6
- uses: tinyland-inc/GloriousFlywheel/.github/actions/nix-job@main
env:
ATTIC_TOKEN: ${{ secrets.ATTIC_TOKEN || '' }}
GF_BAZEL_REMOTE_UPLOAD: ${{ github.event_name == 'push' && github.ref == 'refs/heads/main' && 'true' || 'false' }}
with:
attic-public-key: ${{ vars.ATTIC_PUBLIC_KEY || '' }}
command: |
test -n "${BAZEL_REMOTE_CACHE:-}"
test "${GF_BAZEL_SUBSTRATE_MODE:-}" = "shared-cache-backed"
nix develop -c just flywheel-verify
nix develop -c just flywheel-build //...
push-cache: ${{ github.event_name == 'push' && github.ref == 'refs/heads/main' && secrets.ATTIC_TOKEN != '' && 'true' || 'false' }}The consuming repo should install and import the front-door kit:
flywheel-frontdoor-kit install --target . --patch-justfile --patch-bazelrcThe generated justfile.flywheel and .bazelrc.flywheel keep the consumer
surface endpoint-free. The packaged wrapper behind those recipes must:
- refuse cache-backed work when
BAZEL_REMOTE_CACHEis empty - require
GF_BAZEL_SUBSTRATE_MODE=shared-cache-backed - pass
--remote_cache="$BAZEL_REMOTE_CACHE"explicitly - set
--remote_upload_local_results=falsefor pull requests - accept optional
BAZEL_REPOSITORY_CACHE,BAZEL_DISTDIR, and declared local repository injections for external input authority work
Do not rely on a .bazelrc shell expansion such as
--remote_cache=$BAZEL_REMOTE_CACHE; Bazel rc files do not provide the same
preflight boundary as the wrapper.
The packaged wrapper also exposes an explicit executor-backed mode for approved
target-class proofs. That mode must require
GF_BAZEL_SUBSTRATE_MODE=executor-backed, both BAZEL_REMOTE_CACHE and
BAZEL_REMOTE_EXECUTOR, forced remote execution, no local fallback, and
runtime-injected auth/header values. Localhost cache or executor endpoints are
valid only for an operator-marked port-forward proof such as
GF_BAZEL_LOCAL_PROOF=port-forward.
A pilot is countable when a maintainer can show:
- the workflow uses a shared capability-class runner label
- pull requests fail fast if the Bazel cache endpoint is absent
- pull requests do not publish trusted Nix or Bazel cache results by default
- default-branch cache publication requires an explicit token or upload flag
- logs or wrapper output show
GF_BAZEL_SUBSTRATE_MODE=shared-cache-backed - no ordinary workflow sets
BAZEL_REMOTE_EXECUTOR - any executor-backed candidate lane uses a separate proof workflow or dispatcher and records nonzero remote execution evidence
Move from this workflow to an RBE proof only after a target class has:
- declared toolchain and source inputs
- no ambient host-path or interactive dependencies
- external repository inputs accounted for separately from action outputs
- a forced-execution proof, not only remote cache hits
- worker provenance and nonzero remote-process evidence
Until then, the correct product claim is shared cache acceleration, not broad remote execution.
Spoke CI should delegate executor-backed proof to GloriousFlywheel through the
ci-templates proof dispatcher. The spoke runner should not run the important
Bazel acceptance locally and call that RBE. The proof artifact must include
GloriousFlywheel's proof-result.json; only
countable_remote_execution=true with remote_processes > 0,
executor_attached=true, and a digest-pinned worker_image_digest is
countable RBE evidence. Remote cache hits alone remain cache acceleration.
For Vite, SvelteKit, Playwright, Puppeteer, and Vitest targets, the browser
runtime must be a declared input to the proof. A browser-backed RBE candidate
needs a pinned browser runtime authority, explicit executablePath use, and a
forced-execution proof with nonzero remote processes before it can be counted.
Do not add browser downloads to the remote action. Use package lifecycle environment such as:
lifecycle_hooks_envs = {
"puppeteer": {
"PUPPETEER_SKIP_DOWNLOAD": "true",
"PUPPETEER_SKIP_CHROMIUM_DOWNLOAD": "true",
},
"playwright": {
"PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD": "1",
},
}That maps to PUPPETEER_SKIP_DOWNLOAD=true,
PUPPETEER_SKIP_CHROMIUM_DOWNLOAD=true, and
PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD=1 in the install-time browser download
policy.
That shape is not broad Playwright, broad Puppeteer, Firefox, or deployed E2E support. WebKit is proved for one consumer static-smoke target class through the pinned worker WebKit runtime, not broadly. It is only a candidate proof shape for target classes that launch a declared browser path without action-time browser installation.