diff --git a/.githooks/pre-push b/.githooks/pre-push index 32ac80330..c2dcd84fe 100755 --- a/.githooks/pre-push +++ b/.githooks/pre-push @@ -76,7 +76,7 @@ echo "pre-push: TypeScript change detected — running the build + typecheck gat echo " (skip in an emergency with: git push --no-verify)" >&2 # typecheck resolves cross-package types through each dep's dist/, so build first — -# exactly what .github/workflows/conformance.yml does before `typecheck`. +# exactly what scripts/ci-local.sh's ts-fast lane does before `typecheck`. if ! ( cd "$ROOT" && bun run --filter '*' build ) >/tmp/metaobjects-prepush-build.log 2>&1; then echo "" >&2 echo " ✖ pre-push BLOCKED: workspace build failed. Last lines:" >&2 diff --git a/.github/workflows/conformance.yml b/.github/workflows/conformance.yml index be866a580..a0017d87f 100644 --- a/.github/workflows/conformance.yml +++ b/.github/workflows/conformance.yml @@ -10,6 +10,19 @@ name: conformance # cheap public-repo SECURITY gate (hygiene / leak-scan) still runs on every PR. # Push-to-main coverage comes from local-ci.yml on the self-hosted runner. # +# THIN WRAPPER: scripts/ci-local.sh is the single definition of every check here. Each +# job checks out, installs the toolchain its lane needs, and calls the script with that +# lane's `--only` selector and `--no-integration` — integration-tests.yml owns the docker +# suites. Change a check in the script, never here. Lanes, as the script defines them: +# gates — fixture-lint, doc-template drift, embedded-library drift, leak scan and +# the other offline repo gates (`--only gates`) +# ts-fast — workspace build + typecheck, TS conformance, completeness (mutation) gate +# csharp / java-fast (java + kotlin) / python — each port's conformance corpora +# java-reactor — full-reactor `mvn clean install`, tests on (`--only java-slow`) +# The Java lanes point the script's Maven repository at ~/.m2/repository so setup-java's +# maven cache applies; the script's own default (~/.m2-ci) protects a developer's ~/.m2, +# which an ephemeral hosted runner does not have. +# # WHY A SCHEDULE, given local-ci already runs nightly: local-ci's `java-slow` lane runs # the same full reactor (`gate_java_reactor`, `mvn clean install`) — so "it does not run # the reactor" is NOT the gap. The gap is environmental, and it is measured. Two Java @@ -23,7 +36,8 @@ name: conformance # ordering are exactly the defect classes one environment cannot see on its own. A # tag-only trigger meant the second environment looked only at a cut, which is the most # expensive moment to find out. Standard runners are free for a public repo, so the -# second look costs nothing but wall-clock. +# second look costs nothing but wall-clock. The java-reactor job below sets +# MO_CI_JACOCO=1 so the script keeps JaCoCo ON here — that is the second environment. on: push: tags: @@ -33,34 +47,17 @@ on: - cron: '41 4 * * *' workflow_dispatch: +permissions: + contents: read + jobs: - fixture-lint: + gates: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - - uses: oven-sh/setup-bun@v2 - with: - bun-version: '1.3.14' - - name: Cache Bun install cache - uses: actions/cache@v4 with: - path: ~/.bun/install/cache - key: ${{ runner.os }}-bun-${{ hashFiles('bun.lock') }} - restore-keys: | - ${{ runner.os }}-bun- - - run: bun install - - run: cd server/typescript/packages/conformance && bun bin/conformance.ts lint ../../../../fixtures/conformance - - # TS type-safety gate. `bun test` transpiles per-file and does NOT typecheck, - # so a `tsc` error (e.g. a TS2300 duplicate-identifier, or a real type break) - # passes the test suite and ships green. This job runs the real compiler over - # the whole workspace. `build` first: cross-package imports resolve through each - # package's `dist/` (the `types`/`main` fields), so typecheck needs the deps - # built or it reports spurious TS2307 "cannot find module" across packages. - typecheck: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 + fetch-depth: 0 # leak-scan and the version gates diff against origin/main + persist-credentials: false - uses: oven-sh/setup-bun@v2 with: bun-version: '1.3.14' @@ -71,30 +68,27 @@ jobs: key: ${{ runner.os }}-bun-${{ hashFiles('bun.lock') }} restore-keys: | ${{ runner.os }}-bun- - - run: bun install - - name: Build all workspace packages (emit dist/ for cross-package types) - run: bun run --filter '*' build - - name: Typecheck the whole workspace (fails on any tsc error) - run: bun run --filter '*' typecheck + - run: scripts/ci-local.sh --only gates --strict-toolchains conformance: - needs: fixture-lint runs-on: ubuntu-latest strategy: fail-fast: false matrix: - language: [typescript, csharp, java, python] + lane: [ts-fast, csharp, java-fast, python] steps: - uses: actions/checkout@v4 + with: + persist-credentials: false - name: Set up Bun (TS) - if: matrix.language == 'typescript' + if: matrix.lane == 'ts-fast' uses: oven-sh/setup-bun@v2 with: bun-version: '1.3.14' - name: Cache Bun install cache (TS) - if: matrix.language == 'typescript' + if: matrix.lane == 'ts-fast' uses: actions/cache@v4 with: path: ~/.bun/install/cache @@ -103,13 +97,13 @@ jobs: ${{ runner.os }}-bun- - name: Set up .NET (C#) - if: matrix.language == 'csharp' + if: matrix.lane == 'csharp' uses: actions/setup-dotnet@v4 with: dotnet-version: '8.0.x' - name: Cache NuGet packages (C#) - if: matrix.language == 'csharp' + if: matrix.lane == 'csharp' uses: actions/cache@v4 with: path: ~/.nuget/packages @@ -117,8 +111,8 @@ jobs: restore-keys: | ${{ runner.os }}-nuget- - - name: Set up JDK (Java) - if: matrix.language == 'java' + - name: Set up JDK (Java + Kotlin) + if: matrix.lane == 'java-fast' uses: actions/setup-java@v4 with: distribution: 'temurin' @@ -126,231 +120,37 @@ jobs: cache: maven - name: Set up uv (Python) - if: matrix.language == 'python' + if: matrix.lane == 'python' uses: astral-sh/setup-uv@v3 with: enable-cache: true - - name: TypeScript conformance - if: matrix.language == 'typescript' - run: | - bun install - # Build the workspace BEFORE the suites. The generated-output compile - # gates (e.g. the #214 read-half tsc gate) run the real TypeScript - # compiler over emitted code that imports `@metaobjectsdev/runtime-ts/ - # drizzle-fastify`; tsc resolves that subpath through the package's - # `exports` map, whose `types`/`default` conditions point at `dist/` — - # build output a fresh checkout does not have (tsc does not honour the - # `bun` condition that makes bun's own resolution work off `src/`). - # `scripts/ci-local.sh` builds in its ts-fast lane before this same - # gate, so without this step hosted and local-CI disagree: local stays - # green off a previously-built dist/ while hosted fails with "Cannot - # find module … or its corresponding type declarations". - bun run --filter '*' build - # Loader + YAML + object-model corpora (packages/metadata). - cd server/typescript/packages/metadata && bun test test/conformance.test.ts test/yaml-conformance.test.ts test/object-model-conformance.test.ts - # Registry-conformance gate (TS is the reference emitter — byte-matches - # the committed canonical) + untested-vocabulary coverage report. - # All five ports run the gate now (SP-G reconciliation landed): C#/Python - # via the whole-project/whole-dir scope below; Java + Kotlin via the - # RegistryManifestConformanceTest added to their scoped -Dtest= lists. - bun test test/registry-conformance.test.ts test/registry-coverage.test.ts - # Byte-exact render / verify / extract / output-prompt corpora (packages/render). - cd ../render && bun test test/render-conformance.test.ts test/verify-conformance.test.ts test/extract/extract-conformance.test.ts test/output-prompt-conformance.test.ts - # Validator-parity corpus (generated input validation). No Docker. - cd ../integration-tests && bun test test/validation-conformance.test.ts - # migrate-ts — the sole cross-port schema-migration engine (ADR-0015). - # Its unit + integrity suites need no DB; the PG integration tests - # (gated on MIGRATE_TS_PG_URL) self-skip here and run in local-ci's ts-slow - # lane (every push to main) + integration-tests.yml (the v* tag backstop). - cd ../migrate-ts && bun test - # Doc-template + CLI suites: byte-identity template gate, embedded-template - # gate, neutrality / collision guards, and the docs golden corpus + - # `meta docs` command. Whole-package so future tests auto-run. No Docker. - cd ../codegen-ts && bun test - cd ../cli && bun test - - - name: C# conformance - if: matrix.language == 'csharp' - run: | - # Loader + YAML + object-model corpora (Conformance.Tests project). - cd server/csharp && dotnet test MetaObjects.Conformance.Tests/MetaObjects.Conformance.Tests.csproj --nologo --verbosity quiet - # Byte-exact render / verify / extract / output-prompt corpora (Render.Tests project). - dotnet test MetaObjects.Render.Tests/MetaObjects.Render.Tests.csproj --nologo --verbosity quiet - # Validator-parity corpus (generated DataAnnotations input validation). - dotnet test MetaObjects.Codegen.Tests/MetaObjects.Codegen.Tests.csproj --filter "FullyQualifiedName~ValidationConformance" --nologo --verbosity quiet - # Generator stable-name registry conformance (ADR-0021 D3). - dotnet test MetaObjects.Codegen.Tests/MetaObjects.Codegen.Tests.csproj --filter "FullyQualifiedName~GeneratorRegistryConformance" --nologo --verbosity quiet - # CLI (`dotnet meta`) — compiles MetaObjects.Cli + runs its tests. Without - # this the CLI is never built by any CI job, so a CLI compile break (or a - # gen --list registry drift) ships green. `dotnet test` only compiles the - # projects it actually runs, so this is what brings the CLI into the gate. - dotnet test MetaObjects.Cli.Tests/MetaObjects.Cli.Tests.csproj --nologo --verbosity quiet - # Cross-port api/csharp SDK-docs surface — execs `dotnet meta docs` end-to-end - # against the shared api-docs-cross-port manifest. MUST run AFTER the CLI build - # above: the test skips if the CLI dll is absent, so it only gates for real once - # the CLI exists. TS + Java + Python + Kotlin gate their own surfaces; this is C#. - dotnet test MetaObjects.Codegen.Tests/MetaObjects.Codegen.Tests.csproj --filter "FullyQualifiedName~ApiDocsCrossPort" --nologo --verbosity quiet - - - name: Java conformance - if: matrix.language == 'java' + - name: Conformance (${{ matrix.lane }}) + env: + LANE: ${{ matrix.lane }} run: | - cd server/java - # Install metadata + render first to avoid the SNAPSHOT-resolution race - # ("No type registered for: field.uuid") when the render module's tests - # reference the freshly-built metadata artifact. - mvn -pl metadata,render,codegen-spring -am install -DskipTests -q - # Loader + YAML + object-model corpora (metadata module) - # + registry-conformance gate (metamodel-vocabulary manifest byte-match, SP-G). - mvn -pl metadata test -Dtest='ConformanceTest,YamlConformanceTest,ObjectModelConformanceTest,RegistryManifestConformanceTest' -q - # Byte-exact render / verify / extract / output-prompt corpora (render module). - mvn -pl render test -Dtest='RenderCrossPortReportTest,VerifyConformanceTest,ExtractConformanceTest,OutputPromptConformanceTest' -q - # Validator-parity corpus (generated DTO jakarta.validation, codegen-spring module) - # + generator stable-name registry conformance (ADR-0021 D3). - mvn -pl codegen-spring test -Dtest='ValidationConformanceTest,GeneratorRegistryConformanceTest' -q - - - name: Python conformance - if: matrix.language == 'python' - run: | - cd server/python - # Loader + YAML corpora. - uv run pytest tests/conformance -q - # Byte-exact render / verify / extract / output-prompt corpora. - uv run pytest tests/render -q - # Validator-parity corpus (generated Pydantic input validation). - uv run pytest tests/codegen/test_validation_conformance.py -q - # Generator registry CLI ergonomics (gen --list / --generators selection, ADR-0021 D3). - uv run pytest tests/codegen/test_cli_registry.py -q - - conformance-kotlin: - # Kotlin (codegen-kotlin, KotlinPoet on the JVM) ships no loader / YAML / - # render / verify of its own — it reuses the shared JVM render engine. It - # runs the corpora it actually has: object-model + output-prompt + - # validator-parity (generated payload jakarta.validation). - needs: fixture-lint - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - - name: Set up JDK - uses: actions/setup-java@v4 - with: - distribution: 'temurin' - java-version: '21' - cache: maven - - - name: Kotlin conformance - run: | - cd server/java - # Build the metadata/render/codegen-kotlin chain first to avoid the - # SNAPSHOT-resolution race when the test module references freshly-built - # artifacts. - mvn -pl codegen-kotlin -am install -DskipTests -q - # object-model + output-prompt + validator-parity corpora (the corpora Kotlin has) - # + generator stable-name registry conformance (ADR-0021 D3) - # + registry-conformance gate (metamodel-vocabulary manifest byte-match, SP-G; - # composes the metamodel provider set so codegen-base/om classpath SPI does not pollute it). - mvn -pl codegen-kotlin test -Dtest='ObjectModelConformanceTest,OutputPromptConformanceTest,ValidationConformanceTest,GeneratorRegistryConformanceTest,RegistryManifestConformanceTest' -q + METAOBJECTS_CI_M2_REPO="$HOME/.m2/repository" \ + scripts/ci-local.sh --only "$LANE" --no-integration --strict-toolchains java-reactor: - # FULL-REACTOR build AND test of the Java parent reactor (server/java). - # - # The scoped `mvn -pl ... test` jobs above run ONLY a curated `-Dtest=` subset - # in metadata / render / codegen-spring / codegen-kotlin (+ their -am deps). - # Every other reactor module's tests are otherwise NEVER run by CI — most - # notably the `maven-plugin` mojo tests (MetaDataGeneratorMojoTest, DocsMojoTest, - # MetaDataVerifyMojoTest, ...). That gap let issue #37 ship: a mojo-test fixture - # broke `mvn clean install` on `main` while every PR showed green, because this - # job used to run `install -DskipTests` (compile only, no tests). - # - # Running a full-reactor `mvn install` (tests ON) compiles, packages, AND tests - # every module in the parent reactor — so a red reactor can no longer slip past - # green PR checks. No reactor module's tests need Docker/Testcontainers (verified: - # the only Testcontainers suites are integration-tests* and integration-tests-kotlin, - # which are INTENTIONALLY out-of-reactor and gated separately by integration-tests.yml). - # - # Intentionally still OUT of this gate (documented): - # - integration-tests / integration-tests-kotlin — out-of-reactor Docker/ - # Testcontainers persistence + api-contract corpora; gated by integration-tests.yml. - # - fatjar-smoke — standalone Spring Boot fat-jar bootstrap smoke (no tests); - # out-of-reactor by design, run on demand via scripts/fatjar-smoke.sh. - needs: fixture-lint + # FULL-REACTOR build AND test of the Java parent reactor (server/java) — the + # script's gate_java_reactor, which says why it exists and what stays out of it. + # The integration-tests* modules are out-of-reactor and gated by + # integration-tests.yml, hence --no-integration on the java-slow lane. runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 + with: + persist-credentials: false - name: Set up JDK uses: actions/setup-java@v4 with: distribution: 'temurin' java-version: '21' cache: maven - - name: Full-reactor build + test (all modules, tests on) - run: cd server/java && mvn -q clean install - - completeness-gate: - needs: [conformance, conformance-kotlin] - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - uses: oven-sh/setup-bun@v2 - with: - bun-version: '1.3.14' - - name: Cache Bun install cache - uses: actions/cache@v4 - with: - path: ~/.bun/install/cache - key: ${{ runner.os }}-bun-${{ hashFiles('bun.lock') }} - restore-keys: | - ${{ runner.os }}-bun- - - run: bun install - - run: cd server/typescript/packages/metadata && bun run conformance:mutation - - doc-template-drift: - # Fails if the bundled package copy of the canonical doc templates, or the - # embedded-templates TS module (used by the standalone `meta` binary), is - # stale. sync-doc-templates.sh reproduces both from the canonical root - # templates/docs/ source; a non-empty diff means a dev forgot to run it. - needs: fixture-lint - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - uses: oven-sh/setup-bun@v2 - with: - bun-version: '1.3.14' - - name: Cache Bun install cache - uses: actions/cache@v4 - with: - path: ~/.bun/install/cache - key: ${{ runner.os }}-bun-${{ hashFiles('bun.lock') }} - restore-keys: | - ${{ runner.os }}-bun- - - run: bun install - - name: Regenerate bundled + embedded doc templates from canonical - run: bash scripts/sync-doc-templates.sh - - name: Fail if synced/generated doc-template artifacts are stale - run: git diff --exit-code -- server/typescript/packages/codegen-ts/templates server/typescript/packages/codegen-ts/src/render-engine/embedded-templates.generated.ts - - embedded-library-drift: - # Fails if the embedded library metadata TS module is stale relative to the - # canonical library/**/*.yaml sources. generate-embedded-library.ts - # reproduces the module from the YAML sources; a non-empty diff means a dev - # updated a library YAML file without regenerating the embedded module. - needs: fixture-lint - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - uses: oven-sh/setup-bun@v2 - with: - bun-version: '1.3.14' - - name: Cache Bun install cache - uses: actions/cache@v4 - with: - path: ~/.bun/install/cache - key: ${{ runner.os }}-bun-${{ hashFiles('bun.lock') }} - restore-keys: | - ${{ runner.os }}-bun- - - run: bun install - - name: Regenerate embedded library metadata from canonical - run: bun run scripts/generate-embedded-library.ts - - name: Fail if the embedded library module is stale - run: git diff --exit-code -- server/typescript/packages/metadata/src/library/embedded-library.generated.ts + - name: Full-reactor build + test (all modules, tests on, JaCoCo on) + env: + MO_CI_JACOCO: '1' + run: | + METAOBJECTS_CI_M2_REPO="$HOME/.m2/repository" \ + scripts/ci-local.sh --only java-slow --no-integration --strict-toolchains diff --git a/.github/workflows/hygiene.yml b/.github/workflows/hygiene.yml index 3607bfce2..58cf8e4c6 100644 --- a/.github/workflows/hygiene.yml +++ b/.github/workflows/hygiene.yml @@ -3,6 +3,11 @@ name: hygiene # Public-repo leak gate: scans a PR's added lines for absolute local paths and # private/other-project names (structural patterns; the private denylist is local-only). # The scanner lives in .githooks/leak-scan.sh so its pattern literals don't self-trip. +# +# THIN WRAPPER: scripts/ci-local.sh is the single definition of this check. This +# workflow only checks out and calls it with `--only leak-scan`, pointing the scan at +# the PR's base branch through MO_CI_LEAK_BASE. `leak-scan` is the status main's branch +# protection requires, so the job name must not change. on: pull_request: @@ -17,5 +22,8 @@ jobs: - uses: actions/checkout@v4 with: fetch-depth: 0 + persist-credentials: false - name: Leak scan (added lines vs base) - run: bash .githooks/leak-scan.sh "origin/${{ github.base_ref }}" + env: + MO_CI_LEAK_BASE: origin/${{ github.base_ref }} + run: scripts/ci-local.sh --only leak-scan --strict-toolchains diff --git a/.github/workflows/integration-tests.yml b/.github/workflows/integration-tests.yml index f5010eaf9..cc6fab6c0 100644 --- a/.github/workflows/integration-tests.yml +++ b/.github/workflows/integration-tests.yml @@ -2,8 +2,9 @@ name: integration-tests # Enforces the persistence + api-contract conformance corpora — the Docker / # Testcontainers suites that are NOT in the default `mvn test` / `dotnet test` / -# `bun test` path — against a real Postgres for all five ports. Per-language jobs -# run in parallel; any port red blocks the gate. +# `bun test` path — against a real Postgres for all five ports, plus the migrate-ts +# and runtime-ts real-Postgres suites. Per-lane jobs run in parallel; any lane red +# blocks the gate. # # COST: this 5-port Testcontainers matrix is EXPENSIVE, so it does NOT run on every # push/PR. Triggers: @@ -14,6 +15,21 @@ name: integration-tests # scripts/integration-test.sh # The cheap public-repo SECURITY gate (hygiene / leak-scan) still runs on every PR. # Push-to-main coverage now comes from local-ci.yml on the self-hosted runner. +# +# THIN WRAPPER: scripts/ci-local.sh is the single definition of these checks. Each job +# installs the toolchain its lane needs and calls the script with that lane's `--only` +# selector and `--integration-only`, which runs just the lane's docker/Postgres half: +# ts-slow — migrate-ts real-PG suite, runtime-ts real-PG dialect matrix, TS +# persistence + api-contract corpora +# java-slow — the Java and Kotlin integration modules +# csharp / python — that port's integration corpora +# +# RELEASE BACKSTOP. The PRIMARY gate for the migrate-ts real-PG suites is local-ci.yml's +# ts-slow lane, on every push to main; the ts-slow job here is the cold-environment +# backstop on the v* tag. Tags are pushed AFTER publish (docs/RELEASING.md), so red HERE +# means a broken release is already live on four immutable registries — treat it as an +# incident, never as noise. That inversion is exactly how this lane once sat red for +# eight releases. on: push: @@ -21,21 +37,23 @@ on: - 'v*' workflow_dispatch: +permissions: + contents: read + jobs: release-gate: runs-on: ubuntu-latest strategy: fail-fast: false matrix: - port: [ts, csharp, java, kotlin, python] - # A single job-level Postgres sidecar shared by every port, instead of each - # port booting (and pulling) its own container per scenario. Mirrors the - # migrate-ts-pg job below. Each port's PG helper, when it sees - # METAOBJECTS_TEST_PG_URL, connects to this sidecar and CREATEs a - # uniquely-named database per scenario (dropping it on stop) — preserving the - # "fresh empty DB per scenario" isolation the per-port containers gave, with - # no image pull / container boot on the hot path. Local dev (no env var) still - # boots per-port containers exactly as before. + lane: [ts-slow, csharp, java-slow, python] + # A single job-level Postgres sidecar shared by every lane, instead of each + # port booting (and pulling) its own container per scenario. Each port's PG + # helper, when it sees METAOBJECTS_TEST_PG_URL, connects to this sidecar and + # CREATEs a uniquely-named database per scenario (dropping it on stop) — + # preserving the "fresh empty DB per scenario" isolation the per-port containers + # gave, with no image pull / container boot on the hot path. With the variable + # set, the script's own sidecar logic stands aside. services: postgres: image: postgres:16 @@ -52,9 +70,11 @@ jobs: --health-retries 5 steps: - uses: actions/checkout@v4 + with: + persist-credentials: false - - name: Set up Bun (TS port) - if: matrix.port == 'ts' + - name: Set up Bun (TS lane) + if: matrix.lane == 'ts-slow' uses: oven-sh/setup-bun@v2 with: # Pin a known-good Bun (1.3.8 segfaults on exit; see the flake that cost a @@ -62,8 +82,8 @@ jobs: # output — the actions/cache step below does that. bun-version: '1.3.14' - - name: Cache Bun install cache (TS port) - if: matrix.port == 'ts' + - name: Cache Bun install cache (TS lane) + if: matrix.lane == 'ts-slow' uses: actions/cache@v4 with: path: ~/.bun/install/cache @@ -71,14 +91,14 @@ jobs: restore-keys: | ${{ runner.os }}-bun- - - name: Set up .NET (C# port) - if: matrix.port == 'csharp' + - name: Set up .NET (C# lane) + if: matrix.lane == 'csharp' uses: actions/setup-dotnet@v4 with: dotnet-version: '8.0.x' - - name: Cache NuGet packages (C# port) - if: matrix.port == 'csharp' + - name: Cache NuGet packages (C# lane) + if: matrix.lane == 'csharp' uses: actions/cache@v4 with: path: ~/.nuget/packages @@ -86,77 +106,32 @@ jobs: restore-keys: | ${{ runner.os }}-nuget- - - name: Set up JDK (Java and Kotlin ports) - if: matrix.port == 'java' || matrix.port == 'kotlin' + - name: Set up JDK (Java + Kotlin lane) + if: matrix.lane == 'java-slow' uses: actions/setup-java@v4 with: distribution: 'temurin' java-version: '21' cache: maven - - name: Set up uv (Python port) - if: matrix.port == 'python' + - name: Set up uv (Python lane) + if: matrix.lane == 'python' uses: astral-sh/setup-uv@v3 with: enable-cache: true - - name: Install workspace deps - if: matrix.port == 'ts' - run: bun install - - - name: Run integration tests + - name: Run integration tests (${{ matrix.lane }}) env: + LANE: ${{ matrix.lane }} # The shared sidecar's admin URL. Each port's PG helper sees this and - # creates/drops a uniquely-named database per scenario off it, rather - # than booting its own container. Unset locally → per-port container - # fallback. + # creates/drops a uniquely-named database per scenario off it. METAOBJECTS_TEST_PG_URL: postgres://metaobjects:metaobjects@localhost:5432/metaobjects_test - run: ./scripts/integration-test.sh ${{ matrix.port }} - - # migrate-ts PG integration tests — the apply / lifecycle / rollback + - # introspection suites that exercise REAL Postgres behavior (advisory locks, - # multi-tenant ledger, down-migrations) that pg-mem cannot fake. They - # `describe.skip` unless MIGRATE_TS_PG_URL is set; a `services: postgres` - # container supplies the URL. - # - # The PRIMARY gate for these suites is local-ci.yml's ts-slow lane, on every push - # to main. This job is the cold-environment RELEASE BACKSTOP on the v* tag. Tags - # are pushed AFTER publish (docs/RELEASING.md), so red HERE means a broken release - # is already live on four immutable registries — treat it as an incident, never as - # noise. That inversion is exactly how this lane sat red for eight releases. - migrate-ts-pg: - runs-on: ubuntu-latest - services: - postgres: - image: postgres:16 - env: - POSTGRES_USER: migrate - POSTGRES_PASSWORD: migrate - POSTGRES_DB: migrate_test - ports: - - 5432:5432 - options: >- - --health-cmd "pg_isready -U migrate -d migrate_test" - --health-interval 10s - --health-timeout 5s - --health-retries 5 - steps: - - uses: actions/checkout@v4 - - uses: oven-sh/setup-bun@v2 - with: - bun-version: '1.3.14' - - name: Cache Bun install cache - uses: actions/cache@v4 - with: - path: ~/.bun/install/cache - key: ${{ runner.os }}-bun-${{ hashFiles('bun.lock') }} - restore-keys: | - ${{ runner.os }}-bun- - - run: bun install - - name: Run migrate-ts suite against real Postgres - env: - MIGRATE_TS_PG_URL: postgres://migrate:migrate@localhost:5432/migrate_test - # Arms the in-suite sentinel: if the URL above ever stops being set, the - # suite FAILS instead of silently skipping and reporting a green release gate. + # migrate-ts's real-PG suites run against the same sidecar, and the EXPECT + # flags arm the in-suite sentinels: if a URL ever stops being set, the suite + # FAILS instead of silently skipping and reporting a green release gate. + MIGRATE_TS_PG_URL: postgres://metaobjects:metaobjects@localhost:5432/metaobjects_test MIGRATE_TS_PG_EXPECT: '1' - run: cd server/typescript/packages/migrate-ts && bun test + RUNTIME_TS_PG_EXPECT: '1' + run: | + METAOBJECTS_CI_M2_REPO="$HOME/.m2/repository" \ + scripts/ci-local.sh --only "$LANE" --integration-only --strict-toolchains diff --git a/.github/workflows/local-ci.yml b/.github/workflows/local-ci.yml index 1fb0fe8e6..a7b82e8e1 100644 --- a/.github/workflows/local-ci.yml +++ b/.github/workflows/local-ci.yml @@ -162,7 +162,8 @@ jobs: # push — strictly AFTER the immutable four-registry publish — and so sat red for # eight straight releases (v0.20.11 … v0.21.1) with nobody looking. Reuses this # job's existing sidecar; the suite is proven to coexist in one database in a - # serial run (the hosted tag job runs it against a single migrate_test DB). + # serial run (the hosted tag job, integration-tests.yml's ts-slow lane, runs it + # in the same shared metaobjects_test sidecar database). MIGRATE_TS_PG_URL: postgres://metaobjects:metaobjects@localhost:${{ job.services.postgres.ports['5432'] }}/metaobjects_test # Makes the in-suite sentinel FAIL if the URL above ever rots away (renamed # variable, dropped sidecar) rather than describe.skip-ing in silence. diff --git a/.no-mistakes.yaml b/.no-mistakes.yaml index 6d9aeecf4..cc08aef10 100644 --- a/.no-mistakes.yaml +++ b/.no-mistakes.yaml @@ -2,10 +2,10 @@ # # WHY THIS EXISTS. With no commands declared, the gate's test step is an AGENT choosing # "the smallest relevant tests" to run. This repository already has the answer committed: -# `scripts/ci-local.sh`, which mirrors `.github/workflows/` — hygiene.yml's leak scan, -# conformance.yml's eight jobs, and integration-tests.yml's Testcontainers matrix. Since -# GitHub Actions was disabled on this repository (2026-09-16) that script is the ONLY thing -# that runs those checks at all, so pinning the gate to it is what keeps them running. +# `scripts/ci-local.sh`, the single definition of the checks — every check workflow in +# `.github/workflows/` runs on GitHub as a thin wrapper over it (hygiene.yml's leak scan, +# conformance.yml's lanes, integration-tests.yml's Testcontainers matrix), so pinning the +# gate to it runs exactly the repository's checks, the same ones GitHub runs. # # THE SPLIT. These two commands together are exactly `scripts/ci-local.sh --quick`, cut # along the script's own `--only` section boundaries so neither step repeats the other. @@ -53,9 +53,8 @@ # takes effect until it is merged to `main`. # # There is no `ci` section and none is needed: `no-mistakes status` reports this repository -# as `ci_mode: local`, so the gate skips the CI step and monitors no forge checks. That is -# already the right answer while Actions is disabled — a PR triggers zero workflows, so -# there would be nothing to wait for. +# as `ci_mode: local`, so the gate skips the CI step and monitors no forge checks — the +# commands above already run the same script the GitHub workflows run. commands: test: "scripts/ci-local.sh --only ts-fast --only ts-unit --strict-toolchains" diff --git a/AGENTS.md b/AGENTS.md index 5118b122b..dea015517 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -124,7 +124,8 @@ would publish the committed (non-RC) version; only `publish-csharp.yml` has a `v (To toggle it: `gh api -X PUT repos///actions/permissions -F enabled=true` — `-F` for a TYPED boolean, since `-f` sends the string `"true"` and 422s.) -**`scripts/ci-local.sh` is still the pre-PR gate**, and it mirrors the hosted lanes — +**`scripts/ci-local.sh` is still the pre-PR gate**, and it is the single definition of the +hosted checks — every check workflow in `.github/workflows/` is a thin wrapper that calls it. `--quick` covers `hygiene.yml` in full plus the TypeScript half of `conformance.yml`, and the flagless full run adds the C#/Java/Kotlin/Python conformance lanes, the Java reactor and `integration-tests.yml`'s diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index ea8e3f0e3..f6bab75b8 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -73,10 +73,12 @@ Cross-language persistence / api-contract corpora (Docker + Testcontainers) run ### Local CI (this IS the CI — run it before opening/merging a PR) -GitHub Actions is disabled on this repository, so the files in `.github/workflows/` -still describe the checks but no longer run them. They are kept because the switch is -reversible; meanwhile `scripts/ci-local.sh` is what runs them, and it mirrors all three -check workflows: +GitHub Actions is enabled on this repository. The three check workflows in +`.github/workflows/` run on GitHub as thin wrappers over `scripts/ci-local.sh`, +the single definition of the checks: each wrapper checks out, installs the +toolchains its lane needs, and calls the script, so a local run is the same +check a workflow run would be. Run the script locally before opening or merging +a PR, so nothing red leaves your machine: ```bash scripts/ci-local.sh # full parity: hygiene.yml's leak-scan, all-port diff --git a/fixtures/registry-conformance/README.md b/fixtures/registry-conformance/README.md index 8bc729b8b..8b9f107e5 100644 --- a/fixtures/registry-conformance/README.md +++ b/fixtures/registry-conformance/README.md @@ -325,7 +325,7 @@ divergence: | Java | **live + green** (byte-identical; reconciled SP-G Units 4-7, gate re-enabled Unit 8) | `metadata/src/test/java/com/metaobjects/registry/RegistryManifestConformanceTest.java` | its `java` section (in the metadata `-Dtest=` list) | | Kotlin | **live + green** (byte-identical; composes the metamodel provider set) | `codegen-kotlin/src/test/kotlin/com/metaobjects/generator/kotlin/RegistryManifestConformanceTest.kt` | its `java` section (in the codegen-kotlin `-Dtest=` list) | -`.github/workflows/conformance.yml` describes where each port's runner is wired (the `conformance` matrix plus `conformance-kotlin`) but no longer runs them — Actions is disabled on this repository, see AGENTS.md; `scripts/ci-local.sh` is what runs them, and `--quick` covers TypeScript only. TS / C# / Python were live from the start; Java + Kotlin were re-enabled in SP-G Unit 8 after the Java metamodel-vocabulary reconciliation (Units 4-7) landed (see the **divergence analysis**: +`scripts/ci-local.sh` is where each port's runner is wired (its `ts-fast`, `csharp`, `java-fast` — Java and Kotlin — and `python` lanes); `.github/workflows/conformance.yml` is a thin wrapper that calls the script once per lane, and `--quick` covers TypeScript only. TS / C# / Python were live from the start; Java + Kotlin were re-enabled in SP-G Unit 8 after the Java metamodel-vocabulary reconciliation (Units 4-7) landed (see the **divergence analysis**: [`docs/superpowers/specs/2026-06-02-sp-g-java-registry-divergence-analysis.md`](../../docs/superpowers/specs/2026-06-02-sp-g-java-registry-divergence-analysis.md) and the [reconciliation plan](../../docs/superpowers/plans/2026-06-02-sp-g-java-reconciliation-plan.md)). diff --git a/fixtures/validation-conformance/README.md b/fixtures/validation-conformance/README.md index 1b8755c43..26341ac3a 100644 --- a/fixtures/validation-conformance/README.md +++ b/fixtures/validation-conformance/README.md @@ -154,11 +154,11 @@ runners wrap the bind step so a native-parse failure maps to `valid=false`. ## CI gate All five port runners assert byte-identical boolean verdicts across all five -generated validation artifacts. `.github/workflows/conformance.yml` describes -them (TS/C#/Java/Python under the `conformance` matrix, Kotlin under -`conformance-kotlin`) but no longer runs them — Actions is disabled here, see -AGENTS.md. `scripts/ci-local.sh` is what runs them: the five live in its -`csharp`, `java` and `python` sections plus `ts-fast`, so the flagless +generated validation artifacts. `scripts/ci-local.sh` is what runs them, and +`.github/workflows/conformance.yml` is a thin wrapper that calls it once per +lane. The five live in the script's +`csharp`, `java` (Java and Kotlin, as `java-fast`) and `python` sections plus +`ts-fast`, so the flagless `scripts/ci-local.sh` covers all five and `--quick` covers TypeScript only. See [`docs/CONFORMANCE.md`](../../docs/CONFORMANCE.md). diff --git a/scripts/ci-local.sh b/scripts/ci-local.sh index a52410904..d6493e62a 100755 --- a/scripts/ci-local.sh +++ b/scripts/ci-local.sh @@ -2,14 +2,16 @@ # # Local CI — run the gates .github/workflows/ describes, on your machine. # -# GitHub Actions is DISABLED on this repository (2026-09-16), so nothing in -# .github/workflows/ fires — not hygiene.yml's leak scan on a PR, not -# conformance.yml, not integration-tests.yml, not local-ci.yml on the -# self-hosted runner. The workflow files are kept, and unchanged, because the -# switch is reversible. Until it is reversed THIS SCRIPT IS THE ONLY THING THAT -# RUNS THEM, including the public-repo leak scan. Run it before opening or -# merging a PR, so nothing red leaves your machine. The no-mistakes validation -# gate runs it automatically — see .no-mistakes.yaml. +# THIS SCRIPT IS THE SINGLE DEFINITION of the repository's checks. Every check +# workflow in .github/workflows/ — hygiene.yml, conformance.yml, +# integration-tests.yml and local-ci.yml — is a thin wrapper: it checks out, +# installs the toolchains a lane needs, and calls this script with a selector. +# None carries a step body of its own, so a workflow and a local run cannot +# drift apart. Add or change a check HERE, never in a workflow. When GitHub +# Actions is off, this script is the only thing that runs them, including the +# public-repo leak scan. Run it before opening or merging a PR, so nothing red +# leaves your machine. The no-mistakes validation gate runs it automatically — +# see .no-mistakes.yaml. # # Usage: # scripts/ci-local.sh # FULL parity: all-port conformance + full Java @@ -25,6 +27,8 @@ # # exclusive with --quick (exit 2 if combined). # # gates → leak-scan, pom parity, fixture-lint, # # doc-template drift, embedded-library drift +# # leak-scan → the public-repo leak scan ALONE +# # (hygiene.yml's PR gate; also in gates) # # ts → ts build+typecheck, ts conformance, # # completeness-gate, full unit suites # # (ts-unit), integration-tests ts @@ -47,6 +51,16 @@ # # python → full python test suite + integration-tests # # csharp → full csharp codegen suite + conformance # # + integration-tests +# scripts/ci-local.sh --no-integration +# # Drop the docker/Postgres half of the selected +# # sections: the integration suites, the +# # migrate-ts / runtime-ts real-PG gates and the +# # sidecar. conformance.yml uses it, because +# # integration-tests.yml owns those suites. +# scripts/ci-local.sh --integration-only +# # The inverse: run ONLY that docker/Postgres half +# # (integration-tests.yml). Mutually exclusive +# # with --quick and with --no-integration. # scripts/ci-local.sh --strict-toolchains # # Promote missing-toolchain and docker-down SKIPs # # to FAILs; useful in CI or a full-toolchain @@ -61,6 +75,11 @@ # does from its `services:` block — nothing here changes. Opt out with # MO_CI_NO_PG_SIDECAR=1; rename it with MO_PG_SIDECAR_NAME. # +# MO_CI_LEAK_BASE overrides the ref the leak scan diffs against (default origin/main, +# else HEAD~1); hygiene.yml points it at the PR's base branch. MO_CI_JACOCO=1 keeps +# JaCoCo ON in the Java reactor, which local-ci skips — conformance.yml's nightly sets +# it, because the second, instrumented environment is why that schedule exists. +# # Set MO_CI_LIST_ONLY=1 to print the steps that would run (given the current # flags) and exit 0 without running anything — useful for verifying section # selection without waiting for tests to complete. @@ -72,22 +91,25 @@ # so use it wherever a skip would mean a real gap rather than an absent toolchain: # the pre-release run, and the no-mistakes gate's two commands (.no-mistakes.yaml). # -# Mirrors: hygiene.yml (leak-scan) · conformance.yml (fixture-lint, typecheck, -# 5-port conformance, kotlin, java-reactor, completeness-gate, doc-template-drift, -# embedded-library-drift) · integration-tests.yml (5-port suite + migrate-ts-pg). +# Called by: hygiene.yml (--only leak-scan) · conformance.yml (--only gates / +# ts-fast / csharp / java-fast / python, and java-slow for the reactor, all with +# --no-integration) · integration-tests.yml (--integration-only, one job per lane) · +# local-ci.yml (one job per lane). set -uo pipefail ROOT="$(cd "$(dirname "$0")/.." && pwd)" cd "$ROOT" -QUICK=0; STRICT=0; ONLY="" +QUICK=0; STRICT=0; ONLY=""; NO_INTEG=0; INTEG_ONLY=0 while [ $# -gt 0 ]; do case "$1" in --quick) QUICK=1 ;; --strict-toolchains) STRICT=1 ;; + --no-integration) NO_INTEG=1 ;; + --integration-only) INTEG_ONLY=1 ;; --only) shift; case "${1:-}" in - gates|ts|ts-fast|ts-unit|ts-slow|java|java-fast|java-slow|python|csharp) ONLY="$ONLY ${1}" ;; - *) echo "--only expects gates|ts|ts-fast|ts-unit|ts-slow|java|java-fast|java-slow|python|csharp, got '${1:-}'" >&2; exit 2 ;; + gates|leak-scan|ts|ts-fast|ts-unit|ts-slow|java|java-fast|java-slow|python|csharp) ONLY="$ONLY ${1}" ;; + *) echo "--only expects gates|leak-scan|ts|ts-fast|ts-unit|ts-slow|java|java-fast|java-slow|python|csharp, got '${1:-}'" >&2; exit 2 ;; esac ;; -h|--help) awk 'NR==1{next} /^set -uo/{exit} {sub(/^# ?/,""); print}' "$0"; exit 0 ;; *) echo "unknown arg: $1 (see --help)" >&2; exit 2 ;; @@ -95,12 +117,23 @@ while [ $# -gt 0 ]; do shift done [ -n "$ONLY" ] && [ "$QUICK" -eq 1 ] && { echo "--only and --quick are mutually exclusive" >&2; exit 2; } +[ "$INTEG_ONLY" -eq 1 ] && [ "$QUICK" -eq 1 ] && { echo "--integration-only and --quick are mutually exclusive" >&2; exit 2; } +[ "$INTEG_ONLY" -eq 1 ] && [ "$NO_INTEG" -eq 1 ] && { echo "--integration-only and --no-integration are mutually exclusive" >&2; exit 2; } -want() { # want
— true when the section should run +in_lane() { # in_lane
— true when the section is selected [ -z "$ONLY" ] && return 0 case " $ONLY " in *" $1 "*) return 0 ;; *) return 1 ;; esac } -want_any() { # want_any — true when ANY listed section should run +in_lane_any() { # in_lane_any — true when ANY listed section is selected + local s; for s in "$@"; do in_lane "$s" && return 0; done; return 1 +} +# want / want_any guard the NON-integration steps, so --integration-only turns them all +# off. The docker/Postgres block below asks in_lane / in_lane_any directly. +want() { # want
— true when the section's non-integration steps should run + [ "$INTEG_ONLY" -eq 1 ] && return 1 + in_lane "$1" +} +want_any() { # want_any — true when ANY listed section's should run local s; for s in "$@"; do want "$s" && return 0; done; return 1 } @@ -143,8 +176,8 @@ step_if() { # step_if "" — SKIP (or FAIL under --stric # ── hygiene.yml: public-repo leak scan (the SECURITY gate kept in CI) ────────── gate_leak_scan() { - local base="origin/main" - git rev-parse --verify -q "$base" >/dev/null 2>&1 || base="HEAD~1" + local base="${MO_CI_LEAK_BASE:-origin/main}" + [ -n "${MO_CI_LEAK_BASE:-}" ] || git rev-parse --verify -q "$base" >/dev/null 2>&1 || base="HEAD~1" bash .githooks/leak-scan.sh "$base" } @@ -569,12 +602,17 @@ gate_ts_unit() { ( cd "client/web/packages/$p" && bun test --timeout 30000 ) || return 1 done } +# The ApiDocsCrossPort re-run is ORDER-dependent, not redundant. That test execs the built +# `dotnet meta` and soft-skips when the CLI dll is absent — and the whole-project Codegen.Tests +# run above it comes BEFORE Cli.Tests, which is what builds the CLI. On a clean checkout (any +# CI runner) the first pass therefore skips it; only this run, after the CLI exists, gates it. gate_conf_csharp() { ( cd server/csharp \ && dotnet test MetaObjects.Conformance.Tests/MetaObjects.Conformance.Tests.csproj --nologo --verbosity quiet \ && dotnet test MetaObjects.Render.Tests/MetaObjects.Render.Tests.csproj --nologo --verbosity quiet \ && dotnet test MetaObjects.Codegen.Tests/MetaObjects.Codegen.Tests.csproj --nologo --verbosity quiet \ - && dotnet test MetaObjects.Cli.Tests/MetaObjects.Cli.Tests.csproj --nologo --verbosity quiet ) + && dotnet test MetaObjects.Cli.Tests/MetaObjects.Cli.Tests.csproj --nologo --verbosity quiet \ + && dotnet test MetaObjects.Codegen.Tests/MetaObjects.Codegen.Tests.csproj --filter "FullyQualifiedName~ApiDocsCrossPort" --nologo --verbosity quiet ) } # NOTE: these -Dtest= lists are a CHERRY-PICK, so a new test class that is not named here # runs in NO per-push lane and reports green. Adding a test to this module means adding its @@ -616,7 +654,12 @@ gate_conf_kotlin() { # never fails the build — it is pure instrumentation overhead for a pass/fail CI # signal. `clean` stays: self-hosted workspaces persist and actions/checkout wipes # only git-tracked state, so a stale target/ can otherwise leak between runs. -gate_java_reactor() { ( cd server/java && mvn -q clean install -Djacoco.skip=true ); } +# MO_CI_JACOCO=1 keeps it ON — conformance.yml's hosted nightly, whose header says why. +gate_java_reactor() { + local jacoco="-Djacoco.skip=true" + [ "${MO_CI_JACOCO:-0}" = "1" ] && jacoco="" + ( cd server/java && mvn -q clean install $jacoco ) +} gate_completeness() { ( cd server/typescript/packages/metadata && bun run conformance:mutation ); } gate_doc_template_drift() { bash scripts/sync-doc-templates.sh \ @@ -742,6 +785,8 @@ elif [ "$QUICK" -eq 1 ]; then else _mode="full — all ports + reactor + docker" fi +[ "$NO_INTEG" -eq 1 ] && _mode="$_mode, no integration" +[ "$INTEG_ONLY" -eq 1 ] && _mode="$_mode, integration only" echo "metaobjects local CI (mode: $_mode)" # ── Optional dry-run listing ────────────────────────────────────────────────── @@ -757,7 +802,7 @@ fi # ── Step invocations ────────────────────────────────────────────────────────── # Fast tier: shared gates and TS checks, interleaved in the original order so # that no-flags execution is byte-equivalent to the pre-refactor script. -if want gates; then step "leak-scan (security)" gate_leak_scan; fi +if want_any gates leak-scan; then step "leak-scan (security)" gate_leak_scan; fi if want gates; then step "pom-version parity" gate_pom_versions; fi if want gates; then step "bun-version parity" gate_bun_version; fi if want gates; then step "uv.lock version parity" gate_uv_lock_version; fi @@ -832,7 +877,13 @@ else if want_any java java-fast; then step_if mvn "conformance: kotlin" gate_conf_kotlin; fi if want_any java java-slow; then step_if mvn "java-reactor (install)" gate_java_reactor; fi # Docker integration — full suite when no --only, per-port otherwise. - if [ -z "$ONLY" ]; then + # --no-integration drops this whole half; --integration-only keeps ONLY it, which is + # why it asks in_lane / in_lane_any (selection) where the checks above ask want. + if [ "$NO_INTEG" -eq 1 ]; then + # Not added to SKIP: a selector the caller chose is not a gap, and a SKIP entry would + # make every conformance.yml lane report "passed with skips" under --strict-toolchains. + echo ""; echo "── ⊘ --no-integration: not running the docker/Postgres integration half ──" + elif [ -z "$ONLY" ]; then if docker info >/dev/null 2>&1; then step "integration-tests (5-port + docker)" gate_integration else @@ -842,8 +893,8 @@ else else # ts-slow needs the workspace dist/ to run integration. When the fast lane also # runs (umbrella `ts` / local full), its build already produced it — only build - # here when ts-slow runs in isolation (the CI ts-slow job). - if want_any ts ts-slow && ! want_any ts ts-fast; then step_if bun "ts build (for integration)" gate_ts_build; fi + # here when ts-slow runs without it (the CI ts-slow job, or --integration-only). + if in_lane_any ts ts-slow && ! want_any ts ts-fast; then step_if bun "ts build (for integration)" gate_ts_build; fi # Bring the sidecar up BEFORE the two real-PG gates, not just before the docker # integration step. `ensure_pg_sidecar` exports MIGRATE_TS_PG_URL and its own comment # says that is so "migrate-ts's real-Postgres suites run locally instead of @@ -856,13 +907,13 @@ else # # Ordered BEFORE the docker integration step so a container-readiness flake there # can never prevent the migrate verdict from being produced. - if want_any ts ts-slow && docker info >/dev/null 2>&1; then ensure_pg_sidecar; fi - want_any ts ts-slow && step_if bun "migrate-ts real-PG suite" gate_migrate_ts_pg - want_any ts ts-slow && step_if bun "runtime-ts real-PG dialect matrix" gate_runtime_ts_pg - want_any ts ts-slow && run_integration_for ts ts - want_any java java-slow && run_integration_for java java kotlin - want python && run_integration_for python python - want csharp && run_integration_for csharp csharp + if in_lane_any ts ts-slow && docker info >/dev/null 2>&1; then ensure_pg_sidecar; fi + in_lane_any ts ts-slow && step_if bun "migrate-ts real-PG suite" gate_migrate_ts_pg + in_lane_any ts ts-slow && step_if bun "runtime-ts real-PG dialect matrix" gate_runtime_ts_pg + in_lane_any ts ts-slow && run_integration_for ts ts + in_lane_any java java-slow && run_integration_for java java kotlin + in_lane python && run_integration_for python python + in_lane csharp && run_integration_for csharp csharp fi fi diff --git a/server/csharp/MetaObjects.Codegen.Tests/ApiDocsCrossPortConformanceTests.cs b/server/csharp/MetaObjects.Codegen.Tests/ApiDocsCrossPortConformanceTests.cs index 5b0df8291..cc923a073 100644 --- a/server/csharp/MetaObjects.Codegen.Tests/ApiDocsCrossPortConformanceTests.cs +++ b/server/csharp/MetaObjects.Codegen.Tests/ApiDocsCrossPortConformanceTests.cs @@ -105,7 +105,7 @@ public void CsharpApiDocsSurface_MatchesTheSharedManifest() // builds always build the CLI, so the gate is real there. // Soft-skip when the CLI isn't built: an isolated `dotnet test` on this project alone // does not build MetaObjects.Cli, so there is nothing to exercise. CI runs this only - // AFTER building the CLI (see conformance.yml), so the gate is real there. (xUnit 2.9's + // AFTER building the CLI (see gate_conf_csharp in scripts/ci-local.sh), so the gate is real there. (xUnit 2.9's // dynamic Assert.Skip isn't enabled in this project; a visible console note + early // return is the low-dependency equivalent — never a hard failure for a missing build.) var cliDll = FindCliDll(); diff --git a/server/java/metadata/src/test/java/com/metaobjects/registry/RegistryManifestConformanceTest.java b/server/java/metadata/src/test/java/com/metaobjects/registry/RegistryManifestConformanceTest.java index 15d57675d..472813ff6 100644 --- a/server/java/metadata/src/test/java/com/metaobjects/registry/RegistryManifestConformanceTest.java +++ b/server/java/metadata/src/test/java/com/metaobjects/registry/RegistryManifestConformanceTest.java @@ -53,7 +53,7 @@ * *

LIVE (SP-G Unit 8). Units 4-7 reconciled the Java metamodel * registry to byte-match the canonical; Unit 8 re-enabled this gate (and the - * Kotlin one), wired both into {@code .github/workflows/conformance.yml}, and + * Kotlin one), wired both into the conformance lanes of {@code scripts/ci-local.sh}, and * constrained the runner to the defined metamodel provider set (above). The * history of the reconciled divergences is retained below for context.

*