ci/woodpecker/pr/ci Pipeline failed
Per ops-ci-01's procedure (comments 23386/23394, brain D27): pipeline config travels with the commit under test, so the controlled rerun requires the pinned anchor in the PR head itself. This merge absorbs everything next took since the original merge parent (1bdeed62): the ci-base pin (cb9a0d1, lock-9cb7ffcd8828), the legacy-credential removal (#1325), and the ci-queue-wait statuses:null fix (#1129 — the branch from the divergence analysis, now landed). One conflict, same file as round 1: test:framework-shell union — our 54-entry chain plus next's new test-ci-queue-wait-no-status.sh entry at its position (55 entries, every target existence-verified). Enumeration guard green: population 61, enumerated 46, excluded (signed) 16.
211 lines
11 KiB
YAML
211 lines
11 KiB
YAML
# &node_image is the pre-baked CI base built by .woodpecker/ci-image.yml:
|
|
# node:24-alpine + python3/make/g++/postgresql-client + pnpm + a warm pnpm
|
|
# store. The install step resolves from the baked store (--prefer-offline)
|
|
# instead of paying a ~731s cold fetch + native compile every run.
|
|
#
|
|
# PINNED to an immutable lock-tag (#1328, brain D27): ci-image.yml pushes
|
|
# lock-<sha256(pnpm-lock.yaml)[:12]> atomically with :latest, so the two are
|
|
# byte-identical at push time. A mutable :latest resolves per-pod at pull time
|
|
# on the k8s backend, which made CI verdicts non-reproducible (same tree, same
|
|
# config, different images across runs; see #1324 comment 23382/23386). The pin
|
|
# changes ONLY through reviewed commits; a wrong tag fails loudly at image pull.
|
|
#
|
|
# Bump procedure: when a recipe change (pnpm-lock.yaml / Dockerfile.ci) lands on
|
|
# main, ci-image.yml pushes lock-<new>; a follow-up PR updates this anchor.
|
|
# Until then pipelines keep the old pin: reproducible, with the documented
|
|
# network-fallback lag (frozen-lockfile resolves missing packages from network).
|
|
# Known limitation: lock- addresses the lockfile only, so a Dockerfile-only
|
|
# change re-pushes the same tag with new content (#1328 follow-up: recipe-hash).
|
|
variables:
|
|
- &node_image 'git.mosaicstack.dev/mosaicstack/stack/ci-base:lock-9cb7ffcd8828'
|
|
- &enable_pnpm 'corepack enable'
|
|
|
|
when:
|
|
# PR + manual CI run on any branch — the pull_request pipeline is the merge gate.
|
|
# push CI is restricted to protected branches (main) so a feature-branch push no
|
|
# longer fires a redundant SECOND pipeline alongside its PR pipeline. This ~halves
|
|
# CI load on the storage-constrained runner with zero loss of gating (branch
|
|
# protection requires no push/ci status context; main still gets full push CI).
|
|
- event: [pull_request, manual]
|
|
- event: push
|
|
branch: main
|
|
|
|
# Turbo remote cache (turbo.mosaicstack.dev) is configured via Woodpecker
|
|
# repository-level environment variables (TURBO_API, TURBO_TEAM, TURBO_TOKEN).
|
|
# This avoids from_secret which is blocked on pull_request events.
|
|
# If the env vars aren't set, turbo falls back to local cache only.
|
|
|
|
steps:
|
|
install:
|
|
image: *node_image
|
|
commands:
|
|
- corepack enable
|
|
# python3/make/g++ are baked into ci-base; --prefer-offline resolves from
|
|
# the baked pnpm store.
|
|
- pnpm install --frozen-lockfile --prefer-offline
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# The steps below (sanitization, upgrade-guard, typecheck, lint, format,
|
|
# test) are the COMPLETE mandatory verification set. SDLC-D-034 mirrors them
|
|
# one-for-one in the canonical terminal verification command — root
|
|
# `pnpm verify:release` (scripts/verify-release.mjs) — which the publish
|
|
# pipeline (.woodpecker/publish.yml `verify` step) runs before ANY publish
|
|
# effect. These lines stay direct (not routed through the runner) because the
|
|
# #1017 test-enumeration guard audits framework tool paths through THIS
|
|
# surface; scripts/verify-release.test.mjs enforces that the runner's stage
|
|
# table keeps matching these commands exactly, so the two cannot drift.
|
|
# ---------------------------------------------------------------------------
|
|
|
|
# Canonical verify:release stage `sanitization`.
|
|
# Blocking gate: public framework package must contain no operator-specific
|
|
# personal data or private $HOME defaults. Runs early (no node_modules needed).
|
|
sanitization:
|
|
image: *node_image
|
|
commands:
|
|
- apk add --no-cache bash
|
|
- bash packages/mosaic/framework/tools/quality/scripts/verify-sanitized.sh
|
|
# Resident line-count ceiling over framework-owned resident files
|
|
# (Constitution + dispatcher + each RUNTIME.md slice). See DESIGN §7 / R9.
|
|
- bash packages/mosaic/framework/tools/quality/scripts/check-resident-budget.sh --self-test
|
|
- bash packages/mosaic/framework/tools/quality/scripts/check-resident-budget.sh
|
|
# Test-membership guard (#1017): also first link of test:framework-shell.
|
|
# Invoked from BOTH surfaces it audits (F2, PR #1018) — the guard is link
|
|
# [0] of the pnpm chain, so severing that chain would silence it together
|
|
# with everything it guards; this direct line keeps one instrument running.
|
|
- bash packages/mosaic/framework/tools/quality/scripts/check-test-enumeration.sh
|
|
# Tool-index gate: a shipped wrapper that appears in no resident index doc
|
|
# is undiscoverable from inside a session, and an agent that cannot learn a
|
|
# wrapper exists reaches for raw curl instead — which is how a Gitea review
|
|
# got filed PENDING three times. Ships-and-documented is one commit, or red.
|
|
- bash packages/mosaic/framework/tools/quality/scripts/check-tools-index.sh --self-test
|
|
- bash packages/mosaic/framework/tools/quality/scripts/check-tools-index.sh
|
|
# Hermetic regression for issue-close.sh (#1081): mocks tea/curl onto PATH
|
|
# and sandboxes a throwaway git repo, so it resolves no real credentials and
|
|
# joins CI directly rather than the exclusions file.
|
|
- bash packages/mosaic/framework/tools/git/test-issue-close-fail-closed.sh
|
|
# Hermetic behavioural regression for the PreToolUse wrapper guard: proves
|
|
# it still blocks the three mistakes AND still lets reads, unwrapped
|
|
# endpoints and ordinary commands through. Both directions are asserted —
|
|
# a guard that over-blocks gets routed around, which fails just as hard.
|
|
- bash packages/mosaic/framework/tools/git/test-wrapper-guard.sh
|
|
# Hermetic regression for mosaic-worktree.sh at fleet scale: stubs git onto
|
|
# PATH so `list` faces ~450 KB of porcelain. The defect it pins is invisible
|
|
# at small size — `git … | awk '…exit'` gives the producer SIGPIPE, which
|
|
# under `set -euo pipefail` aborts the caller silently with rc=141 and no
|
|
# output. A repo only reaches that once it has enough worktrees, so the
|
|
# stub supplies the scale instead of the host's own checkout.
|
|
- bash packages/mosaic/framework/tools/git/test-mosaic-worktree-large-repo.sh
|
|
|
|
# Canonical verify:release stage `upgrade-guard`.
|
|
# Blocking gate (#791): a framework upgrade must never write or delete an
|
|
# operator-owned path. The HARD GATE proves an unanticipated operator sentinel
|
|
# survives a keep-mode reseed byte-identical (with rsync present AND absent —
|
|
# keep mode is a single cp-based path that must not depend on rsync), and that a
|
|
# corrupt/empty/missing manifest aborts fail-closed leaving operator files
|
|
# untouched (B2/B3). The rollback gate proves a mid-sync failure is rolled back
|
|
# from the pre-update snapshot (B1). The durable-snapshot gate (#791 PR2) proves
|
|
# the retained, operator-scoped pre-update backup is taken before any mutation
|
|
# (0700/0600, secret never logged, retention-pruned) and that the post-sync
|
|
# verify net restores any operator file a manifest bug lets the sync touch. The
|
|
# migration matrix pins the v2→v3 contract-file semantics. Pure bash, no
|
|
# node_modules — runs early alongside sanitization.
|
|
upgrade-guard:
|
|
image: *node_image
|
|
commands:
|
|
- apk add --no-cache bash rsync
|
|
- bash packages/mosaic/framework/tools/quality/scripts/test-upgrade-manifest-guard.sh
|
|
- bash packages/mosaic/framework/tools/quality/scripts/test-upgrade-rollback.sh
|
|
- bash packages/mosaic/framework/tools/quality/scripts/test-upgrade-durable-snapshot.sh
|
|
- bash packages/mosaic/framework/tools/quality/scripts/test-install-migration.sh
|
|
|
|
# Canonical verify:release stage `typecheck` — the same `pnpm typecheck`
|
|
# invocation (which runs the checkout preflight first, then turbo).
|
|
typecheck:
|
|
image: *node_image
|
|
commands:
|
|
- *enable_pnpm
|
|
- pnpm typecheck
|
|
depends_on:
|
|
- install
|
|
- sanitization
|
|
- upgrade-guard
|
|
|
|
# lint, format, and test are independent — run in parallel after typecheck.
|
|
# Each runs exactly its canonical verify:release stage command.
|
|
lint:
|
|
image: *node_image
|
|
commands:
|
|
- *enable_pnpm
|
|
- pnpm lint
|
|
depends_on:
|
|
- typecheck
|
|
|
|
format:
|
|
image: *node_image
|
|
commands:
|
|
- *enable_pnpm
|
|
- pnpm format:check
|
|
depends_on:
|
|
- typecheck
|
|
|
|
# Canonical verify:release stage `test` — the `pnpm test` line below is the
|
|
# shared command; everything else in this step is PIPELINE-LEVEL
|
|
# prerequisite the canonical command expects its caller to provide (SDLC-D-034):
|
|
# the ci-postgres service + pg_isready wait + db:migrate (postgres path),
|
|
# `apk add openssl`, and the pinned pi install. None of those can move into
|
|
# the runner (it must also work locally on the PGlite path with no database).
|
|
test:
|
|
image: *node_image
|
|
environment:
|
|
# Avoid the namespace-level Woodpecker DB service named "postgres".
|
|
# The Kubernetes backend exposes service containers by step name.
|
|
DATABASE_URL: postgresql://mosaic:mosaic@ci-postgres:5432/mosaic
|
|
commands:
|
|
- *enable_pnpm
|
|
# openssl (#912) is the wake HMAC signer: the digest H1/H2, beacon B12,
|
|
# and install I8 legs hard-require it in CI. It is baked into ci-base via
|
|
# Dockerfile.ci, but ci-base only rebuilds on push-to-main/tag — this
|
|
# `apk add` guarantees openssl is present on PR pipelines too (and is a
|
|
# fast no-op once the rebuilt image already ships it).
|
|
- apk add --no-cache openssl
|
|
# Pi runtime (Invariant R): invariant_r_unittest.py hard-requires an
|
|
# installed `pi` binary at exactly this measured version — the test
|
|
# boots Pi's real tool registry to prove the read-only carve-out
|
|
# resolves to real, unshadowed builtins, and fails loud (by design)
|
|
# when the runtime is absent or drifts. The canonical Pi is
|
|
# @earendil-works/[email protected] exactly (@mariozechner/* is
|
|
# embedded-legacy). Step-level install because ci-base image publishes
|
|
# are currently blocked on registry auth; fold into Dockerfile.ci once
|
|
# that is fixed, keeping this as a fast no-op guard.
|
|
- npm install -g @earendil-works/[email protected]
|
|
# postgresql-client (pg_isready) is baked into ci-base.
|
|
# Wait up to 60s for CI postgres to be ready; fail fast if it never comes up.
|
|
- |
|
|
ready=0
|
|
for i in $(seq 1 60); do
|
|
if pg_isready -h ci-postgres -p 5432 -U mosaic; then
|
|
ready=1
|
|
break
|
|
fi
|
|
echo "Waiting for ci-postgres ($i/60)..."
|
|
sleep 1
|
|
done
|
|
if [ "$ready" -ne 1 ]; then
|
|
echo "ci-postgres did not become ready" >&2
|
|
exit 1
|
|
fi
|
|
# Run migrations (DATABASE_URL is set in environment above)
|
|
- pnpm --filter @mosaicstack/db run db:migrate
|
|
# Run all tests
|
|
- pnpm test
|
|
depends_on:
|
|
- typecheck
|
|
|
|
services:
|
|
ci-postgres:
|
|
image: pgvector/pgvector:pg17
|
|
environment:
|
|
POSTGRES_USER: mosaic
|
|
POSTGRES_PASSWORD: mosaic
|
|
POSTGRES_DB: mosaic
|