feat(cli): adds init/add-host scaffold with sops-nix safety gates #105
Workflow file for this run
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: PR Checks | |
| on: | |
| push: | |
| branches: [main] | |
| pull_request: | |
| permissions: | |
| contents: read | |
| jobs: | |
| lint-and-test: | |
| runs-on: macos-latest | |
| steps: | |
| - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7 | |
| - uses: astral-sh/setup-uv@11f9893b081a58869d3b5fccaea48c9e9e46f990 # v8.3.2 | |
| with: | |
| python-version: "3.13" | |
| - name: Install dependencies | |
| run: make install | |
| - name: Lint | |
| run: make lint | |
| - name: Type check | |
| run: make typecheck | |
| - name: Test | |
| run: make test | |
| # Runs on every pull_request, unconditional (not gated like the | |
| # `nix-darwin-switch` job below) — this plan's Key Decisions require every | |
| # deliverable validated before merge, not after. No `needs:` on | |
| # lint-and-test — runs in parallel. Inherits the workflow's default | |
| # read-only GITHUB_TOKEN (no elevated permissions) since this job runs | |
| # `nix build` against PR-supplied template content, including from forks. | |
| # | |
| # NOTE FOR WHOEVER MERGES THIS PR: this job needs to be added to the | |
| # repository's branch-protection required-status-checks list manually via | |
| # GitHub repo settings — a one-time change outside this workflow file's scope. | |
| nix-integration: | |
| if: github.event_name == 'pull_request' | |
| runs-on: macos-latest | |
| steps: | |
| - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7 | |
| - uses: astral-sh/setup-uv@11f9893b081a58869d3b5fccaea48c9e9e46f990 # v8.3.2 | |
| with: | |
| python-version: "3.13" | |
| - name: Install dependencies | |
| run: make install | |
| - uses: DeterminateSystems/nix-installer-action@ef8a148080ab6020fd15196c2084a2eea5ff2d25 # v22 | |
| - name: Install sops and age | |
| run: brew install sops age | |
| # Authenticates nix's own github: flake-input fetches (see | |
| # tests/_scaffold_helpers.py's _nix_extra_access_tokens_args() for why | |
| # this matters on a shared, unauthenticated-rate-limited runner IP | |
| # pool). Default read-only GITHUB_TOKEN — no elevated permissions, | |
| # matches this job's existing security posture against fork PRs. | |
| - name: Nix build integration tests | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| run: make test-nix | |
| # Job-level (not workflow-level `on.pull_request.paths`) scoping: a workflow-level | |
| # path filter leaves a required status check permanently "Waiting for status to be | |
| # reported" on any PR that doesn't touch the filtered paths. A job-level skip | |
| # reports a real, non-blocking "success" status instead. | |
| detect-changes: | |
| if: github.event_name == 'pull_request' | |
| runs-on: macos-latest | |
| outputs: | |
| vm-relevant: ${{ steps.check.outputs.vm-relevant }} | |
| steps: | |
| - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7 | |
| - name: Detect VM-relevant changes | |
| id: check | |
| run: | | |
| git fetch origin "${{ github.base_ref }}" --depth=1 | |
| changed=$(git diff --name-only "origin/${{ github.base_ref }}" HEAD) | |
| echo "$changed" | |
| if echo "$changed" | grep -qE '^(src/mac2nix/generators/|src/mac2nix/templates/|src/mac2nix/vm/|tests/generators/|tests/vm/|tests/vm_fixtures\.py)'; then | |
| echo "vm-relevant=true" >> "$GITHUB_OUTPUT" | |
| else | |
| echo "vm-relevant=false" >> "$GITHUB_OUTPUT" | |
| fi | |
| # Real, invasive (applies a genuine nix-darwin switch to the runner itself) and | |
| # gated: only runs when detect-changes says the PR touches VM/generator-relevant | |
| # paths, and even then waits for a maintainer's manual approval via the | |
| # vm-validated environment's required reviewer. | |
| # | |
| # Nested macOS virtualization is categorically unsupported on GitHub-hosted | |
| # runners (confirmed via GitHub's own docs — an Apple Virtualization Framework | |
| # limitation, not a Tart-specific one: `tart run` exits immediately with no | |
| # boot at all), so a Tart-VM-based apply-and-verify check — what this job | |
| # originally did, and what `tests/vm/test_scaffold_vm.py` still does for local | |
| # development on real Apple Silicon hardware — cannot run here. Instead this | |
| # job applies the switch directly to the runner itself, which is safe only | |
| # because the runner is already fully disposable (destroyed after the job). | |
| # See tests/generators/test_scaffold_switch_native.py's own docstring for the | |
| # full safety gating (skips unless GITHUB_ACTIONS=true, fails loudly rather | |
| # than reusing a real key that shouldn't exist on a fresh runner). | |
| # | |
| # test_integration.py's TartVMManager-lifecycle/FileSystemComparator tests are | |
| # deliberately NOT run here — they test the VM-control layer itself, which | |
| # needs an actual VM boot regardless of what's being tested, so they can never | |
| # run on any GitHub-hosted runner. They remain a local-only check | |
| # (`make test-integration`, requires real Tart-capable hardware). | |
| # | |
| # NOTE FOR WHOEVER MERGES THIS PR: the `vm-validated` GitHub Environment and its | |
| # required reviewer need to be configured once via repo settings — this is outside | |
| # this workflow file's scope. | |
| nix-darwin-switch: | |
| needs: detect-changes | |
| if: needs.detect-changes.outputs.vm-relevant == 'true' | |
| runs-on: macos-latest | |
| environment: vm-validated | |
| steps: | |
| - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7 | |
| - uses: astral-sh/setup-uv@11f9893b081a58869d3b5fccaea48c9e9e46f990 # v8.3.2 | |
| with: | |
| python-version: "3.13" | |
| - name: Install dependencies | |
| run: make install | |
| - uses: DeterminateSystems/nix-installer-action@ef8a148080ab6020fd15196c2084a2eea5ff2d25 # v22 | |
| # Installed via Nix, not Homebrew, so they survive the Homebrew removal | |
| # below — add_host() needs sops/age-keygen on PATH independent of | |
| # whatever happens to Homebrew during the switch. | |
| - name: Install sops and age via nix | |
| run: nix profile install nixpkgs#sops nixpkgs#age | |
| # nix-darwin refuses to overwrite any /etc file it doesn't already manage | |
| # and finds with unrecognized content — a safety guard against silently | |
| # clobbering pre-existing system config, not a mac2nix-specific problem. | |
| # DeterminateSystems/nix-installer-action itself writes nix.custom.conf, | |
| # which nix-darwin's own Nix management then wants to own — exactly the | |
| # scenario nix-darwin's own error message describes, with its own | |
| # suggested fix (rename it out of the way first). A real user migrating a | |
| # Mac that already has Nix installed independently of mac2nix would hit | |
| # this identically and need the same one-time manual step. | |
| - name: Move pre-existing /etc/nix/nix.custom.conf out of nix-darwin's way | |
| run: | | |
| if [ -f /etc/nix/nix.custom.conf ]; then | |
| sudo mv /etc/nix/nix.custom.conf /etc/nix/nix.custom.conf.before-nix-darwin | |
| fi | |
| # nix-homebrew's autoMigrate adopts an existing Homebrew install, but | |
| # this runner's pre-provisioned Homebrew has real content from dozens of | |
| # formulae/casks across several non-default taps (Azure, AWS, etc.) — | |
| # migration kept cascading through one conflict after another (an | |
| # existing Library/Taps directory, then a formula whose originating tap | |
| # had just been removed). Rather than keep chasing individual | |
| # conflicts on an image this heavily provisioned, remove Homebrew | |
| # entirely first: nix-homebrew then does a normal fresh install against | |
| # an empty prefix, sidestepping autoMigrate's adoption path altogether. | |
| # autoMigrate = true stays set in the scaffold template regardless — | |
| # it's still the objectively correct setting for a real user's actual | |
| # migration target, which (unlike this disposable runner) genuinely | |
| # needs its existing Homebrew adopted rather than wiped. | |
| - name: Remove pre-existing Homebrew installation | |
| run: sudo rm -rf /opt/homebrew | |
| # See the nix-integration job's own comment on GITHUB_TOKEN — same | |
| # reasoning applies here, for this job's own github: flake-input | |
| # fetches during the real switch (which sudo's env-stripping means | |
| # can't just be set via NIX_CONFIG at the job level; the test itself | |
| # threads this through as an explicit nix CLI argument instead). | |
| - name: Real nix-darwin switch (applied to this disposable runner) | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| run: make test-nix-darwin-switch |