diff --git a/.woodpecker/publish.yml b/.woodpecker/publish.yml index 4212c304..a5ada7b1 100644 --- a/.woodpecker/publish.yml +++ b/.woodpecker/publish.yml @@ -202,6 +202,20 @@ steps: echo "@mosaicstack:registry=https://git.mosaicstack.dev/api/packages/mosaicstack/npm/" >> ~/.npmrc DIST_TAGS_JSON="$(npm view @mosaicstack/mosaic dist-tags --registry https://git.mosaicstack.dev/api/packages/mosaicstack/npm/ --json)" DIST_TAGS_JSON="$DIST_TAGS_JSON" node -e 'const tags = JSON.parse(process.env.DIST_TAGS_JSON || "{}"); if (!tags || typeof tags !== "object" || !Object.hasOwn(tags, "latest")) { throw new Error("Gitea npm registry did not return a usable dist-tags object"); } console.log("[publish-next] registry dist-tags OK: latest=" + tags.latest);' + # #1404: snapshot every publishable manifest BEFORE the transform so the + # workspace can be restored byte-exact after publish. The transform + # rewrites package.json in place (needed: pnpm publish reads the + # workspace manifests); without restore, later steps in this pipeline + # (build-gateway kaniko COPY + pnpm install --frozen-lockfile) see + # manifests that no longer match pnpm-lock.yaml and fail + # ERR_PNPM_OUTDATED_LOCKFILE. Snapshot dir is step-local tmp. + SNAPSHOT_DIR="$(mktemp -d /tmp/publish-next-manifests.XXXXXX)" + export SNAPSHOT_DIR + find apps packages plugins -name package.json -not -path "*/node_modules/*" -not -path "*/dist/*" | while read -r mf; do + mkdir -p "$SNAPSHOT_DIR/$(dirname "$mf")" + cp -p "$mf" "$SNAPSHOT_DIR/$mf" + done + echo "[publish-next] snapshotted $(find "$SNAPSHOT_DIR" -name package.json | wc -l) manifests to $SNAPSHOT_DIR" node <<'NODE' const fs = require('node:fs'); const path = require('node:path'); @@ -357,6 +371,29 @@ steps: } console.log('[publish-next-guard] OK: all ' + published.length + ' published manifests carry exact same-pipeline @mosaicstack/* dep pins'); GUARD + # #1404 restore: put the workspace manifests back byte-exact so later + # steps (build-gateway frozen-lockfile install) see the committed tree. + RESTORE_FAIL=0 + while read -r mf; do + if [ -f "$SNAPSHOT_DIR/$mf" ]; then + cp -p "$SNAPSHOT_DIR/$mf" "$mf" + else + echo "[publish-next] FATAL: no snapshot for $mf — cannot restore (snapshot incomplete?)" >&2 + RESTORE_FAIL=1 + fi + done < <(find apps packages plugins -name package.json -not -path "*/node_modules/*" -not -path "*/dist/*") + # Pristine guard (#1404 red-first control): the publish step must leave + # the workspace byte-identical to the checkout for every manifest. + # git diff is the arbiter — any residual mutation fails THIS step + # instead of surfacing as ERR_PNPM_OUTDATED_LOCKFILE in build-gateway. + if ! git diff --exit-code -- '**/package.json' >/dev/null 2>&1; then + echo "[publish-next] FATAL: workspace package.json files still differ from HEAD after restore (#1404 class)" >&2 + git diff --stat -- '**/package.json' >&2 || true + RESTORE_FAIL=1 + fi + rm -rf "$SNAPSHOT_DIR" + if [ "$RESTORE_FAIL" -ne 0 ]; then exit 1; fi + echo "[publish-next] workspace manifests restored byte-exact (git diff clean); later steps see the committed tree" depends_on: - build - verify