Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
73 changes: 73 additions & 0 deletions .github/workflows/_nix-build.yml
Original file line number Diff line number Diff line change
@@ -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') }}
107 changes: 107 additions & 0 deletions .github/workflows/_nix-validate.yml
Original file line number Diff line number Diff line change
@@ -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') }}
6 changes: 6 additions & 0 deletions zizmor.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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