diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 18e3e97..60f3145 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -44,7 +44,7 @@ updates: - "typescript" update-types: ["major"] - # The published npm wrapper. Effect is pinned to v4 RC -- effect@3 is a + # The published npm wrapper. Effect is on v4 -- effect@3 is a # deliberate migration the wrapper never makes, so block a stray 3.x pin. - package-ecosystem: npm directory: "/npm/packages/comment-checker" @@ -57,6 +57,9 @@ updates: # typescript majors are deliberate migrations, not routine bumps. - dependency-name: "typescript" update-types: ["version-update:semver-major"] + # @types/node tracks the Node 24 LTS line; a newer major is a deliberate move. + - dependency-name: "@types/node" + update-types: ["version-update:semver-major"] groups: oxlint-minor-patch: @@ -88,4 +91,5 @@ updates: exclude-patterns: - "oxlint" - "typescript" + - "@types/node" update-types: ["major"] \ No newline at end of file diff --git a/.github/workflows/js-gate.yml b/.github/workflows/js-gate.yml index 2f62fd1..d394f89 100644 --- a/.github/workflows/js-gate.yml +++ b/.github/workflows/js-gate.yml @@ -29,9 +29,10 @@ jobs: - name: Lint GitHub Actions workflows (issue #9) run: ./scripts/tools/lint-workflows.ts - - name: Release scripts lint + matrix agreement (deno) + - name: Release scripts lint + matrix agreement + dependency pins (deno) run: | cd scripts deno task lint deno check --config ./deno.jsonc tools/*.ts deno task check-matrix + deno task check-dep-pins diff --git a/AGENTS.md b/AGENTS.md index 462bfd8..2a9083e 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -2,14 +2,14 @@ A high-quality, mutation-tested Rust implementation of a Claude Code `PostToolUse` hook that classifies code comments as justified or unnecessary. SOTA engineering: 100% mutation on the core classifier, property-based tests, constitution-aligned, distributed via GitHub Releases and this repository's Nix flake. -The JS launcher layer uses Effect v4 RC. Never install, import, or pin `effect@3.*` in the JS side. +The JS launcher layer uses Effect v4. Never install, import, or pin `effect@3.*` in the JS side. ## Directory map | Path | What it holds | |------|---------------| | `crates/` | Rust core (comment-checker crate) | -| `npm/packages/comment-checker/` | JS/npm wrapper (ESM + Effect v4 RC launcher for the Rust binary) | +| `npm/packages/comment-checker/` | JS/npm wrapper (ESM + Effect v4 launcher for the Rust binary) | | `tests/` | Integration / F1 tests | | `eval/` | Evaluation corpus | | `.github/workflows/` | CI/release (pnpm + cross-platform Rust) | diff --git a/npm/packages/comment-checker/package.json b/npm/packages/comment-checker/package.json index 4c24c36..4ac4ada 100644 --- a/npm/packages/comment-checker/package.json +++ b/npm/packages/comment-checker/package.json @@ -23,9 +23,9 @@ "lint": "f=${OXLINT_FORMAT:-${AGENT:+agent}}; oxlint . --format=${f:-default}" }, "devDependencies": { - "@effect/platform-node": "4.0.0-rc.118", - "@types/node": "latest", - "effect": "4.0.0-rc.118", + "@effect/platform-node": "^4.0.2", + "@types/node": "^24.19.1", + "effect": "^4.0.2", "oxlint": "latest", "tsdown": "latest", "typescript": "latest" diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index caea2e3..146c8a7 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -4,13 +4,16 @@ settings: autoInstallPeers: true excludeLinksFromLockfile: false +overrides: + '@types/node': ^24.19.1 + importers: .: devDependencies: '@effect/tsgo': specifier: latest - version: 0.48.1 + version: 0.51.1 '@systemfsoftware/tsconfig': specifier: ^2.0.1 version: 2.0.1 @@ -30,14 +33,14 @@ importers: npm/packages/comment-checker: devDependencies: '@effect/platform-node': - specifier: 4.0.0-rc.118 - version: 4.0.0-rc.118(effect@4.0.0-rc.118)(redis@6.2.1) + specifier: ^4.0.2 + version: 4.0.2(effect@4.0.2)(redis@6.2.1) '@types/node': - specifier: latest - version: 26.6.4 + specifier: ^24.19.1 + version: 24.19.1 effect: - specifier: 4.0.0-rc.118 - version: 4.0.0-rc.118 + specifier: ^4.0.2 + version: 4.0.2 oxlint: specifier: latest version: 1.87.0 @@ -50,56 +53,56 @@ importers: packages: - '@effect/platform-node-shared@4.0.0-rc.118': - resolution: {integrity: sha512-pNbmIlxbAUxAthUNsp2xm1NLG9kxoYecsPkoYl9mNF2oxPcKDxAvkOar7tYnLSUpXJeATm4pxYVipdZjQfqO4Q==} + '@effect/platform-node-shared@4.0.2': + resolution: {integrity: sha512-xn8aDw9p/QUPt1Yi0C3HUMcycT+MjHfAe19FDUDrUuzcwq0xxskkamVSqbdykF/Dsuus4aZTsCBZA/vkhHIfbQ==} engines: {node: '>=18.0.0'} peerDependencies: - effect: ^4.0.0-rc.118 + effect: ^4.0.2 - '@effect/platform-node@4.0.0-rc.118': - resolution: {integrity: sha512-qdAxPZ+5bKxVnzkwziqcHZTHRHz2hyfFX6NhwEmm75CBpmaIzXIZR2LRqGd1/8eI3J3cOrXcAYzByhln7T1/EA==} + '@effect/platform-node@4.0.2': + resolution: {integrity: sha512-RWesXBQStnFHdD465gR5thFKgXO7FSyCKRZ56nWXB7PedNVzr8vV0nb/3MULLdri493sMz/b/pmGBTb6K8cBjQ==} engines: {node: '>=18.0.0'} peerDependencies: - effect: ^4.0.0-rc.118 + effect: ^4.0.2 redis: '>=5.0.0 <7.0.0' - '@effect/tsgo-darwin-arm64@0.48.1': - resolution: {integrity: sha512-DVLF9H0L+2cUqDnrkQjYYe1GyuBehYILQ1EUF3KHFafR/iBSDfZreezW5K+5zLZsLy/gJ5PV5AhbWCLTjZYqFA==} + '@effect/tsgo-darwin-arm64@0.51.1': + resolution: {integrity: sha512-Jx+1RB3pz1B2zYs1EmxybwDOq9KRpfdu/rn1XTULNMZmqUJZuvTQU9CRqB0g/ZgIa6MeNBZEYvmAu4Dq2/QVWA==} cpu: [arm64] os: [darwin] - '@effect/tsgo-darwin-x64@0.48.1': - resolution: {integrity: sha512-BeEOf4+lLAK986uH36OBD+n/rA5kMvIxGMfHlsZOQufYl8b1lickDO8H/8IRIXnBPFb6cIUi8813SPwOtaE9sw==} + '@effect/tsgo-darwin-x64@0.51.1': + resolution: {integrity: sha512-eRz7eHOOb2eJPUj66m89Zhzu924vGvNLDAXzcEAIaXVsWc4Zw29RVUsRnp/ozTmCbDVCvMqPSh8i9bkdAmt8mA==} cpu: [x64] os: [darwin] - '@effect/tsgo-linux-arm64@0.48.1': - resolution: {integrity: sha512-MsoIwtFYaJQbSfpOaCGqnquV/TgHoP4x/gmGCxRg8gCZP7cOrv/5qcywVWTGfkLWpfmbI1nOVUBULV7DlPmGMg==} + '@effect/tsgo-linux-arm64@0.51.1': + resolution: {integrity: sha512-LqZKkEEgJcq7dOho+h0esioU/idlB/+VhPuMR7l9B5Y7mk17Eivs8TiJQHtxLVfrh/nizbvZih7O5GujRNG0bw==} cpu: [arm64] os: [linux] - '@effect/tsgo-linux-arm@0.48.1': - resolution: {integrity: sha512-ydkNW7loScSD+6fPcliUEbSGfCAAShH4xYJ830f3DN9AJfLDF3GxNqgkayKyMEmuF9l6sU+/bbK8ER0qhn3GOw==} + '@effect/tsgo-linux-arm@0.51.1': + resolution: {integrity: sha512-vub6MCJIbczHp+Im1GTEUh5YyS+6YHcvH0ZmBdSjdvqzzBKbRnl5xUbO52tRJlty74f+3ey+uSh0nIO99NnggA==} cpu: [arm] os: [linux] - '@effect/tsgo-linux-x64@0.48.1': - resolution: {integrity: sha512-TAnxBsMAOU0KG7v0YunhmVL6DLzRKXi7MrsGvRkeOAzhe1ooLv0tp4S1FFhj/oYE3LxcvFJDJiniaFwglF3z/A==} + '@effect/tsgo-linux-x64@0.51.1': + resolution: {integrity: sha512-zyKKcdL2Pr4C//9raggaEuxhsSUR7jT6517vHvPwykbBPjPnnmGtM1Kq7NKEIZnvXWzXcaK+4wAK/0FqSqMOmw==} cpu: [x64] os: [linux] - '@effect/tsgo-win32-arm64@0.48.1': - resolution: {integrity: sha512-Atlc8JUYLahT8ShAX3FQ2Lsyjo6uz6Vu6H/cQ46dXEX3purIFaktdj3PFLzIvl4o9323Pjdlf8jbM5n9tIfnMQ==} + '@effect/tsgo-win32-arm64@0.51.1': + resolution: {integrity: sha512-yIF88Za3Bs793Mb8OShet8gc3kj/NNty6+ZoTdgiPXWlY6xh84bZZCOETqE7VcCmxXFT3NZSYQuDrU35/CTvRQ==} cpu: [arm64] os: [win32] - '@effect/tsgo-win32-x64@0.48.1': - resolution: {integrity: sha512-CDCaScEJ1gRwA8DRgRFn3Kh0mHPUCm1ln/gUwsRxC9qXJQdzqWEYzECa907b4Ts/dKW3uD4oFc47oKr+JQxYCg==} + '@effect/tsgo-win32-x64@0.51.1': + resolution: {integrity: sha512-5HO1TJkWMlErIXbfKKWEMzg0Jj0MPQAzs0g1k7wA3B1ZxNDoDY9iRu5eGM+G1a4HmKl3zA7hQZd0ch2Dh4MohQ==} cpu: [x64] os: [win32] - '@effect/tsgo@0.48.1': - resolution: {integrity: sha512-yLgXNwPaUWfGxcQw7FwLYwOF7me69r+tY4oHYV5hwMFE16qClXbIRnotNPp8pZ5pnbeLgPeRvCE7AWGQyYqQ2A==} + '@effect/tsgo@0.51.1': + resolution: {integrity: sha512-i6mA6cNah+0mkPdu8qZILCO3Pnd9kWo03/s82Ju+1DFhdL28HFagi+1PRGxqgq1uMaHziWQ+ciKvOmIoks2RdA==} hasBin: true '@oxc-project/types@0.151.0': @@ -398,11 +401,11 @@ packages: cpu: [arm64] os: [win32] - '@types/node@26.6.4': - resolution: {integrity: sha512-ldVPDCzj7fsaGZrLB0NuHuTvJcsNasysBAqMolr/cgxrLd1xbqxIr3XJiPnHHJUCxj5sNF1vnRj9aWnrVh5Jcg==} + '@types/node@24.19.1': + resolution: {integrity: sha512-aS3/DG0oM05K0RIXXP+hKjinGG5IgSSVGzswZxW3O0sS3pH4/fycXundUC9XsszgKCk4gHXylTEK6hyFxVxnoQ==} - '@types/ws@8.18.1': - resolution: {integrity: sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==} + '@types/ws@8.18.2': + resolution: {integrity: sha512-67MQl+fpWKVTT1NYdnmo3U4sc/xPo/zQBncVnI74qmQa0z/b+1g6iYqNmGCPbxO+zz2aklb08a0oHfegiVd0/w==} '@typescript/typescript-aix-ppc64@7.0.2': resolution: {integrity: sha512-MTKKkWB7p/0E9xi1d1tHtZ5PiLkGEMIq88pK2CubZjOsLtYTLqhgIgi6zepFa+9GHZ6h05NMCkQxGKiPXMxXtQ==} @@ -703,8 +706,8 @@ packages: oxc-resolver: optional: true - effect@4.0.0-rc.118: - resolution: {integrity: sha512-hxhpCd+swzrtu9lnyPpEFre2ODQzJ9qxgyL1oHIEY8NKnW4PxTbM1XzplMZXiAsLD62HO+KJc41Nqxr9T791ZQ==} + effect@4.0.2: + resolution: {integrity: sha512-YeYnLqSFrL4+tCOXRrfNP03w8FILoF/wDl+ukraJAGzXrjcS383W7LAAvGBbLatEyktmLxRq+9xJinx7QzXW5A==} empathic@2.0.1: resolution: {integrity: sha512-YGRs8knHhKHVShLkFET/rWAU8kmHbOV5LwN938RHI0pljAJ1Gf6SzXsSmRaEzcXTtOOmVqJ5+WtQPL5uigY50Q==} @@ -859,8 +862,8 @@ packages: unconfig-core@7.5.0: resolution: {integrity: sha512-Su3FauozOGP44ZmKdHy2oE6LPjk51M/TRRjHv2HNCWiDvfvCoxC2lno6jevMA91MYAdCdwP05QnWdWpSbncX/w==} - undici-types@8.9.0: - resolution: {integrity: sha512-KTDyRTYX8sWmKXAikPHHSyc63CRPETMctyjKFupcC6OBLXT3xsN0e9aF7m+mIXutFWpUXuedtowG7iLOzp0kQg==} + undici-types@7.24.6: + resolution: {integrity: sha512-WRNW+sJgj5OBN4/0JpHFqtqzhpbnV0GuB+OozA9gCL7a993SmU+1JBZCzLNxYsbMfIeDL+lTsphD5jN5N+n0zg==} undici@8.11.2: resolution: {integrity: sha512-u4UB2/IrKdU6lFxumHmmo1a3fCQO5tzQllRorfoRS63txhrB7xTpSn1PftwC4qEHkOaqP95fCWW4lJzwErwzhQ==} @@ -893,55 +896,55 @@ packages: snapshots: - '@effect/platform-node-shared@4.0.0-rc.118(effect@4.0.0-rc.118)': + '@effect/platform-node-shared@4.0.2(effect@4.0.2)': dependencies: - '@types/ws': 8.18.1 - effect: 4.0.0-rc.118 + '@types/ws': 8.18.2 + effect: 4.0.2 ws: 8.22.0 transitivePeerDependencies: - bufferutil - utf-8-validate - '@effect/platform-node@4.0.0-rc.118(effect@4.0.0-rc.118)(redis@6.2.1)': + '@effect/platform-node@4.0.2(effect@4.0.2)(redis@6.2.1)': dependencies: - '@effect/platform-node-shared': 4.0.0-rc.118(effect@4.0.0-rc.118) - effect: 4.0.0-rc.118 + '@effect/platform-node-shared': 4.0.2(effect@4.0.2) + effect: 4.0.2 redis: 6.2.1 undici: 8.11.2 transitivePeerDependencies: - bufferutil - utf-8-validate - '@effect/tsgo-darwin-arm64@0.48.1': + '@effect/tsgo-darwin-arm64@0.51.1': optional: true - '@effect/tsgo-darwin-x64@0.48.1': + '@effect/tsgo-darwin-x64@0.51.1': optional: true - '@effect/tsgo-linux-arm64@0.48.1': + '@effect/tsgo-linux-arm64@0.51.1': optional: true - '@effect/tsgo-linux-arm@0.48.1': + '@effect/tsgo-linux-arm@0.51.1': optional: true - '@effect/tsgo-linux-x64@0.48.1': + '@effect/tsgo-linux-x64@0.51.1': optional: true - '@effect/tsgo-win32-arm64@0.48.1': + '@effect/tsgo-win32-arm64@0.51.1': optional: true - '@effect/tsgo-win32-x64@0.48.1': + '@effect/tsgo-win32-x64@0.51.1': optional: true - '@effect/tsgo@0.48.1': + '@effect/tsgo@0.51.1': optionalDependencies: - '@effect/tsgo-darwin-arm64': 0.48.1 - '@effect/tsgo-darwin-x64': 0.48.1 - '@effect/tsgo-linux-arm': 0.48.1 - '@effect/tsgo-linux-arm64': 0.48.1 - '@effect/tsgo-linux-x64': 0.48.1 - '@effect/tsgo-win32-arm64': 0.48.1 - '@effect/tsgo-win32-x64': 0.48.1 + '@effect/tsgo-darwin-arm64': 0.51.1 + '@effect/tsgo-darwin-x64': 0.51.1 + '@effect/tsgo-linux-arm': 0.51.1 + '@effect/tsgo-linux-arm64': 0.51.1 + '@effect/tsgo-linux-x64': 0.51.1 + '@effect/tsgo-win32-arm64': 0.51.1 + '@effect/tsgo-win32-x64': 0.51.1 '@oxc-project/types@0.151.0': {} @@ -1093,13 +1096,13 @@ snapshots: '@turbo/windows-arm64@2.11.7': optional: true - '@types/node@26.6.4': + '@types/node@24.19.1': dependencies: - undici-types: 8.9.0 + undici-types: 7.24.6 - '@types/ws@8.18.1': + '@types/ws@8.18.2': dependencies: - '@types/node': 26.6.4 + '@types/node': 24.19.1 '@typescript/typescript-aix-ppc64@7.0.2': optional: true @@ -1243,7 +1246,7 @@ snapshots: dts-resolver@3.0.0: {} - effect@4.0.0-rc.118: {} + effect@4.0.2: {} empathic@2.0.1: {} @@ -1412,7 +1415,7 @@ snapshots: '@quansync/fs': 1.0.0 quansync: 1.0.0 - undici-types@8.9.0: {} + undici-types@7.24.6: {} undici@8.11.2: {} diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index ef687f3..db70c17 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -2,3 +2,5 @@ packages: - 'npm/packages/*' allowBuilds: msgpackr-extract: false +overrides: + '@types/node': ^24.19.1 diff --git a/scripts/deno.jsonc b/scripts/deno.jsonc index 7190bd7..bed6bdf 100644 --- a/scripts/deno.jsonc +++ b/scripts/deno.jsonc @@ -1,17 +1,18 @@ { "imports": { - "@effect/platform-deno": "npm:@effect/platform-deno@4.0.0-rc.118", + "@effect/platform-deno": "npm:@effect/platform-deno@4.0.2", "@libs/diff": "jsr:@libs/diff@4.0.1", "@std/cli": "jsr:@std/cli@1.0.32", "@std/path": "jsr:@std/path@1.1.6", "@std/toml": "jsr:@std/toml@1.0.11", "@std/yaml": "jsr:@std/yaml@1.3.0", "arktype": "npm:arktype@2.2.5", - "effect": "npm:effect@4.0.0-rc.118" + "effect": "npm:effect@4.0.2" }, "tasks": { "manifest:generate": "./tools/generate-platform-manifest.ts", "check-matrix": "./tools/check-matrix.ts", + "check-dep-pins": "./tools/check-dep-pins.ts", "lint": "deno lint --config ./deno.jsonc .", "lint:workflows": "./tools/lint-workflows.ts" }, diff --git a/scripts/deno.lock b/scripts/deno.lock index b773672..cdea175 100644 --- a/scripts/deno.lock +++ b/scripts/deno.lock @@ -8,9 +8,9 @@ "jsr:@std/path@1.1.6": "1.1.6", "jsr:@std/toml@1.0.11": "1.0.11", "jsr:@std/yaml@1.3.0": "1.3.0", - "npm:@effect/platform-deno@4.0.0-rc.118": "4.0.0-rc.118_effect@4.0.0-rc.118", + "npm:@effect/platform-deno@4.0.2": "4.0.2_effect@4.0.2", "npm:arktype@2.2.5": "2.2.5", - "npm:effect@4.0.0-rc.118": "4.0.0-rc.118" + "npm:effect@4.0.2": "4.0.2" }, "jsr": { "@libs/diff@4.0.1": { @@ -54,8 +54,8 @@ "@ark/util@0.56.4": { "integrity": "sha512-KKqwGSia44cvuuDY70jGfbiUdG2E8F2cM4npdqvJ22S5a0LknlwG6nIrfQG1nF8fzxpzP30098wp0a8ASs0prQ==" }, - "@effect/platform-deno@4.0.0-rc.118_effect@4.0.0-rc.118": { - "integrity": "sha512-BXyJw2PxEtbxPlF3WauhxRzzOc52vl5ct3HSREtJsfGL1JTMMW/uEVZY+81Lfnu3XcLIfyGpJrgGnwWPdgls+Q==", + "@effect/platform-deno@4.0.2_effect@4.0.2": { + "integrity": "sha512-j/elEyfYiE6X1bojvKg5b11RNQat+kTiew+COFTml/KpoT7iTyU9/MOAw7drjs2n9YT+E49AENoIWz78iyhTkg==", "dependencies": [ "@db/redis@npm:@jsr/db__redis@0.42.0", "@effect/platform-node-shared", @@ -66,8 +66,8 @@ "effect" ] }, - "@effect/platform-node-shared@4.0.0-rc.118_effect@4.0.0-rc.118": { - "integrity": "sha512-pNbmIlxbAUxAthUNsp2xm1NLG9kxoYecsPkoYl9mNF2oxPcKDxAvkOar7tYnLSUpXJeATm4pxYVipdZjQfqO4Q==", + "@effect/platform-node-shared@4.0.2_effect@4.0.2": { + "integrity": "sha512-xn8aDw9p/QUPt1Yi0C3HUMcycT+MjHfAe19FDUDrUuzcwq0xxskkamVSqbdykF/Dsuus4aZTsCBZA/vkhHIfbQ==", "dependencies": [ "@types/ws", "effect", @@ -156,14 +156,14 @@ ], "tarball": "https://npm.jsr.io/~/11/@jsr/std__streams/1.2.0.tgz" }, - "@types/node@26.6.3": { - "integrity": "sha512-dsqMQQoeTLqu9wynDD00q573mNzso3IdQOAfHRJqLCcmCFPoGo9A1bDpUcv/9tnKpErQWv9uKeGfl37EIS02Yg==", + "@types/node@26.6.4": { + "integrity": "sha512-ldVPDCzj7fsaGZrLB0NuHuTvJcsNasysBAqMolr/cgxrLd1xbqxIr3XJiPnHHJUCxj5sNF1vnRj9aWnrVh5Jcg==", "dependencies": [ "undici-types" ] }, - "@types/ws@8.18.1": { - "integrity": "sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==", + "@types/ws@8.18.2": { + "integrity": "sha512-67MQl+fpWKVTT1NYdnmo3U4sc/xPo/zQBncVnI74qmQa0z/b+1g6iYqNmGCPbxO+zz2aklb08a0oHfegiVd0/w==", "dependencies": [ "@types/node" ] @@ -185,8 +185,8 @@ "cluster-key-slot@1.1.0": { "integrity": "sha512-2Nii8p3RwAPiFwsnZvukotvow2rIHM+yQ6ZcBXGHdniadkYGZYiGmkHJIbZPIV9nfv7m/U1IPMVVcAhoWFeklw==" }, - "effect@4.0.0-rc.118": { - "integrity": "sha512-hxhpCd+swzrtu9lnyPpEFre2ODQzJ9qxgyL1oHIEY8NKnW4PxTbM1XzplMZXiAsLD62HO+KJc41Nqxr9T791ZQ==" + "effect@4.0.2": { + "integrity": "sha512-YeYnLqSFrL4+tCOXRrfNP03w8FILoF/wDl+ukraJAGzXrjcS383W7LAAvGBbLatEyktmLxRq+9xJinx7QzXW5A==" }, "undici-types@8.9.0": { "integrity": "sha512-KTDyRTYX8sWmKXAikPHHSyc63CRPETMctyjKFupcC6OBLXT3xsN0e9aF7m+mIXutFWpUXuedtowG7iLOzp0kQg==" @@ -202,9 +202,9 @@ "jsr:@std/path@1.1.6", "jsr:@std/toml@1.0.11", "jsr:@std/yaml@1.3.0", - "npm:@effect/platform-deno@4.0.0-rc.118", + "npm:@effect/platform-deno@4.0.2", "npm:arktype@2.2.5", - "npm:effect@4.0.0-rc.118" + "npm:effect@4.0.2" ] } } diff --git a/scripts/lib/shared.ts b/scripts/lib/shared.ts index d2b48a3..2fada5a 100644 --- a/scripts/lib/shared.ts +++ b/scripts/lib/shared.ts @@ -13,6 +13,8 @@ export const LAUNCHER_MANIFEST_PATH = join( export const RELEASE_WORKFLOW_PATH = join(ROOT, '.github', 'workflows', 'release.yml') export const CI_WORKFLOW_PATH = join(ROOT, '.github', 'workflows', 'ci.yml') export const PLATFORM_WORKFLOW_PATH = join(ROOT, '.github', 'workflows', 'platform.yml') +export const PNPM_LOCK_PATH = join(ROOT, 'pnpm-lock.yaml') +export const DEPENDABOT_PATH = join(ROOT, '.github', 'dependabot.yml') export interface Target { target: string diff --git a/scripts/tools/check-dep-pins.ts b/scripts/tools/check-dep-pins.ts new file mode 100755 index 0000000..e632eaa --- /dev/null +++ b/scripts/tools/check-dep-pins.ts @@ -0,0 +1,112 @@ +#!/usr/bin/env -S deno run --allow-read +import { resolve } from '@std/path' +import { parse as parseYaml } from '@std/yaml' +import { Schema } from 'effect' +import { parseCliArgs } from '../lib/cli.ts' +import { DEPENDABOT_PATH, PNPM_LOCK_PATH } from '../lib/shared.ts' + +const NODE_TYPES_LTS_MAJOR = 24 +const LAUNCHER_DIRECTORY = '/npm/packages/comment-checker' +const MAJOR_IGNORED = ['typescript', '@types/node'] +const SEMVER_MAJOR = 'version-update:semver-major' + +const ResolutionMap = Schema.Record(Schema.String, Schema.Unknown) +const Lockfile = Schema.Struct({ packages: ResolutionMap, snapshots: ResolutionMap }) +const IgnoreRule = Schema.Struct({ + 'dependency-name': Schema.String, + 'update-types': Schema.optionalKey(Schema.Array(Schema.String)), +}) +const UpdateEntry = Schema.Struct({ + 'package-ecosystem': Schema.String, + directory: Schema.optionalKey(Schema.String), + ignore: Schema.optionalKey(Schema.Array(IgnoreRule)), +}) +const DependabotConfig = Schema.Struct({ updates: Schema.Array(UpdateEntry) }) + +const failures: string[] = [] +const fail = (reason: string) => failures.push(reason) + +const flags = parseCliArgs({ + string: ['lockfile', 'dependabot'], +}) +const lockfilePath = typeof flags.lockfile === 'string' ? resolve(flags.lockfile) : PNPM_LOCK_PATH +const dependabotPath = typeof flags.dependabot === 'string' + ? resolve(flags.dependabot) + : DEPENDABOT_PATH + +async function readYamlOrExit(path: string, label: string): Promise { + try { + return parseYaml(await Deno.readTextFile(path)) + } catch (error) { + console.error( + `check-dep-pins: FAIL: cannot read ${label} ${path}: ${ + error instanceof Error ? error.message : String(error) + }`, + ) + Deno.exit(1) + } +} + +const withoutPeerSuffix = (key: string) => key.split('(')[0] + +function resolvedVersionsOf(name: string, lockfile: typeof Lockfile.Type): Set { + const versions = new Set() + const keys = [...Object.keys(lockfile.packages), ...Object.keys(lockfile.snapshots)] + for (const key of keys.map(withoutPeerSuffix)) { + const at = key.lastIndexOf('@') + if (at > 0 && key.slice(0, at) === name) versions.add(key.slice(at + 1)) + } + return versions +} + +function checkLockfile(raw: unknown) { + if (!Schema.is(Lockfile)(raw)) { + fail('pnpm-lock.yaml has no packages/snapshots maps') + return + } + const versions = resolvedVersionsOf('@types/node', raw) + if (versions.size === 0) { + fail('pnpm-lock.yaml resolves no @types/node; the pin cannot be confirmed') + } + for (const version of versions) { + if (Number(version.split('.')[0]) !== NODE_TYPES_LTS_MAJOR) { + fail( + `pnpm-lock.yaml resolves @types/node@${version}; only ${NODE_TYPES_LTS_MAJOR}.x is allowed`, + ) + } + } +} + +function checkDependabot(raw: unknown) { + if (!Schema.is(DependabotConfig)(raw)) { + fail('dependabot.yml does not parse as an updates list') + return + } + const entry = raw.updates.find((update) => + update['package-ecosystem'] === 'npm' && update.directory === LAUNCHER_DIRECTORY + ) + if (entry === undefined) { + fail(`dependabot.yml has no npm entry for ${LAUNCHER_DIRECTORY}`) + return + } + for (const name of MAJOR_IGNORED) { + const blocked = (entry.ignore ?? []).some((rule) => + rule['dependency-name'] === name && (rule['update-types'] ?? []).includes(SEMVER_MAJOR) + ) + if (!blocked) { + fail(`dependabot.yml ${LAUNCHER_DIRECTORY} entry does not ignore ${name} ${SEMVER_MAJOR}`) + } + } +} + +checkLockfile(await readYamlOrExit(lockfilePath, 'lockfile')) +checkDependabot(await readYamlOrExit(dependabotPath, 'dependabot config')) + +if (failures.length > 0) { + for (const reason of failures) { + console.error(`check-dep-pins: FAIL: ${reason}`) + } + Deno.exit(1) +} + +console.error('check-dep-pins: ok')