diff --git a/.github/workflows/_nix-build.yml b/.github/workflows/_nix-build.yml new file mode 100644 index 0000000..f3fe983 --- /dev/null +++ b/.github/workflows/_nix-build.yml @@ -0,0 +1,73 @@ +# Reusable: Nix Build (macOS) +# Builds a Nix flake on macOS with store caching. +# Default build command targets home-manager; override for darwin-rebuild. +name: _nix-build + +on: + workflow_call: + inputs: + build-command: + description: "Build command to run (default: home-manager build)" + required: false + type: string + default: "nix build .#homeConfigurations.$(whoami).activationPackage --print-build-logs" + +permissions: {} + +concurrency: + group: nix-build-${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + build: + name: Build + runs-on: macos-latest + permissions: + contents: read + steps: + - name: Checkout + uses: actions/checkout@v6 + + # determinate-nix-action@v3 is ONLY a Nix installer (replaced nix-installer-action@v21). + # It does NOT include flake checking — flake validation is in _nix-validate.yml. + - name: Install Nix + uses: DeterminateSystems/determinate-nix-action@v3 + with: + extra-conf: | + max-jobs = auto + cores = 0 + http-connections = 50 + connect-timeout = 5 + stalled-download-timeout = 10 + narinfo-cache-positive-ttl = 86400 + fallback = true + + - name: Restore Nix Store Cache + uses: actions/cache/restore@v5 + id: nix-cache + with: + path: | + /nix/store + ~/.cache/nix + key: nix-macos-${{ runner.os }}-${{ hashFiles('flake.lock') }} + restore-keys: | + nix-macos-${{ runner.os }}- + + - name: Build + run: ${{ inputs.build-command }} + + - name: Garbage Collect Nix Store + if: github.event_name == 'push' && github.ref == 'refs/heads/main' && steps.nix-cache.outputs.cache-hit != 'true' + run: | + echo "Store size before GC: $(du -sh /nix/store | cut -f1)" + nix-collect-garbage --delete-older-than 1d + echo "Store size after GC: $(du -sh /nix/store | cut -f1)" + + - name: Save Nix Store Cache + if: github.event_name == 'push' && github.ref == 'refs/heads/main' && steps.nix-cache.outputs.cache-hit != 'true' + uses: actions/cache/save@v5 + with: + path: | + /nix/store + ~/.cache/nix + key: nix-macos-${{ runner.os }}-${{ hashFiles('flake.lock') }} diff --git a/.github/workflows/_nix-validate.yml b/.github/workflows/_nix-validate.yml new file mode 100644 index 0000000..482933a --- /dev/null +++ b/.github/workflows/_nix-validate.yml @@ -0,0 +1,107 @@ +# Reusable: Nix Validate +# Runs `nix flake check` for the current system, optionally with --all-systems +# to catch packages broken on darwin from a linux runner before they merge. +name: _nix-validate + +on: + workflow_call: + inputs: + runner_label: + description: >- + GitHub Actions runner label to run the validate job on. Defaults to + ubuntu-latest. Pass a RunsOn label (see runs-on.com job labels docs) + to opt the calling repo into self-hosted runners. + type: string + required: false + default: ubuntu-latest + all_systems: + description: >- + Pass --all-systems to nix flake check to evaluate outputs for every + declared system from the runner. Defaults true. Set false for repos + whose check derivations are platform-specific (require darwin + binaries to build), since `nix flake check --all-systems` attempts + to build cross-platform checks and fails with "platform mismatch". + type: boolean + required: false + default: true + +concurrency: + group: nix-validate-${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +permissions: + contents: read + +jobs: + validate: + name: Validate + runs-on: ${{ inputs.runner_label }} + # Hard cap so a wedged `nix flake check` (network hang, runner stall) can't + # block `Merge Gate` indefinitely. Queue-stuck handling lives in the + # _ci-gate watchdog; this is the running-job side of defense in depth. + timeout-minutes: 30 + steps: + - name: Checkout + uses: actions/checkout@v6 + + # Free ~30GB on the ubuntu-latest runner so `nix flake check --all-systems` + # has space to substitute darwin source paths (rustc-src, cctools, + # apple-sdk, etc.) without hitting "No space left on device". Only needed + # when all_systems is true; gated to ubuntu-* runners so self-hosted + # runner long-lived state is never modified. + - name: Free disk space + if: inputs.all_systems && startsWith(inputs.runner_label, 'ubuntu-') + uses: jlumbroso/free-disk-space@54081f138730dfa15788a46383842cd2f914a1be # v1.3.1 + with: + tool-cache: false + android: true + dotnet: true + haskell: true + large-packages: false + swap-storage: false + docker-images: true + + # determinate-nix-action@v3 is ONLY a Nix installer (replaced nix-installer-action@v21). + # It does NOT include flake checking — that was a separate action (flake-checker-action). + # Flake evaluation is handled by `nix flake check` below, not by the installer. + - name: Install Nix + uses: DeterminateSystems/determinate-nix-action@v3 + with: + extra-conf: | + max-jobs = auto + cores = 0 + http-connections = 50 + connect-timeout = 5 + stalled-download-timeout = 10 + narinfo-cache-positive-ttl = 86400 + fallback = true + log-lines = 1000 + + - name: Restore Nix Store Cache + uses: actions/cache/restore@v5 + id: nix-cache + with: + path: | + /nix/store + ~/.cache/nix + key: nix-linux-${{ runner.os }}-${{ hashFiles('flake.lock') }} + restore-keys: | + nix-linux-${{ runner.os }}- + + - name: Check flake + # --all-systems evaluates outputs for every declared system to catch + # darwin-only broken packages from the linux runner. Disabled by setting + # `all_systems: false` for repos whose checks build platform-specific + # derivations (require darwin binaries). + env: + ALL_SYSTEMS_FLAG: ${{ inputs.all_systems && '--all-systems' || '' }} + run: nix flake check $ALL_SYSTEMS_FLAG --print-build-logs --show-trace --keep-going + + - name: Save Nix Store Cache + if: github.event_name == 'push' && steps.nix-cache.outputs.cache-hit != 'true' + uses: actions/cache/save@v5 + with: + path: | + /nix/store + ~/.cache/nix + key: nix-linux-${{ runner.os }}-${{ hashFiles('flake.lock') }} diff --git a/zizmor.yml b/zizmor.yml index 80604b7..0104b58 100644 --- a/zizmor.yml +++ b/zizmor.yml @@ -10,10 +10,16 @@ # Trusted publishers below are allowed to use `ref-pin` (tag or branch # references). Everything else falls through to zizmor's implicit # `hash-pin` default. +# +# `dryvist/*` is the org itself: first-party reusable workflows (e.g. +# dryvist/.github's `_nix-validate.yml`, dryvist/ai-workflows) are +# referenced via `@main` per the self-reference convention, so they must +# be trusted here rather than SHA-pinned. rules: unpinned-uses: config: policies: actions/*: ref-pin DeterminateSystems/*: ref-pin + dryvist/*: ref-pin googleapis/*: ref-pin