feat(parity): gate-ready pipeline-doctor + OCI verify-digest + stage/push-staged audit

pipeline-doctor (#191/#193): add --strict (fail on WARN) so a .woodpecker.yaml
step or pre-receive hook can gate on exit code; add documented ci/local.sh
escape-hatch (#196); fix false-negative — token/dev-tag/dry-run/meta contracts
are guaranteed by parity-lib for a consumer, so consumers PASS by delegation
instead of being penalized for not re-implementing them inline. Self-check and
numpy-s390x both pass 9/9.

mkNix2ContainerPublish (#195): add verify-digest app that builds each local arch
image and prints its OCI manifest digest (no registry contact), formalizing the
content-addressed manifest digest as the parity contract. reproducible=false is
kept deliberately (non-reproducible layer deps); digest-as-contract is the
low-risk path. Generalized from claude-plugin-registry 55f2d0b.

stage/push-staged audit (#194): verified all 8 builders expose stage-<arch> +
push-staged; all already complete, no gaps.
This commit is contained in:
Oleks
2026-06-02 21:11:49 +03:00
parent af64a8ea4c
commit 79f9a2dd62
3 changed files with 163 additions and 15 deletions
+42
View File
@@ -12,6 +12,10 @@
# publish all locally-buildable arches + publish-index; ':latest' is a
# digest copy of ':TAG' done LAST as the idempotent mutation.
# push-staged replay artifacts from ./.parity-stage to the registry.
# verify-digest (OCI only, cluster #195) build each local arch image and print
# its OCI manifest digest — the content-addressed parity contract
# (layers are reproducible = false, so parity is at the digest,
# not byte-identical tars). No registry contact.
#
# Not every archetype yields every app: a single-arch wheel/binary/npm addon
# has no multi-arch index, so it exposes stage/publish/push-staged only. The OCI
@@ -699,6 +703,42 @@ let
'';
};
# Compute one arch image's OCI manifest digest with NO registry contact
# (cluster #195). copyTo a throwaway local OCI dir and read back the
# content-addressed manifest digest skopeo derives — this is the digest the
# registry stores the image under. Because the layer is built with
# reproducible = false (a DELIBERATE fix for the "Digest did not match"
# from non-reproducible layer deps — nix2container's lazy tar regeneration
# rehashes differently across hosts), byte-identical-tar parity is NOT
# promised; the manifest digest IS the parity contract. Identical local vs
# CI digest ⇒ identical image in the registry.
digestArch = arch: ''
echo " ${imageName}:$VERSION-${arch} (local build, no registry contact)"
ocidir="$(mktemp -d)"
${lib.escapeShellArg "${images.${arch}.copyTo}"}/bin/copy-to "oci:$ocidir:${arch}" >/dev/null
digest="$(skopeo manifest-digest "$ocidir/blobs/sha256/$(
jq -r '.manifests[0].digest | sub("sha256:";"")' "$ocidir/index.json"
)")"
rm -rf "$ocidir"
echo " ${arch}: $digest"
'';
verifyDigest = pkgs.writeShellApplication {
name = "verify-digest";
runtimeInputs = baseInputs ++ [
pkgs.nix
pkgs.skopeo
pkgs.jq
];
text = ''
${head}
VERSION="$(parity_derive_version ${lib.escapeShellArg version})"
echo "OCI manifest digests for ${imageName}:$VERSION (content-addressed parity, cluster #195)"
echo " reproducible = false parity is asserted at this digest, not byte-identical tars."
${lib.concatMapStringsSep "" digestArch localArches}
'';
};
mkArchPublish =
arch:
pkgs.writeShellApplication {
@@ -838,6 +878,8 @@ let
mkApp publishAll "Publish all local arches + index; :latest = digest copy of :TAG (last mutation).";
"push-staged" =
mkApp pushStaged "Replay staged ${imageName} arch closures from .parity-stage to the registry.";
"verify-digest" =
mkApp verifyDigest "Build each local arch image and print its OCI manifest digest (content-addressed parity check, cluster #195; no registry contact).";
};
# =========================================================================