diff --git a/.github/workflows/changelog-check.yml b/.github/workflows/changelog-check.yml index 0ba8feaf6..664ad489d 100644 --- a/.github/workflows/changelog-check.yml +++ b/.github/workflows/changelog-check.yml @@ -3,9 +3,25 @@ name: Changelog Check env: FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true +# The gate itself decides per package whether a fragment is required +# (changed_packages / code_touched in internals/checks); these paths only +# decide whether the decision is worth a runner. packages/** covers both the +# code that demands a fragment and the fragment dirs that satisfy it, so the +# near-miss diagnostics (malformed fragment names) still run on +# fragment-only PRs. +# +# plugins/ is deliberately absent: the gate's matchers are hardcoded to +# packages//, so it cannot see plugin fragments -- running it on a +# plugin change is a vacuous pass. Plugin changelog enforcement is a gate +# gap, tracked in its own issue. on: pull_request: - branches: [main] + paths: + - 'packages/**' + # Attestations are filtered out of the gate's code_touched, so they + # cannot alter its verdict. + - '!packages/*/e2e-attestations/**' + - '.github/workflows/changelog-check.yml' concurrency: group: ${{ github.workflow }}-${{ github.ref }} @@ -31,7 +47,7 @@ jobs: # Logic (packages// source detection, per-package fragment # detection, raw-body skip-changelog scanning) lives in internals/checks' # changelog_gate check, colocated-tested at 100% coverage, per - # AGENTS.md "CI Workflows". Mirrors template-lib's reference gate (#566). + # AGENTS.md "CI Workflows". - name: Require a per-package changelog fragment for SDK code changes env: BASE_SHA: ${{ github.event.pull_request.base.sha }} diff --git a/.github/workflows/conventions.yml b/.github/workflows/conventions.yml deleted file mode 100644 index 68b67a16d..000000000 --- a/.github/workflows/conventions.yml +++ /dev/null @@ -1,81 +0,0 @@ -name: Conventions - -on: [pull_request] - -concurrency: - group: ${{ github.workflow }}-${{ github.ref }} - cancel-in-progress: true - -permissions: - contents: read - -jobs: - python-sdk: - uses: thekevinscott/testing-conventions/.github/workflows/testing-conventions.yml@v0 - with: - languages: '["python"]' - source: packages/python/dirsql - config: testing-conventions.toml - base: ${{ github.event.pull_request.base.sha || 'origin/main' }} - - typescript-sdk: - uses: thekevinscott/testing-conventions/.github/workflows/testing-conventions.yml@v0 - with: - languages: '["typescript"]' - source: packages/ts/src - config: testing-conventions.toml - base: ${{ github.event.pull_request.base.sha || 'origin/main' }} - rust_toolchain: true - - rust: - uses: thekevinscott/testing-conventions/.github/workflows/testing-conventions.yml@v0 - with: - languages: '["rust"]' - source: packages/rust - config: testing-conventions.toml - base: ${{ github.event.pull_request.base.sha || 'origin/main' }} - gates: '["colocated-test", "unit-lint", "integration-lint", "mutation", "packaging"]' - - rust-napi-binding: - uses: thekevinscott/testing-conventions/.github/workflows/testing-conventions.yml@v0 - with: - languages: '["rust"]' - source: packages/ts/napi - config: testing-conventions.toml - base: ${{ github.event.pull_request.base.sha || 'origin/main' }} - gates: '["colocated-test", "unit-lint"]' - - rust-python-binding: - uses: thekevinscott/testing-conventions/.github/workflows/testing-conventions.yml@v0 - with: - languages: '["rust"]' - source: packages/python - config: testing-conventions.toml - base: ${{ github.event.pull_request.base.sha || 'origin/main' }} - gates: '["colocated-test", "unit-lint"]' - - internals-checks: - uses: thekevinscott/testing-conventions/.github/workflows/testing-conventions.yml@v0 - with: - languages: '["python"]' - source: internals/checks/src - config: internals/checks/testing-conventions.toml - base: ${{ github.event.pull_request.base.sha || 'origin/main' }} - gates: '["colocated-test", "unit-lint", "integration-lint", "unit-coverage", "mutation", "e2e-verify"]' - - internals-distcheck: - uses: thekevinscott/testing-conventions/.github/workflows/testing-conventions.yml@v0 - with: - languages: '["python"]' - source: internals/distcheck/src - base: ${{ github.event.pull_request.base.sha || 'origin/main' }} - gates: '["colocated-test", "unit-lint", "integration-lint", "unit-coverage", "mutation"]' - - plugins-embeddings: - uses: thekevinscott/testing-conventions/.github/workflows/testing-conventions.yml@v0 - with: - languages: '["python"]' - source: plugins/dirsql-plugin-embeddings/src - config: plugins/dirsql-plugin-embeddings/testing-conventions.toml - base: ${{ github.event.pull_request.base.sha || 'origin/main' }} - gates: '["colocated-test", "unit-lint", "integration-lint", "unit-coverage", "mutation", "e2e-verify"]' diff --git a/.github/workflows/python-test.yml b/.github/workflows/dirsql-python-ci.yml similarity index 61% rename from .github/workflows/python-test.yml rename to .github/workflows/dirsql-python-ci.yml index 6b5ce480d..0df152c44 100644 --- a/.github/workflows/python-test.yml +++ b/.github/workflows/dirsql-python-ci.yml @@ -1,8 +1,7 @@ -name: Python Test - -env: - FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true +name: dirsql Python CI +# packages/rust/** fans in: the core compiles into the binding, and +# [e2e].extra_scope means a core change stales this package's attestation. on: push: branches: [main] @@ -11,20 +10,92 @@ on: - 'packages/rust/**' - 'Cargo.toml' - 'Cargo.lock' - - '.github/workflows/python-test.yml' + - 'testing-conventions.toml' + - '.github/workflows/dirsql-python-ci.yml' pull_request: paths: - 'packages/python/**' + - '!packages/python/changelog.d/**' + - '!packages/python/migrations.d/**' + - '!packages/python/README.md' - 'packages/rust/**' + - '!packages/rust/changelog.d/**' + - '!packages/rust/migrations.d/**' + - '!packages/rust/README.md' - 'Cargo.toml' - 'Cargo.lock' - - '.github/workflows/python-test.yml' + - 'testing-conventions.toml' + - '.github/workflows/dirsql-python-ci.yml' concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true +permissions: + contents: read + jobs: + python-sdk: + uses: thekevinscott/testing-conventions/.github/workflows/testing-conventions.yml@v0 + with: + languages: '["python"]' + source: packages/python/dirsql + config: testing-conventions.toml + base: ${{ github.event.pull_request.base.sha || 'origin/main' }} + + rust-python-binding: + uses: thekevinscott/testing-conventions/.github/workflows/testing-conventions.yml@v0 + with: + languages: '["rust"]' + source: packages/python + config: testing-conventions.toml + base: ${{ github.event.pull_request.base.sha || 'origin/main' }} + gates: '["colocated-test", "unit-lint"]' + + lint: + name: lint (python) + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v6 + + - name: Install uv + uses: astral-sh/setup-uv@v7 + + - name: Install just + uses: extractions/setup-just@v2 + + # ruff never executes the compiled extension, so skip the maturin + # project install -- a plain `uv run` would pay a full Rust build. + - name: Install dependencies + run: uv sync --no-install-project + working-directory: packages/python + + - name: Lint + run: uv run --no-sync ruff check . + working-directory: packages/python + + - name: Format check + run: uv run --no-sync ruff format --check . + working-directory: packages/python + + ty: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v6 + + - name: Install uv + uses: astral-sh/setup-uv@v7 + + # `ty` checks statically and never imports the compiled extension, so + # skip the maturin project install -- `uv run` would pay a Rust build. + - name: Install dependencies + run: uv sync --no-install-project + working-directory: packages/python + + - name: Type check (all rules promoted to error) + run: uv run --no-sync ty check --error all --error-on-warning dirsql/ + working-directory: packages/python + unit: name: Unit tests runs-on: ubuntu-latest @@ -74,9 +145,8 @@ jobs: working-directory: packages/python integration: - # Hermetic tier (#289): SDK public API with the Rust core and the - # filesystem mocked. The PyO3 extension is still built because - # `dirsql/__init__.py` imports `dirsql._dirsql` at module load. + # Hermetic tier: core and filesystem mocked. The PyO3 extension is still + # built because `dirsql/__init__.py` imports `dirsql._dirsql` at load. name: Integration tests runs-on: ubuntu-latest strategy: @@ -118,9 +188,8 @@ jobs: working-directory: packages/python binding: - # Binding tier (#289): the SDK public API against the real PyO3 core and - # a real filesystem — proves the SDK↔core marshaling that the hermetic - # integration tier mocks out. + # Binding tier: real PyO3 core and real filesystem -- proves the + # SDK↔core marshaling the hermetic tier mocks out. name: Binding tests runs-on: ubuntu-latest strategy: @@ -161,24 +230,10 @@ jobs: run: uv run python -m pytest tests/integration/binding/ -x -q --tb=short working-directory: packages/python - # Unit coverage now runs in the reusable testing-conventions workflow - # (conventions.yml, `python-sdk` gates: unit-coverage) after upstream #284 - # taught the coverage suite job to install/build from the derived package - # root (python_env=uv -> `uv sync` builds maturin; provision_rust). The - # bespoke coverage job that lived here is retired (#412). - distcheck: - # Functional publishability gate (#344, mirroring ts-test.yml's distcheck - # job): build the real `dirsql` CLI binary via cargo, stage it under - # `dirsql/_binary/` the way the release pipeline's bundle_cli step - # would, build the wheel with `maturin build`, `pip install` it into a - # fresh venv, and run the installed `dirsql --version` console script - # plus `import dirsql`. Red here means the PyPI release is broken and - # must not go out. This is the install-and-run check the `packaging` - # gate does NOT do (that gate only asserts no test files ship in the - # artifact). No mocks. The flow lives in the internals/distcheck package - # (#520); this job invokes its `dirsql-distcheck python` entry point. - name: Packaging distcheck (install + run) + # Installs and runs the built wheel, which the `packaging` gate does not + # do -- that gate only asserts no test files ship in the artifact. + name: Packaging distcheck (python) runs-on: ubuntu-latest steps: - uses: actions/checkout@v6 diff --git a/.github/workflows/rust-test.yml b/.github/workflows/dirsql-rust-ci.yml similarity index 82% rename from .github/workflows/rust-test.yml rename to .github/workflows/dirsql-rust-ci.yml index e4cae1841..a2b21b8b0 100644 --- a/.github/workflows/rust-test.yml +++ b/.github/workflows/dirsql-rust-ci.yml @@ -1,8 +1,9 @@ -name: Rust Test - -env: - FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true +name: dirsql Rust CI +# The binding crates fan in (packages/ts/napi, packages/python/src): they are +# cargo workspace members, so the workspace-wide unit/lint jobs compile them. +# The root testing-conventions.toml carries this lane's config and the +# [rust.coverage] floors the coverage job reads. on: push: branches: [main] @@ -12,21 +13,38 @@ on: - 'packages/python/src/**' - 'Cargo.toml' - 'Cargo.lock' - - '.github/workflows/rust-test.yml' + - 'testing-conventions.toml' + - '.github/workflows/dirsql-rust-ci.yml' pull_request: paths: - 'packages/rust/**' + - '!packages/rust/changelog.d/**' + - '!packages/rust/migrations.d/**' + - '!packages/rust/README.md' - 'packages/ts/napi/**' - 'packages/python/src/**' - 'Cargo.toml' - 'Cargo.lock' - - '.github/workflows/rust-test.yml' + - 'testing-conventions.toml' + - '.github/workflows/dirsql-rust-ci.yml' concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true +permissions: + contents: read + jobs: + rust: + uses: thekevinscott/testing-conventions/.github/workflows/testing-conventions.yml@v0 + with: + languages: '["rust"]' + source: packages/rust + config: testing-conventions.toml + base: ${{ github.event.pull_request.base.sha || 'origin/main' }} + gates: '["colocated-test", "unit-lint", "integration-lint", "mutation", "packaging"]' + unit: name: Unit tests runs-on: ubuntu-latest @@ -115,17 +133,9 @@ jobs: with: node-version: "24" - # Unit-only coverage floor (#295). Replaces the bespoke `cargo llvm-cov - # --fail-under-*` gate. The CLI scopes to `--lib` (so the integration - # tier under packages/rust/tests/ no longer pads the number -- - # testing-conventions #269), reads `[rust].features` (= cli) so the - # `#[cfg(feature = "cli")]` module is measured (#270), and enforces the - # `[rust.coverage]` lines/functions/regions/branch floors from - # testing-conventions.toml (#271). - # # Whole-tree only: unlike the Python/TS jobs there is no `--base` # changed-lines check. Rust's effectful paths (filesystem / subprocess / - # HTTP / `notify`) are unit-uncovered by design (#233); a per-PR + # HTTP / `notify`) are unit-uncovered by design, so a per-PR # changed-lines floor would fail legitimate integration-tier edits. - name: Unit coverage floor (testing-conventions) run: npx -y testing-conventions unit coverage --language rust packages/rust/src diff --git a/.github/workflows/ts-test.yml b/.github/workflows/dirsql-typescript-ci.yml similarity index 64% rename from .github/workflows/ts-test.yml rename to .github/workflows/dirsql-typescript-ci.yml index 6b4c0ef55..b2bd5bef3 100644 --- a/.github/workflows/ts-test.yml +++ b/.github/workflows/dirsql-typescript-ci.yml @@ -1,8 +1,7 @@ -name: TS Test - -env: - FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true +name: dirsql TypeScript CI +# packages/rust/** fans in: the core compiles into the binding, and +# [e2e].extra_scope means a core change stales this package's attestation. on: push: branches: [main] @@ -11,23 +10,107 @@ on: - 'packages/rust/**' - 'Cargo.toml' - 'Cargo.lock' - - '.github/workflows/ts-test.yml' + - 'testing-conventions.toml' + - '.github/workflows/dirsql-typescript-ci.yml' pull_request: paths: - 'packages/ts/**' + - '!packages/ts/changelog.d/**' + - '!packages/ts/migrations.d/**' + - '!packages/ts/README.md' - 'packages/rust/**' + - '!packages/rust/changelog.d/**' + - '!packages/rust/migrations.d/**' + - '!packages/rust/README.md' - 'Cargo.toml' - 'Cargo.lock' - - '.github/workflows/ts-test.yml' + - 'testing-conventions.toml' + - '.github/workflows/dirsql-typescript-ci.yml' concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true +permissions: + contents: read + jobs: + typescript-sdk: + uses: thekevinscott/testing-conventions/.github/workflows/testing-conventions.yml@v0 + with: + languages: '["typescript"]' + source: packages/ts/src + config: testing-conventions.toml + base: ${{ github.event.pull_request.base.sha || 'origin/main' }} + rust_toolchain: true + + rust-napi-binding: + uses: thekevinscott/testing-conventions/.github/workflows/testing-conventions.yml@v0 + with: + languages: '["rust"]' + source: packages/ts/napi + config: testing-conventions.toml + base: ${{ github.event.pull_request.base.sha || 'origin/main' }} + gates: '["colocated-test", "unit-lint"]' + + lint: + name: lint (ts) + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v6 + + - name: Install Node + uses: actions/setup-node@v4 + with: + node-version: "24" + + - name: Install pnpm + uses: pnpm/action-setup@v4 + with: + version: 11 + + - name: Install dependencies + working-directory: packages/ts + run: pnpm install + + - name: Lint + working-directory: packages/ts + run: pnpm lint + + - name: Format check + working-directory: packages/ts + run: pnpm format:check + +# Redundant with the `tsc` inside `pnpm build`, but this leg skips the +# napi-rs build chain (seconds, not minutes) and surfaces a type error as +# its own check rather than as a unit-test failure. The addon loads at +# runtime via `createRequire`, so `--noEmit` needs no built artifact. + tsc: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v6 + + - name: Install Node + uses: actions/setup-node@v4 + with: + node-version: "24" + + - name: Install pnpm + uses: pnpm/action-setup@v4 + with: + version: 11 + + - name: Install dependencies + working-directory: packages/ts + run: pnpm install + + - name: Type check + working-directory: packages/ts + run: pnpm exec tsc --noEmit + build: - # Builds the napi addon once and shares it with unit/binding/distcheck below - # (#483) instead of each job/matrix-cell rebuilding it independently. + # Builds the napi addon once and shares it with unit/binding/distcheck + # below instead of each job/matrix-cell rebuilding it independently. # The addon is N-API (napi6, see packages/ts/napi/Cargo.toml), which is # ABI-stable across Node majors, so one build serves the whole Node # 20/22/24 matrix. @@ -113,9 +196,8 @@ jobs: run: pnpm exec vitest run --dir src integration: - # Hermetic tier (#289): SDK public API with the native core mocked - # (`vi.mock`) and no filesystem access — no Rust toolchain or napi build - # needed. + # Hermetic tier: native core mocked (`vi.mock`), no filesystem access -- + # no Rust toolchain or napi build needed. name: Integration tests (Node ${{ matrix.node }}) runs-on: ubuntu-latest strategy: @@ -144,9 +226,8 @@ jobs: run: pnpm test:integration binding: - # Binding tier (#289): the SDK public API against the real napi core and - # a real filesystem — proves the SDK↔core marshaling that the hermetic - # integration tier mocks out. + # Binding tier: real napi core and real filesystem -- proves the SDK↔core + # marshaling the hermetic tier mocks out. # # Keeps its own Rust toolchain + rust-cache (unlike unit/distcheck below): # tests/binding/*.test.ts shell out to `cargo` directly at test-run time @@ -196,22 +277,10 @@ jobs: working-directory: packages/ts run: pnpm exec vitest run --dir tests/integration/binding --passWithNoTests - # Unit coverage now runs in the reusable testing-conventions workflow - # (conventions.yml, `typescript-sdk` gates: unit-coverage) after upstream - # #284 taught the coverage suite job to install/build from the derived - # package root. The bespoke coverage job that lived here is retired (#412). - distcheck: - # Functional publishability gate: build the real binary + napi addon, - # pack the main `dirsql` package and the host's `@dirsql/cli-` - # sub-package the way the release pipeline would, `npm install` both - # into a fresh dir, and run `node_modules/.bin/dirsql --version`. Red - # here means the npm release is broken and must not go out. This is the - # install-and-run check the `packaging` gate does NOT do (that gate only - # asserts no test files ship in the artifact). No mocks. The flow lives - # in the internals/distcheck package (#520); this job invokes its - # `dirsql-distcheck node` entry point (which drives npm/pnpm from Python). - name: Packaging distcheck (install + run) + # Installs and runs the packed tarballs, which the `packaging` gate does + # not do -- that gate only asserts no test files ship in the artifact. + name: Packaging distcheck (ts) needs: build runs-on: ubuntu-latest steps: diff --git a/.github/workflows/docs-ci.yml b/.github/workflows/docs-ci.yml new file mode 100644 index 000000000..80bbc0c7e --- /dev/null +++ b/.github/workflows/docs-ci.yml @@ -0,0 +1,36 @@ +name: Docs Build + +# Verification only. docs-release.yml publishes on push to main; its deploy +# job consumes the Pages artifact its build job uploads in the same run, so +# it cannot be reused here -- gating that deploy with `if:` would still +# report a second, skipped check on every docs PR. +on: + pull_request: + paths: + - 'docs/**' + - '.github/workflows/docs-ci.yml' + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +permissions: + contents: read + +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v6 + + - uses: actions/setup-node@v4 + with: + node-version: '24' + + - name: Install dependencies + working-directory: docs + run: npm install + + - name: Build VitePress + working-directory: docs + run: npx vitepress build diff --git a/.github/workflows/docs.yml b/.github/workflows/docs-release.yml similarity index 95% rename from .github/workflows/docs.yml rename to .github/workflows/docs-release.yml index fbc22e3ea..ecc7ab81f 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs-release.yml @@ -9,7 +9,7 @@ on: paths: - 'docs/**' - 'MIGRATIONS.md' - - '.github/workflows/docs.yml' + - '.github/workflows/docs-release.yml' workflow_dispatch: permissions: diff --git a/.github/workflows/internals-checks-ci.yml b/.github/workflows/internals-checks-ci.yml new file mode 100644 index 000000000..4223db0e8 --- /dev/null +++ b/.github/workflows/internals-checks-ci.yml @@ -0,0 +1,34 @@ +name: Internals checks CI + +# Verification for the internals/checks uv package (the dirsql-checks CLI). +# Deliberately does NOT fan out to this package's consumers (changelog-check, +# the release prechecks): the lane's own 100%-floor unit suite and mutation +# gate are the guard; a consumer run for every helper edit is not. +on: + pull_request: + paths: + - 'internals/checks/src/**' + - 'internals/checks/tests/**' + - 'internals/checks/pyproject.toml' + # Gate config and attestation receipts feed the lane: an unknown config + # key fails every gate, and e2e-verify walks the receipts for freshness. + - 'internals/checks/testing-conventions.toml' + - 'internals/checks/e2e-attestations/**' + - '.github/workflows/internals-checks-ci.yml' + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +permissions: + contents: read + +jobs: + internals-checks: + uses: thekevinscott/testing-conventions/.github/workflows/testing-conventions.yml@v0 + with: + languages: '["python"]' + source: internals/checks/src + config: internals/checks/testing-conventions.toml + base: ${{ github.event.pull_request.base.sha || 'origin/main' }} + gates: '["colocated-test", "unit-lint", "integration-lint", "unit-coverage", "mutation", "e2e-verify"]' diff --git a/.github/workflows/internals-distcheck-ci.yml b/.github/workflows/internals-distcheck-ci.yml new file mode 100644 index 000000000..db64e3de5 --- /dev/null +++ b/.github/workflows/internals-distcheck-ci.yml @@ -0,0 +1,32 @@ +name: Internals distcheck CI + +# Verification for the internals/distcheck uv package (the dirsql-distcheck +# publishability flows). Consumers (the SDK test workflows' distcheck jobs) +# are deliberately not fanned out to; this lane's own gates are the guard. +on: + pull_request: + paths: + - 'internals/distcheck/src/**' + - 'internals/distcheck/tests/**' + - 'internals/distcheck/pyproject.toml' + # This lane passes no `config:`, so the reusable workflow's default + # applies and the ROOT testing-conventions.toml governs it -- an unknown + # key there fails this lane too. + - 'testing-conventions.toml' + - '.github/workflows/internals-distcheck-ci.yml' + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +permissions: + contents: read + +jobs: + internals-distcheck: + uses: thekevinscott/testing-conventions/.github/workflows/testing-conventions.yml@v0 + with: + languages: '["python"]' + source: internals/distcheck/src + base: ${{ github.event.pull_request.base.sha || 'origin/main' }} + gates: '["colocated-test", "unit-lint", "integration-lint", "unit-coverage", "mutation"]' diff --git a/.github/workflows/plugin-dirsql-embedding-ci.yml b/.github/workflows/plugin-dirsql-embedding-ci.yml new file mode 100644 index 000000000..64fa7efb5 --- /dev/null +++ b/.github/workflows/plugin-dirsql-embedding-ci.yml @@ -0,0 +1,64 @@ +name: Plugin dirsql-embedding CI + +# All PR verification for plugins/dirsql-plugin-embeddings: the +# testing-conventions gate suite (unit tier, lint, coverage, mutation, +# e2e attestation) and the integration matrix. The workflow's own file is +# deliberately absent from paths -- editing a workflow must not run it, so +# each glob stays observable against an empty baseline. +on: + push: + branches: [main] + paths: + - 'plugins/dirsql-plugin-embeddings/**' + - '.github/workflows/plugin-dirsql-embedding-ci.yml' + pull_request: + paths: + - 'plugins/dirsql-plugin-embeddings/src/**' + - 'plugins/dirsql-plugin-embeddings/tests/**' + - 'plugins/dirsql-plugin-embeddings/pyproject.toml' + # Gate config and attestation receipts feed the conventions job: an + # unknown config key fails every gate, and e2e-verify walks the + # receipts for freshness. + - 'plugins/dirsql-plugin-embeddings/testing-conventions.toml' + - 'plugins/dirsql-plugin-embeddings/e2e-attestations/**' + - '.github/workflows/plugin-dirsql-embedding-ci.yml' + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +permissions: + contents: read + +jobs: + plugins-embeddings: + uses: thekevinscott/testing-conventions/.github/workflows/testing-conventions.yml@v0 + with: + languages: '["python"]' + source: plugins/dirsql-plugin-embeddings/src + config: plugins/dirsql-plugin-embeddings/testing-conventions.toml + base: ${{ github.event.pull_request.base.sha || 'origin/main' }} + gates: '["colocated-test", "unit-lint", "integration-lint", "unit-coverage", "mutation", "e2e-verify"]' + + embeddings-integration: + # The conventions job runs the plugin's colocated unit tier and checks its + # e2e attestation, but nothing there runs tests/integration/ — the tier + # that proves the worker protocol over real pipes and each + # `[project.scripts]` target is importable and runnable as declared. + name: dirsql-plugin-embeddings integration tests + runs-on: ubuntu-latest + strategy: + matrix: + # Low leg = the plugin's `requires-python` floor. + python-version: ["3.10", "3.12", "3.13"] + steps: + - uses: actions/checkout@v6 + + - name: Install uv + uses: astral-sh/setup-uv@v7 + with: + python-version: ${{ matrix.python-version }} + + - name: Run integration tests + run: uv run --with-editable . python -m pytest tests/integration/ -x -q --tb=short + working-directory: plugins/dirsql-plugin-embeddings diff --git a/.github/workflows/plugins-test.yml b/.github/workflows/plugins-test.yml deleted file mode 100644 index 07153d34c..000000000 --- a/.github/workflows/plugins-test.yml +++ /dev/null @@ -1,43 +0,0 @@ -name: Plugins Test - -env: - FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true - -on: - push: - branches: [main] - paths: - - 'plugins/**' - - '.github/workflows/plugins-test.yml' - pull_request: - paths: - - 'plugins/**' - - '.github/workflows/plugins-test.yml' - -concurrency: - group: ${{ github.workflow }}-${{ github.ref }} - cancel-in-progress: true - -jobs: - embeddings-integration: - # conventions.yml runs the plugin's colocated unit tier and checks its e2e - # attestation, but nothing ran tests/integration/ — the tier that proves - # the worker protocol over real pipes and each `[project.scripts]` target - # is importable and runnable as declared. - name: dirsql-plugin-embeddings integration tests - runs-on: ubuntu-latest - strategy: - matrix: - # Low leg = the plugin's `requires-python` floor; see python-test.yml. - python-version: ["3.10", "3.12", "3.13"] - steps: - - uses: actions/checkout@v6 - - - name: Install uv - uses: astral-sh/setup-uv@v7 - with: - python-version: ${{ matrix.python-version }} - - - name: Run integration tests - run: uv run --with-editable . python -m pytest tests/integration/ -x -q --tb=short - working-directory: plugins/dirsql-plugin-embeddings diff --git a/.github/workflows/release-config-check.yml b/.github/workflows/putitoutthere-config-check.yml similarity index 81% rename from .github/workflows/release-config-check.yml rename to .github/workflows/putitoutthere-config-check.yml index 6bedcb60e..378a41b98 100644 --- a/.github/workflows/release-config-check.yml +++ b/.github/workflows/putitoutthere-config-check.yml @@ -1,10 +1,10 @@ -name: Release Config Check +name: putitoutthere Config Check # Fast PR-time config sanity gate. Runs putitoutthere's `check` reusable # workflow, which parses `putitoutthere.toml`, validates the schema, checks # for common-mistake typos (`[[packages]]` vs `[[package]]`, etc.), enforces # unique package names, and detects `depends_on` cycles -- all without -# spinning up the per-target compile matrix. Pairs with `release-precheck.yml` +# spinning up the per-target compile matrix. Pairs with release-ci.yml # (which exercises the full build matrix): config errors fail here in # seconds; build/toolchain regressions fail there. # @@ -15,6 +15,9 @@ name: Release Config Check on: pull_request: branches: [main] + paths: + - 'putitoutthere.toml' + - '.github/workflows/putitoutthere-config-check.yml' concurrency: group: ${{ github.workflow }}-${{ github.ref }} diff --git a/.github/workflows/python-lint.yml b/.github/workflows/python-lint.yml deleted file mode 100644 index 6d18a5938..000000000 --- a/.github/workflows/python-lint.yml +++ /dev/null @@ -1,49 +0,0 @@ -name: Python Lint - -env: - FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true - -on: - push: - branches: [main] - paths: - - 'packages/python/**/*.py' - - 'packages/python/pyproject.toml' - pull_request: - paths: - - 'packages/python/**/*.py' - - 'packages/python/pyproject.toml' - -concurrency: - group: ${{ github.workflow }}-${{ github.ref }} - cancel-in-progress: true - -jobs: - lint: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v6 - - - name: Install uv - uses: astral-sh/setup-uv@v7 - - - name: Install just - uses: extractions/setup-just@v2 - - # Lint/format only touch the `.py` source and never execute the - # compiled extension, so skip installing/building the local maturin - # project (`uv sync` would otherwise pay a full Rust build just to run - # ruff). `uv run --no-sync` keeps the `ruff` invocations from - # re-triggering the project install that a plain `uv run` performs - # implicitly. - - name: Install dependencies - run: uv sync --no-install-project - working-directory: packages/python - - - name: Lint - run: uv run --no-sync ruff check . - working-directory: packages/python - - - name: Format check - run: uv run --no-sync ruff format --check . - working-directory: packages/python diff --git a/.github/workflows/python-typecheck.yml b/.github/workflows/python-typecheck.yml deleted file mode 100644 index d19147ef5..000000000 --- a/.github/workflows/python-typecheck.yml +++ /dev/null @@ -1,44 +0,0 @@ -name: Python Typecheck - -env: - FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true - -on: - push: - branches: [main] - paths: - - 'packages/python/**/*.py' - - 'packages/python/**/*.pyi' - - 'packages/python/pyproject.toml' - pull_request: - paths: - - 'packages/python/**/*.py' - - 'packages/python/**/*.pyi' - - 'packages/python/pyproject.toml' - -concurrency: - group: ${{ github.workflow }}-${{ github.ref }} - cancel-in-progress: true - -jobs: - ty: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v6 - - - name: Install uv - uses: astral-sh/setup-uv@v7 - - # `ty` type-checks the `.py` source statically and never imports the - # compiled extension, so skip installing/building the local maturin - # project (`uv sync` would otherwise pay a full Rust build just to - # typecheck). `uv run --no-sync` keeps the `ty` invocation from - # re-triggering the project install that a plain `uv run` performs - # implicitly. - - name: Install dependencies - run: uv sync --no-install-project - working-directory: packages/python - - - name: Type check (all rules promoted to error) - run: uv run --no-sync ty check --error all --error-on-warning dirsql/ - working-directory: packages/python diff --git a/.github/workflows/release-precheck.yml b/.github/workflows/release-ci.yml similarity index 63% rename from .github/workflows/release-precheck.yml rename to .github/workflows/release-ci.yml index afbded173..041fe1802 100644 --- a/.github/workflows/release-precheck.yml +++ b/.github/workflows/release-ci.yml @@ -1,4 +1,4 @@ -name: Release Precheck +name: Release CI # PR-time gate that runs putitoutthere's plan + build matrix using the # same `_matrix.yml` reusable workflow that drives the release path. @@ -14,9 +14,40 @@ name: Release Precheck # Pin to `@v0` deliberately: matches what `release.yml` calls so the # precheck stays in lockstep with the real release matrix. +# Paths are the union of every [[package]] globs entry in putitoutthere.toml +# plus the config itself: the plan job change-detects against those same +# globs, so per-package subdivision happens inside the run (a python-only PR +# plans only dirsql-py). putitoutthere's build.yml exposes no package filter, +# and the release cascade (rust -> py -> npm) is cross-package by design, so +# this stays one workflow rather than splitting per package. on: pull_request: branches: [main] + paths: + - 'packages/rust/**' + - '!packages/rust/changelog.d/**' + - '!packages/rust/migrations.d/**' + - 'packages/python/**' + - '!packages/python/changelog.d/**' + - '!packages/python/migrations.d/**' + - '!packages/python/e2e-attestations/**' + - 'packages/ts/**' + - '!packages/ts/changelog.d/**' + - '!packages/ts/migrations.d/**' + - '!packages/ts/e2e-attestations/**' + # putitoutthere's plugin globs are plugins/dirsql-plugin-embeddings/**; + # README stays in scope here (hatchling embeds it as the PyPI + # long-description, so it is a build input), fragments and receipts + # do not ship. + - 'plugins/dirsql-plugin-embeddings/**' + - '!plugins/dirsql-plugin-embeddings/changelog.d/**' + - '!plugins/dirsql-plugin-embeddings/migrations.d/**' + - '!plugins/dirsql-plugin-embeddings/e2e-attestations/**' + - '!plugins/dirsql-plugin-embeddings/tests/**' + - 'Cargo.toml' + - 'Cargo.lock' + - 'putitoutthere.toml' + - '.github/workflows/release-ci.yml' concurrency: group: ${{ github.workflow }}-${{ github.ref }} @@ -29,16 +60,14 @@ jobs: precheck: uses: thekevinscott/putitoutthere/.github/workflows/build.yml@v0 - # Artifact completeness (#790). `upload-artifact` only *warns* when its - # path matches nothing, so a build row that stages into the wrong - # directory uploads nothing and still reports success. Precheck has no - # publish job, so it never reached the completeness check publish - # performs -- #776/#777/#778 were all green while leaving `main` - # unpublishable, and it surfaced only after merge (#788). This asserts - # every (package, target) our own release config declares produced a - # non-empty artifact. It deliberately matches on package name + target - # and NOT on the engine's `{mode}` segment, which is exactly what - # shifted under us in #788. + # `upload-artifact` only *warns* when its path matches nothing, so a build + # row staging into the wrong directory uploads nothing and still reports + # success. Precheck has no publish job, so it never reaches the + # completeness check publish performs -- PRs have gone green while leaving + # `main` unpublishable. Asserts every (package, target) the release config + # declares produced a non-empty artifact. Deliberately matches on package + # name + target and NOT the engine's `{mode}` segment, which has shifted + # under us before. artifact-completeness: name: Artifact completeness needs: precheck @@ -58,7 +87,7 @@ jobs: - name: Assert every declared (package, target) produced an artifact run: uv run --project internals/checks dirsql-checks artifact-completeness --dist-dir dist/ - # Extension-load guard (#755): the precheck matrix builds the Linux + # Extension-load guard: the precheck matrix builds the Linux # wheel with the same bundle_cli linking as the release path, so this # is the only PR-time tier where a statically-linked bundled binary # (which cannot dlopen -> `Dynamic loading not supported`) is @@ -87,7 +116,7 @@ jobs: - name: Probe extension loading through the installed wheel run: uv run --project internals/checks dirsql-checks wheel-extension-load --dist-dir dist/ - # npm twin of the wheel probe (#762): the precheck matrix builds the + # npm twin of the wheel probe: the precheck matrix builds the # npm bundled-cli Linux binary with the same linking as the release # path (cargo-zigbuild dynamic gnu since putitoutthere#605), so this # is the only PR-time tier where a statically-linked binary (which diff --git a/.github/workflows/ts-lint.yml b/.github/workflows/ts-lint.yml deleted file mode 100644 index ad12c0cfa..000000000 --- a/.github/workflows/ts-lint.yml +++ /dev/null @@ -1,51 +0,0 @@ -name: TS Lint - -env: - FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true - -on: - push: - branches: [main] - paths: - - 'packages/ts/**/*.ts' - - 'packages/ts/**/*.js' - - 'packages/ts/biome.json' - - 'packages/ts/package.json' - pull_request: - paths: - - 'packages/ts/**/*.ts' - - 'packages/ts/**/*.js' - - 'packages/ts/biome.json' - - 'packages/ts/package.json' - -concurrency: - group: ${{ github.workflow }}-${{ github.ref }} - cancel-in-progress: true - -jobs: - lint: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v6 - - - name: Install Node - uses: actions/setup-node@v4 - with: - node-version: "24" - - - name: Install pnpm - uses: pnpm/action-setup@v4 - with: - version: 11 - - - name: Install dependencies - working-directory: packages/ts - run: pnpm install - - - name: Lint - working-directory: packages/ts - run: pnpm lint - - - name: Format check - working-directory: packages/ts - run: pnpm format:check diff --git a/.github/workflows/ts-typecheck.yml b/.github/workflows/ts-typecheck.yml deleted file mode 100644 index 57c55b7f4..000000000 --- a/.github/workflows/ts-typecheck.yml +++ /dev/null @@ -1,53 +0,0 @@ -name: TS Typecheck - -env: - FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true - -on: - push: - branches: [main] - paths: - - 'packages/ts/**/*.ts' - - 'packages/ts/tsconfig.json' - - 'packages/ts/package.json' - pull_request: - paths: - - 'packages/ts/**/*.ts' - - 'packages/ts/tsconfig.json' - - 'packages/ts/package.json' - -concurrency: - group: ${{ github.workflow }}-${{ github.ref }} - cancel-in-progress: true - -# `tsc` already runs as part of `pnpm build` in `ts-test.yml`, so this is -# redundant in the strict sense. It exists for the same reason -# `python-typecheck.yml` does: a dedicated, fast-failing type-check signal -# that doesn't require the napi-rs build chain (~seconds instead of minutes) -# and shows up as its own red check rather than "Unit tests" failing on a -# pure type error. The native addon is loaded at runtime via `createRequire` -# (see `src/load-native-core.ts`), so `tsc --noEmit` does not need -# `dirsql.node` or the generated `index.d.ts` to succeed. -jobs: - tsc: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v6 - - - name: Install Node - uses: actions/setup-node@v4 - with: - node-version: "24" - - - name: Install pnpm - uses: pnpm/action-setup@v4 - with: - version: 11 - - - name: Install dependencies - working-directory: packages/ts - run: pnpm install - - - name: Type check - working-directory: packages/ts - run: pnpm exec tsc --noEmit