--- # Self-test pipeline: runs on normal pushes to cc-ci (M2). Sanity-checks the exec runner can drive # host abra/docker. Recipe CI is the separate `custom`-event pipeline below. kind: pipeline type: exec name: self-test platform: os: linux arch: amd64 trigger: event: - push steps: # Lint/format gate (Phase 1b, RL1). Runs the exact toolchain from the pinned `lint` devshell # (flake.nix) via scripts/lint.sh in check mode — FAILS the build on any unclean file so future # commits stay formatted + lint-clean. HOME=/root so nix reuses root's store/eval cache. - name: lint environment: HOME: /root commands: - nix develop .#lint --command bash scripts/lint.sh - name: hello commands: - echo "cc-ci self-test on the exec runner" - whoami - abra --version - docker info --format 'swarm={{.Swarm.LocalNodeState}}' --- # Recipe-CI pipeline: runs on bridge-triggered builds (event=custom, params RECIPE/REF/PR/SRC set by # the comment-bridge). Deploys the recipe at the PR head, runs install/upgrade/backup + any # recipe-local tests via the shared harness, then guarantees teardown (plan §4.2/§4.3). # # Resource safety (plan §4.2/§4.3): DRONE_RUNNER_CAPACITY=2 (nix/modules/drone-runner.nix, the # single concurrency knob) allows two recipe runs in parallel. Concurrent-run safety is enforced by # the harness, not by serialisation: every run holds an exclusive flock on its app domain # (/run/lock/cc-ci-app-.lock) for its whole process lifetime, the run-start janitor probes # that lock to reap only orphans (held lock = live run, never touched), and recipe working trees # are per-run ($ABRA_DIR/recipes — no shared checkout, no recipe lock). See docs/concurrency.md. kind: pipeline type: exec name: recipe-ci platform: os: linux arch: amd64 trigger: event: - custom # NB deliberately NO `concurrency.limit` here: DRONE_RUNNER_CAPACITY (nix/modules/drone-runner.nix # maxTests) is the single concurrency knob (P4 — two knobs in two files drifted). steps: - name: ci environment: STAGES: install,upgrade,backup,restore,custom # The exec runner points HOME at a per-build workspace; force it to /root so abra's server # config is found via the per-run ABRA_DIR's servers/ symlink -> /root/.abra/servers. # Recipe trees are PER-RUN ($ABRA_DIR/recipes, exported by run_recipe_ci before any abra # call), so concurrent builds never share a recipe checkout; app .env files are per-domain # in the shared canonical servers/ path, guarded by the app-domain flock. HOME: /root commands: # RECIPE/REF/PR/SRC (+ CCCI_QUICK for `!testme --quick`) are injected as env vars from the # build's custom params. CCCI_QUICK=1 makes run_recipe_ci take the opt-in fast lane (WC7); # absent => full cold (default). run_quick ignores STAGES (always upgrade+custom). - 'echo "recipe-ci: RECIPE=$RECIPE REF=$REF PR=$PR SRC=$SRC stages=$STAGES quick=${CCCI_QUICK:-0}"' # P1 lock-lifetime hardening: run the harness in its own session/process group (setsid) and # forward a drone cancel (TERM to this step shell) to the WHOLE group, so the harness's # SIGTERM handler runs its teardown funnel instead of being leaked (the exec runner kills # only the step shell, not the tree). PDEATHSIG inside the harness backstops the case where # this shell dies without the trap firing. The harness exit code is captured explicitly and # the traps cleared before exiting: the runner shell is `set -e`, and an EXIT-trap kill of # the already-gone process group returns ESRCH, which otherwise poisons a GREEN run's exit # status to 1 (observed live, build 269: all tiers pass, step exit 1). - | setsid cc-ci-run runner/run_recipe_ci.py & PID=$! trap 'kill -TERM -- "-$PID" 2>/dev/null || true' TERM EXIT rc=0 wait "$PID" || rc=$? trap - TERM EXIT exit "$rc"