Align Node 26 tooling and enforce recorded compatibility checks

This commit is contained in:
murashit 2026-07-10 12:48:36 +09:00
parent 5e362add5b
commit 7f09418f7c
8 changed files with 40 additions and 9 deletions

View file

@ -23,11 +23,17 @@ jobs:
- name: Setup Node.js
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6
with:
node-version: 24
node-version: 26
cache: npm
- name: Install dependencies
run: npm ci --ignore-scripts
- name: Check recorded API baselines
run: npm run api:baseline -- --recorded-only
- name: Check release metadata
run: npm run release:check
- name: Check
run: npm run check

View file

@ -24,7 +24,7 @@ jobs:
- name: Setup Node.js
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6
with:
node-version: 24
node-version: 26
package-manager-cache: false
- name: Install dependencies

1
.node-version Normal file
View file

@ -0,0 +1 @@
26

View file

@ -10,6 +10,8 @@ npm run fix
npm run check
```
Use Node.js 26, matching `.node-version`, CI, and the installed Node type definitions.
Use this as the normal edit loop: make the change, run `npm run fix`, then run `npm run check`. Treat `npm run fix` as trusted mechanical cleanup for formatting, import ordering, and Knip safe fixes; review the resulting diff at normal change-boundary checkpoints rather than after each tool adjustment.
Use focused scripts such as `npm run typecheck`, `npm run test`, or `npm run build` only when diagnosing a specific failure or when a full check would obscure the signal while iterating. Do not treat focused scripts as a substitute for the final `npm run check`. CI and release preflight run the same `npm run check` command as local development.
@ -83,3 +85,5 @@ npm run api:baseline
Obsidian runtime compatibility is declared through `manifest.json` and `versions.json`. The `obsidian` npm package provides compile-time TypeScript API definitions; it is not runtime validation for an Obsidian app-version matrix. Because the project does not run app-version smoke tests, keep the API type package in the same minor as `manifest.minAppVersion` and use the latest patch in that minor for local type checking. `npm run api:baseline` exits non-zero when the local environment or recorded baselines drift. Raise `manifest.minAppVersion` only when intentionally adopting a newer Obsidian app/API minor.
Codex app-server compatibility is managed by Codex CLI minor version. README records the tested Codex CLI patch version, `src/app-server/connection/compatibility.json` records the machine-readable app-server capability baseline used by the panel client profile, and the baseline check verifies that the local `codex --version` is in the same minor before app-server binding or compatibility work.
Pull-request CI runs `npm run api:baseline -- --recorded-only`, which validates checked-in compatibility declarations without requiring a Codex CLI installation. Local compatibility work should still run `npm run api:baseline` so the installed CLI is checked too.

3
package-lock.json generated
View file

@ -8,6 +8,9 @@
"name": "codex-panel",
"version": "4.5.1",
"license": "Apache-2.0",
"engines": {
"node": ">=26"
},
"dependencies": {
"@preact/signals": "^2.9.2",
"@tanstack/query-core": "^5.101.1",

View file

@ -13,6 +13,9 @@
"url": "https://github.com/murashit/codex-panel/issues"
},
"homepage": "https://github.com/murashit/codex-panel#readme",
"engines": {
"node": ">=26"
},
"scripts": {
"api:baseline": "node scripts/api-baseline.mjs",
"build": "node esbuild.config.mjs",

View file

@ -3,19 +3,20 @@ import { readFile } from "node:fs/promises";
import path from "node:path";
import { fileURLToPath } from "node:url";
const validArgs = new Set(["--json"]);
const validArgs = new Set(["--json", "--recorded-only"]);
if (isMain()) {
const args = new Set(process.argv.slice(2));
const asJson = args.has("--json");
const recordedOnly = args.has("--recorded-only");
for (const arg of args) {
if (!validArgs.has(arg)) {
console.error("Usage: node scripts/api-baseline.mjs [--json]");
console.error("Usage: node scripts/api-baseline.mjs [--json] [--recorded-only]");
process.exit(1);
}
}
const report = await createApiBaselineReport();
const report = await createApiBaselineReport({ skipLocalCodex: recordedOnly });
if (asJson) {
console.log(JSON.stringify(report, null, 2));
} else {
@ -59,7 +60,11 @@ export async function createApiBaselineReport(options = {}) {
const readmeBaselines = readCompatibilityBaselines(inputs.readme);
const codexReadmeVersion = readmeBaselines.codexTestedCliVersion;
const codexLocalVersion = options.readCodexVersion ? await options.readCodexVersion() : readCodexVersion();
const codexLocalVersion = options.skipLocalCodex
? null
: options.readCodexVersion
? await options.readCodexVersion()
: readCodexVersion();
const codexReadmeSemver = parseSemver(codexReadmeVersion);
const codexLocalSemver = parseSemver(codexLocalVersion);
@ -85,7 +90,7 @@ export async function createApiBaselineReport(options = {}) {
if (!codexReadmeSemver) {
fail("README.md Compatibility table must define `codex.testedCliVersion` as X.Y.Z.");
}
if (!codexLocalSemver) {
if (!options.skipLocalCodex && !codexLocalSemver) {
fail("local codex --version could not be read.");
}
if (codexReadmeSemver && codexLocalSemver && minorKey(codexReadmeSemver) !== minorKey(codexLocalSemver)) {
@ -143,6 +148,7 @@ export async function createApiBaselineReport(options = {}) {
readmeTestedMinor: minorKey(codexReadmeSemver),
localCliVersion: codexLocalVersion,
localCliMinor: minorKey(codexLocalSemver),
localCliCheckSkipped: options.skipLocalCodex === true,
localCliMatchesTestedMinor: codexReadmeSemver && codexLocalSemver ? minorKey(codexReadmeSemver) === minorKey(codexLocalSemver) : null,
appServerGenerationExperimentalDeclared,
appServerGenerationExperimental,
@ -275,8 +281,8 @@ function printReport(report) {
console.log(` policy: ${report.codex.policy}`);
console.log(` compatibility table CLI: ${displayValue(report.codex.readmeTestedCliVersion)}`);
console.log(` compatibility table minor: ${displayValue(report.codex.readmeTestedMinor)}`);
console.log(` local codex CLI: ${displayValue(report.codex.localCliVersion)}`);
console.log(` local codex minor: ${displayValue(report.codex.localCliMinor)}`);
console.log(` local codex CLI: ${report.codex.localCliCheckSkipped ? "(skipped)" : displayValue(report.codex.localCliVersion)}`);
console.log(` local codex minor: ${report.codex.localCliCheckSkipped ? "(skipped)" : displayValue(report.codex.localCliMinor)}`);
console.log(` declared generate-ts --experimental: ${report.codex.appServerGenerationExperimentalDeclared ? "yes" : "no"}`);
console.log(` generate-ts --experimental: ${report.codex.appServerGenerationExperimental ? "yes" : "no"}`);
console.log(` initialize experimentalApi: ${report.codex.initializeExperimentalApi ? "yes" : "no"}`);

View file

@ -131,6 +131,14 @@ describe("development scripts", () => {
expect(report.codex.initializeExperimentalApi).toBe(true);
expect(report.codex.initializeRequestAttestationDisabled).toBe(true);
expect(report.failures).toEqual([]);
const recordedOnlyReport = await createApiBaselineReport({
cwd,
readCodexVersion: () => null,
skipLocalCodex: true,
});
expect(recordedOnlyReport.codex.localCliCheckSkipped).toBe(true);
expect(recordedOnlyReport.failures).toEqual([]);
});
it("reports representative CSS usage policy failures", async () => {