Downstream Migration Checklist

Downstream Migration Checklist

Use this guide when a downstream GitHub Actions repo is moving onto the current GloriousFlywheel runner and cache contract.

This is the shortest honest checklist for the tranche-1 migration lane tracked in #210, and the same operator path should be used for any later promotion candidate surfaced by just orgwide-enrollment-queue.

Treat this as a bounded migration guide. It is not a claim that shared-label configuration automatically means accessible runners, broad authority, or implemented hosted fallback behavior.

Who This Is For

  • org repos that want to use shared capability runner labels
  • user-owned repos that still need owner-scope cleanup such as XoxdWM
  • downstream repos that want GloriousFlywheel composite actions without manual platform archaeology

Current Downstream Contract

Shared runner labels:

Label Use when
tinyland-docker general CI, lint, test, and non-Docker builds
tinyland-nix Nix builds and reproducible flake workflows
tinyland-nix-heavy memory-heavy Rust/Nix jobs when the additive heavy lane is available
tinyland-dind Docker-in-Docker or privileged container build paths

Composite actions:

Action Use when
tinyland-inc/GloriousFlywheel/.github/actions/setup-flywheel@main you only want cache/runtime auto-configuration
tinyland-inc/GloriousFlywheel/.github/actions/docker-job@main you want a simple shared-runner job with Bazel cache configured
tinyland-inc/GloriousFlywheel/.github/actions/nix-job@main you want a Nix job with cache setup handled for you

Default stance:

  • prefer shared runner labels first
  • use composite actions when you want the cache/runtime setup packaged for you
  • treat GloriousFlywheel’s own first-party validation and security scans as dogfood gates; they should run on shared tinyland-* lanes, not GitHub-hosted runners
  • do not depend on legacy Jesssullivan/GloriousFlywheel/... paths
  • do not create repo-specific runner labels as the normal migration answer
  • do not solve GitHub owner-scope gaps inside a downstream repo workflow; route them through the owner implementation overlay or mark the repo blocked
  • treat the current tinyland-* proof surfaces as primary when describing the live GitHub runner contract

Current Queue Boundary

The live rollout queue now starts from just orgwide-enrollment-queue, not from stale issue comments or fixed exemplar lists.

That queue should distinguish:

  • real current authority consumers
  • hybrid-by-policy repos
  • blocked or nominal shared-label repos
  • hosted template consumers that should stay out of runner-enrollment claims
  • current examples include tinyland.dev as mostly hosted on main, betterkvm as a platform-prereq repo, XoxdWM as toggle-gated, and yt-text as hybrid by policy with tubebrain-nix tracked separately as a standalone compatibility lane pending owner-overlay rehome or retirement

Checklist

  1. Inventory your workflows. Identify which jobs are general CI, Nix builds, or container builds.

  2. Map each job to one shared runner label.

    For Nix jobs, choose explicitly between:

    • tinyland-nix for the ordinary 8Gi baseline lane
    • tinyland-nix-heavy for recurring memory-heavy jobs when that additive lane is deployed
  3. Decide whether the repo needs composite actions. If plain shell steps are enough, labels alone are acceptable.

  4. Remove or replace any stale personal upstream references. Prefer tinyland-inc/GloriousFlywheel/.github/actions/...@main.

  5. Validate cache expectations. tinyland-nix gets Attic defaults; tinyland-docker and tinyland-nix get Bazel cache defaults on self-hosted runners. Reachable self-hosted cache and cluster-local DNS are part of the proof contract, not optional polish. For Bazel jobs, fail early when BAZEL_REMOTE_CACHE is missing on a dogfood lane instead of silently falling back to heavy local work. Keep package pull requests read-only for Bazel remote-cache writes; allow uploads only on trusted push jobs that explicitly set GF_BAZEL_REMOTE_UPLOAD=true.

  6. Validate self-hosted Nix bootstrap. For raw nix commands on tinyland-nix, use DeterminateSystems/determinate-nix-action@v3 or the nix-job composite action instead of assuming Nix is preinstalled on the runner forever.

  7. Run one branch-scoped proof workflow on the shared runner path before converting the full repo.

  8. Validate local development entrypoints. If the repo has a flake.nix, copy the shape in examples/flake/flake.nix and examples/flake/.envrc: local sessions should enter through direnv allow or nix develop, derive GF_BAZEL_SUBSTRATE_MODE from BAZEL_REMOTE_CACHE, and avoid presenting compatibility-local-only as GloriousFlywheel-backed execution.

  9. Keep a rollback path ready. A downstream job can temporarily return to ubuntu-latest or another GitHub-hosted runner while its shared-runner contract is being stabilized.

    This rollback language is for downstream migration only. It is not an acceptable fallback for GloriousFlywheel’s own merge-blocking validation, security scans, Bzlmod/Bazel canaries, or RBE proof/chaos workflows. The reusable Nix workflow now uses the same nix-job substrate path as the direct action, but it still does not implement automatic failover back to hosted runners.

Canonical Consumer Pattern

This is the reference downstream shape for a repo that wants one cache-backed Bazel job and one Nix job:

name: CI

on:
  push:
  pull_request:

permissions:
  contents: read

jobs:
  bazel:
    runs-on: tinyland-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 bash ./scripts/gloriousflywheel-bazel.sh build //...
          push-cache: ${{ github.event_name == 'push' && github.ref == 'refs/heads/main' && secrets.ATTIC_TOKEN != '' && 'true' || 'false' }}

  build:
    runs-on: tinyland-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 build .#default
          push-cache: ${{ github.event_name == 'push' && github.ref == 'refs/heads/main' && secrets.ATTIC_TOKEN != '' && 'true' || 'false' }}

Why this is the reference pattern:

  • it uses stable shared labels
  • it passes ATTIC_PUBLIC_KEY for public-read cache trust and ATTIC_TOKEN only when private reads or trusted writes are required
  • it uses the current public GloriousFlywheel action path
  • it proves Bazel cache attachment before running the repo-managed cache-backed Bazel wrapper
  • it leaves Bazel remote-cache writes disabled unless a trusted push job explicitly opts into GF_BAZEL_REMOTE_UPLOAD=true
  • it keeps pull requests read-only for Attic and only enables Nix cache publication on trusted default-branch pushes with an ATTIC_TOKEN
  • the reusable Nix workflow follows this same nix-job cache/bootstrap path for nix flake check, nix flake show, and nix build

Verification

After the first shared-runner migration:

  1. confirm the job lands on the expected runner label
  2. confirm the workflow still passes without hidden repo-local assumptions
  3. confirm Nix jobs see the expected cache environment
  4. confirm Bazel jobs see BAZEL_REMOTE_CACHE and GF_BAZEL_SUBSTRATE_MODE=shared-cache-backed
  5. compare runtime against the previous GitHub-hosted baseline

Rollback

If the shared-runner path is not ready yet:

  • for a downstream repo only, move the affected job back to ubuntu-latest
  • keep the consumer workflow change small and reversible
  • leave the rest of the repo on the previous runner path until the shared contract is fixed
  • do not use this rollback for GloriousFlywheel’s own merge-blocking validation or security workflows
  • do not describe that rollback as a supported automatic platform behavior

See Also

GloriousFlywheel