claim(M1-nixenv): single-source harness runtime env — ccciPyEnv+ccciRuntimeTools+cc-ci-run in packages.nix, referenced by harness/sweep/both hosts; sweep execs cc-ci-run (no dup pyEnv, no DEFECT-3 PATH patch); cc-ci host gains git-lfs+openssl; both #cc-ci and #cc-ci-hetzner build; awaiting Adversary
Some checks failed
continuous-integration/drone/push Build is failing

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
autonomic-bot
2026-06-17 17:23:28 +00:00
parent 706583bee3
commit 8b8fc1ff8e
8 changed files with 242 additions and 64 deletions

View File

@ -1,20 +1,11 @@
# CI harness runtime (M4): a reproducible Python env with pytest + Playwright and the
# Nix-provided browsers, exposed as `cc-ci-run` on the host so the Drone exec pipeline (and
# manual dev) can run the harness with `cc-ci-run runner/run_recipe_ci.py`. Playwright on NixOS
# needs the browsers from nixpkgs (not a downloaded copy) via PLAYWRIGHT_BROWSERS_PATH.
# CI harness runtime (M4): `cc-ci-run` exposes a reproducible Python env (pytest + Playwright,
# Nix-provided browsers) plus the recipe-test tooling, so the Drone exec pipeline (and manual dev)
# can run the harness with `cc-ci-run runner/run_recipe_ci.py`.
#
# Phase `nixenv`: `cc-ci-run` (and the env it carries) is now defined ONCE in modules/packages.nix
# as `pkgs.cc-ci-run` — the SAME definition the nightly sweep execs and the same tool set the host
# `systemPackages` reference. This module just installs it on the host.
{ pkgs, ... }:
let
pyEnv = pkgs.python3.withPackages (ps: with ps; [ pytest playwright ]);
ccciRun = pkgs.writeShellApplication {
name = "cc-ci-run";
runtimeInputs = [ pyEnv pkgs.abra pkgs.docker pkgs.git pkgs.coreutils pkgs.util-linux ];
text = ''
export PLAYWRIGHT_BROWSERS_PATH=${pkgs.playwright-driver.browsers}
export PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD=1
exec ${pyEnv}/bin/python3 "$@"
'';
};
in
{
environment.systemPackages = [ ccciRun ];
environment.systemPackages = [ pkgs.cc-ci-run ];
}

View File

@ -13,30 +13,22 @@
# and lets sweep-logic changes ship via a checkout pull without a store rebuild.
{ pkgs, ... }:
let
# The sweep drives run_recipe_ci.py (pytest/playwright) — needs the full harness env like cc-ci-run.
pyEnv = pkgs.python3.withPackages (ps: with ps; [ pytest playwright ]);
# Phase `nixenv`: the sweep drives nightly_sweep.py (pytest/playwright) through the SAME
# entrypoint the Drone runner uses — `cc-ci-run` (pkgs.cc-ci-run, defined once in packages.nix).
# So the python env + recipe-test tooling are IDENTICAL to the Drone path by construction: no
# duplicate pyEnv, no parallel runtimeInputs list, and no host-PATH-prepend patch (the old
# DEFECT-3 fix) — git-lfs/bash/util-linux/openssl/etc. now come from cc-ci-run's runtimeInputs,
# which is the single shared `ccciRuntimeTools`. This wrapper only sets the sweep-specific
# environment (HOME, the deployed-checkout repo) before handing off.
sweep = pkgs.writeShellApplication {
name = "cc-ci-nightly-sweep";
# util-linux provides `script` (abra's PTY wrapper for backup/restore TTY ops) — same as cc-ci-run.
# bash: the sweep's mirror_sync shells out to `bash scripts/recipe-mirror-sync.sh`; writeShellApplication
# sets a clean PATH limited to runtimeInputs, so bash must be listed (a real timer fire caught its
# absence — manual ssh runs had bash on PATH and masked it).
runtimeInputs = with pkgs; [ bash abra docker git curl jq gnused gnugrep gnutar coreutils util-linux procps ];
runtimeInputs = [ pkgs.cc-ci-run ];
text = ''
export HOME=/root
export PLAYWRIGHT_BROWSERS_PATH=${pkgs.playwright-driver.browsers}
export PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD=1
# ENV PARITY with the Drone recipe-CI runner (canon DEFECT-3): the recipes + their tests shell
# out to host tooling (git-lfs for gitea, openssl, etc.). Drone's exec runner runs them with
# PATH=/run/current-system/sw/bin:/run/wrappers/bin; writeShellApplication otherwise gives a
# clean nix-only PATH, so the timer sweep silently lacked tools the recipes assume (a real fire
# caught git-lfs + bash gaps that manual ssh runs, with a login PATH, masked). Prepend the host
# system PATH so the sweep validates recipes in the SAME environment Drone does.
export PATH="/run/current-system/sw/bin:/run/wrappers/bin:$PATH"
# canon M1.4: read enrollment + run the harness from the deployed checkout (has tests/).
export CCCI_REPO=/etc/cc-ci
cd "$CCCI_REPO"
exec ${pyEnv}/bin/python3 "$CCCI_REPO/runner/nightly_sweep.py"
exec cc-ci-run "$CCCI_REPO/runner/nightly_sweep.py"
'';
};
in

