ci/woodpecker/pr/ci Pipeline failed
Two defects found by running the guard rather than reading it. 1. The guard resolved its sibling wrappers through a hardcoded $HOME/.config/mosaic/tools/git. On a host with no installed mosaic home — a CI container, a bare checkout — every wrapper lookup missed, `[ -x ]` failed, and the guard fell through allowing the raw API write it exists to block. It failed OPEN, silently, in exactly the environment least likely to notice. It now resolves relative to its own path, so it names the wrappers from the install it was launched from, with $HOME as the fallback. 2. There was no test. Adding one surfaced the guard's other sharp edge immediately: it matches the literal text of the Bash command, so a harness that embeds a blocked pattern inline trips the guard on itself rather than on the fixture. That is the correct fail-closed posture and it is now recorded in the test's own comments, because the next person will hit it too. test-wrapper-guard.sh asserts twelve fixtures and asserts the ALLOWED cases as hard as the blocked ones. A guard that over-blocks gets routed around and a guard that under-blocks is decoration; only pinning both edges keeps it useful. It is hermetic — no network, no credentials, no repository — so it joins the CI sanitization step directly rather than the exclusions file.
157 lines
7.0 KiB
YAML
157 lines
7.0 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.
|
|
variables:
|
|
- &node_image 'git.mosaicstack.dev/mosaicstack/stack/ci-base:latest'
|
|
- &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
|
|
|
|
# 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
|
|
|
|
# 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
|
|
|
|
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
|
|
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
|
|
|
|
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
|
|
# 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
|