name: validate-pr on: pull_request: paths: - 'recipes/**' jobs: validate: runs-on: self-hosted steps: - uses: actions/checkout@v4 # 1. Identify which recipes the PR touches. - name: detect changed packages id: changed run: | set -e base="${{ github.event.pull_request.base.sha }}" changed=$(git diff --name-only "$base"...HEAD -- 'recipes/' \ | awk -F/ '{print $2}' | sort -u) if [[ -z "$changed" ]]; then echo "no recipe changes — nothing to validate" echo "packages=" >> "$GITHUB_OUTPUT" exit 0 fi echo "changed packages: $changed" echo "packages=$changed" >> "$GITHUB_OUTPUT" # 2. Schema check (placeholder — refine once a JSON schema lives in-tree). - name: schema check if: steps.changed.outputs.packages != '' run: | for pkg in ${{ steps.changed.outputs.packages }}; do for f in recipes/$pkg/versions/*.toml; do echo "checking $f" # TODO: validate against a schema definition once one exists. grep -q '^schema = 1$' "$f" || { echo "missing schema = 1"; exit 1; } grep -q '^name =' "$f" || { echo "missing name"; exit 1; } grep -q '^version =' "$f" || { echo "missing version"; exit 1; } grep -q '^\[source\]' "$f" || { echo "missing [source]"; exit 1; } grep -q '^commit =' "$f" || { echo "missing source.commit"; exit 1; } grep -q '^sha256 =' "$f" || { echo "missing source.sha256"; exit 1; } done done # 3. Source fixity — re-fetch and confirm the sha256 matches. - name: source fixity if: steps.changed.outputs.packages != '' run: | for pkg in ${{ steps.changed.outputs.packages }}; do for f in recipes/$pkg/versions/*.toml; do url=$(awk -F'"' '/^url =/ {print $2; exit}' "$f") rev=$(awk -F'"' '/^commit =/ {print $2; exit}' "$f") expected=$(awk -F'"' '/^sha256 =/ {print $2; exit}' "$f") actual=$(nix flake prefetch --extra-experimental-features 'nix-command flakes' \ "git+${url}?rev=${rev}" --json | jq -r .hash) if [[ "$actual" != "$expected" ]]; then echo "sha256 mismatch in $f: expected $expected, got $actual" exit 1 fi done done # 4. Build smoke — every changed package must build. - name: build smoke if: steps.changed.outputs.packages != '' run: | for pkg in ${{ steps.changed.outputs.packages }}; do nix build --extra-experimental-features 'nix-command flakes' \ .#${pkg} --no-link --print-out-paths done # 5. Cache push (only on the validated outputs, before merge). - name: push to binary cache if: steps.changed.outputs.packages != '' env: NIX_SECRET_KEY_FILE: ${{ secrets.NIX_CACHE_SECRET_KEY_FILE }} CACHE_URL: ${{ vars.CARGOXX_CACHE_URL }} run: | for pkg in ${{ steps.changed.outputs.packages }}; do nix copy --extra-experimental-features 'nix-command flakes' \ --to "${CACHE_URL}?secret-key=${NIX_SECRET_KEY_FILE}" \ .#${pkg} done # 6. Maintainer match. - name: maintainer check if: steps.changed.outputs.packages != '' run: | author='${{ github.event.pull_request.user.login }}' for pkg in ${{ steps.changed.outputs.packages }}; do list="recipes/$pkg/maintainers.txt" if [[ ! -f "$list" ]]; then echo "new package $pkg — maintainers.txt will be added by this PR" continue fi if ! grep -E -q "^\s*${author}\s*(\#.*)?$" "$list"; then echo "PR author '$author' is not in $list" gh pr edit ${{ github.event.pull_request.number }} \ --add-label needs-human-review exit 1 fi done - name: label auto-merge if: steps.changed.outputs.packages != '' run: | gh pr edit ${{ github.event.pull_request.number }} --add-label auto-merge