Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .githooks/pre-push
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
298 changes: 49 additions & 249 deletions .github/workflows/conformance.yml

Large diffs are not rendered by default.

10 changes: 9 additions & 1 deletion .github/workflows/hygiene.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand All @@ -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
133 changes: 54 additions & 79 deletions .github/workflows/integration-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand All @@ -14,28 +15,45 @@ name: integration-tests
# scripts/integration-test.sh <port>
# 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:
tags:
- '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
Expand All @@ -52,111 +70,68 @@ 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
# full re-run). setup-bun caches the Bun binary but NOT `bun install`
# 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
key: ${{ runner.os }}-bun-${{ hashFiles('bun.lock') }}
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
key: ${{ runner.os }}-nuget-${{ hashFiles('server/csharp/**/*.csproj') }}
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
3 changes: 2 additions & 1 deletion .github/workflows/local-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
13 changes: 6 additions & 7 deletions .no-mistakes.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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"
Expand Down
3 changes: 2 additions & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -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/<owner>/<repo>/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
Expand Down
10 changes: 6 additions & 4 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion fixtures/registry-conformance/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)).

Expand Down
10 changes: 5 additions & 5 deletions fixtures/validation-conformance/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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).

Expand Down
Loading
Loading