feat(parity): mkAtticClosurePublish builder + pipeline-doctor non-flake mode (#198, #193)

Adds the attic-closure archetype builder (build closure + attic push, no
registry artifact) so caddy/overlay-xonsh/flake-hub/woodpecker-peek share one
implementation. Adds non-flake mode to pipeline-doctor so ci-script repos
(gitea-mcp, helms) pass the gate. Self-check 9/9; gitea-mcp now passes.
This commit is contained in:
Oleks
2026-06-02 23:30:59 +03:00
parent 79f9a2dd62
commit db0bf3b9ab
4 changed files with 237 additions and 12 deletions
+64 -12
View File
@@ -37,6 +37,18 @@ done
REPO="$(cd "$REPO" && pwd)"
FLAKE="$REPO/flake.nix"
# Non-flake parity form (cluster #193, issue #193). A repo with no root flake.nix
# is still a VALID parity repo if it ships the ci-script form: a ci/local.sh (or a
# ci/build.sh + ci/publish.sh pair) that is the single local==CI entrypoint, with
# a thin .woodpecker.yaml that calls those scripts. In that case the "consumes
# parity / single entrypoint" contract is satisfied via the CI-SCRIPT path instead
# of a parity-lib flake input, and the token-leak + #191 no-set-x scans below still
# apply to its ci/*.sh.
CI_SCRIPT_FORM=0
if [ -f "$REPO/ci/local.sh" ] || { [ -f "$REPO/ci/build.sh" ] && [ -f "$REPO/ci/publish.sh" ]; }; then
CI_SCRIPT_FORM=1
fi
fail=0
warn_n=0
pass_n=0
@@ -57,12 +69,17 @@ warn() {
echo "pipeline-doctor: $REPO"
if [ ! -f "$FLAKE" ]; then
echo "FAIL: no flake.nix at $REPO" >&2
exit 1
if [ "$CI_SCRIPT_FORM" -eq 1 ]; then
# Valid non-flake parity repo: assert via the ci-script form below.
flake_txt=""
else
echo "FAIL: no flake.nix and no ci/local.sh (or ci/build.sh + ci/publish.sh) at $REPO" >&2
exit 1
fi
else
flake_txt="$(cat "$FLAKE")"
fi
flake_txt="$(cat "$FLAKE")"
# Documented escape-hatch (cluster #196): a repo that must keep a hand-written
# Dockerfile/OCI pipeline (BuildKit, etc.) instead of a parity-lib builder is
# allowed to opt out PROVIDED it ships a `ci/local.sh` that gives a dev the same
@@ -74,27 +91,46 @@ if [ -f "$REPO/ci/local.sh" ]; then
ESCAPE_HATCH=1
fi
# Text of the ci/*.sh + .woodpecker.yaml — the archetype marker and the single
# local==CI entrypoint of a non-flake (ci-script form) repo live here, not in a
# flake.nix (cluster #193, issue #193).
ci_txt=""
if [ -d "$REPO/ci" ]; then
ci_txt="$(cat "$REPO"/ci/*.sh 2>/dev/null || true)"
fi
for wp in "$REPO/.woodpecker.yaml" "$REPO/.woodpecker.yml"; do
[ -f "$wp" ] && ci_txt="$ci_txt
$(cat "$wp")"
done
# 1. archetype declared (a publish/stage app whose name encodes the arch, or an
# explicit archetype marker comment).
# explicit archetype marker comment) — in the flake OR, for a non-flake
# ci-script repo, in its ci/*.sh / .woodpecker.yaml.
if printf '%s' "$flake_txt" | grep -Eq 'archetype|stage-[a-z0-9_]+|publish-[a-z0-9_]+'; then
ok "archetype declared (stage-*/publish-* app or archetype marker present)"
elif [ "$CI_SCRIPT_FORM" -eq 1 ] && printf '%s' "$ci_txt" | grep -Eq 'archetype|ci/build\.sh|ci/publish\.sh|ci/local\.sh'; then
ok "archetype declared (non-flake ci-script form: ci/*.sh entrypoints, cluster #193)"
elif [ "$ESCAPE_HATCH" -eq 1 ]; then
warn "no parity-lib archetype, but ci/local.sh escape-hatch present (cluster #196)"
else
bad "no archetype: expected a stage-<arch>/publish-<arch> app or 'archetype' marker (or a ci/local.sh escape-hatch)"
bad "no archetype: expected a stage-<arch>/publish-<arch> app or 'archetype' marker (or a ci-script form)"
fi
# 2. consumes parity-lib (the shared module) OR a documented ci/local.sh hatch.
# 2. consumes parity-lib (the shared module) OR is a valid non-flake ci-script
# repo whose ci/local.sh (or ci/build.sh + ci/publish.sh) is the single
# local==CI entrypoint, OR a documented ci/local.sh escape-hatch.
CONSUMES_PARITY=0
if printf '%s' "$flake_txt" | grep -Eq 'parity-lib|parity\.lib|mk(PyPiWheel|S390xNpm|GenericBinary|Nix2Container|GoBinary|Helm)Publish'; then
if printf '%s' "$flake_txt" | grep -Eq 'parity-lib|parity\.lib|mk(PyPiWheel|S390xNpm|GenericBinary|Nix2Container|GoBinary|Helm|AtticClosure)Publish'; then
CONSUMES_PARITY=1
fi
if [ "$CONSUMES_PARITY" -eq 1 ]; then
ok "consumes parity-lib (input or one of the mk*Publish builders)"
elif [ "$CI_SCRIPT_FORM" -eq 1 ]; then
ok "single local==CI entrypoint via ci-script form (ci/local.sh or ci/build.sh + ci/publish.sh, cluster #193)"
elif [ "$ESCAPE_HATCH" -eq 1 ]; then
warn "does not consume parity-lib, but ci/local.sh escape-hatch present (cluster #196)"
else
bad "does not consume parity-lib: inline the input and use a mk*Publish builder (or ship a ci/local.sh escape-hatch)"
bad "does not consume parity-lib: inline the input and use a mk*Publish builder (or ship a ci-script form)"
fi
# Checks 36 below are GUARANTEED by parity-lib for a consumer: the token
@@ -167,11 +203,17 @@ else
bad "set -x in token-bearing ci/*.sh:$xtrace_hits (xtrace would echo the token)"
fi
# 4. dev-tag guard present.
# 4. dev-tag guard present. For a non-flake ci-script repo the equivalent guard
# is the dry-run-DEFAULT publish + the .woodpecker.yaml `refs/tags/v*` gate:
# nothing mutates the registry unless a v* tag run flips PUBLISH/DRY_RUN.
if [ "$CONSUMES_PARITY" -eq 1 ]; then
ok "dev-tag guard present (delegated to parity-lib parity_devtag_guard)"
elif printf '%s' "$token_src" | grep -Eq 'parity_devtag_guard|refusing to publish without an explicit'; then
ok "dev-tag guard present"
elif [ "$CI_SCRIPT_FORM" -eq 1 ] &&
printf '%s' "$ci_txt" | grep -Eq 'DRY_RUN|dry-run|PUBLISH' &&
printf '%s' "$ci_txt" | grep -Eq 'refs/tags/v\*|event:[[:space:]]*tag|tag:'; then
ok "dev-tag guard equivalent (ci-script dry-run default + .woodpecker refs/tags/v* gate, cluster #193)"
else
bad "no dev-tag guard: a default-version --publish could clobber the registry"
fi
@@ -185,20 +227,30 @@ else
bad "no dry-run default: publish must NOT mutate the registry by default"
fi
# 6. apps carry meta.description.
# 6. apps carry meta.description. N/A for a non-flake ci-script repo (it exposes
# shell-script entrypoints, not flake apps with meta); its self-documenting
# ci/*.sh header comments are the equivalent.
if [ "$CONSUMES_PARITY" -eq 1 ]; then
ok "apps carry meta.description (delegated to parity-lib mkApp)"
elif printf '%s' "$flake_txt" | grep -Eq 'meta\.description|meta = \{'; then
ok "apps carry meta.description"
elif [ "$CI_SCRIPT_FORM" -eq 1 ]; then
ok "meta.description N/A (non-flake ci-script form; ci/*.sh are the documented entrypoints)"
else
bad "apps missing meta.description"
fi
# 7. enumerable publish-* naming.
# 7. enumerable publish-* naming. For a non-flake ci-script repo the equivalent
# is the ci/build.sh + ci/publish.sh (or ci/local.sh) entrypoint set.
pubs="$(printf '%s' "$flake_txt" | grep -oE '(stage|publish)(-[a-z0-9_]+)?' | sort -u || true)"
if [ -n "$pubs" ]; then
ok "enumerable publish-* / stage-* app naming:"
printf '%s\n' "$pubs" | sed 's/^/ /'
elif [ "$CI_SCRIPT_FORM" -eq 1 ]; then
ok "enumerable ci-script entrypoints (non-flake parity form, cluster #193):"
for s in ci/local.sh ci/build.sh ci/publish.sh; do
[ -f "$REPO/$s" ] && printf ' %s\n' "$s"
done
else
bad "no enumerable publish-*/stage-* apps found"
fi