name: Nix on: push: branches: [main] pull_request: permissions: contents: read pull-requests: write concurrency: group: nix-${{ github.ref }} cancel-in-progress: true jobs: nix: strategy: matrix: os: [ubuntu-latest, macos-latest] runs-on: ${{ matrix.os }} timeout-minutes: 30 steps: - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 - uses: ./.github/actions/nix-setup with: cachix-auth-token: ${{ secrets.CACHIX_AUTH_TOKEN }} - name: Resolve head SHA if: github.event_name == 'pull_request' id: sha shell: bash run: | FULL="${{ github.event.pull_request.head.sha || github.sha }}" echo "full=$FULL" >> "$GITHUB_OUTPUT" echo "short=${FULL:0:7}" >> "$GITHUB_OUTPUT" - name: Check flake id: flake if: runner.os == 'Linux' continue-on-error: true run: nix flake check --print-build-logs - name: Build package id: build if: runner.os == 'Linux' continue-on-error: true run: nix build --print-build-logs # When the real Nix build fails, run a targeted diagnostic to see if # the failure is specifically a stale npm lockfile hash in one of the # known npm subpackages (tui / web). This avoids surfacing a generic # "build failed" message when the fix is a single known command. - name: Diagnose npm lockfile hashes id: hash_check if: (steps.flake.outcome == 'failure' || steps.build.outcome == 'failure') && runner.os == 'Linux' continue-on-error: true env: LINK_SHA: ${{ steps.sha.outputs.full }} run: nix run .#fix-lockfiles -- --check # If fix-lockfiles itself crashes (infrastructure blip, cache throttle, # etc.) it won't set stale=true/false. Treat that as a distinct failure # mode rather than silently ignoring it. - name: Fail if hash check crashed without reporting if: steps.hash_check.outcome == 'failure' && steps.hash_check.outputs.stale != 'true' && steps.hash_check.outputs.stale != 'false' run: | echo "::error::fix-lockfiles exited without reporting stale status — likely an infrastructure or script failure" exit 1 - name: Post sticky PR comment (stale hashes) if: steps.hash_check.outputs.stale == 'true' && github.event_name == 'pull_request' uses: marocchino/sticky-pull-request-comment@52423e01640425a022ef5fd42c6fb5f633a02728 # v2.9.1 with: header: nix-lockfile-check message: | ### ⚠️ npm lockfile hash out of date Checked against commit [`${{ steps.sha.outputs.short }}`](${{ github.server_url }}/${{ github.repository }}/commit/${{ steps.sha.outputs.full }}) (PR head at check time). The `hash = "sha256-..."` line in these nix files no longer matches the committed `package-lock.json`: ${{ steps.hash_check.outputs.report }} #### Apply the fix - [ ] **Apply lockfile fix** — tick to push a commit with the correct hashes to this PR branch - Or [run the Nix Lockfile Fix workflow](${{ github.server_url }}/${{ github.repository }}/actions/workflows/nix-lockfile-fix.yml) manually (pass PR `#${{ github.event.pull_request.number }}`) - Or locally: `nix run .#fix-lockfiles` and commit the diff # Clear the sticky comment when either the build passed outright (no # hash check needed) or the hash check explicitly returned stale=false # (build failed for a non-hash reason). - name: Clear sticky PR comment (resolved) if: | github.event_name == 'pull_request' && runner.os == 'Linux' && (steps.hash_check.outputs.stale == 'false' || (steps.flake.outcome == 'success' && steps.build.outcome == 'success')) uses: marocchino/sticky-pull-request-comment@52423e01640425a022ef5fd42c6fb5f633a02728 # v2.9.1 with: header: nix-lockfile-check delete: true - name: Final fail if build or flake failed if: steps.flake.outcome == 'failure' || steps.build.outcome == 'failure' run: | if [ "${{ steps.hash_check.outputs.stale }}" == "true" ]; then echo "::error::Nix build failed due to stale npm lockfile hash. Run: nix run .#fix-lockfiles" else echo "::error::Nix build/flake check failed. See logs above." fi exit 1 - name: Evaluate flake (macOS) if: runner.os == 'macOS' run: nix flake show --json > /dev/null