View File

@ -1,25 +1,77 @@
# Project package overlay. `abra` (the Co-op Cloud CLI) is exposed as `pkgs.abra` so every
# module (systemPackages, the proxy/drone reconcile oneshots) can use the same pinned build.
#
# Phase `nixenv` — SINGLE SOURCE OF TRUTH for the harness/recipe-test runtime env. The Drone
# runner entrypoint (`cc-ci-run`), the nightly/weekly sweep timer, and host `systemPackages` ALL
# reference the definitions here, so a dependency can never be present for one path and missing
# for another (that was DEFECT-3, caught in `canon`: the sweep's tool list had drifted from what
# the Drone path provided). Adding the next dependency to `ccciRuntimeTools` propagates atomically
# to every consumer.
_:
{
nixpkgs.overlays = [
(_: prev: {
abra = prev.stdenv.mkDerivation rec {
pname = "abra";
version = "0.13.0-beta";
src = prev.fetchurl {
url = "https://git.coopcloud.tech/toolshed/abra/releases/download/${version}/abra_${version}_linux_amd64.tar.gz";
sha256 = "12csk6wp1pk9cspzqfl4a6h5jdz8p055sf0ggxw9k7ljhpd5qvc6";
(final: prev:
let
# The harness drives run_recipe_ci.py / nightly_sweep.py (pytest + Playwright). Defined
# ONCE here; consumed only via `cc-ci-run` below (no module builds its own pyEnv anymore).
ccciPyEnv = final.python3.withPackages (ps: with ps; [ pytest playwright ]);
in
{
abra = prev.stdenv.mkDerivation rec {
pname = "abra";
version = "0.13.0-beta";
src = prev.fetchurl {
url = "https://git.coopcloud.tech/toolshed/abra/releases/download/${version}/abra_${version}_linux_amd64.tar.gz";
sha256 = "12csk6wp1pk9cspzqfl4a6h5jdz8p055sf0ggxw9k7ljhpd5qvc6";
};
sourceRoot = ".";
nativeBuildInputs = [ prev.autoPatchelfHook ];
buildInputs = [ prev.stdenv.cc.cc.lib ];
installPhase = ''
runHook preInstall
install -Dm755 abra "$out/bin/abra"
runHook postInstall
'';
};
sourceRoot = ".";
nativeBuildInputs = [ prev.autoPatchelfHook ];
buildInputs = [ prev.stdenv.cc.cc.lib ];
installPhase = ''
runHook preInstall
install -Dm755 abra "$out/bin/abra"
runHook postInstall
'';
};
})
# === Single source of truth: the recipe-test runtime tooling ===
# The union of everything a recipe test + the harness shell out to. This is a
# superset-or-equal of every prior list (cc-ci-run's old runtimeInputs, the sweep's old
# runtimeInputs, and the host PATH's hand-maintained `curl git git-lfs jq`):
# abra docker git coreutils util-linux (old cc-ci-run)
# + bash curl jq gnused gnugrep gnutar procps (old sweep)
# + git-lfs openssl (formerly only on the host PATH / plan §2)
# `util-linux` provides `script` (abra's PTY wrapper for backup/restore TTY ops).
ccciRuntimeTools = with final; [
abra
docker
git
git-lfs
bash
coreutils
util-linux
curl
jq
gnused
gnugrep
gnutar
openssl
procps
];
# === The harness entrypoint, used by BOTH the Drone exec pipeline (.drone.yml:
# `cc-ci-run runner/run_recipe_ci.py`) and the nightly sweep (which execs this same
# binary). Carries the full tool set in runtimeInputs so the recipe shell-outs resolve
# the same tooling on every path, independent of the caller's inherited PATH. ===
cc-ci-run = final.writeShellApplication {
name = "cc-ci-run";
runtimeInputs = [ ccciPyEnv ] ++ final.ccciRuntimeTools;
text = ''
export PLAYWRIGHT_BROWSERS_PATH=${final.playwright-driver.browsers}
export PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD=1
exec ${ccciPyEnv}/bin/python3 "$@"
'';
};
})
];
}