From 7f4744898084e3547c72ab57e9f82fb9826258cd Mon Sep 17 00:00:00 2001 From: moons Date: Tue, 14 Jul 2026 19:49:56 +0900 Subject: [PATCH] ci: strengthen NixOS checks --- .github/workflows/nixos-build.yml | 70 -------------- .github/workflows/nixos-ci.yml | 150 ++++++++++++++++++++++++++++++ .github/workflows/nixos-eval.yml | 69 -------------- .github/workflows/renovate.yml | 6 +- hosts/default.nix | 14 ++- hosts/installer/default.nix | 2 + renovate.json | 4 +- 7 files changed, 169 insertions(+), 146 deletions(-) delete mode 100644 .github/workflows/nixos-build.yml create mode 100644 .github/workflows/nixos-ci.yml delete mode 100644 .github/workflows/nixos-eval.yml diff --git a/.github/workflows/nixos-build.yml b/.github/workflows/nixos-build.yml deleted file mode 100644 index a93bc19..0000000 --- a/.github/workflows/nixos-build.yml +++ /dev/null @@ -1,70 +0,0 @@ -name: NixOS build -on: - pull_request: - branches: - - main - paths: - - ".github/workflows/nixos-build.yml" - - "flake.lock" - - "flake.nix" - - "flake/**" - - "hosts/**" - - "modules/**" - - "overlays/**" - - "profiles/**" - - "shells/**" - - ".sops.yaml" - - "secrets/**" - workflow_dispatch: -permissions: - contents: read -concurrency: - group: ${{ github.workflow }}-${{ github.ref }} - cancel-in-progress: true -jobs: - discover-hosts: - name: Discover NixOS hosts - runs-on: ubuntu-latest - outputs: - hosts: ${{ steps.hosts.outputs.hosts }} - steps: - - name: Checkout repository - uses: actions/checkout@v4 - - name: Install Nix - uses: cachix/install-nix-action@v31 - with: - extra_nix_config: | - experimental-features = nix-command flakes - accept-flake-config = true - access-tokens = github.com=${{ github.token }} - - name: Evaluate NixOS hosts - id: hosts - run: | - set -euo pipefail - hosts_json=$(nix eval --json '.#nixosConfigurations' --apply 'configs: builtins.attrNames configs') - echo "hosts=${hosts_json}" >> "$GITHUB_OUTPUT" - echo "Discovered hosts: ${hosts_json}" - build-host: - name: Build ${{ matrix.host }} - needs: discover-hosts - if: ${{ needs.discover-hosts.outputs.hosts != '[]' }} - runs-on: ubuntu-latest - strategy: - fail-fast: false - matrix: - host: ${{ fromJSON(needs.discover-hosts.outputs.hosts) }} - steps: - - name: Checkout repository - uses: actions/checkout@v4 - - name: Install Nix - uses: cachix/install-nix-action@v31 - with: - extra_nix_config: | - experimental-features = nix-command flakes - accept-flake-config = true - access-tokens = github.com=${{ github.token }} - - name: Build NixOS system - run: | - set -euo pipefail - nix build ".#nixosConfigurations.${{ matrix.host }}.config.system.build.toplevel" \ - --print-build-logs diff --git a/.github/workflows/nixos-ci.yml b/.github/workflows/nixos-ci.yml new file mode 100644 index 0000000..0e63141 --- /dev/null +++ b/.github/workflows/nixos-ci.yml @@ -0,0 +1,150 @@ +name: NixOS CI +on: + pull_request: + branches: + - main + push: + branches: + - main + workflow_dispatch: +permissions: + contents: read +concurrency: + group: nixos-ci-${{ github.event.pull_request.number || github.run_id }} + cancel-in-progress: ${{ github.event_name == 'pull_request' }} +jobs: + validate: + name: Validate flake + runs-on: ubuntu-latest + timeout-minutes: 30 + outputs: + hosts: ${{ steps.hosts.outputs.hosts }} + steps: + - name: Checkout repository + uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 + with: + persist-credentials: false + - name: Install Nix + uses: cachix/install-nix-action@8aa03977d8d733052d78f4e008a241fd1dbf36b3 # v31.10.6 + with: + extra_nix_config: | + experimental-features = nix-command flakes + accept-flake-config = true + access-tokens = github.com=${{ github.token }} + - name: Check flake and evaluate all outputs + run: nix flake check --all-systems --no-build --show-trace + - name: Discover NixOS hosts + id: hosts + run: | + hosts=$(nix eval --json '.#nixosConfigurations' --apply 'configs: builtins.attrNames configs') + echo "hosts=$hosts" >> "$GITHUB_OUTPUT" + echo "Discovered hosts: $hosts" + build: + name: Build ${{ matrix.host }} + needs: validate + if: ${{ needs.validate.outputs.hosts != '[]' }} + runs-on: ubuntu-latest + timeout-minutes: 120 + strategy: + fail-fast: false + matrix: + host: ${{ fromJSON(needs.validate.outputs.hosts) }} + steps: + - name: Checkout repository + uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 + with: + persist-credentials: false + - name: Install Nix + uses: cachix/install-nix-action@8aa03977d8d733052d78f4e008a241fd1dbf36b3 # v31.10.6 + with: + extra_nix_config: | + experimental-features = nix-command flakes + accept-flake-config = true + access-tokens = github.com=${{ github.token }} + - name: Build NixOS system + run: | + nix build ".#nixosConfigurations.${{ matrix.host }}.config.system.build.toplevel" \ + --no-link \ + --print-build-logs \ + --show-trace + report-main-status: + name: Report main status + needs: + - validate + - build + if: ${{ always() && !cancelled() && github.event_name == 'push' && github.ref == 'refs/heads/main' }} + runs-on: ubuntu-latest + timeout-minutes: 5 + permissions: + contents: read + issues: write + env: + CI_FAILED: ${{ needs.validate.result == 'failure' || needs.build.result == 'failure' }} + JOB_RESULTS: ${{ toJSON(needs) }} + steps: + - name: Create or resolve failure issue + uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9 + with: + script: | + const owner = context.repo.owner; + const repo = context.repo.repo; + const title = "NixOS CI is failing on main"; + const marker = ""; + const failed = process.env.CI_FAILED === "true"; + const jobs = JSON.parse(process.env.JOB_RESULTS); + const failedJobs = Object.entries(jobs) + .filter(([, job]) => job.result === "failure") + .map(([name]) => `\`${name}\``) + .join(", "); + const runUrl = `${context.serverUrl}/${owner}/${repo}/actions/runs/${context.runId}`; + const commitUrl = `${context.serverUrl}/${owner}/${repo}/commit/${context.sha}`; + const issues = await github.paginate(github.rest.issues.listForRepo, { + owner, + repo, + state: "open", + per_page: 100, + }); + const existing = issues.find( + (issue) => !issue.pull_request && issue.title === title && issue.body?.includes(marker), + ); + + if (failed) { + const body = [ + marker, + "The NixOS CI workflow failed after a push to `main`.", + "", + `- Failed jobs: ${failedJobs || "unknown"}`, + `- Commit: [\`${context.sha.slice(0, 7)}\`](${commitUrl})`, + `- Workflow run: [${context.runId}](${runUrl})`, + "", + "This issue is updated on subsequent failures and closed automatically after CI recovers.", + ].join("\n"); + + if (existing) { + await github.rest.issues.update({ + owner, + repo, + issue_number: existing.number, + body, + }); + } else { + await github.rest.issues.create({ owner, repo, title, body }); + } + return; + } + + if (existing) { + await github.rest.issues.createComment({ + owner, + repo, + issue_number: existing.number, + body: `CI recovered in [workflow run ${context.runId}](${runUrl}).`, + }); + await github.rest.issues.update({ + owner, + repo, + issue_number: existing.number, + state: "closed", + state_reason: "completed", + }); + } diff --git a/.github/workflows/nixos-eval.yml b/.github/workflows/nixos-eval.yml deleted file mode 100644 index 39cbf7d..0000000 --- a/.github/workflows/nixos-eval.yml +++ /dev/null @@ -1,69 +0,0 @@ -name: NixOS eval -on: - pull_request: - branches: - - main - paths: - - ".github/workflows/nixos-eval.yml" - - "flake.lock" - - "flake.nix" - - "flake/**" - - "hosts/**" - - "modules/**" - - "overlays/**" - - "profiles/**" - - "shells/**" - - ".sops.yaml" - - "secrets/**" - workflow_dispatch: -permissions: - contents: read -concurrency: - group: ${{ github.workflow }}-${{ github.ref }} - cancel-in-progress: true -jobs: - discover-hosts: - name: Discover NixOS hosts - runs-on: ubuntu-latest - outputs: - hosts: ${{ steps.hosts.outputs.hosts }} - steps: - - name: Checkout repository - uses: actions/checkout@v4 - - name: Install Nix - uses: cachix/install-nix-action@v31 - with: - extra_nix_config: | - experimental-features = nix-command flakes - accept-flake-config = true - access-tokens = github.com=${{ github.token }} - - name: Evaluate NixOS hosts - id: hosts - run: | - set -euo pipefail - hosts_json=$(nix eval --json '.#nixosConfigurations' --apply 'configs: builtins.attrNames configs') - echo "hosts=${hosts_json}" >> "$GITHUB_OUTPUT" - echo "Discovered hosts: ${hosts_json}" - eval-host: - name: Eval ${{ matrix.host }} - needs: discover-hosts - if: ${{ needs.discover-hosts.outputs.hosts != '[]' }} - runs-on: ubuntu-latest - strategy: - fail-fast: false - matrix: - host: ${{ fromJSON(needs.discover-hosts.outputs.hosts) }} - steps: - - name: Checkout repository - uses: actions/checkout@v4 - - name: Install Nix - uses: cachix/install-nix-action@v31 - with: - extra_nix_config: | - experimental-features = nix-command flakes - accept-flake-config = true - access-tokens = github.com=${{ github.token }} - - name: Evaluate NixOS system derivation - run: | - set -euo pipefail - nix eval ".#nixosConfigurations.${{ matrix.host }}.config.system.build.toplevel.drvPath" diff --git a/.github/workflows/renovate.yml b/.github/workflows/renovate.yml index b77b670..ccd61df 100644 --- a/.github/workflows/renovate.yml +++ b/.github/workflows/renovate.yml @@ -16,10 +16,12 @@ jobs: timeout-minutes: 60 steps: - name: Checkout repository - uses: actions/checkout@v6.0.3 + uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 + with: + persist-credentials: false # Use a PAT or GitHub App token so Renovate PRs trigger the other workflows. - name: Run Renovate - uses: renovatebot/github-action@v46.1.19 + uses: renovatebot/github-action@22e0a16091fc706b04affe6ae53d5e3358ac4023 # v46.1.19 with: renovate-version: 43.262.1 token: ${{ secrets.RENOVATE_TOKEN }} diff --git a/hosts/default.nix b/hosts/default.nix index b023171..a454377 100644 --- a/hosts/default.nix +++ b/hosts/default.nix @@ -47,9 +47,8 @@ let ; }; }; -in -{ - flake.nixosConfigurations = { + + nixosConfigurations = { nix-example = mkSystem { host = "nix-example"; system = "x86_64-linux"; @@ -102,4 +101,13 @@ in }; }; }; +in +{ + flake = { + inherit nixosConfigurations; + + checks.x86_64-linux = lib.mapAttrs' ( + name: nixos: lib.nameValuePair "nixos-${name}" nixos.config.system.build.toplevel + ) nixosConfigurations; + }; } diff --git a/hosts/installer/default.nix b/hosts/installer/default.nix index fcbe24f..11a508b 100644 --- a/hosts/installer/default.nix +++ b/hosts/installer/default.nix @@ -9,6 +9,8 @@ "${modulesPath}/installer/cd-dvd/installation-cd-minimal.nix" ]; + boot.zfs.forceImportRoot = false; + networking = { hostName = "nixos-installer"; diff --git a/renovate.json b/renovate.json index b4f18f2..82b1080 100644 --- a/renovate.json +++ b/renovate.json @@ -1,7 +1,7 @@ { "$schema": "https://docs.renovatebot.com/renovate-schema.json", - "extends": ["config:recommended"], - "enabledManagers": ["nix"], + "extends": ["config:recommended", "helpers:pinGitHubActionDigests"], + "enabledManagers": ["github-actions", "nix"], "nix": { "enabled": true },