From 6aff9dc207c20210de269e170d552348d72ab856 Mon Sep 17 00:00:00 2001 From: "k.hiro1818" Date: Wed, 21 Oct 2026 07:36:54 +0000 Subject: [PATCH 01/10] build: enable prettier cache in lint script Co-Authored-By: Claude Opus 5.5 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index a3a8fc549..019864cf7 100644 --- a/package.json +++ b/package.json @@ -13,7 +13,7 @@ "test": "npm run test:e2e && npm run test:unit", "check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json", "check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch", - "lint": "prettier --check . && oxlint . && eslint .", + "lint": "prettier --check . --cache && oxlint . && eslint .", "format": "prettier --write .", "test:e2e": "playwright test", "test:unit": "vitest", From e74eebcb197bc47fc5523f5ca364b374b96351a5 Mon Sep 17 00:00:00 2001 From: "k.hiro1818" Date: Wed, 21 Oct 2026 07:38:06 +0000 Subject: [PATCH 02/10] chore: drop emit-only options from tsconfig The generated $app/tsconfig sets noEmit, so outDir, sourceMap, and incremental have no effect; outDir also breaks svelte-check --tsgo. Co-Authored-By: Claude Opus 5.5 --- tsconfig.json | 3 --- 1 file changed, 3 deletions(-) diff --git a/tsconfig.json b/tsconfig.json index 294a1483e..182fd6019 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,9 +1,6 @@ { "extends": "$app/tsconfig", "compilerOptions": { - "incremental": true, - "outDir": "build", - "sourceMap": true, "strict": true, "types": ["$app/types", "node", "vitest/globals"] // Note: "types" is replaced (not merged) when extending tsconfig, so keep "$app/types" and add "node" }, From 8924f83604b1e7789428732a664c5de0d7234b74 Mon Sep 17 00:00:00 2001 From: "k.hiro1818" Date: Wed, 21 Oct 2026 07:38:37 +0000 Subject: [PATCH 03/10] ci: pin GitHub Actions to commit SHAs Pin each action to the commit its current major tag points to, and let Renovate keep the digests and version comments up to date. Co-Authored-By: Claude Opus 5.5 --- .github/workflows/ci.yml | 18 +++++++++--------- renovate.json | 2 +- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3a2597e88..c1fcd916f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -33,17 +33,17 @@ jobs: steps: - name: Check out Git repository - uses: actions/checkout@v7 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - name: Setup pnpm - uses: pnpm/action-setup@v6 + uses: pnpm/action-setup@0977fd99725f1db4007ccb2928dbb4e90d06cc86 # v6.0.10 with: # Auto-detect pnpm version from package.json (requires packageManager in package.json) package_json_file: package.json run_install: false - name: Use Node.js ${{ matrix.node-version }} - uses: actions/setup-node@v7 + uses: actions/setup-node@949feb2413d6458794dcd2491c4babbbce0c15c1 # v7.1.0 with: node-version: ${{ matrix.node-version }} check-latest: true @@ -82,17 +82,17 @@ jobs: steps: - name: Check out Git repository - uses: actions/checkout@v7 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - name: Setup pnpm - uses: pnpm/action-setup@v6 + uses: pnpm/action-setup@0977fd99725f1db4007ccb2928dbb4e90d06cc86 # v6.0.10 with: # Auto-detect pnpm version from package.json (requires packageManager in package.json) package_json_file: package.json run_install: false - name: Setup Node.js - uses: actions/setup-node@v7 + uses: actions/setup-node@949feb2413d6458794dcd2491c4babbbce0c15c1 # v7.1.0 with: node-version: 24 check-latest: true @@ -134,17 +134,17 @@ jobs: steps: - name: Check out Git repository - uses: actions/checkout@v7 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - name: Setup pnpm - uses: pnpm/action-setup@v6 + uses: pnpm/action-setup@0977fd99725f1db4007ccb2928dbb4e90d06cc86 # v6.0.10 with: # Auto-detect pnpm version from package.json (requires packageManager in package.json) package_json_file: package.json run_install: false - name: Setup Node.js - uses: actions/setup-node@v7 + uses: actions/setup-node@949feb2413d6458794dcd2491c4babbbce0c15c1 # v7.1.0 with: node-version: 24 check-latest: true diff --git a/renovate.json b/renovate.json index 91e3277b6..29856c5bb 100644 --- a/renovate.json +++ b/renovate.json @@ -1,6 +1,6 @@ { "$schema": "https://docs.renovatebot.com/renovate-schema.json", - "extends": ["config:recommended"], + "extends": ["config:recommended", "helpers:pinGitHubActionDigestsToSemver"], "dependencyDashboard": false, "prHourlyLimit": 10, "automerge": false, From ec81f37b2340ff24b70b0d0b4d52f4b811d43138 Mon Sep 17 00:00:00 2001 From: "k.hiro1818" Date: Wed, 21 Oct 2026 07:39:26 +0000 Subject: [PATCH 04/10] ci: drop duplicate build from deploy jobs vercel build already runs install and the build command, so the prior pnpm build was redundant. Run prisma migrate deploy after vercel build to keep migrations gated on a successful build. Co-Authored-By: Claude Opus 5.5 --- .github/workflows/ci.yml | 28 ++++++++++++---------------- 1 file changed, 12 insertions(+), 16 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c1fcd916f..16d29bda2 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -102,21 +102,19 @@ jobs: - name: Install dependencies run: pnpm install - - name: Build - run: pnpm build + - name: Pull Vercel Environment Information + run: pnpm vercel pull --yes --environment=preview --token=${{ secrets.VERCEL_TOKEN }} + + - name: Build Project Artifacts + run: pnpm vercel build --token=${{ secrets.VERCEL_TOKEN }} + # Migrate only after the build succeeds, so a broken build leaves the DB untouched - name: Apply all pending migrations to the database run: pnpm exec prisma migrate deploy env: DATABASE_URL: ${{ secrets.PREVIEW_DATABASE_URL }} DIRECT_URL: ${{ secrets.PREVIEW_DIRECT_URL }} - - name: Pull Vercel Environment Information - run: pnpm vercel pull --yes --environment=preview --token=${{ secrets.VERCEL_TOKEN }} - - - name: Build Project Artifacts - run: pnpm vercel build --token=${{ secrets.VERCEL_TOKEN }} - - name: Deploy Project Artifacts to Vercel id: deploy run: echo "url=\"$(pnpm vercel deploy --prebuilt --token=${{ secrets.VERCEL_TOKEN }})\"" >> $GITHUB_OUTPUT @@ -154,20 +152,18 @@ jobs: - name: Install dependencies run: pnpm install - - name: Build - run: pnpm build + - name: Pull Vercel Environment Information + run: pnpm vercel pull --yes --environment=production --token=${{ secrets.VERCEL_TOKEN }} + + - name: Build Project Artifacts + run: pnpm vercel build --prod --token=${{ secrets.VERCEL_TOKEN }} + # Migrate only after the build succeeds, so a broken build leaves the DB untouched - name: Apply all pending migrations to the database run: pnpm exec prisma migrate deploy env: DATABASE_URL: ${{ secrets.PRODUCTION_DATABASE_URL }} DIRECT_URL: ${{ secrets.PRODUCTION_DIRECT_URL }} - - name: Pull Vercel Environment Information - run: pnpm vercel pull --yes --environment=production --token=${{ secrets.VERCEL_TOKEN }} - - - name: Build Project Artifacts - run: pnpm vercel build --prod --token=${{ secrets.VERCEL_TOKEN }} - - name: Deploy Project Artifacts to Vercel run: pnpm vercel deploy --prod --prebuilt --token=${{ secrets.VERCEL_TOKEN }} From 1e3a6222016f11529a551d75c7d924bb59b0dc09 Mon Sep 17 00:00:00 2001 From: "k.hiro1818" Date: Wed, 21 Oct 2026 08:14:45 +0000 Subject: [PATCH 05/10] ci: extract pnpm and Node.js setup into a composite action Every job ran the same pnpm, Node.js, and install steps; keep them and their pinned SHAs in one place. Co-Authored-By: Claude Opus 5.5 --- .github/actions/setup/action.yml | 29 +++++++++++++++++ .github/workflows/ci.yml | 55 ++++---------------------------- 2 files changed, 35 insertions(+), 49 deletions(-) create mode 100644 .github/actions/setup/action.yml diff --git a/.github/actions/setup/action.yml b/.github/actions/setup/action.yml new file mode 100644 index 000000000..ef444c614 --- /dev/null +++ b/.github/actions/setup/action.yml @@ -0,0 +1,29 @@ +name: Setup pnpm and Node.js +description: Install pnpm and Node.js, then install dependencies + +inputs: + node-version: + description: Node.js version to install + default: '24' + +runs: + using: composite + steps: + - name: Setup pnpm + uses: pnpm/action-setup@0977fd99725f1db4007ccb2928dbb4e90d06cc86 # v6.0.10 + with: + # Auto-detect pnpm version from package.json (requires packageManager in package.json) + package_json_file: package.json + run_install: false + + - name: Use Node.js ${{ inputs.node-version }} + uses: actions/setup-node@949feb2413d6458794dcd2491c4babbbce0c15c1 # v7.1.0 + with: + node-version: ${{ inputs.node-version }} + check-latest: true + cache: 'pnpm' + cache-dependency-path: pnpm-lock.yaml + + - name: Install dependencies + run: pnpm install # postinstall runs prisma generate + shell: bash diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 16d29bda2..17cadaa02 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -35,23 +35,10 @@ jobs: - name: Check out Git repository uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - - name: Setup pnpm - uses: pnpm/action-setup@0977fd99725f1db4007ccb2928dbb4e90d06cc86 # v6.0.10 - with: - # Auto-detect pnpm version from package.json (requires packageManager in package.json) - package_json_file: package.json - run_install: false - - - name: Use Node.js ${{ matrix.node-version }} - uses: actions/setup-node@949feb2413d6458794dcd2491c4babbbce0c15c1 # v7.1.0 + - name: Setup pnpm and Node.js + uses: ./.github/actions/setup with: node-version: ${{ matrix.node-version }} - check-latest: true - cache: 'pnpm' - cache-dependency-path: pnpm-lock.yaml - - - name: Install dependencies - run: pnpm install - name: Build run: pnpm build @@ -84,23 +71,8 @@ jobs: - name: Check out Git repository uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - - name: Setup pnpm - uses: pnpm/action-setup@0977fd99725f1db4007ccb2928dbb4e90d06cc86 # v6.0.10 - with: - # Auto-detect pnpm version from package.json (requires packageManager in package.json) - package_json_file: package.json - run_install: false - - - name: Setup Node.js - uses: actions/setup-node@949feb2413d6458794dcd2491c4babbbce0c15c1 # v7.1.0 - with: - node-version: 24 - check-latest: true - cache: 'pnpm' - cache-dependency-path: pnpm-lock.yaml - - - name: Install dependencies - run: pnpm install + - name: Setup pnpm and Node.js + uses: ./.github/actions/setup - name: Pull Vercel Environment Information run: pnpm vercel pull --yes --environment=preview --token=${{ secrets.VERCEL_TOKEN }} @@ -134,23 +106,8 @@ jobs: - name: Check out Git repository uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - - name: Setup pnpm - uses: pnpm/action-setup@0977fd99725f1db4007ccb2928dbb4e90d06cc86 # v6.0.10 - with: - # Auto-detect pnpm version from package.json (requires packageManager in package.json) - package_json_file: package.json - run_install: false - - - name: Setup Node.js - uses: actions/setup-node@949feb2413d6458794dcd2491c4babbbce0c15c1 # v7.1.0 - with: - node-version: 24 - check-latest: true - cache: 'pnpm' - cache-dependency-path: pnpm-lock.yaml - - - name: Install dependencies - run: pnpm install + - name: Setup pnpm and Node.js + uses: ./.github/actions/setup - name: Pull Vercel Environment Information run: pnpm vercel pull --yes --environment=production --token=${{ secrets.VERCEL_TOKEN }} From 4dc5dbd01974f6cfb03723033fe449e3fe9c784b Mon Sep 17 00:00:00 2001 From: "k.hiro1818" Date: Wed, 21 Oct 2026 08:15:15 +0000 Subject: [PATCH 06/10] ci: run lint, check, and unit tests as parallel jobs None of them uses the build output, so split them out of the build job and make the deploy jobs wait for all four. The build job keeps its matrix so the existing build (24) check name stays the same. Co-Authored-By: Claude Opus 5.5 --- .github/workflows/ci.yml | 35 +++++++++++++++++++++++++++++++++-- 1 file changed, 33 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 17cadaa02..5f328ce7a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -43,12 +43,43 @@ jobs: - name: Build run: pnpm build + # lint, check, and unit-test do not use the build output, so they run in parallel with build + lint: + runs-on: ubuntu-latest + + steps: + - name: Check out Git repository + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + + - name: Setup pnpm and Node.js + uses: ./.github/actions/setup + - name: Lint run: pnpm lint + check: + runs-on: ubuntu-latest + + steps: + - name: Check out Git repository + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + + - name: Setup pnpm and Node.js + uses: ./.github/actions/setup + - name: Check run: pnpm check + unit-test: + runs-on: ubuntu-latest + + steps: + - name: Check out Git repository + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + + - name: Setup pnpm and Node.js + uses: ./.github/actions/setup + - name: Unit test run: pnpm test:unit @@ -59,7 +90,7 @@ jobs: preview: if: ${{ github.ref != 'refs/heads/main' }} - needs: build + needs: [build, lint, check, unit-test] runs-on: ubuntu-latest @@ -98,7 +129,7 @@ jobs: production: if: ${{ github.ref == 'refs/heads/main' }} - needs: build + needs: [build, lint, check, unit-test] runs-on: ubuntu-latest From 64567dc66d141b7886e483a1f4c368fd2a3bfb8c Mon Sep 17 00:00:00 2001 From: "k.hiro1818" Date: Wed, 21 Oct 2026 08:32:22 +0000 Subject: [PATCH 07/10] docs: add CI and typecheck speedup plan Co-Authored-By: Claude Sonnet 5.5 --- .../ci-and-typecheck-speedup/plan.md | 185 ++++++++++++++++++ 1 file changed, 185 insertions(+) create mode 100644 docs/dev-notes/2026-10-20/ci-and-typecheck-speedup/plan.md diff --git a/docs/dev-notes/2026-10-20/ci-and-typecheck-speedup/plan.md b/docs/dev-notes/2026-10-20/ci-and-typecheck-speedup/plan.md new file mode 100644 index 000000000..abd7e0137 --- /dev/null +++ b/docs/dev-notes/2026-10-20/ci-and-typecheck-speedup/plan.md @@ -0,0 +1,185 @@ +# CI と型チェックの高速化、actions の SHA 固定 + +## 概要 + +Kit 3 への移行(PR B、branch `#4192`)の後、build・check が遅いと感じるという相談から、時間の内訳を測って4つの対策を選んだ。 +あわせて、セキュリティのために、GitHub Actions の `uses:` を commit SHA と `# vX.Y.Z` のコメントの形に固定する。 +PR B とは別の PR にし、PR B のマージ後に `staging` から branch を切る。 +運用実績の少ない代替ツール(rsvelte など)は入れない。tsgo は型チェックの診断だけに使い、TypeScript 6 は残す。 + +## 計測(2026-10-19、devcontainer 上) + +| コマンド | 時間 | 内訳 | +| ---------------- | ------- | ------------------------------------------------------------------------------------------------ | +| `pnpm build` | 14.4 秒 | `prisma generate` 1.8 秒、Vite(server 2.6 秒・client 2.0 秒)、adapter-vercel の出力 2.3 秒など | +| `pnpm lint` | 11.6 秒 | prettier 9.3 秒、ESLint 4.4 秒、oxlint 0.1 秒 | +| `pnpm check` | 10.6 秒 | `svelte-kit sync` 0.9 秒、svelte-check 9.3 秒 | +| `pnpm test:unit` | 6.0 秒 | | + +- CI は `staging` の直近10回が全体で 2分21秒〜2分50秒。#4191 のマージ(2分44秒)では `preview` が 1分16秒で、残りが `build` ジョブ。 +- `build (24)` ジョブの step ごとの時間(ユーザーが共有した平均的な実行、全体 1分19秒): + + | 区分 | step と時間 | 計 | + | -------- | --------------------------------------------------------------------------------------------- | ---- | + | 準備 | Set up job 2秒、Setup pnpm 6秒、Use Node.js 24 6秒、Install dependencies 10秒(checkout 0秒) | 24秒 | + | 本体 | Build 13秒、Lint 11秒、Check 11秒、Unit test 13秒 | 48秒 | + | 後片付け | Post Use Node.js 24 5秒(他は 0秒) | 5秒 | + + ジョブごとの固定の負担は約29秒。本体の4つは順に実行されるので、合計の48秒がそのまま掛かる。 + +- 今の `build` ジョブは build・lint・check・unit test を1つのジョブで順に実行する。preview・production のジョブは `pnpm build` の後に `vercel build` を実行する。`vercel build` は内部で install と build command を実行するので、その前の `pnpm build` が重複している(根拠は Phase 5)。`vercel build` 自体は `vercel deploy --prebuilt` に必要なので残す。 +- svelte-check 4.7.7 の `--tsgo`: 使い捨てのコピーに `@typescript/native-preview@7.0.0-dev.20260707.2` を入れて測った。そのままでは「rootDir の外」の設定エラーが103件出る。`tsconfig.json` の `"outDir": "build"` を消すと、通常の check と同じ 0 errors・30 warnings になり、9.0 秒 → 2.6 秒。`.ts`・`.svelte`・テスト・`prisma/` に1件ずつ入れた型エラーを、通常の check と同じ位置ですべて検出した。表示のファイル数は 2740 → 約130 になるが、数え方の違い。 +- `--incremental` も `outDir` が原因で同じエラーになる。`outDir` を消すと 2回目は 4.1 秒。 +- `prettier --check . --cache`: 1回目 9.2 秒、2回目 1.7 秒。 + +## actions の現状(2026-10-20) + +workflow は `.github/workflows/ci.yml` の1本で、3つの action を major tag で参照している(各3か所)。`git ls-remote --tags` で、各 repo の tag が指す commit を確かめた。 + +| action | 現在の参照 | tag が指す版 | commit SHA | +| -------------------- | ---------- | ------------ | ------------------------------------------ | +| `actions/checkout` | `@v7` | v7.0.1 | `3d3c42e5aac5ba805825da76410c181273ba90b1` | +| `actions/setup-node` | `@v7` | v7.1.0 | `949feb2413d6458794dcd2491c4babbbce0c15c1` | +| `pnpm/action-setup` | `@v6` | v6.0.10 | `0977fd99725f1db4007ccb2928dbb4e90d06cc86` | + +- `pnpm/action-setup` は annotated tag なので、tag object ではなく、tag が指す commit(`^{}` の行)を使う。v6.1.0 も出ているが、`v6` の tag はまだ v6.0.10 を指す。 +- GitHub の公式の security hardening guide は、action を commit SHA に固定するよう勧めている。tag は「can be moved or deleted if a bad actor gains access to the repository storing the action」ため、作者を信頼していても危険がある。 +- この workflow は `VERCEL_TOKEN` と、preview・production の `DATABASE_URL`・`DIRECT_URL` を扱う。action の repo が乗っ取られて tag が書き換えられると、これらが漏れうる。 + +## 方針 + +- **低リスクから順に入れる:** ローカルだけに効くもの(Phase 1・2)→ 挙動の変わらない CI の変更(Phase 3)→ CI の構成(Phase 4)→ 配備のジョブ(Phase 5)。 +- **型チェックの正は tsc のまま:** CI の `pnpm check` は変えず、tsgo はローカル用の `check:fast` として足す。プレビュー版の tsgo が壊れても、影響は手元の型チェックの結果だけにとどまる。CI を tsgo に切り替えるかは、運用してから決める(「後で決めること」)。 +- **`outDir` は消す:** tsc で出力することは無く(`$app/tsconfig` が `noEmit`、`vite build` は型を消すだけ)、`--tsgo`・`--incremental` を壊す原因になっている。同じ理由で効果の無い `sourceMap` も消す(source map は tsc が出力するファイルにしか付かない。Vite・Vitest の source map は tsconfig の外で決まる)。 +- **SHA は今の tag と同じ commit にする:** 固定するだけで、動く action のコードは変えない。版の更新は Renovate の PR に任せる。 +- **SHA の更新は Renovate に任せる:** `helpers:pinGitHubActionDigestsToSemver` は、digest を固定し、コメントを `vX.Y.Z` の完全な版にする preset。更新の PR は SHA とコメントを一緒に書き換える。 +- **CI の check 名は残す:** `build` ジョブは matrix ごと残し、check 名 `build (24)` を変えない。lint・check・unit test は新しいジョブにする。 +- **migration は build の成功後:** 今は `pnpm build` が migration の前にあり、build が失敗すれば DB は変わらない。二重 build を消すときに `prisma migrate deploy` を `vercel build` の後へ移し、この順序を保つ。 + +## 採らなかった案 + +- **rsvelte などの代替ツール:** 運用実績が少ない(ユーザー判断)。 +- **CI の check を最初から `--tsgo` にする:** tsgo はプレビュー版で、tsc との差を見る期間が無い。 +- **`--tsgo-experimental-api`:** svelte-check の help に experimental と明記されている。 +- **svelte-check の `--incremental`:** 2回目でも 4.1 秒で、tsgo(初回から 2.6 秒)より遅い。CI で効かせるにはキャッシュの保存も要る。 +- **ESLint の `--cache`:** 未計測。ESLint は 4.4 秒で、今回は prettier の方が大きい。 +- **Vite・adapter・`prisma generate` の調整:** どれも数秒で、削れる余地が小さい。Vite 8 は既に Rolldown を使う。 +- **preview の `needs: build` を外して並行に走らせる:** lint・check が失敗した変更でも preview DB に migration が当たる。 +- **setup の step を各ジョブに複製する:** 4ジョブ×3 step の重複になる。composite action にまとめる。 +- **workflow dependency lockfile(`.github/workflows/actions.lock`):** GitHub の guide は「the strongest protection available, and supersedes SHA pinning」とし、間接的に使われる action も固定できる。ただし GitHub 側は public preview(「subject to change」)で、`gh actions-lock` という gh の拡張で生成する。Renovate も対応している(`actions.lock` があれば、action の更新のたびに `gh actions-lock` で作り直す)が、Renovate の docs は experimental と明記している。両方とも安定版ではなく運用実績が少ないので、今は SHA 固定にする。後で乗り換えるときは、`gh actions-lock` が SHA の固定を版の参照に戻す。 +- **第三者の action(`pnpm/action-setup`)だけ固定する:** guide は第三者の action を主な対象にしているが、`actions/*` も tag で参照する限り同じ仕組みで書き換えられうる。3つしかないので全部固定する。 + +## スコープ外(別 PR) + +- **oxlint・oxfmt への移行(formatter の置き換えを含む):** [#2226](https://github.com/AtCoder-NoviSteps/AtCoderNoviSteps/issues/2226) で別 PR として扱う。この PR の lint は、prettier・oxlint・ESLint の今の構成のままにし、Phase 1 は prettier に `--cache` を付けるだけにする。 + - 確認の結果(2026-10-21、Kit 3 の branch の HEAD のコピー): `oxfmt@0.74.0` は Svelte を有効にすると、`.svelte` の100ファイルすべてで Prettier と同じ結果になり、`--check` は 7.4 秒 → 0.97 秒。違いは `.ts` 2ファイルの union 型の改行などだけ。`oxlint` 1.88.0 には Svelte のルールが無いので、`.svelte` は ESLint のまま。詳細は #2226 に貼るコメントにまとめた。 + - prettier が `.prettierrc` を優先して読み、`prettier.config.js` の `prettier-plugin-tailwindcss` が使われていない問題も、#2226 の PR で扱う。 + - oxfmt に移ると Phase 1 の `prettier --cache` は不要になるが、変更は1か所だけなので先に入れる。 + +## フェーズ + +### Phase 1: prettier の cache(ローカル) + +- [x] `package.json` の `lint` を `prettier --check . --cache && oxlint . && eslint .` にする。cache は `node_modules/.cache/prettier/.prettier-cache`(`node_modules` ごと gitignore 済み)。既定の `--cache-strategy` は `content` で、prettier の版や設定が変わると cache は無効になる。 +- [x] CI は `node_modules` を保存しないので効果は無いが、害も無い。lefthook の pre-commit は staged ファイルだけを整形するので変わらない。 +- 確認: `pnpm lint` を2回流して時間を比べる。2回目の前に1ファイルの整形を崩し、cache があっても検出されることを確かめる。 +- 設定のみで分岐が無いので、テスト先行は省略する。 + +### Phase 2: tsgo の型チェック(ローカル) + +- [x] `tsconfig.json` から `"outDir": "build"`・`"sourceMap": true`・`"incremental": true` を消す(`incremental` は `noEmit` と併用でき、`tsc` を直接実行すれば `*.tsbuildinfo` に型チェックの情報を保存する。ただしこの repo には `tsc` を直接実行する script・lefthook・CI が無く、svelte-check も自分の `--incremental` を使わない限り読まないので、効果が無い。テンプレートにも無い。3つを消した後の `pnpm check`・`pnpm build`・`pnpm test:unit` でも `*.tsbuildinfo` は生成されなかった)。`pnpm check` が 2740 files・0 errors・30 warnings のまま変わらず、`pnpm build`・`pnpm test:unit` も通ることを確かめる。 +- 消してよい根拠(2026-10-21 に確認): + - TypeScript の TSConfig Reference: `sourceMap` は「Source map files are emitted as `.js.map` … next to the corresponding `.js` output file」、`outDir` は「If specified, `.js` (as well as `.d.ts`, `.js.map`, etc.) files will be emitted into this directory」。どちらも出力のときだけ効く。`noEmit` は「Do not emit compiler output files like JavaScript source code, source-maps or declarations」。 + - `noEmit: true` は Kit が生成する `node_modules/$app/tsconfig.json` にあり、`tsconfig.json` は上書きしていない。Kit のソース(`src/core/sync/write_tsconfig/utils.js`)のコメントは「Vite handles the build and ignores this」。svelte.dev の docs は接続できず、ソースで確認した。 + - Vite の docs(vitejs/vite の `docs/guide/features.md`): 「Vite only performs transpilation on `.ts` files and does **NOT** perform type checking」。Vite が読む `compilerOptions` の一覧(`isolatedModules`・`target`・`paths`・`jsx` など)に `sourceMap`・`outDir` は無い。 + - 出どころ: `sourceMap` は 2023 年の作成時のテンプレート(create-svelte 5.0.5)にあり、`outDir` はテンプレートの外で足したもの。現行の `sv create`(sv 1.1.1)の tsconfig は `extends: "$app/tsconfig"`・`strict: true`・`include` だけで、どちらも無い。 + - 確認: `git archive HEAD` のコピーで2つを消し、`pnpm check` は 2740 files・0 errors・30 warnings、`pnpm build` は成功、`pnpm test:unit --run` は 97 files・2726 tests が成功した。 + - デプロイとは無関係: Vercel に上げる成果物の置き場は tsconfig ではなく adapter が決める。adapter-vercel は `.vercel/output`(Build Output API)に固定で書き出し(ソースの `const dir = '.vercel/output'`)、CI の `vercel deploy --prebuilt` はそれを上げる。上の確認の `pnpm build` でも `.vercel/output` が生成され、`build/` は作られなかった。`build/` が成果物になるのは adapter-node の `out` の既定値(docs: 「It defaults to `build`」)で、この repo では使わない。 +- [ ] `@typescript/native-preview` を devDependencies に exact で追加する(dev 版なので範囲指定にしない)。 +- [ ] script を足す: `"check:fast": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --tsgo"`。CI と lefthook は変えない。 +- [ ] `renovate.json` に `@typescript/native-preview` の packageRule を足し、dev 版の毎日の更新を週1回にまとめる(例: `"schedule": ["before 9am on monday"]`)。 +- [ ] `AGENTS.md` の Commands に `check:fast` を加え、「CI の正は `check`」と書く。 +- 確認: `pnpm check:fast` が 0 errors・30 warnings。spike と同じく `.ts`・`.svelte`・テスト・`prisma/` に一時的な型エラーを入れ、`check` と `check:fast` の両方が同じ4件を出すことを確かめてから戻す。 +- 設定・script のみなので、テスト先行は省略する。 + +### Phase 3: actions の SHA 固定 + +- [x] `ci.yml` の9か所の `uses:` を、「actions の現状」の表の SHA と `# vX.Y.Z` に置き換える。 + + ```yaml + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + - uses: pnpm/action-setup@0977fd99725f1db4007ccb2928dbb4e90d06cc86 # v6.0.10 + - uses: actions/setup-node@949feb2413d6458794dcd2491c4babbbce0c15c1 # v7.1.0 + ``` + +- [x] 作業の直前に `git ls-remote --tags https://github.com//` で SHA を取り直す。fork ではなく各 action の公式 repo から取る(guide の注意)。 +- [x] `renovate.json` の `extends` に `helpers:pinGitHubActionDigestsToSemver` を足す。 +- 確認: `grep -rn 'uses:' .github` に tag だけの参照が残らない。PR の CI が今と同じく通る。マージ後の Renovate の PR(例: `pnpm/action-setup` v6.1.0)が SHA とコメントを一緒に更新する。 +- 参照の書き換えだけで分岐が無いので、テスト先行は省略する。 + +### Phase 4: CI のジョブの並列化 + +- [x] setup を composite action にする(checkout は各ジョブに残す。local action の読み込みに必要なため)。Phase 3 の SHA を使う。Renovate の github-actions manager は `action.yml` も更新の対象にする。 + + ```yaml + # .github/actions/setup/action.yml + name: Setup pnpm and Node.js + inputs: + node-version: + default: '24' + runs: + using: composite + steps: + - uses: pnpm/action-setup@0977fd99725f1db4007ccb2928dbb4e90d06cc86 # v6.0.10 + with: + package_json_file: package.json + run_install: false + - uses: actions/setup-node@949feb2413d6458794dcd2491c4babbbce0c15c1 # v7.1.0 + with: + node-version: ${{ inputs.node-version }} + check-latest: true + cache: 'pnpm' + cache-dependency-path: pnpm-lock.yaml + - run: pnpm install # postinstall runs prisma generate + shell: bash + ``` + +- [x] `build`(matrix の node 24 を残す)・`lint`・`check`・`unit-test` の4ジョブに分ける。Prisma client は `pnpm install` の `postinstall` で生成されるので、check・unit-test に `prisma generate` は要らない。 +- [x] `preview`・`production` の `needs` を `[build, lint, check, unit-test]` にする。 +- lint・check・unit-test は build の出力を使わない: `tsconfig.json` が extends する `$app/tsconfig`(`node_modules/$app/`)は build ではなく `svelte-kit sync` が生成し、check は script の先頭の sync、unit-test は Vitest が読み込む SvelteKit の Vite plugin で生成する。lint は型情報を使わない(ESLint に `projectService`・`project` が無い)。 + - 確認(2026-10-21、devcontainer): `git archive HEAD` を空のディレクトリ3つに展開し、それぞれ `pnpm install --frozen-lockfile` の後に1つだけを実行した。install 直後は `node_modules/$app`・`.svelte-kit` とも無い。`pnpm lint` は成功(既存の oxlint の warning 1件)、`pnpm check` は 2740 files・0 errors・30 warnings、`pnpm test:unit --run` は 97 files・2726 tests が成功。どのコピーにも `build/` は生成されず、lint のコピーには `.svelte-kit` も生成されなかった。 +- [x] 人が行う: branch protection の required checks に `lint`・`check`・`unit-test` を追加する。今の required checks は repo から見えないので、admin が確認する。 + - 理由: 分割前は lint・check・unit test の失敗が `build (24)` に出ていた。分割後は別の check に出るので、追加しないと `build (24)` だけ通った PR がマージできてしまう(preview・production は `needs` で4つを待つので、配備は止まる)。 + - 対処(2026-10-21、admin): この repo は Settings → Branches の Branch protection rules の方式。「Require status checks to pass before merging」の下に3つを追加した。 + - 「Require branches to be up to date before merging」は今回は変えない。入れると、マージのたびに他の PR で「Update branch」と CI のやり直しが要る。Renovate の PR が多く(`prHourlyLimit: 10`)、`rebaseWhen` の既定の `auto` は、この設定があると base が進むたびに PR を rebase するので、代償が大きい(`rebaseWhen` の挙動は docs で未確認)。 +- 見込み: 各ジョブは固定の約29秒と自分の本体だけになるので、この段は「約29秒 + 最も遅い本体(Build・Unit test の13秒)」≈ 42秒(今は 1分19秒、約37秒短縮)。runner の使用時間は約2倍(4ジョブ × 約29秒 + 48秒 ≈ 2分44秒)になるが、public repo なので費用は掛からない。 +- 確認: PR の CI で4ジョブが並行に走り、`build` の段の時間と全体の時間を、1分19秒・直近の 2分21秒〜2分50秒と比べる。 + +### Phase 5: preview・production の二重 build の削除 + +消すのは `vercel build` の前の `pnpm build` だけで、`vercel pull`・`vercel build`・`vercel deploy --prebuilt` は残す。根拠(2026-10-20 に調査): + +- 経緯: 2023-10 の #121 では deploy のジョブは `pnpm install vercel@latest` → `vercel pull` → `vercel build` → `vercel deploy --prebuilt` だけで動いていた(`pnpm install vercel@latest` は全依存も入れる)。2024-04 の #362 で `pnpm install`・`pnpm build`・`prisma migrate deploy` が migration のために一緒に追加された(`a6afb2d4`・`d0eb57b9`)。commit には `pnpm build` が必要だった失敗の記録は無い。 +- `vercel deploy --prebuilt` は `vercel build` の出力を使う。CLI 64.5.0 の help: 「`--prebuilt`: Use in combination with `vc build`. Deploy an existing build」、例は `vercel build` → `vercel deploy --prebuilt` で、別の build は挟まない。 +- `vercel build` が実行する build command は、ダッシュボードの設定が無ければ SvelteKit の preset の `vite build`(CLI の framework 定義)。`@vercel/static-build` は `installCommand` が未設定なら `runNpmInstall` で install も行う。 +- `prisma migrate deploy` に build は要らない。CLI の help の option は `--schema` だけで、schema・migrations・接続先の環境変数で動く。 +- 再現: `git archive HEAD` の新しいコピー(`node_modules`・`.svelte-kit` 無し)で `pnpm install`(`postinstall` が `prisma generate`)→ `vite build` だけを実行し、`.vercel/output`(`config.json`・`functions`・`static`)が生成された。`vercel build` 自体は Vercel の token が要るので実行していない。 +- 公式 docs(vercel.com・prisma.io)は WebFetch が失敗し、shell からの取得も許可されていないため、CLI の help と配布コードで確認した。 + +- [x] preview・production のジョブから `pnpm build` の step を消す。Prisma client は `pnpm install` の `postinstall` で生成済み。 +- [x] step の順序を `install → vercel pull → vercel build → prisma migrate deploy → vercel deploy` にする。build が失敗したら migration を当てない、という今の性質を保つ。 +- [ ] 人が行う: Vercel のダッシュボードの Build Command を確認する(`vercel.json` に `buildCommand` は無い)。`vite build` だけでも client は生成済みなので動くが、想定外のコマンドなら先に揃える。 +- 見込み: preview・production のジョブから Build の約13秒(`build (24)` の Build step と同じ処理)が減る。Phase 4 と合わせて、CI 全体は 2分21秒〜2分50秒 → 約1分30秒〜2分。 +- 確認: PR の preview ジョブが成功し、preview の URL でログインできる。production はマージ後の最初の実行を見る。 + +## 後で決めること + +- CI の check を `--tsgo` に切り替えるか: `check` と `check:fast` の結果の違いを数週間見て、違いが無ければ CI に `check:fast` を並べ、さらに問題が無ければ置き換える。ただし Phase 4 の後は Check(11秒)が最も遅い本体ではないので、切り替えても CI 全体の時間は縮まない。主な効果はローカルにとどまる。TypeScript 7 の安定版が出たら、svelte-check は `typescript` 7 以上も tsgo として検出するので、`@typescript/native-preview` から乗り換える。 +- ESLint の `--cache`: 測ってから判断する。- workflow dependency lockfile: GitHub 側が GA になり、Renovate の対応から experimental が外れたら、SHA 固定から乗り換えるかを決める。 + +## 確認限界 + +- CI の step ごとの時間は、ユーザーが共有した `build (24)` の1回分だけで、preview・production のジョブの内訳は見ていない。Phase 4・5 の見込みはこの値からの計算で、実際の効果は PR の CI で測る。 +- Vercel のダッシュボードの設定と、branch protection の required checks は repo から見えない。required checks の追加は admin が行い、agent は結果を見ていない。 +- tsgo の計測は devcontainer の1台。CI の runner での時間は Phase 2 では測らない(CI では動かさないため)。 +- workflow dependency lockfile は、GitHub docs の2ページ(security hardening guide と「workflow dependency lockfiles」)と Renovate の github-actions manager の docs だけで確認し、試していない。GitHub の plan・org の要件は docs に記載が無い。 From 3be986269aa3696f06b34d1c5e7751867ef61eb9 Mon Sep 17 00:00:00 2001 From: "k.hiro1818" Date: Wed, 21 Oct 2026 08:41:49 +0000 Subject: [PATCH 08/10] docs: condense CI and typecheck speedup plan into a completion record Co-Authored-By: Claude Sonnet 5.5 --- .../ci-and-typecheck-speedup/plan.md | 202 +++++------------- 1 file changed, 53 insertions(+), 149 deletions(-) diff --git a/docs/dev-notes/2026-10-20/ci-and-typecheck-speedup/plan.md b/docs/dev-notes/2026-10-20/ci-and-typecheck-speedup/plan.md index abd7e0137..25bb4b2aa 100644 --- a/docs/dev-notes/2026-10-20/ci-and-typecheck-speedup/plan.md +++ b/docs/dev-notes/2026-10-20/ci-and-typecheck-speedup/plan.md @@ -2,10 +2,10 @@ ## 概要 -Kit 3 への移行(PR B、branch `#4192`)の後、build・check が遅いと感じるという相談から、時間の内訳を測って4つの対策を選んだ。 -あわせて、セキュリティのために、GitHub Actions の `uses:` を commit SHA と `# vX.Y.Z` のコメントの形に固定する。 -PR B とは別の PR にし、PR B のマージ後に `staging` から branch を切る。 -運用実績の少ない代替ツール(rsvelte など)は入れない。tsgo は型チェックの診断だけに使い、TypeScript 6 は残す。 +Kit 3 への移行(PR B、branch `#4192`)の後、「build・check が遅い」という相談を受けて時間の内訳を測り、対策を選んだ。 +あわせて、セキュリティのために GitHub Actions の `uses:` を commit SHA に固定した。 +branch `ci-and-typecheck-speedup` で、Phase 1・2(tsgo 以外)・3・4・5 を実装済み(2026-10-21)。 +tsgo による型チェックの高速化は後回しにし、残タスクとして [TASK.md](../../../tasks/TASK.md) に移した。 ## 計測(2026-10-19、devcontainer 上) @@ -17,169 +17,73 @@ PR B とは別の PR にし、PR B のマージ後に `staging` から branch | `pnpm test:unit` | 6.0 秒 | | - CI は `staging` の直近10回が全体で 2分21秒〜2分50秒。#4191 のマージ(2分44秒)では `preview` が 1分16秒で、残りが `build` ジョブ。 -- `build (24)` ジョブの step ごとの時間(ユーザーが共有した平均的な実行、全体 1分19秒): +- 分割前の `build (24)` ジョブ(ユーザーが共有した1回分、全体 1分19秒): 準備 24秒(Setup pnpm 6秒、Node.js 6秒、install 10秒など)、本体 48秒(Build 13秒、Lint 11秒、Check 11秒、Unit test 13秒を順に実行)、後片付け 5秒。ジョブごとの固定の負担は約29秒。 +- `prettier --check . --cache` は、1回目 8.6 秒、2回目 1.6 秒(実装後の計測)。 - | 区分 | step と時間 | 計 | - | -------- | --------------------------------------------------------------------------------------------- | ---- | - | 準備 | Set up job 2秒、Setup pnpm 6秒、Use Node.js 24 6秒、Install dependencies 10秒(checkout 0秒) | 24秒 | - | 本体 | Build 13秒、Lint 11秒、Check 11秒、Unit test 13秒 | 48秒 | - | 後片付け | Post Use Node.js 24 5秒(他は 0秒) | 5秒 | +## 決めたこと - ジョブごとの固定の負担は約29秒。本体の4つは順に実行されるので、合計の48秒がそのまま掛かる。 +- **低リスクから順に入れる:** ローカルの設定(Phase 1・2)→ 挙動の変わらない CI の変更(Phase 3)→ CI の構成(Phase 4)→ 配備のジョブ(Phase 5)。Phase ごとに1 commit(Phase 4 は composite action の切り出しと、ジョブの分割の2 commit)。 +- **SHA は当時の major tag と同じ commit にする:** 固定するだけで、動く action のコードは変えない。作業の直前に各 action の公式 repo で `git ls-remote --tags` を取り直し、計画時の値から動いていないことを確かめた。`pnpm/action-setup` は annotated tag なので、tag object ではなく、tag が指す commit(`^{}` の行)を使う。 +- **SHA の更新は Renovate に任せる:** `helpers:pinGitHubActionDigestsToSemver` が、digest とコメントの `vX.Y.Z` を一緒に更新する。Renovate の github-actions manager は、既定で `.github/actions/` 配下と `action.yml` も対象にする(docs で確認)。 +- **SHA 固定の理由:** GitHub の security hardening guide は、tag は「can be moved or deleted if a bad actor gains access to the repository storing the action」として SHA 固定を勧める。この workflow は `VERCEL_TOKEN` と、preview・production の DB の接続先を扱う。第三者の action だけでなく `actions/*` も同じ仕組みで書き換えられうるので、3つとも固定した。 +- **CI の check 名 `build (24)` は残す:** `build` ジョブだけ matrix を残し、lint・check・unit-test を別のジョブにした。 +- **setup は composite action にまとめ、deploy のジョブでも使う:** 6ジョブの setup は中身が同じで、SHA の更新を1か所で済ませるため。checkout は local action の読み込みに要るので、各ジョブに残す。 +- **migration は build の成功後:** 二重 build を消した後も「build が失敗したら DB を変えない」を保つため、`prisma migrate deploy` を `vercel build` の後に移した。 +- **branch protection(2026-10-21、admin が対処):** この repo は Settings → Branches の Branch protection rules の方式。分割後は lint・check・unit test の失敗が `build (24)` に出なくなるので、「Require status checks to pass before merging」に `lint`・`check`・`unit-test` を追加した。 +- **「Require branches to be up to date before merging」は変えない:** 入れると、マージのたびに他の PR で更新と CI のやり直しが要る。Renovate の PR が多く(`prHourlyLimit: 10`)、`rebaseWhen` の既定の `auto` は、この設定があると base が進むたびに rebase する(`rebaseWhen` の挙動は docs で未確認)。 -- 今の `build` ジョブは build・lint・check・unit test を1つのジョブで順に実行する。preview・production のジョブは `pnpm build` の後に `vercel build` を実行する。`vercel build` は内部で install と build command を実行するので、その前の `pnpm build` が重複している(根拠は Phase 5)。`vercel build` 自体は `vercel deploy --prebuilt` に必要なので残す。 -- svelte-check 4.7.7 の `--tsgo`: 使い捨てのコピーに `@typescript/native-preview@7.0.0-dev.20260707.2` を入れて測った。そのままでは「rootDir の外」の設定エラーが103件出る。`tsconfig.json` の `"outDir": "build"` を消すと、通常の check と同じ 0 errors・30 warnings になり、9.0 秒 → 2.6 秒。`.ts`・`.svelte`・テスト・`prisma/` に1件ずつ入れた型エラーを、通常の check と同じ位置ですべて検出した。表示のファイル数は 2740 → 約130 になるが、数え方の違い。 -- `--incremental` も `outDir` が原因で同じエラーになる。`outDir` を消すと 2回目は 4.1 秒。 -- `prettier --check . --cache`: 1回目 9.2 秒、2回目 1.7 秒。 +## コードから読み取れない根拠 -## actions の現状(2026-10-20) +### lint・check・unit-test は build の出力を使わない(Phase 4) -workflow は `.github/workflows/ci.yml` の1本で、3つの action を major tag で参照している(各3か所)。`git ls-remote --tags` で、各 repo の tag が指す commit を確かめた。 +- `tsconfig.json` が extends する `$app/tsconfig`(`node_modules/$app/`)は、build ではなく `svelte-kit sync` が生成する。check は script の先頭の sync で、unit-test は Vitest が読み込む SvelteKit の Vite plugin で生成する。lint は型情報を使わない。 +- 確認(2026-10-21): `git archive HEAD` を空のディレクトリ3つに展開し、それぞれ `pnpm install --frozen-lockfile` の後に1つだけを実行した。3つとも成功し、どのコピーにも `build/` は生成されなかった。 -| action | 現在の参照 | tag が指す版 | commit SHA | -| -------------------- | ---------- | ------------ | ------------------------------------------ | -| `actions/checkout` | `@v7` | v7.0.1 | `3d3c42e5aac5ba805825da76410c181273ba90b1` | -| `actions/setup-node` | `@v7` | v7.1.0 | `949feb2413d6458794dcd2491c4babbbce0c15c1` | -| `pnpm/action-setup` | `@v6` | v6.0.10 | `0977fd99725f1db4007ccb2928dbb4e90d06cc86` | +### tsconfig の `outDir`・`sourceMap`・`incremental` を消してよい(Phase 2) -- `pnpm/action-setup` は annotated tag なので、tag object ではなく、tag が指す commit(`^{}` の行)を使う。v6.1.0 も出ているが、`v6` の tag はまだ v6.0.10 を指す。 -- GitHub の公式の security hardening guide は、action を commit SHA に固定するよう勧めている。tag は「can be moved or deleted if a bad actor gains access to the repository storing the action」ため、作者を信頼していても危険がある。 -- この workflow は `VERCEL_TOKEN` と、preview・production の `DATABASE_URL`・`DIRECT_URL` を扱う。action の repo が乗っ取られて tag が書き換えられると、これらが漏れうる。 +- 3つとも出力に関わる設定で、`noEmit: true` の下では効かない。`noEmit` は Kit が生成する `$app/tsconfig` にあり(Kit のソースのコメントは「Vite handles the build and ignores this」)、`tsconfig.json` は上書きしていない。 +- TypeScript の TSConfig Reference によると、`sourceMap` は出力した `.js` の横に `.js.map` を出し、`outDir` は出力先の指定。Vite の docs によると、Vite は型を消すだけで、読む `compilerOptions` に `sourceMap`・`outDir` は無い。 +- `incremental` は `noEmit` と併用でき、`tsc` を直接実行すれば `*.tsbuildinfo` を保存する。ただしこの repo には `tsc` を直接実行する script・lefthook・CI が無く、svelte-check も自分の `--incremental` を使わない限り読まない。 +- 出どころ: `sourceMap` は 2023 年の作成時のテンプレート(create-svelte 5.0.5)にあり、`outDir`・`incremental` はテンプレートの外で足したもの。現行の `sv create`(sv 1.1.1)の tsconfig には3つとも無い。 +- `outDir` は svelte-check の `--tsgo`・`--incremental` で「rootDir の外」のエラーを起こしていた。これが tsgo の前提だったので、tsgo を後回しにしてもこの変更は先に入れた。 +- デプロイとは無関係: Vercel に上げる成果物の置き場は adapter が決め、adapter-vercel は `.vercel/output` に固定で書き出す。`build/` が成果物になるのは adapter-node の既定値で、この repo では使わない。 -## 方針 +### deploy のジョブの `pnpm build` は重複していた(Phase 5) -- **低リスクから順に入れる:** ローカルだけに効くもの(Phase 1・2)→ 挙動の変わらない CI の変更(Phase 3)→ CI の構成(Phase 4)→ 配備のジョブ(Phase 5)。 -- **型チェックの正は tsc のまま:** CI の `pnpm check` は変えず、tsgo はローカル用の `check:fast` として足す。プレビュー版の tsgo が壊れても、影響は手元の型チェックの結果だけにとどまる。CI を tsgo に切り替えるかは、運用してから決める(「後で決めること」)。 -- **`outDir` は消す:** tsc で出力することは無く(`$app/tsconfig` が `noEmit`、`vite build` は型を消すだけ)、`--tsgo`・`--incremental` を壊す原因になっている。同じ理由で効果の無い `sourceMap` も消す(source map は tsc が出力するファイルにしか付かない。Vite・Vitest の source map は tsconfig の外で決まる)。 -- **SHA は今の tag と同じ commit にする:** 固定するだけで、動く action のコードは変えない。版の更新は Renovate の PR に任せる。 -- **SHA の更新は Renovate に任せる:** `helpers:pinGitHubActionDigestsToSemver` は、digest を固定し、コメントを `vX.Y.Z` の完全な版にする preset。更新の PR は SHA とコメントを一緒に書き換える。 -- **CI の check 名は残す:** `build` ジョブは matrix ごと残し、check 名 `build (24)` を変えない。lint・check・unit test は新しいジョブにする。 -- **migration は build の成功後:** 今は `pnpm build` が migration の前にあり、build が失敗すれば DB は変わらない。二重 build を消すときに `prisma migrate deploy` を `vercel build` の後へ移し、この順序を保つ。 +- 経緯: 2023-10 の #121 では、deploy のジョブは `vercel pull` → `vercel build` → `vercel deploy --prebuilt` だけで動いていた。2024-04 の #362 で、migration のために `pnpm install`・`pnpm build`・`prisma migrate deploy` が一緒に追加された(`a6afb2d4`・`d0eb57b9`)。`pnpm build` が必要だった失敗の記録は無い。 +- `vercel deploy --prebuilt` は `vercel build` の出力を使う(CLI 64.5.0 の help)。`vercel build` は、ダッシュボードに設定が無ければ SvelteKit の preset の `vite build` を実行し、`installCommand` が未設定なら install も行う(CLI の配布コード)。 +- `prisma migrate deploy` は build を必要とせず、schema・migrations・接続先の環境変数で動く。Prisma client は `pnpm install` の `postinstall` で生成される。 +- 再現: `git archive HEAD` の新しいコピーで `pnpm install` → `vite build` だけを実行し、`.vercel/output` が生成された。`vercel build` 自体は Vercel の token が要るので実行していない。vercel.com・prisma.io の docs は取得できず、CLI の help と配布コードで確認した。 +- build のときに DB を読む経路(prerender など)は無い。将来 prerender を入れると、migration 前の DB を読むことになる(Codex のレビューの指摘)。 ## 採らなかった案 - **rsvelte などの代替ツール:** 運用実績が少ない(ユーザー判断)。 -- **CI の check を最初から `--tsgo` にする:** tsgo はプレビュー版で、tsc との差を見る期間が無い。 -- **`--tsgo-experimental-api`:** svelte-check の help に experimental と明記されている。 - **svelte-check の `--incremental`:** 2回目でも 4.1 秒で、tsgo(初回から 2.6 秒)より遅い。CI で効かせるにはキャッシュの保存も要る。 -- **ESLint の `--cache`:** 未計測。ESLint は 4.4 秒で、今回は prettier の方が大きい。 - **Vite・adapter・`prisma generate` の調整:** どれも数秒で、削れる余地が小さい。Vite 8 は既に Rolldown を使う。 -- **preview の `needs: build` を外して並行に走らせる:** lint・check が失敗した変更でも preview DB に migration が当たる。 -- **setup の step を各ジョブに複製する:** 4ジョブ×3 step の重複になる。composite action にまとめる。 -- **workflow dependency lockfile(`.github/workflows/actions.lock`):** GitHub の guide は「the strongest protection available, and supersedes SHA pinning」とし、間接的に使われる action も固定できる。ただし GitHub 側は public preview(「subject to change」)で、`gh actions-lock` という gh の拡張で生成する。Renovate も対応している(`actions.lock` があれば、action の更新のたびに `gh actions-lock` で作り直す)が、Renovate の docs は experimental と明記している。両方とも安定版ではなく運用実績が少ないので、今は SHA 固定にする。後で乗り換えるときは、`gh actions-lock` が SHA の固定を版の参照に戻す。 -- **第三者の action(`pnpm/action-setup`)だけ固定する:** guide は第三者の action を主な対象にしているが、`actions/*` も tag で参照する限り同じ仕組みで書き換えられうる。3つしかないので全部固定する。 - -## スコープ外(別 PR) - -- **oxlint・oxfmt への移行(formatter の置き換えを含む):** [#2226](https://github.com/AtCoder-NoviSteps/AtCoderNoviSteps/issues/2226) で別 PR として扱う。この PR の lint は、prettier・oxlint・ESLint の今の構成のままにし、Phase 1 は prettier に `--cache` を付けるだけにする。 - - 確認の結果(2026-10-21、Kit 3 の branch の HEAD のコピー): `oxfmt@0.74.0` は Svelte を有効にすると、`.svelte` の100ファイルすべてで Prettier と同じ結果になり、`--check` は 7.4 秒 → 0.97 秒。違いは `.ts` 2ファイルの union 型の改行などだけ。`oxlint` 1.88.0 には Svelte のルールが無いので、`.svelte` は ESLint のまま。詳細は #2226 に貼るコメントにまとめた。 - - prettier が `.prettierrc` を優先して読み、`prettier.config.js` の `prettier-plugin-tailwindcss` が使われていない問題も、#2226 の PR で扱う。 - - oxfmt に移ると Phase 1 の `prettier --cache` は不要になるが、変更は1か所だけなので先に入れる。 - -## フェーズ - -### Phase 1: prettier の cache(ローカル) - -- [x] `package.json` の `lint` を `prettier --check . --cache && oxlint . && eslint .` にする。cache は `node_modules/.cache/prettier/.prettier-cache`(`node_modules` ごと gitignore 済み)。既定の `--cache-strategy` は `content` で、prettier の版や設定が変わると cache は無効になる。 -- [x] CI は `node_modules` を保存しないので効果は無いが、害も無い。lefthook の pre-commit は staged ファイルだけを整形するので変わらない。 -- 確認: `pnpm lint` を2回流して時間を比べる。2回目の前に1ファイルの整形を崩し、cache があっても検出されることを確かめる。 -- 設定のみで分岐が無いので、テスト先行は省略する。 - -### Phase 2: tsgo の型チェック(ローカル) - -- [x] `tsconfig.json` から `"outDir": "build"`・`"sourceMap": true`・`"incremental": true` を消す(`incremental` は `noEmit` と併用でき、`tsc` を直接実行すれば `*.tsbuildinfo` に型チェックの情報を保存する。ただしこの repo には `tsc` を直接実行する script・lefthook・CI が無く、svelte-check も自分の `--incremental` を使わない限り読まないので、効果が無い。テンプレートにも無い。3つを消した後の `pnpm check`・`pnpm build`・`pnpm test:unit` でも `*.tsbuildinfo` は生成されなかった)。`pnpm check` が 2740 files・0 errors・30 warnings のまま変わらず、`pnpm build`・`pnpm test:unit` も通ることを確かめる。 -- 消してよい根拠(2026-10-21 に確認): - - TypeScript の TSConfig Reference: `sourceMap` は「Source map files are emitted as `.js.map` … next to the corresponding `.js` output file」、`outDir` は「If specified, `.js` (as well as `.d.ts`, `.js.map`, etc.) files will be emitted into this directory」。どちらも出力のときだけ効く。`noEmit` は「Do not emit compiler output files like JavaScript source code, source-maps or declarations」。 - - `noEmit: true` は Kit が生成する `node_modules/$app/tsconfig.json` にあり、`tsconfig.json` は上書きしていない。Kit のソース(`src/core/sync/write_tsconfig/utils.js`)のコメントは「Vite handles the build and ignores this」。svelte.dev の docs は接続できず、ソースで確認した。 - - Vite の docs(vitejs/vite の `docs/guide/features.md`): 「Vite only performs transpilation on `.ts` files and does **NOT** perform type checking」。Vite が読む `compilerOptions` の一覧(`isolatedModules`・`target`・`paths`・`jsx` など)に `sourceMap`・`outDir` は無い。 - - 出どころ: `sourceMap` は 2023 年の作成時のテンプレート(create-svelte 5.0.5)にあり、`outDir` はテンプレートの外で足したもの。現行の `sv create`(sv 1.1.1)の tsconfig は `extends: "$app/tsconfig"`・`strict: true`・`include` だけで、どちらも無い。 - - 確認: `git archive HEAD` のコピーで2つを消し、`pnpm check` は 2740 files・0 errors・30 warnings、`pnpm build` は成功、`pnpm test:unit --run` は 97 files・2726 tests が成功した。 - - デプロイとは無関係: Vercel に上げる成果物の置き場は tsconfig ではなく adapter が決める。adapter-vercel は `.vercel/output`(Build Output API)に固定で書き出し(ソースの `const dir = '.vercel/output'`)、CI の `vercel deploy --prebuilt` はそれを上げる。上の確認の `pnpm build` でも `.vercel/output` が生成され、`build/` は作られなかった。`build/` が成果物になるのは adapter-node の `out` の既定値(docs: 「It defaults to `build`」)で、この repo では使わない。 -- [ ] `@typescript/native-preview` を devDependencies に exact で追加する(dev 版なので範囲指定にしない)。 -- [ ] script を足す: `"check:fast": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --tsgo"`。CI と lefthook は変えない。 -- [ ] `renovate.json` に `@typescript/native-preview` の packageRule を足し、dev 版の毎日の更新を週1回にまとめる(例: `"schedule": ["before 9am on monday"]`)。 -- [ ] `AGENTS.md` の Commands に `check:fast` を加え、「CI の正は `check`」と書く。 -- 確認: `pnpm check:fast` が 0 errors・30 warnings。spike と同じく `.ts`・`.svelte`・テスト・`prisma/` に一時的な型エラーを入れ、`check` と `check:fast` の両方が同じ4件を出すことを確かめてから戻す。 -- 設定・script のみなので、テスト先行は省略する。 - -### Phase 3: actions の SHA 固定 - -- [x] `ci.yml` の9か所の `uses:` を、「actions の現状」の表の SHA と `# vX.Y.Z` に置き換える。 - - ```yaml - - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - - uses: pnpm/action-setup@0977fd99725f1db4007ccb2928dbb4e90d06cc86 # v6.0.10 - - uses: actions/setup-node@949feb2413d6458794dcd2491c4babbbce0c15c1 # v7.1.0 - ``` - -- [x] 作業の直前に `git ls-remote --tags https://github.com//` で SHA を取り直す。fork ではなく各 action の公式 repo から取る(guide の注意)。 -- [x] `renovate.json` の `extends` に `helpers:pinGitHubActionDigestsToSemver` を足す。 -- 確認: `grep -rn 'uses:' .github` に tag だけの参照が残らない。PR の CI が今と同じく通る。マージ後の Renovate の PR(例: `pnpm/action-setup` v6.1.0)が SHA とコメントを一緒に更新する。 -- 参照の書き換えだけで分岐が無いので、テスト先行は省略する。 - -### Phase 4: CI のジョブの並列化 - -- [x] setup を composite action にする(checkout は各ジョブに残す。local action の読み込みに必要なため)。Phase 3 の SHA を使う。Renovate の github-actions manager は `action.yml` も更新の対象にする。 - - ```yaml - # .github/actions/setup/action.yml - name: Setup pnpm and Node.js - inputs: - node-version: - default: '24' - runs: - using: composite - steps: - - uses: pnpm/action-setup@0977fd99725f1db4007ccb2928dbb4e90d06cc86 # v6.0.10 - with: - package_json_file: package.json - run_install: false - - uses: actions/setup-node@949feb2413d6458794dcd2491c4babbbce0c15c1 # v7.1.0 - with: - node-version: ${{ inputs.node-version }} - check-latest: true - cache: 'pnpm' - cache-dependency-path: pnpm-lock.yaml - - run: pnpm install # postinstall runs prisma generate - shell: bash - ``` - -- [x] `build`(matrix の node 24 を残す)・`lint`・`check`・`unit-test` の4ジョブに分ける。Prisma client は `pnpm install` の `postinstall` で生成されるので、check・unit-test に `prisma generate` は要らない。 -- [x] `preview`・`production` の `needs` を `[build, lint, check, unit-test]` にする。 -- lint・check・unit-test は build の出力を使わない: `tsconfig.json` が extends する `$app/tsconfig`(`node_modules/$app/`)は build ではなく `svelte-kit sync` が生成し、check は script の先頭の sync、unit-test は Vitest が読み込む SvelteKit の Vite plugin で生成する。lint は型情報を使わない(ESLint に `projectService`・`project` が無い)。 - - 確認(2026-10-21、devcontainer): `git archive HEAD` を空のディレクトリ3つに展開し、それぞれ `pnpm install --frozen-lockfile` の後に1つだけを実行した。install 直後は `node_modules/$app`・`.svelte-kit` とも無い。`pnpm lint` は成功(既存の oxlint の warning 1件)、`pnpm check` は 2740 files・0 errors・30 warnings、`pnpm test:unit --run` は 97 files・2726 tests が成功。どのコピーにも `build/` は生成されず、lint のコピーには `.svelte-kit` も生成されなかった。 -- [x] 人が行う: branch protection の required checks に `lint`・`check`・`unit-test` を追加する。今の required checks は repo から見えないので、admin が確認する。 - - 理由: 分割前は lint・check・unit test の失敗が `build (24)` に出ていた。分割後は別の check に出るので、追加しないと `build (24)` だけ通った PR がマージできてしまう(preview・production は `needs` で4つを待つので、配備は止まる)。 - - 対処(2026-10-21、admin): この repo は Settings → Branches の Branch protection rules の方式。「Require status checks to pass before merging」の下に3つを追加した。 - - 「Require branches to be up to date before merging」は今回は変えない。入れると、マージのたびに他の PR で「Update branch」と CI のやり直しが要る。Renovate の PR が多く(`prHourlyLimit: 10`)、`rebaseWhen` の既定の `auto` は、この設定があると base が進むたびに PR を rebase するので、代償が大きい(`rebaseWhen` の挙動は docs で未確認)。 -- 見込み: 各ジョブは固定の約29秒と自分の本体だけになるので、この段は「約29秒 + 最も遅い本体(Build・Unit test の13秒)」≈ 42秒(今は 1分19秒、約37秒短縮)。runner の使用時間は約2倍(4ジョブ × 約29秒 + 48秒 ≈ 2分44秒)になるが、public repo なので費用は掛からない。 -- 確認: PR の CI で4ジョブが並行に走り、`build` の段の時間と全体の時間を、1分19秒・直近の 2分21秒〜2分50秒と比べる。 - -### Phase 5: preview・production の二重 build の削除 - -消すのは `vercel build` の前の `pnpm build` だけで、`vercel pull`・`vercel build`・`vercel deploy --prebuilt` は残す。根拠(2026-10-20 に調査): - -- 経緯: 2023-10 の #121 では deploy のジョブは `pnpm install vercel@latest` → `vercel pull` → `vercel build` → `vercel deploy --prebuilt` だけで動いていた(`pnpm install vercel@latest` は全依存も入れる)。2024-04 の #362 で `pnpm install`・`pnpm build`・`prisma migrate deploy` が migration のために一緒に追加された(`a6afb2d4`・`d0eb57b9`)。commit には `pnpm build` が必要だった失敗の記録は無い。 -- `vercel deploy --prebuilt` は `vercel build` の出力を使う。CLI 64.5.0 の help: 「`--prebuilt`: Use in combination with `vc build`. Deploy an existing build」、例は `vercel build` → `vercel deploy --prebuilt` で、別の build は挟まない。 -- `vercel build` が実行する build command は、ダッシュボードの設定が無ければ SvelteKit の preset の `vite build`(CLI の framework 定義)。`@vercel/static-build` は `installCommand` が未設定なら `runNpmInstall` で install も行う。 -- `prisma migrate deploy` に build は要らない。CLI の help の option は `--schema` だけで、schema・migrations・接続先の環境変数で動く。 -- 再現: `git archive HEAD` の新しいコピー(`node_modules`・`.svelte-kit` 無し)で `pnpm install`(`postinstall` が `prisma generate`)→ `vite build` だけを実行し、`.vercel/output`(`config.json`・`functions`・`static`)が生成された。`vercel build` 自体は Vercel の token が要るので実行していない。 -- 公式 docs(vercel.com・prisma.io)は WebFetch が失敗し、shell からの取得も許可されていないため、CLI の help と配布コードで確認した。 - -- [x] preview・production のジョブから `pnpm build` の step を消す。Prisma client は `pnpm install` の `postinstall` で生成済み。 -- [x] step の順序を `install → vercel pull → vercel build → prisma migrate deploy → vercel deploy` にする。build が失敗したら migration を当てない、という今の性質を保つ。 -- [ ] 人が行う: Vercel のダッシュボードの Build Command を確認する(`vercel.json` に `buildCommand` は無い)。`vite build` だけでも client は生成済みなので動くが、想定外のコマンドなら先に揃える。 -- 見込み: preview・production のジョブから Build の約13秒(`build (24)` の Build step と同じ処理)が減る。Phase 4 と合わせて、CI 全体は 2分21秒〜2分50秒 → 約1分30秒〜2分。 -- 確認: PR の preview ジョブが成功し、preview の URL でログインできる。production はマージ後の最初の実行を見る。 +- **preview の `needs` を外して並行に走らせる:** lint・check が失敗した変更でも、preview の DB に migration が当たる。 +- **setup の step を各ジョブに複製する:** 重複が増え、SHA の更新箇所も増える。 +- **workflow dependency lockfile(`.github/workflows/actions.lock`):** GitHub の guide は SHA 固定より強いとし、間接的に使われる action も固定できる。ただし GitHub 側は public preview で、Renovate の対応も experimental。どちらも運用実績が少ないので、今は SHA 固定にする。 + +## スコープ外 + +- **oxlint・oxfmt への移行:** [#2226](https://github.com/AtCoder-NoviSteps/AtCoderNoviSteps/issues/2226) と [oxfmt-migration の計画](../../2026-10-21/oxfmt-migration/plan.md) で扱う。移行すると Phase 1 の `prettier --cache` は不要になるが、変更は1か所なので先に入れた。prettier が `.prettierrc` を優先し、`prettier-plugin-tailwindcss` が使われていない問題も同じ PR で扱う。 + +## 残りの確認(人が行う) + +- [ ] Vercel のダッシュボードの Build Command を確認する(`vercel.json` に `buildCommand` は無い)。想定外のコマンドなら先に揃える。 +- [ ] PR の CI で4ジョブが並行に走るか確かめ、`build` の段と全体の時間を、分割前の 1分19秒・2分21秒〜2分50秒と比べる。見込みは、`build` の段が約42秒、全体が約1分30秒〜2分。 +- [ ] PR の preview ジョブが成功し、preview の URL でログインできる。production はマージ後の最初の実行で確かめる。 +- [ ] マージ後の Renovate の action の更新 PR(例: `pnpm/action-setup` v6.1.0)が、SHA とコメントを一緒に書き換える。 ## 後で決めること -- CI の check を `--tsgo` に切り替えるか: `check` と `check:fast` の結果の違いを数週間見て、違いが無ければ CI に `check:fast` を並べ、さらに問題が無ければ置き換える。ただし Phase 4 の後は Check(11秒)が最も遅い本体ではないので、切り替えても CI 全体の時間は縮まない。主な効果はローカルにとどまる。TypeScript 7 の安定版が出たら、svelte-check は `typescript` 7 以上も tsgo として検出するので、`@typescript/native-preview` から乗り換える。 -- ESLint の `--cache`: 測ってから判断する。- workflow dependency lockfile: GitHub 側が GA になり、Renovate の対応から experimental が外れたら、SHA 固定から乗り換えるかを決める。 +- ESLint の `--cache`: 未計測。測ってから判断する。 +- workflow dependency lockfile: GitHub 側が GA になり、Renovate の対応から experimental が外れたら、SHA 固定から乗り換えるかを決める。乗り換えるときは、`gh actions-lock` が SHA の固定を版の参照に戻す。 ## 確認限界 -- CI の step ごとの時間は、ユーザーが共有した `build (24)` の1回分だけで、preview・production のジョブの内訳は見ていない。Phase 4・5 の見込みはこの値からの計算で、実際の効果は PR の CI で測る。 -- Vercel のダッシュボードの設定と、branch protection の required checks は repo から見えない。required checks の追加は admin が行い、agent は結果を見ていない。 -- tsgo の計測は devcontainer の1台。CI の runner での時間は Phase 2 では測らない(CI では動かさないため)。 -- workflow dependency lockfile は、GitHub docs の2ページ(security hardening guide と「workflow dependency lockfiles」)と Renovate の github-actions manager の docs だけで確認し、試していない。GitHub の plan・org の要件は docs に記載が無い。 +- CI の step ごとの時間は、`build (24)` の1回分だけで、preview・production のジョブの内訳は見ていない。見込みはこの値からの計算。 +- Vercel のダッシュボードの設定と branch protection は repo から見えない。required checks の追加は admin が行い、agent は結果を見ていない。 +- actionlint は、ダウンロードが許可されず実行していない。workflow は yaml のパーサーと Prettier でしか確かめていない。 +- workflow dependency lockfile は、GitHub docs の2ページと Renovate の docs だけで確認し、試していない。 From f9c4085ea2dd5aded48f0565cf58f5c6841f0e41 Mon Sep 17 00:00:00 2001 From: "k.hiro1818" Date: Wed, 21 Oct 2026 08:42:31 +0000 Subject: [PATCH 09/10] docs: restructure CI and typecheck speedup record by phase Co-Authored-By: Claude Sonnet 5.5 --- .../ci-and-typecheck-speedup/plan.md | 123 +++++++++--------- 1 file changed, 61 insertions(+), 62 deletions(-) diff --git a/docs/dev-notes/2026-10-20/ci-and-typecheck-speedup/plan.md b/docs/dev-notes/2026-10-20/ci-and-typecheck-speedup/plan.md index 25bb4b2aa..a5be98c2c 100644 --- a/docs/dev-notes/2026-10-20/ci-and-typecheck-speedup/plan.md +++ b/docs/dev-notes/2026-10-20/ci-and-typecheck-speedup/plan.md @@ -2,88 +2,87 @@ ## 概要 -Kit 3 への移行(PR B、branch `#4192`)の後、「build・check が遅い」という相談を受けて時間の内訳を測り、対策を選んだ。 -あわせて、セキュリティのために GitHub Actions の `uses:` を commit SHA に固定した。 -branch `ci-and-typecheck-speedup` で、Phase 1・2(tsgo 以外)・3・4・5 を実装済み(2026-10-21)。 -tsgo による型チェックの高速化は後回しにし、残タスクとして [TASK.md](../../../tasks/TASK.md) に移した。 +Kit 3 への移行の後、「build・check が遅い」という相談を受け、時間の内訳を測って効く所だけを直した。 +あわせて、GitHub Actions の action を commit SHA に固定した。 +branch `ci-and-typecheck-speedup` で実装済み(2026-10-21)。 +tsgo による型チェックの高速化は後回しにし、[TASK.md](../../../tasks/TASK.md) に移した。 -## 計測(2026-10-19、devcontainer 上) +| Phase | 変更 | 効果 | +| ----- | ------------------------------------------------------ | --------------------------------------------- | +| 1 | `pnpm lint` の prettier に `--cache` を付けた | ローカルの prettier 8.6 秒 → 1.6 秒 | +| 2 | `tsconfig.json` から効いていない3項目を消した | tsgo を使う前提を整えた(挙動は同じ) | +| 3 | action を commit SHA に固定した | tag の書き換えによる攻撃を防ぐ | +| 4 | CI の build・lint・check・unit test を並列ジョブにした | CI の `build` の段 1分19秒 → 約42秒(見込み) | +| 5 | deploy のジョブの重複した `pnpm build` を消した | deploy のジョブ 約13秒短縮(見込み) | -| コマンド | 時間 | 内訳 | -| ---------------- | ------- | ------------------------------------------------------------------------------------------------ | -| `pnpm build` | 14.4 秒 | `prisma generate` 1.8 秒、Vite(server 2.6 秒・client 2.0 秒)、adapter-vercel の出力 2.3 秒など | -| `pnpm lint` | 11.6 秒 | prettier 9.3 秒、ESLint 4.4 秒、oxlint 0.1 秒 | -| `pnpm check` | 10.6 秒 | `svelte-kit sync` 0.9 秒、svelte-check 9.3 秒 | -| `pnpm test:unit` | 6.0 秒 | | +CI 全体は 2分21秒〜2分50秒 → 約1分30秒〜2分の見込み。実測は PR の CI で行う。 -- CI は `staging` の直近10回が全体で 2分21秒〜2分50秒。#4191 のマージ(2分44秒)では `preview` が 1分16秒で、残りが `build` ジョブ。 -- 分割前の `build (24)` ジョブ(ユーザーが共有した1回分、全体 1分19秒): 準備 24秒(Setup pnpm 6秒、Node.js 6秒、install 10秒など)、本体 48秒(Build 13秒、Lint 11秒、Check 11秒、Unit test 13秒を順に実行)、後片付け 5秒。ジョブごとの固定の負担は約29秒。 -- `prettier --check . --cache` は、1回目 8.6 秒、2回目 1.6 秒(実装後の計測)。 +## 判断と根拠 -## 決めたこと +### Phase 3: SHA 固定 -- **低リスクから順に入れる:** ローカルの設定(Phase 1・2)→ 挙動の変わらない CI の変更(Phase 3)→ CI の構成(Phase 4)→ 配備のジョブ(Phase 5)。Phase ごとに1 commit(Phase 4 は composite action の切り出しと、ジョブの分割の2 commit)。 -- **SHA は当時の major tag と同じ commit にする:** 固定するだけで、動く action のコードは変えない。作業の直前に各 action の公式 repo で `git ls-remote --tags` を取り直し、計画時の値から動いていないことを確かめた。`pnpm/action-setup` は annotated tag なので、tag object ではなく、tag が指す commit(`^{}` の行)を使う。 -- **SHA の更新は Renovate に任せる:** `helpers:pinGitHubActionDigestsToSemver` が、digest とコメントの `vX.Y.Z` を一緒に更新する。Renovate の github-actions manager は、既定で `.github/actions/` 配下と `action.yml` も対象にする(docs で確認)。 -- **SHA 固定の理由:** GitHub の security hardening guide は、tag は「can be moved or deleted if a bad actor gains access to the repository storing the action」として SHA 固定を勧める。この workflow は `VERCEL_TOKEN` と、preview・production の DB の接続先を扱う。第三者の action だけでなく `actions/*` も同じ仕組みで書き換えられうるので、3つとも固定した。 -- **CI の check 名 `build (24)` は残す:** `build` ジョブだけ matrix を残し、lint・check・unit-test を別のジョブにした。 -- **setup は composite action にまとめ、deploy のジョブでも使う:** 6ジョブの setup は中身が同じで、SHA の更新を1か所で済ませるため。checkout は local action の読み込みに要るので、各ジョブに残す。 -- **migration は build の成功後:** 二重 build を消した後も「build が失敗したら DB を変えない」を保つため、`prisma migrate deploy` を `vercel build` の後に移した。 -- **branch protection(2026-10-21、admin が対処):** この repo は Settings → Branches の Branch protection rules の方式。分割後は lint・check・unit test の失敗が `build (24)` に出なくなるので、「Require status checks to pass before merging」に `lint`・`check`・`unit-test` を追加した。 -- **「Require branches to be up to date before merging」は変えない:** 入れると、マージのたびに他の PR で更新と CI のやり直しが要る。Renovate の PR が多く(`prHourlyLimit: 10`)、`rebaseWhen` の既定の `auto` は、この設定があると base が進むたびに rebase する(`rebaseWhen` の挙動は docs で未確認)。 +- tag は action の repo が乗っ取られると書き換えられる(GitHub の security hardening guide)。この workflow は Vercel の token と DB の接続先を扱うので、`actions/*` も含めて3つとも固定した。 +- SHA は当時の major tag が指す commit にし、動くコードは変えていない。`pnpm/action-setup` は annotated tag なので、tag が指す commit(`git ls-remote` の `^{}` の行)を使った。 +- 以後の更新は Renovate の `helpers:pinGitHubActionDigestsToSemver` が、SHA とコメントの版を一緒に書き換える。 -## コードから読み取れない根拠 +### Phase 4: ジョブの並列化 -### lint・check・unit-test は build の出力を使わない(Phase 4) +- lint・check・unit test は build の出力を使わないので、並列にできる。型チェックに要る `node_modules/$app/` は build ではなく `svelte-kit sync` が生成し、check と unit test はそれぞれ自分で sync を実行する。 + - 確認: 空のディレクトリで `pnpm install` の後に1つずつ単独で実行し、3つとも成功した。 +- `build` ジョブだけ matrix を残し、既存の check 名 `build (24)` を変えていない。 +- setup(pnpm・Node.js・install)は composite action にまとめ、deploy のジョブでも使う。SHA の更新を1か所で済ませるため。 +- branch protection(admin が対処済み): 分割後は lint などの失敗が `build (24)` に出なくなるので、required checks に `lint`・`check`・`unit-test` を追加した。 +- 「Require branches to be up to date before merging」は変えていない。入れると、Renovate の多数の PR がマージのたびに rebase と CI のやり直しになる。 -- `tsconfig.json` が extends する `$app/tsconfig`(`node_modules/$app/`)は、build ではなく `svelte-kit sync` が生成する。check は script の先頭の sync で、unit-test は Vitest が読み込む SvelteKit の Vite plugin で生成する。lint は型情報を使わない。 -- 確認(2026-10-21): `git archive HEAD` を空のディレクトリ3つに展開し、それぞれ `pnpm install --frozen-lockfile` の後に1つだけを実行した。3つとも成功し、どのコピーにも `build/` は生成されなかった。 +### Phase 2: tsconfig の3項目 -### tsconfig の `outDir`・`sourceMap`・`incremental` を消してよい(Phase 2) +- `outDir`・`sourceMap`・`incremental` は、TypeScript がファイルを出力するとき(または `tsc` を直接実行するとき)だけ効く。Kit が生成する `$app/tsconfig` が `noEmit: true` で、`tsc` を直接実行する所も無いので、どれも効いていなかった。 +- `outDir` は svelte-check の `--tsgo` を壊していた(「rootDir の外」のエラー)。tsgo は後回しにしたが、この削除は先に入れた。 +- Vercel への成果物(`.vercel/output`)は adapter-vercel が決めるので、`outDir: "build"` とは関係しない。 +- 削除の前後で、check・build・unit test の結果は変わらない。 -- 3つとも出力に関わる設定で、`noEmit: true` の下では効かない。`noEmit` は Kit が生成する `$app/tsconfig` にあり(Kit のソースのコメントは「Vite handles the build and ignores this」)、`tsconfig.json` は上書きしていない。 -- TypeScript の TSConfig Reference によると、`sourceMap` は出力した `.js` の横に `.js.map` を出し、`outDir` は出力先の指定。Vite の docs によると、Vite は型を消すだけで、読む `compilerOptions` に `sourceMap`・`outDir` は無い。 -- `incremental` は `noEmit` と併用でき、`tsc` を直接実行すれば `*.tsbuildinfo` を保存する。ただしこの repo には `tsc` を直接実行する script・lefthook・CI が無く、svelte-check も自分の `--incremental` を使わない限り読まない。 -- 出どころ: `sourceMap` は 2023 年の作成時のテンプレート(create-svelte 5.0.5)にあり、`outDir`・`incremental` はテンプレートの外で足したもの。現行の `sv create`(sv 1.1.1)の tsconfig には3つとも無い。 -- `outDir` は svelte-check の `--tsgo`・`--incremental` で「rootDir の外」のエラーを起こしていた。これが tsgo の前提だったので、tsgo を後回しにしてもこの変更は先に入れた。 -- デプロイとは無関係: Vercel に上げる成果物の置き場は adapter が決め、adapter-vercel は `.vercel/output` に固定で書き出す。`build/` が成果物になるのは adapter-node の既定値で、この repo では使わない。 +### Phase 5: deploy のジョブの重複 -### deploy のジョブの `pnpm build` は重複していた(Phase 5) - -- 経緯: 2023-10 の #121 では、deploy のジョブは `vercel pull` → `vercel build` → `vercel deploy --prebuilt` だけで動いていた。2024-04 の #362 で、migration のために `pnpm install`・`pnpm build`・`prisma migrate deploy` が一緒に追加された(`a6afb2d4`・`d0eb57b9`)。`pnpm build` が必要だった失敗の記録は無い。 -- `vercel deploy --prebuilt` は `vercel build` の出力を使う(CLI 64.5.0 の help)。`vercel build` は、ダッシュボードに設定が無ければ SvelteKit の preset の `vite build` を実行し、`installCommand` が未設定なら install も行う(CLI の配布コード)。 -- `prisma migrate deploy` は build を必要とせず、schema・migrations・接続先の環境変数で動く。Prisma client は `pnpm install` の `postinstall` で生成される。 -- 再現: `git archive HEAD` の新しいコピーで `pnpm install` → `vite build` だけを実行し、`.vercel/output` が生成された。`vercel build` 自体は Vercel の token が要るので実行していない。vercel.com・prisma.io の docs は取得できず、CLI の help と配布コードで確認した。 -- build のときに DB を読む経路(prerender など)は無い。将来 prerender を入れると、migration 前の DB を読むことになる(Codex のレビューの指摘)。 +- `vercel build` が install と `vite build` を自分で行うので、その前の `pnpm build` は重複していた。2024 年に migration の step と一緒に追加されたもので、必要だった記録は無い。 +- `prisma migrate deploy` を `vercel build` の後に移し、「build が失敗したら DB を変えない」を保った。 +- 今は build のときに DB を読む処理(prerender など)が無い。将来 prerender を入れると、migration 前の DB を読むことになるので注意する。 ## 採らなかった案 -- **rsvelte などの代替ツール:** 運用実績が少ない(ユーザー判断)。 -- **svelte-check の `--incremental`:** 2回目でも 4.1 秒で、tsgo(初回から 2.6 秒)より遅い。CI で効かせるにはキャッシュの保存も要る。 -- **Vite・adapter・`prisma generate` の調整:** どれも数秒で、削れる余地が小さい。Vite 8 は既に Rolldown を使う。 -- **preview の `needs` を外して並行に走らせる:** lint・check が失敗した変更でも、preview の DB に migration が当たる。 -- **setup の step を各ジョブに複製する:** 重複が増え、SHA の更新箇所も増える。 -- **workflow dependency lockfile(`.github/workflows/actions.lock`):** GitHub の guide は SHA 固定より強いとし、間接的に使われる action も固定できる。ただし GitHub 側は public preview で、Renovate の対応も experimental。どちらも運用実績が少ないので、今は SHA 固定にする。 - -## スコープ外 - -- **oxlint・oxfmt への移行:** [#2226](https://github.com/AtCoder-NoviSteps/AtCoderNoviSteps/issues/2226) と [oxfmt-migration の計画](../../2026-10-21/oxfmt-migration/plan.md) で扱う。移行すると Phase 1 の `prettier --cache` は不要になるが、変更は1か所なので先に入れた。prettier が `.prettierrc` を優先し、`prettier-plugin-tailwindcss` が使われていない問題も同じ PR で扱う。 +- **rsvelte などの代替ツール:** 運用実績が少ない。 +- **svelte-check の `--incremental`:** 2回目でも 4.1 秒で、tsgo(2.6 秒)より遅い。 +- **Vite・adapter・`prisma generate` の調整:** それぞれ数秒で、削れる余地が小さい。 +- **deploy を lint などと並行に走らせる:** lint・check が失敗した変更でも、preview の DB に migration が当たる。 +- **workflow dependency lockfile(`actions.lock`):** SHA 固定より強いが、GitHub 側は public preview、Renovate の対応も experimental。 +- **oxfmt への移行:** 別 PR([#2226](https://github.com/AtCoder-NoviSteps/AtCoderNoviSteps/issues/2226)、[計画](../../2026-10-21/oxfmt-migration/plan.md))。移行すると Phase 1 は不要になるが、1行の変更なので先に入れた。 ## 残りの確認(人が行う) -- [ ] Vercel のダッシュボードの Build Command を確認する(`vercel.json` に `buildCommand` は無い)。想定外のコマンドなら先に揃える。 -- [ ] PR の CI で4ジョブが並行に走るか確かめ、`build` の段と全体の時間を、分割前の 1分19秒・2分21秒〜2分50秒と比べる。見込みは、`build` の段が約42秒、全体が約1分30秒〜2分。 -- [ ] PR の preview ジョブが成功し、preview の URL でログインできる。production はマージ後の最初の実行で確かめる。 -- [ ] マージ後の Renovate の action の更新 PR(例: `pnpm/action-setup` v6.1.0)が、SHA とコメントを一緒に書き換える。 +- [ ] Vercel のダッシュボードの Build Command が想定外のコマンドになっていないか(`vercel.json` には指定が無い)。 +- [ ] PR の CI で4ジョブが並行に走り、時間が見込みどおり縮むか。 +- [ ] PR の preview ジョブが成功し、preview の URL でログインできるか。production はマージ後の最初の実行で確かめる。 +- [ ] マージ後の Renovate の action の更新 PR が、SHA とコメントを一緒に書き換えるか。 ## 後で決めること -- ESLint の `--cache`: 未計測。測ってから判断する。 -- workflow dependency lockfile: GitHub 側が GA になり、Renovate の対応から experimental が外れたら、SHA 固定から乗り換えるかを決める。乗り換えるときは、`gh actions-lock` が SHA の固定を版の参照に戻す。 +- **ESLint の `--cache`:** 測ってから判断する。 +- **workflow dependency lockfile:** GitHub と Renovate の対応が安定したら、SHA 固定から乗り換えるかを決める。 ## 確認限界 -- CI の step ごとの時間は、`build (24)` の1回分だけで、preview・production のジョブの内訳は見ていない。見込みはこの値からの計算。 -- Vercel のダッシュボードの設定と branch protection は repo から見えない。required checks の追加は admin が行い、agent は結果を見ていない。 -- actionlint は、ダウンロードが許可されず実行していない。workflow は yaml のパーサーと Prettier でしか確かめていない。 -- workflow dependency lockfile は、GitHub docs の2ページと Renovate の docs だけで確認し、試していない。 +- CI の見込みは、分割前の `build (24)` の1回分の時間からの計算。 +- `vercel build` そのものは token が要るので実行しておらず、`vite build` までで確かめた。 +- actionlint は実行できず、workflow は yaml のパーサーと Prettier でしか確かめていない。 +- Vercel のダッシュボードと branch protection の設定は、repo からは見えない。 + +## 付録: 計測(2026-10-19、devcontainer) + +| コマンド | 時間 | 主な内訳 | +| ---------------- | ------- | ------------------------------------------------------------ | +| `pnpm build` | 14.4 秒 | Vite 4.6 秒、adapter-vercel 2.3 秒、`prisma generate` 1.8 秒 | +| `pnpm lint` | 11.6 秒 | prettier 9.3 秒、ESLint 4.4 秒、oxlint 0.1 秒 | +| `pnpm check` | 10.6 秒 | svelte-check 9.3 秒 | +| `pnpm test:unit` | 6.0 秒 | | + +- CI の `staging` の直近10回は、全体で 2分21秒〜2分50秒。 +- 分割前の `build (24)` ジョブは 1分19秒。うち setup などジョブごとの固定分が約29秒、Build・Lint・Check・Unit test の本体が順に計48秒。 From d4aabb53600c25fe141006f4bdeba5c74ecdf916 Mon Sep 17 00:00:00 2001 From: "k.hiro1818" Date: Wed, 21 Oct 2026 08:43:07 +0000 Subject: [PATCH 10/10] docs: remove ci-and-typecheck-speedup plan Co-Authored-By: Claude Sonnet 5.5 --- .../ci-and-typecheck-speedup/plan.md | 88 ------------------- 1 file changed, 88 deletions(-) delete mode 100644 docs/dev-notes/2026-10-20/ci-and-typecheck-speedup/plan.md diff --git a/docs/dev-notes/2026-10-20/ci-and-typecheck-speedup/plan.md b/docs/dev-notes/2026-10-20/ci-and-typecheck-speedup/plan.md deleted file mode 100644 index a5be98c2c..000000000 --- a/docs/dev-notes/2026-10-20/ci-and-typecheck-speedup/plan.md +++ /dev/null @@ -1,88 +0,0 @@ -# CI と型チェックの高速化、actions の SHA 固定 - -## 概要 - -Kit 3 への移行の後、「build・check が遅い」という相談を受け、時間の内訳を測って効く所だけを直した。 -あわせて、GitHub Actions の action を commit SHA に固定した。 -branch `ci-and-typecheck-speedup` で実装済み(2026-10-21)。 -tsgo による型チェックの高速化は後回しにし、[TASK.md](../../../tasks/TASK.md) に移した。 - -| Phase | 変更 | 効果 | -| ----- | ------------------------------------------------------ | --------------------------------------------- | -| 1 | `pnpm lint` の prettier に `--cache` を付けた | ローカルの prettier 8.6 秒 → 1.6 秒 | -| 2 | `tsconfig.json` から効いていない3項目を消した | tsgo を使う前提を整えた(挙動は同じ) | -| 3 | action を commit SHA に固定した | tag の書き換えによる攻撃を防ぐ | -| 4 | CI の build・lint・check・unit test を並列ジョブにした | CI の `build` の段 1分19秒 → 約42秒(見込み) | -| 5 | deploy のジョブの重複した `pnpm build` を消した | deploy のジョブ 約13秒短縮(見込み) | - -CI 全体は 2分21秒〜2分50秒 → 約1分30秒〜2分の見込み。実測は PR の CI で行う。 - -## 判断と根拠 - -### Phase 3: SHA 固定 - -- tag は action の repo が乗っ取られると書き換えられる(GitHub の security hardening guide)。この workflow は Vercel の token と DB の接続先を扱うので、`actions/*` も含めて3つとも固定した。 -- SHA は当時の major tag が指す commit にし、動くコードは変えていない。`pnpm/action-setup` は annotated tag なので、tag が指す commit(`git ls-remote` の `^{}` の行)を使った。 -- 以後の更新は Renovate の `helpers:pinGitHubActionDigestsToSemver` が、SHA とコメントの版を一緒に書き換える。 - -### Phase 4: ジョブの並列化 - -- lint・check・unit test は build の出力を使わないので、並列にできる。型チェックに要る `node_modules/$app/` は build ではなく `svelte-kit sync` が生成し、check と unit test はそれぞれ自分で sync を実行する。 - - 確認: 空のディレクトリで `pnpm install` の後に1つずつ単独で実行し、3つとも成功した。 -- `build` ジョブだけ matrix を残し、既存の check 名 `build (24)` を変えていない。 -- setup(pnpm・Node.js・install)は composite action にまとめ、deploy のジョブでも使う。SHA の更新を1か所で済ませるため。 -- branch protection(admin が対処済み): 分割後は lint などの失敗が `build (24)` に出なくなるので、required checks に `lint`・`check`・`unit-test` を追加した。 -- 「Require branches to be up to date before merging」は変えていない。入れると、Renovate の多数の PR がマージのたびに rebase と CI のやり直しになる。 - -### Phase 2: tsconfig の3項目 - -- `outDir`・`sourceMap`・`incremental` は、TypeScript がファイルを出力するとき(または `tsc` を直接実行するとき)だけ効く。Kit が生成する `$app/tsconfig` が `noEmit: true` で、`tsc` を直接実行する所も無いので、どれも効いていなかった。 -- `outDir` は svelte-check の `--tsgo` を壊していた(「rootDir の外」のエラー)。tsgo は後回しにしたが、この削除は先に入れた。 -- Vercel への成果物(`.vercel/output`)は adapter-vercel が決めるので、`outDir: "build"` とは関係しない。 -- 削除の前後で、check・build・unit test の結果は変わらない。 - -### Phase 5: deploy のジョブの重複 - -- `vercel build` が install と `vite build` を自分で行うので、その前の `pnpm build` は重複していた。2024 年に migration の step と一緒に追加されたもので、必要だった記録は無い。 -- `prisma migrate deploy` を `vercel build` の後に移し、「build が失敗したら DB を変えない」を保った。 -- 今は build のときに DB を読む処理(prerender など)が無い。将来 prerender を入れると、migration 前の DB を読むことになるので注意する。 - -## 採らなかった案 - -- **rsvelte などの代替ツール:** 運用実績が少ない。 -- **svelte-check の `--incremental`:** 2回目でも 4.1 秒で、tsgo(2.6 秒)より遅い。 -- **Vite・adapter・`prisma generate` の調整:** それぞれ数秒で、削れる余地が小さい。 -- **deploy を lint などと並行に走らせる:** lint・check が失敗した変更でも、preview の DB に migration が当たる。 -- **workflow dependency lockfile(`actions.lock`):** SHA 固定より強いが、GitHub 側は public preview、Renovate の対応も experimental。 -- **oxfmt への移行:** 別 PR([#2226](https://github.com/AtCoder-NoviSteps/AtCoderNoviSteps/issues/2226)、[計画](../../2026-10-21/oxfmt-migration/plan.md))。移行すると Phase 1 は不要になるが、1行の変更なので先に入れた。 - -## 残りの確認(人が行う) - -- [ ] Vercel のダッシュボードの Build Command が想定外のコマンドになっていないか(`vercel.json` には指定が無い)。 -- [ ] PR の CI で4ジョブが並行に走り、時間が見込みどおり縮むか。 -- [ ] PR の preview ジョブが成功し、preview の URL でログインできるか。production はマージ後の最初の実行で確かめる。 -- [ ] マージ後の Renovate の action の更新 PR が、SHA とコメントを一緒に書き換えるか。 - -## 後で決めること - -- **ESLint の `--cache`:** 測ってから判断する。 -- **workflow dependency lockfile:** GitHub と Renovate の対応が安定したら、SHA 固定から乗り換えるかを決める。 - -## 確認限界 - -- CI の見込みは、分割前の `build (24)` の1回分の時間からの計算。 -- `vercel build` そのものは token が要るので実行しておらず、`vite build` までで確かめた。 -- actionlint は実行できず、workflow は yaml のパーサーと Prettier でしか確かめていない。 -- Vercel のダッシュボードと branch protection の設定は、repo からは見えない。 - -## 付録: 計測(2026-10-19、devcontainer) - -| コマンド | 時間 | 主な内訳 | -| ---------------- | ------- | ------------------------------------------------------------ | -| `pnpm build` | 14.4 秒 | Vite 4.6 秒、adapter-vercel 2.3 秒、`prisma generate` 1.8 秒 | -| `pnpm lint` | 11.6 秒 | prettier 9.3 秒、ESLint 4.4 秒、oxlint 0.1 秒 | -| `pnpm check` | 10.6 秒 | svelte-check 9.3 秒 | -| `pnpm test:unit` | 6.0 秒 | | - -- CI の `staging` の直近10回は、全体で 2分21秒〜2分50秒。 -- 分割前の `build (24)` ジョブは 1分19秒。うち setup などジョブごとの固定分が約29秒、Build・Lint・Check・Unit test の本体が順に計48秒。