diff --git a/.changeset/README.md b/.changeset/README.md new file mode 100644 index 0000000..0635acb --- /dev/null +++ b/.changeset/README.md @@ -0,0 +1,14 @@ +# Changesets + +This directory holds change-intent files consumed by pnpm-native workspace +versioning (`pnpm version -r`). One file per change, authored with: + +```bash +pnpm change --bump --summary "" [...] +``` + +- A PR that changes a publishable package (`npm/packages/comment-checker`) MUST ship with + an intent here. +- `--bump none` records a change that needs no release (e.g. devDependency or script touch). +- Intents are consumed (deleted) by `pnpm version -r` in the release workflow. +- Standard pnpm 11 native changesets format. diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 819dab5..b86a199 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -11,10 +11,11 @@ jobs: steps: - uses: actions/checkout@v4 - # Toolchain comes from the repo flake (nix develop): cargo, clippy, - # rustfmt, gcc — same versions as every local dev shell. - - uses: DeterminateSystems/nix-installer-action@v22 - + # Stock rust-lang toolchain through rustup with clippy and rustfmt components + - uses: actions-rust-lang/setup-rust-toolchain@0267444136ce4919088f5eae0461f736f21356de # v1 + with: + toolchain: stable + components: clippy, rustfmt - name: Cache cargo build uses: actions/cache@v4 with: @@ -25,18 +26,14 @@ jobs: key: ${{ runner.os }}-cargo-${{ hashFiles('Cargo.lock') }} restore-keys: ${{ runner.os }}-cargo- - # No `bash -l`: login shells re-source runner profiles which re-prepend - # ~/.cargo/bin ahead of the dev shell, and the rustup shim then hijacks - # the toolchain (observed: rustup updated stable mid-build, removed - # rust-std, E0463). `nix develop -c` already exports the full PATH. - name: Format check - run: nix develop -c bash -c 'CARGO_BUILD_JOBS=4 cargo fmt --check' + run: cargo fmt --check - name: Clippy (deny warnings) - run: nix develop -c bash -c 'CARGO_BUILD_JOBS=4 cargo clippy --all-targets -- -D warnings' + run: cargo clippy --all-targets -- -D warnings - name: Test (unit + property + composition + F1) - run: nix develop -c bash -c 'cargo test --all-targets -- --test-threads=4' + run: cargo test --all-targets -- --test-threads=4 npm: runs-on: ubuntu-latest @@ -73,28 +70,31 @@ jobs: steps: - uses: actions/checkout@v4 - # Toolchain + cargo-mutants from the repo flake — no cargo install. - - uses: DeterminateSystems/nix-installer-action@v22 + - uses: actions-rust-lang/setup-rust-toolchain@0267444136ce4919088f5eae0461f736f21356de # v1 + with: + toolchain: stable + + - uses: pnpm/action-setup@v4 + with: + version: 11.21.0 + + - uses: actions/setup-node@v4 + with: + node-version: 24 + + - name: Install cargo-mutants subcommand + run: | + cargo install cargo-mutants + echo "$HOME/.cargo/bin" >> "$GITHUB_PATH" - # Turbo verdict cache. The path must mirror turbo.json's `cacheDir`. - # Keyed on the mutation gate's input set plus the files turbo - # self-hashes (turbo.json, the npm package.json carrying the gate - # script): unchanged inputs restore an exact key, which also - # suppresses the post-job save; turbo then replays the cached verdict - # (FULL TURBO) instead of re-running the mutant loop. The restore-key - # prefix picks up the newest previous bag after any input change. - name: Cache turbo verdicts uses: actions/cache@v4 with: path: .turbo/cache - key: turbo-${{ runner.os }}-${{ hashFiles('package.json', 'Cargo.toml', 'Cargo.lock', 'flake.lock', 'flake.nix', '.cargo/config.toml', 'crates/comment-checker/Cargo.toml', 'crates/comment-checker/src/**', 'crates/comment-checker/tests/**', 'turbo.json', 'npm/packages/comment-checker/package.json') }} + key: turbo-${{ runner.os }}-${{ hashFiles('package.json', 'Cargo.toml', 'Cargo.lock', '.cargo/config.toml', 'crates/comment-checker/Cargo.toml', 'crates/comment-checker/src/**', 'crates/comment-checker/tests/**', 'turbo.json', 'npm/packages/comment-checker/package.json') }} restore-keys: | turbo-${{ runner.os }}- - # Cargo build state for the turbo-miss path (any input change - # recompiles). Keyed on Cargo.lock + flake.lock (deps + toolchain); - # the shared `cargo-` restore prefix also picks up bags the gate - # job saved under the same namespace. - name: Cache cargo build (mutants miss path) uses: actions/cache@v4 with: @@ -102,12 +102,9 @@ jobs: ~/.cargo/registry ~/.cargo/git target - key: ${{ runner.os }}-cargo-mut-${{ hashFiles('Cargo.lock', 'flake.lock') }} + key: ${{ runner.os }}-cargo-mut-${{ hashFiles('Cargo.lock') }} restore-keys: ${{ runner.os }}-cargo- - # pnpm store: `pnpm install` runs before turbo can replay anything - # (turbo itself comes from node_modules), so the store is cached to - # keep that install a fast relink instead of a cold fetch. - name: Cache pnpm store uses: actions/cache@v4 with: @@ -117,4 +114,4 @@ jobs: pnpm-store-${{ runner.os }}- - name: Mutation gate via turbo (core classifier, 100%) - run: nix develop -c bash -c 'pnpm install --frozen-lockfile && CARGO_BUILD_JOBS=4 pnpm mutants' + run: pnpm install --frozen-lockfile && pnpm mutants diff --git a/AGENTS.md b/AGENTS.md index 5147d34..5dcc4a0 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -63,20 +63,19 @@ These are the checks that must pass. The one-shot gate below runs them as phases - Core classifier mutation (when changing `crates/comment-checker/src/classify.rs`): `cargo mutants --file crates/comment-checker/src/classify.rs --timeout 90` ```bash -# One-shot verification command (with explicit caps) -CARGO_BUILD_JOBS=4 cargo fmt --check && CARGO_BUILD_JOBS=4 cargo clippy --all-targets -- -D warnings && cargo test -- --test-threads=4 +# One-shot verification command +cargo fmt --check && cargo clippy --all-targets -- -D warnings && cargo test --all-targets ``` -**For classifier changes**, the mutants command above must be run. - +**For classifier changes**, the mutants command above must be run: ```bash -# One-shot verification command (with explicit caps) -CARGO_BUILD_JOBS=4 cargo fmt --check && CARGO_BUILD_JOBS=4 cargo clippy --all-targets -- -D warnings && cargo test -- --test-threads=4 +cargo mutants --file crates/comment-checker/src/classify.rs --timeout 90 ``` -**Rust/Cargo specifics for manifest resolution**: Commands are direct `cargo` invocations (Cargo.toml serves as manifest; no `[scripts]` like package.json). The gate resolves via the Cargo toolchain present in PATH. The instructions surface names these as the verifiable entrypoints. -Keep the gate bounded when changing it. Concurrency multiplies: the runner's task cap times each task's own worker pool. For Cargo, use `CARGO_BUILD_JOBS=4` (or CI equivalent) or `-j` where supported to cap; defaults are safe for this small crate but the gate documents the cap. Prefer the runner's CPU-relative cap. Add a phase only when the check gating it is far cheaper than the phase behind it; never chain independent same-cost checks with `&&`, and never fan out uncapped. +**Rust/Cargo specifics for manifest resolution**: Commands are direct `cargo` invocations (Cargo.toml serves as manifest; no `[scripts]` like package.json). The gate resolves via the Cargo toolchain present in PATH. The instructions surface names these as the verifiable entrypoints. +Run checks using full system concurrency (`cargo` and `cargo-test` use host CPU defaults). +Add a phase only when the check gating it is far cheaper than the phase behind it; never chain independent same-cost checks with `&&`. ### Anti-Bypass Rules - Run the full one-shot command, not individual tests in isolation. - Evidence comes from the current run — never an old CI result or prior session; any failure blocks done, even unrelated-looking ones. diff --git a/scripts/tools/sync-root-version.ts b/scripts/tools/sync-root-version.ts index 89b8113..7aa3098 100755 --- a/scripts/tools/sync-root-version.ts +++ b/scripts/tools/sync-root-version.ts @@ -11,33 +11,36 @@ import { const VERSION_RE = /^\d+\.\d+\.\d+(-[A-Za-z0-9.-]+)?$/ -// Validated before any write. `$` matches before a trailing newline, so reject -// one explicitly. -const version = Deno.env.get('VERSION') ?? '' -if (!VERSION_RE.test(version) || version.includes('\n')) { - console.error(`sync-root-version: invalid VERSION: ${JSON.stringify(version)}`) - Deno.exit(1) -} - const flags = parseCliArgs({ alias: { 'dry-run': 'dryRun', 'manifest-path': 'manifestPath' }, boolean: ['dry-run'], - string: ['manifest-path'], + string: ['manifest-path', 'version'], }) const dryRun = flags.dryRun === true const manifestPath = typeof flags.manifestPath === 'string' ? resolve(flags.manifestPath) : LAUNCHER_MANIFEST_PATH +const original = await Deno.readTextFile(manifestPath) +const manifest: LauncherManifest = JSON.parse(original) + +// Version priority: --version flag -> VERSION env var -> existing manifest.version (bumped by pnpm version) +const rawVersion = typeof flags.version === 'string' + ? flags.version + : (Deno.env.get('VERSION') ?? manifest.version ?? '') + +if (!VERSION_RE.test(rawVersion) || rawVersion.includes('\n')) { + console.error(`sync-root-version: invalid version: ${JSON.stringify(rawVersion)}`) + Deno.exit(1) +} +const version = rawVersion + const targets: Target[] = JSON.parse(await Deno.readTextFile(TARGETS_PATH)) if (!Array.isArray(targets) || targets.length !== 5) { console.error('sync-root-version: targets.json must declare exactly five platform targets') Deno.exit(1) } -const original = await Deno.readTextFile(manifestPath) -const manifest: LauncherManifest = JSON.parse(original) -manifest.version = version // The committed manifest carries no optionalDependencies — pnpm cannot lock // unpublished platform packages — so inject the pins at publish time, when they exist. manifest.optionalDependencies = Object.fromEntries( diff --git a/turbo.json b/turbo.json index 1b3f1a9..e99f09e 100644 --- a/turbo.json +++ b/turbo.json @@ -61,9 +61,6 @@ "$TURBO_ROOT$/crates/comment-checker/tests/**" ], "outputs": [], - "env": [ - "CARGO_BUILD_JOBS" - ], "cache": true } }