diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 93941de..90d3fcb 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -7,6 +7,12 @@ on: pull_request: branches: [main] +# デフォルトは read-only。デプロイ等が必要な job は各 job の permissions で個別に昇格する +# (161 / 260 / 321 行目付近)。リポジトリ設定は既に default_workflow_permissions: read だが、 +# 将来の設定変更に対する防御として明示する。 +permissions: + contents: read + # ref ごとに 1 run。PR は新しい push で古い run をキャンセル (CI 節約)。 # push (= deploy) はキャンセルせずキューイングし、最新 run が最後に走るので # 「古い deploy が後から完了して新しい成果物を上書きする」競合を防ぐ。 @@ -119,6 +125,26 @@ jobs: - name: Provision E2E env (test keys + 生成 checkpoint 鍵) run: npm run setup -w @typedcode/e2e + # キーには Playwright のバージョンを含める。package-lock.json のハッシュだけだと + # Playwright と無関係な依存更新でもキャッシュが無効化されてしまうため、 + # `npx playwright --version` (インストール済み @playwright/test の CLI) が + # 報告する実バージョンを切り出してキーに使う (package.json の ^1.62.0 のような + # 範囲指定ではなく、実際に解決されたバージョンで判定するため)。 + - name: Get installed Playwright version + id: playwright-version + run: echo "version=$(npx playwright --version | sed 's/Version //')" >> "$GITHUB_OUTPUT" + + - name: Cache Playwright browsers + uses: actions/cache@v4 + with: + path: ~/.cache/ms-playwright + key: playwright-browsers-${{ runner.os }}-${{ steps.playwright-version.outputs.version }} + restore-keys: | + playwright-browsers-${{ runner.os }}- + + # キャッシュヒット時も install は実行したままにする。ブラウザ本体が揃っていれば + # 即座に終わる一方、--with-deps が入れる OS 依存パッケージ (apt) はキャッシュの + # 対象外 (~/.cache/ms-playwright に含まれない) なので毎回必要。 - name: Install Playwright browser run: npx playwright install --with-deps chromium @@ -197,13 +223,27 @@ jobs: # --commit-message を明示的に ASCII で渡す。CF Pages API は非 ASCII # コミットメッセージを 8000111 で拒否することがあるため (wrangler issue)、 # SHA を message として送って追跡性を保つ。 + # + # ここは ${{ }} の直接展開のままでよい (#239 で「env 経由に変えるべき」と + # 指摘されたが、実測の結果その前提が誤りだった)。wrangler-action v4 は + # command: を **シェルに渡さず**トークン分割して npx を直接 exec する。 + # 実測: env で $CF_PROJECT_NAME を渡すと wrangler がプロジェクト名として + # 文字列 "$CF_PROJECT_NAME" を受け取り deploy が失敗する。 + # シェルが介在しないので head_ref の `;` `$` バッククォートは不活性であり、 + # git はブランチ名に空白を許さないため引数分割も起きない。 + # **env 経由に「直す」とデプロイが壊れる。** command: pages deploy ./dist --project-name=${{ secrets.CLOUDFLARE_PROJECT_NAME }} --branch=${{ github.head_ref }} --commit-hash=${{ github.sha }} --commit-message="${{ github.sha }}" - name: Comment PR with preview URL uses: actions/github-script@v9 + # wrangler の出力にバッククォートや `${` が混ざると、テンプレートリテラルへの + # 直接展開ではスクリプトごと壊れて job が落ちる。env: 経由で渡し、 + # process.env 参照にすることで出力内容に関わらず安全に扱う。 + env: + PREVIEW_DEPLOY_OUTPUT: ${{ steps.preview-deploy.outputs.command-output }} with: script: | - const output = `${{ steps.preview-deploy.outputs.command-output }}`; + const output = process.env.PREVIEW_DEPLOY_OUTPUT; const urlMatch = output.match(/https:\/\/[^\s]+\.pages\.dev/); const previewUrl = urlMatch ? urlMatch[0] : null; diff --git a/packages/e2e/playwright.config.ts b/packages/e2e/playwright.config.ts index 04cb375..c16898d 100644 --- a/packages/e2e/playwright.config.ts +++ b/packages/e2e/playwright.config.ts @@ -5,9 +5,9 @@ import { defineConfig, devices } from '@playwright/test'; * round-trip を基本形にする (UI の見た目ではなく、暗号的に検証可能な成果物を * オラクルにする)。詳細は packages/e2e/README.md。 * - * 2 つのローカルサーバを起動する: - * - workers (wrangler dev :8787): /api/session/start・/api/verify-captcha・署名 CP - * - editor (vite dev :5173): テスト対象アプリ + * 2 つのローカルサーバを起動する (ポートは既定値。E2E_EDITOR_PORT / E2E_WORKERS_PORT で上書き可能): + * - workers (wrangler dev 既定 :8787): /api/session/start・/api/verify-captcha・署名 CP + * - editor (vite dev 既定 :5173): テスト対象アプリ * * editor/.env は既に Cloudflare の Turnstile テストキー (1x...AA = 常に pass) と * VITE_API_URL=http://localhost:8787 を指すため、追加設定なしでフルスタックが回る。 @@ -64,10 +64,17 @@ export default defineConfig({ }, ], + // `port:` は TCP 応答しか見ないため、既定ポートを別プロセスが握っていると + // reuseExistingServer 時に「自分のサーバ」と誤認しうる (ローカル開発時のみ影響、 + // CI は reuseExistingServer: false)。起動コマンド自体にもポートを渡し、 + // E2E_EDITOR_PORT / E2E_WORKERS_PORT を指定したときに実際にそのポートで + // 上がるようにする (指定しない限りは無指定と同じ既定ポートに上がる)。 + // 配線は scripts/dev.mjs (118-145 行目) と同じパターンを踏襲する。 webServer: [ { name: 'workers', - command: 'npm run dev -w @typedcode/workers', + // wrangler dev は env を読まないため、コマンド側に --port を渡す。 + command: `npm run dev -w @typedcode/workers -- --port ${WORKERS_PORT}`, cwd: '../..', port: WORKERS_PORT, reuseExistingServer: !isCI, @@ -78,6 +85,10 @@ export default defineConfig({ { name: 'editor', command: 'npm run dev -w @typedcode/editor', + // packages/editor/vite.config.ts が EDITOR_PORT (server.port + strictPort) と + // WORKERS_PORT (VITE_API_URL 追従) を既に読む実装になっている。受け口は + // 実装済みで、これまで渡していなかっただけなので env で渡す。 + env: { EDITOR_PORT: String(EDITOR_PORT), WORKERS_PORT: String(WORKERS_PORT) }, cwd: '../..', port: EDITOR_PORT, reuseExistingServer: !isCI,