Simplify check scripts and lint policy layout

This commit is contained in:
murashit 2026-06-24 22:43:20 +09:00
parent f125a4cf17
commit 6ac54c494e
27 changed files with 34 additions and 158 deletions

View file

@ -11,7 +11,7 @@ permissions:
jobs:
check:
name: npm run check:ci
name: npm run check
runs-on: ubuntu-latest
steps:
@ -30,4 +30,4 @@ jobs:
run: npm ci --ignore-scripts
- name: Check
run: npm run check:ci
run: npm run check

View file

@ -36,7 +36,7 @@ jobs:
RELEASE_VERSION: ${{ github.ref_name }}
- name: Run checks and build
run: npm run check:ci
run: npm run check
- name: Generate artifact attestations
uses: actions/attest@59d89421af93a897026c735860bf21b6eb4f7b26 # v4

View file

@ -7,28 +7,28 @@
},
"plugins": [
// App-server and generated binding boundaries.
"./scripts/lint/grit/no-app-server-projection-rpcs.grit",
"./scripts/lint/grit/no-app-server-protocol-boundary-imports.grit",
"./scripts/lint/grit/no-generated-app-server-boundary-imports.grit",
"./scripts/lint/grit/no-generated-app-server-import-shapes.grit",
"./scripts/lint/grit/no-lower-level-boundary-imports.grit",
"./scripts/lint/no-app-server-projection-rpcs.grit",
"./scripts/lint/no-app-server-protocol-boundary-imports.grit",
"./scripts/lint/no-generated-app-server-boundary-imports.grit",
"./scripts/lint/no-generated-app-server-import-shapes.grit",
"./scripts/lint/no-lower-level-boundary-imports.grit",
// Chat feature architecture.
"./scripts/lint/grit/no-chat-domain-outer-layer-imports.grit",
"./scripts/lint/grit/no-chat-signal-imports.grit",
"./scripts/lint/grit/no-chat-state-escape-hatches.grit",
"./scripts/lint/grit/no-pure-chat-state-side-effects.grit",
"./scripts/lint/grit/no-ui-root-imports.grit",
"./scripts/lint/no-chat-domain-outer-layer-imports.grit",
"./scripts/lint/no-chat-signal-imports.grit",
"./scripts/lint/no-chat-state-escape-hatches.grit",
"./scripts/lint/no-pure-chat-state-side-effects.grit",
"./scripts/lint/no-ui-root-imports.grit",
// Project-wide source shape.
"./scripts/lint/grit/no-handwritten-reexports.grit",
"./scripts/lint/grit/no-non-reexport-index.grit",
"./scripts/lint/grit/no-self-referential-initializer-callback.grit",
"./scripts/lint/grit/no-unsafe-iterator-value.grit",
"./scripts/lint/grit/no-uncontrolled-preact-form-state.grit",
"./scripts/lint/no-handwritten-reexports.grit",
"./scripts/lint/no-non-reexport-index.grit",
"./scripts/lint/no-self-referential-initializer-callback.grit",
"./scripts/lint/no-unsafe-iterator-value.grit",
"./scripts/lint/no-uncontrolled-preact-form-state.grit",
// CSS source policy.
"./scripts/lint/grit/no-restricted-css-policy.grit"
"./scripts/lint/no-restricted-css-policy.grit"
],
"files": {
"includes": [

View file

@ -12,9 +12,9 @@ npm run check
Use this as the normal edit loop. `npm run format` applies Biome formatting and import organization; `npm run check` is the local preflight for type checking, tests, lint, format/assist checks, CSS checks, and the production bundle.
Use focused scripts for tight loops: `npm run typecheck`, `npm run test`, `npm run lint`, or `npm run build`. The `*:ci` variants match CI cache behavior.
Use focused scripts for tight loops: `npm run typecheck`, `npm run test`, `npm run build`, or the targeted `npm run check:*` scripts. CI and release preflight run the same `npm run check` command as local development.
Biome owns formatting, import organization, general JavaScript/TypeScript/JSON/CSS linting, GritQL source-shape plugins, GritQL import-boundary restrictions, and GritQL CSS source policy. Biome warnings fail `npm run lint` and `npm run check`; info diagnostics stay advisory, including accessibility while Obsidian-specific UI semantics are reviewed rule by rule. The CSS usage script still checks dead authored classes. ESLint remains for typed unsafe-any checks, Obsidian plugin policy, and Codex Panel responsibility-boundary rules that need TypeScript type information.
Biome owns formatting, import organization, general JavaScript/TypeScript/JSON/CSS linting, GritQL source-shape plugins, GritQL import-boundary restrictions, and GritQL CSS source policy. Biome warnings fail `npm run check`; info diagnostics stay advisory, including accessibility while Obsidian-specific UI semantics are reviewed rule by rule. The CSS usage script still checks dead authored classes. ESLint remains for typed unsafe-any checks, Obsidian plugin policy, and Codex Panel responsibility-boundary rules that need TypeScript type information.
## Generated and Loaded Files

View file

@ -20,8 +20,8 @@ jj git push --remote origin --bookmark main
git push origin X.Y.Z
```
`release:prepare` updates the version files and creates a `## Changes` release notes template. `release:preflight` verifies the local Jujutsu/Git state, release metadata, API baselines, lockfile, and the same non-cached `npm run check:ci` validation used by CI after the release commit is on `main`.
`release:prepare` updates the version files and creates a `## Changes` release notes template. `release:preflight` verifies the local Jujutsu/Git state, release metadata, API baselines, lockfile, and the same `npm run check` validation used by CI after the release commit is on `main`.
Release notes should normally include only user-facing changes. Internal implementation changes, validation details, and release procedure notes should be omitted when minor; when they are important enough to mention, group them into at most one concise bullet.
The release workflow runs `npm ci`, `npm run release:check`, `npm run check:ci`, attaches the install assets, and generates GitHub artifact attestations for them. The release notes file is required and must contain a single `## Changes` section. If a tag-triggered release fails before creating the GitHub Release, fix the commit, move the local tag with `jj tag set --allow-move -r main X.Y.Z`, then update the remote tag with `git push --force origin X.Y.Z`.
The release workflow runs `npm ci`, `npm run release:check`, `npm run check`, attaches the install assets, and generates GitHub artifact attestations for them. The release notes file is required and must contain a single `## Changes` section. If a tag-triggered release fails before creating the GitHub Release, fix the commit, move the local tag with `jj tag set --allow-move -r main X.Y.Z`, then update the remote tag with `git push --force origin X.Y.Z`.

View file

@ -17,20 +17,20 @@
"api:baseline": "node scripts/api-baseline.mjs",
"build": "node esbuild.config.mjs",
"build:styles": "node scripts/build-styles.mjs",
"check": "node scripts/check.mjs",
"check:ci": "node scripts/check.mjs --ci",
"check": "concurrently --group --pad-prefix 'node:check:*' && node --run build",
"check:biome": "biome check --no-errors-on-unmatched --diagnostic-level=warn --error-on-warnings",
"check:css-usage": "node scripts/lint/check-css-usage.mjs",
"check:eslint": "eslint src --max-warnings=0",
"check:test": "node --run test",
"check:typecheck": "node --run typecheck",
"check:unused": "knip --no-progress",
"format": "biome check --write --linter-enabled=false --no-errors-on-unmatched",
"format:check": "biome check --linter-enabled=false --no-errors-on-unmatched",
"generate:app-server-types": "node scripts/generate-app-server-types.mjs",
"lint": "node scripts/check.mjs --lint",
"lint:ci": "node scripts/check.mjs --ci --lint",
"release:check": "node scripts/release/check.mjs",
"release:preflight": "node scripts/release/preflight.mjs",
"release:prepare": "node scripts/release/prepare.mjs",
"test": "vitest run --cache --pool threads",
"test:ci": "vitest run --pool threads",
"typecheck": "tsc -p tsconfig.json --noEmit --incremental --tsBuildInfoFile node_modules/.cache/typescript/tsconfig.tsbuildinfo",
"typecheck:ci": "tsc -p tsconfig.json --noEmit"
"test": "vitest run --pool threads",
"typecheck": "tsc -p tsconfig.json --noEmit --incremental --tsBuildInfoFile node_modules/.cache/typescript/tsconfig.tsbuildinfo"
},
"devDependencies": {
"@biomejs/biome": "^2.5.1",

View file

@ -1,45 +0,0 @@
export const biomeLintCommand = "biome lint --no-errors-on-unmatched --diagnostic-level=warn --error-on-warnings";
export const biomeCheckCommand = "biome check --no-errors-on-unmatched --diagnostic-level=warn --error-on-warnings";
export function commandPlan({ ciMode, lintOnly }) {
if (lintOnly) {
return {
phases: [{ name: "lint", commands: lintCommands({ ciMode }) }],
};
}
return {
phases: [
{ name: "check", commands: checkCommands({ ciMode }) },
{ name: "build", commands: [{ name: "build", command: "node esbuild.config.mjs" }] },
],
};
}
function checkCommands({ ciMode }) {
const suffix = ciMode ? ":ci" : "";
return [
{ name: "typecheck", command: `npm run --silent typecheck${suffix}` },
{ name: "test", command: `npm run --silent test${suffix}` },
{ name: "lint:biome", command: biomeCheckCommand },
...nonBiomeLintCommands({ ciMode, namePrefix: "lint:" }),
];
}
function lintCommands({ ciMode, namePrefix = "" }) {
return [{ name: `${namePrefix}biome`, command: biomeLintCommand }, ...nonBiomeLintCommands({ ciMode, namePrefix })];
}
function nonBiomeLintCommands({ ciMode, namePrefix = "" }) {
const eslintCacheArgs = ciMode ? "" : " --cache --cache-strategy content --cache-location node_modules/.cache/eslint/.eslintcache";
return [
{
name: `${namePrefix}eslint`,
command: `eslint src --max-warnings=0${eslintCacheArgs}`,
},
{ name: `${namePrefix}css-usage`, command: "node scripts/lint/check-css-usage.mjs" },
{ name: `${namePrefix}unused`, command: "knip --no-progress" },
];
}

View file

@ -1,32 +0,0 @@
import concurrently from "concurrently";
import { commandPlan } from "./check-plan.mjs";
const args = process.argv.slice(2);
const validArgs = new Set(["--ci", "--lint"]);
const unknownArgs = args.filter((arg) => !validArgs.has(arg));
if (unknownArgs.length > 0) {
console.error("Usage: node scripts/check.mjs [--ci] [--lint]");
process.exit(1);
}
const ciMode = args.includes("--ci");
const lintOnly = args.includes("--lint");
const concurrentOptions = {
group: true,
padPrefix: true,
prefix: "name",
};
try {
const plan = commandPlan({ ciMode, lintOnly });
for (const phase of plan.phases) {
await runConcurrently(phase.commands);
}
} catch {
process.exit(1);
}
async function runConcurrently(commands) {
await concurrently(commands, concurrentOptions).result;
}

View file

@ -84,6 +84,6 @@ if (maybeRun("jj", ["root"])) {
run("npm", ["run", "release:check"]);
run("npm", ["run", "api:baseline"]);
run("npm", ["ci", "--dry-run"]);
run("npm", ["run", "check:ci"]);
run("npm", ["run", "check"]);
console.log(`release preflight passed for ${packageVersion}`);

View file

@ -1,47 +0,0 @@
import { describe, expect, it } from "vitest";
import { commandPlan } from "../../scripts/check-plan.mjs";
describe("check command plan", () => {
it("runs the local preflight checks before the build", () => {
expect(commandPlan({ ciMode: false, lintOnly: false }).phases).toEqual([
{
name: "check",
commands: [
{ name: "typecheck", command: "npm run --silent typecheck" },
{ name: "test", command: "npm run --silent test" },
{ name: "lint:biome", command: "biome check --no-errors-on-unmatched --diagnostic-level=warn --error-on-warnings" },
{
name: "lint:eslint",
command:
"eslint src --max-warnings=0 --cache --cache-strategy content --cache-location node_modules/.cache/eslint/.eslintcache",
},
{ name: "lint:css-usage", command: "node scripts/lint/check-css-usage.mjs" },
{ name: "lint:unused", command: "knip --no-progress" },
],
},
{
name: "build",
commands: [{ name: "build", command: "node esbuild.config.mjs" }],
},
]);
});
it("keeps lint-only mode on Biome lint", () => {
expect(commandPlan({ ciMode: false, lintOnly: true }).phases[0]).toEqual({
name: "lint",
commands: expect.arrayContaining([
{
name: "biome",
command: "biome lint --no-errors-on-unmatched --diagnostic-level=warn --error-on-warnings",
},
]),
});
});
it("omits cache-only local flags in CI mode", () => {
expect(commandPlan({ ciMode: true, lintOnly: false }).phases[0]?.commands).toContainEqual({
name: "lint:eslint",
command: "eslint src --max-warnings=0",
});
});
});

View file

@ -646,7 +646,7 @@ async function tempBiomeWorkspace(plugins) {
JSON.stringify({
$schema: "https://biomejs.dev/schemas/2.5.1/schema.json",
vcs: { enabled: false },
plugins: plugins.map((plugin) => path.join(repoRoot, "scripts", "lint", "grit", plugin)),
plugins: plugins.map((plugin) => path.join(repoRoot, "scripts", "lint", plugin)),
css: { linter: { enabled: true } },
}),
);