feat(cli): verify --db can target Cloudflare D1 via --d1, and warns w… #234
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # Local CI — runs on the maintainer's self-hosted runner at ZERO Actions cost. | |
| # | |
| # SECURITY (public repo + self-hosted runner): this workflow must NEVER gain a | |
| # `pull_request` trigger. Push events to main can only come from users with | |
| # write access, so fork-submitted code cannot reach the runner. Keep it that way. | |
| # | |
| # Tiers: | |
| # push to main -> affected ports only (scripts/ci-affected-ports.sh) | |
| # nightly + dispatch -> full matrix | |
| # | |
| # The ts and java ports are each split into parallel jobs — a FAST lane (the | |
| # quick pure-code correctness signal: ts-fast = build+typecheck/conformance/ | |
| # completeness, java-fast = java+kotlin conformance), a UNIT lane for ts (ts-unit = | |
| # the full per-package suites — metadata/render/runtime-ts + client/web — that | |
| # ts-fast's named-file conformance gate leaves ungated), and a SLOW lane (docker | |
| # integration + heavy build: ts-slow, java-slow = reactor + integration). The | |
| # split surfaces the verdict fast and means a cancel/OOM/flake mid-integration no | |
| # longer kills the fast signal. `detect` emits the umbrella token (`ts` / `java`); | |
| # all of that port's jobs gate on it. | |
| # | |
| # Integration-running jobs (ts-slow, python, csharp, java-slow) share a Postgres | |
| # SIDECAR instead of each test booting its own Testcontainers Postgres per | |
| # scenario (the java+kotlin lanes alone booted ~30-40 serial containers). Each | |
| # port's PG helper honors METAOBJECTS_TEST_PG_URL and CREATEs a uniquely-named | |
| # database per scenario against the sidecar. The sidecar publishes 5432 to a | |
| # DYNAMIC host port (`ports: - 5432`, read back via job.services.*.ports) so | |
| # concurrent jobs on the shared self-hosted host never collide on a fixed port. | |
| # | |
| # Measured 2026-07-03 (pre-split, full tier, all ports in parallel, warm caches): | |
| # detect 0.2m · gates 0.1m · csharp 2m · python 2.6m · ts 5.3m · java 9.8m | |
| name: local-ci | |
| on: | |
| push: | |
| branches: [main] | |
| schedule: | |
| - cron: '17 8 * * *' | |
| workflow_dispatch: | |
| inputs: | |
| tier: | |
| description: 'full = all ports; affected = ports touched by HEAD~1..HEAD' | |
| type: choice | |
| options: [full, affected] | |
| default: full | |
| concurrency: | |
| group: local-ci-${{ github.ref }} | |
| cancel-in-progress: true | |
| jobs: | |
| detect: | |
| runs-on: [self-hosted, linux] | |
| outputs: | |
| ports: ${{ steps.sel.outputs.ports }} | |
| steps: | |
| - uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 0 | |
| persist-credentials: false | |
| - id: sel | |
| env: | |
| BASE: ${{ github.event.before }} | |
| run: | | |
| if [ "${{ github.event_name }}" = "push" ]; then | |
| ports="$(scripts/ci-affected-ports.sh "$BASE" "$GITHUB_SHA")" | |
| elif [ "${{ github.event_name }}" = "workflow_dispatch" ] && [ "${{ inputs.tier }}" = "affected" ]; then | |
| ports="$(scripts/ci-affected-ports.sh "$GITHUB_SHA~1" "$GITHUB_SHA")" | |
| else | |
| ports="ts java python csharp" | |
| fi | |
| echo "selected ports: '$ports'" | |
| echo "ports=$ports" >> "$GITHUB_OUTPUT" | |
| gates: | |
| needs: detect | |
| runs-on: [self-hosted, linux] | |
| timeout-minutes: 20 | |
| steps: | |
| - uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 0 # leak-scan diffs against origin/main | |
| persist-credentials: false | |
| - run: scripts/ci-local.sh --only gates --strict-toolchains | |
| # TS FAST lane — build+typecheck, conformance, completeness (the pure-code | |
| # correctness signal). No docker/integration, so no sidecar. | |
| ts-fast: | |
| needs: detect | |
| if: contains(needs.detect.outputs.ports, 'ts') | |
| runs-on: [self-hosted, linux] | |
| timeout-minutes: 30 | |
| steps: | |
| - uses: actions/checkout@v4 | |
| with: | |
| persist-credentials: false | |
| - run: scripts/ci-local.sh --only ts-fast --strict-toolchains | |
| # TS UNIT lane — the per-package suites (metadata, render, runtime-ts + the | |
| # client/web packages) that NO lane gated until 2026-07-19: ts-fast's conformance | |
| # gate runs only named conformance FILES, so the rest of those suites ran nowhere | |
| # and a regression could sit red on `main` under a green ts lane. | |
| # Deliberately separate from ts-fast rather than folded into it: ts-fast carries | |
| # the 30-min mutation gate, which flakes under runner contention, and a mutation | |
| # flake must not mask a unit-test failure. The suites total ~5s, so this lane is | |
| # nearly free and gives an independent, fast signal. | |
| ts-unit: | |
| needs: detect | |
| if: contains(needs.detect.outputs.ports, 'ts') | |
| runs-on: [self-hosted, linux] | |
| timeout-minutes: 20 | |
| steps: | |
| - uses: actions/checkout@v4 | |
| with: | |
| persist-credentials: false | |
| - run: scripts/ci-local.sh --only ts-unit --strict-toolchains | |
| # TS SLOW lane — build + docker/sidecar integration tests. Uses the shared | |
| # Postgres sidecar (dynamic host port; see the note above the integration jobs). | |
| ts-slow: | |
| needs: detect | |
| if: contains(needs.detect.outputs.ports, 'ts') | |
| runs-on: [self-hosted, linux] | |
| timeout-minutes: 45 | |
| services: | |
| postgres: | |
| image: postgres:16 | |
| env: | |
| POSTGRES_USER: metaobjects | |
| POSTGRES_PASSWORD: metaobjects | |
| POSTGRES_DB: metaobjects_test | |
| ports: | |
| - 5432 # dynamic host port — no fixed-port collision | |
| options: >- | |
| --health-cmd "pg_isready -U metaobjects" | |
| --health-interval 10s | |
| --health-timeout 5s | |
| --health-retries 5 | |
| steps: | |
| - uses: actions/checkout@v4 | |
| with: | |
| persist-credentials: false | |
| - env: | |
| METAOBJECTS_TEST_PG_URL: postgres://metaobjects:metaobjects@localhost:${{ job.services.postgres.ports['5432'] }}/metaobjects_test | |
| run: scripts/ci-local.sh --only ts-slow --strict-toolchains | |
| # Java FAST lane — java + kotlin conformance only (the quick correctness signal). | |
| # No docker/integration, so no sidecar. | |
| java-fast: | |
| needs: detect | |
| if: contains(needs.detect.outputs.ports, 'java') | |
| runs-on: [self-hosted, linux] | |
| timeout-minutes: 25 | |
| steps: | |
| - uses: actions/checkout@v4 | |
| with: | |
| persist-credentials: false | |
| - run: scripts/ci-local.sh --only java-fast --strict-toolchains | |
| # Java SLOW lane — full reactor + docker integration (java + kotlin). Uses the | |
| # shared Postgres sidecar so integration stops booting per-scenario containers. | |
| java-slow: | |
| needs: detect | |
| if: contains(needs.detect.outputs.ports, 'java') | |
| runs-on: [self-hosted, linux] | |
| timeout-minutes: 45 | |
| services: | |
| postgres: | |
| image: postgres:16 | |
| env: | |
| POSTGRES_USER: metaobjects | |
| POSTGRES_PASSWORD: metaobjects | |
| POSTGRES_DB: metaobjects_test | |
| ports: | |
| - 5432 | |
| options: >- | |
| --health-cmd "pg_isready -U metaobjects" | |
| --health-interval 10s | |
| --health-timeout 5s | |
| --health-retries 5 | |
| steps: | |
| - uses: actions/checkout@v4 | |
| with: | |
| persist-credentials: false | |
| - env: | |
| METAOBJECTS_TEST_PG_URL: postgres://metaobjects:metaobjects@localhost:${{ job.services.postgres.ports['5432'] }}/metaobjects_test | |
| run: scripts/ci-local.sh --only java-slow --strict-toolchains | |
| python: | |
| needs: detect | |
| if: contains(needs.detect.outputs.ports, 'python') | |
| runs-on: [self-hosted, linux] | |
| timeout-minutes: 45 | |
| services: | |
| postgres: | |
| image: postgres:16 | |
| env: | |
| POSTGRES_USER: metaobjects | |
| POSTGRES_PASSWORD: metaobjects | |
| POSTGRES_DB: metaobjects_test | |
| ports: | |
| - 5432 | |
| options: >- | |
| --health-cmd "pg_isready -U metaobjects" | |
| --health-interval 10s | |
| --health-timeout 5s | |
| --health-retries 5 | |
| steps: | |
| - uses: actions/checkout@v4 | |
| with: | |
| persist-credentials: false | |
| - env: | |
| METAOBJECTS_TEST_PG_URL: postgres://metaobjects:metaobjects@localhost:${{ job.services.postgres.ports['5432'] }}/metaobjects_test | |
| run: scripts/ci-local.sh --only python --strict-toolchains | |
| csharp: | |
| needs: detect | |
| if: contains(needs.detect.outputs.ports, 'csharp') | |
| runs-on: [self-hosted, linux] | |
| timeout-minutes: 45 | |
| services: | |
| postgres: | |
| image: postgres:16 | |
| env: | |
| POSTGRES_USER: metaobjects | |
| POSTGRES_PASSWORD: metaobjects | |
| POSTGRES_DB: metaobjects_test | |
| ports: | |
| - 5432 | |
| options: >- | |
| --health-cmd "pg_isready -U metaobjects" | |
| --health-interval 10s | |
| --health-timeout 5s | |
| --health-retries 5 | |
| steps: | |
| - uses: actions/checkout@v4 | |
| with: | |
| persist-credentials: false | |
| - env: | |
| METAOBJECTS_TEST_PG_URL: postgres://metaobjects:metaobjects@localhost:${{ job.services.postgres.ports['5432'] }}/metaobjects_test | |
| run: scripts/ci-local.sh --only csharp --strict-toolchains | |
| notify: | |
| needs: [detect, gates, ts-fast, ts-unit, ts-slow, java-fast, java-slow, python, csharp] | |
| if: always() && contains(needs.*.result, 'failure') | |
| runs-on: [self-hosted, linux] | |
| steps: | |
| - env: | |
| # env-indirection: head_commit.message is untrusted text — never | |
| # interpolate ${{ }} directly into the script body. | |
| HEAD_MSG: ${{ github.event.head_commit.message }} | |
| RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} | |
| run: | | |
| export DBUS_SESSION_BUS_ADDRESS="unix:path=/run/user/$(id -u)/bus" | |
| notify-send -u critical "local-ci FAILED: metaobjects" \ | |
| "$(printf '%s' "$HEAD_MSG" | head -n1) | |
| $RUN_URL" || echo "notify-send unavailable; GitHub email still fires" |