Skip to content

fix(ci): move full (unfree mprime) off-CI per the standard's exception #67

fix(ci): move full (unfree mprime) off-CI per the standard's exception

fix(ci): move full (unfree mprime) off-CI per the standard's exception #67

Workflow file for this run

# Canonical CI workflow — Nix Packaging Standard.
# Source of truth: github:Daaboulex/nix-packaging-standard. Synced into each
# packaging repo by sync.sh; its byte-identity is then enforced by the
# `std-conformance` flake check (flakeModules.base), so it cannot drift.
#
# Archetype-blind by design: this file knows nothing about what kind of repo
# it runs in. It builds every output the flake declares for the runner's
# system (`nix-fast-build` over `.#checks.<system>`, which includes every
# package aliased in by flakeModules.base, the git-hooks lint/format check,
# the conformance + schema checks, and any repo-specific smoke check).
# Per-arch native runners: declared == built (a flake that declares no
# outputs for an arch simply no-ops on that runner).
name: CI
on:
push:
branches: [main, master]
pull_request:
workflow_dispatch:
permissions:
contents: read
concurrency:
group: ci-${{ github.ref }}
cancel-in-progress: true
jobs:
no-ai-files:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
- name: Fail if AI artifacts are tracked
run: |
set -e
FORBIDDEN='^(CLAUDE\.md|GEMINI\.md|AGENTS\.md|AI-progress\.json|AI-tasks\.json|\.session-handoff.*\.md|\.claude/|\.gemini/|\.codex/|\.ai-context/|\.cursor/|\.superpowers/|\.planning/|memory/|handoffs/|deferred-work\.jsonl|self-improvements-pending-.*\.jsonl)'
MATCHES=$(git ls-files | grep -E "$FORBIDDEN" || true)
if [ -n "$MATCHES" ]; then
echo "::error::Tracked AI artifacts detected. These must be gitignored, never committed:"
echo "$MATCHES" | sed 's/^/ - /'
exit 1
fi
echo "Clean — no AI artifacts tracked."
build:
needs: no-ai-files
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, ubuntu-24.04-arm]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
- uses: DeterminateSystems/nix-installer-action@ef8a148080ab6020fd15196c2084a2eea5ff2d25 # v22 (node24)
- name: Build + check every output declared for this system
run: |
SYS=$(nix eval --impure --raw --expr 'builtins.currentSystem')
if ! nix eval ".#checks.$SYS" --apply 'x: builtins.attrNames x != [ ]' 2>/dev/null | grep -qx true; then
echo "Flake declares no checks for $SYS — skipping (declared == built)."
exit 0
fi
# Substitution (cache.nixos.org) reuses every unmodified path for free;
# --skip-cached builds ONLY what no substituter already has.
build() {
nix run nixpkgs#nix-fast-build -- --skip-cached --no-nom --flake ".#checks.$SYS" 2>&1 | tee /tmp/build.log
return "${PIPESTATUS[0]}"
}
# A genuine compile failure fails fast. Transient EXTERNAL-fetch failures
# — crates.io rate-limiting bulk crate pulls (403/429), or a registry/CDN
# blip — are retried ONCE after a backoff so the rate-limit window passes.
# The marker grep keeps real build bugs from being silently retried.
if ! build; then
if grep -qiE 'crates\.io|error: cannot download|status code: (403|429)|curl: \(|couldn.t resolve host|connection reset by peer|temporary failure in name resolution|operation timed out' /tmp/build.log; then
echo "::warning::Transient external-fetch failure (likely crates.io rate-limit); retrying once in 60s."
sleep 60
build || { echo "::error::Build failed after transient-fetch retry."; exit 1; }
else
echo "::error::Build failed (no transient-fetch marker — not retrying)."
exit 1
fi
fi