From a4c41c6a8dedb9d60f3ca90c53de349f93d234eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9D=90=93=F0=9D=90=9E=F0=9D=90=9A=F0=9D=90=A4?= =?UTF-8?q?=F0=9D=90=A8=F0=9D=90=B0=F0=9D=90=9A?= <27560638+Teakowa@users.noreply.github.com> Date: Thu, 17 Sep 2026 17:41:25 +0800 Subject: [PATCH 01/10] ci: add PR checks and Worker previews --- .github/workflows/deploy-cloudflare.yml | 60 ++++++++++++++++++++++++- 1 file changed, 58 insertions(+), 2 deletions(-) diff --git a/.github/workflows/deploy-cloudflare.yml b/.github/workflows/deploy-cloudflare.yml index b905313..880081c 100644 --- a/.github/workflows/deploy-cloudflare.yml +++ b/.github/workflows/deploy-cloudflare.yml @@ -1,13 +1,24 @@ -name: Deploy to Cloudflare +name: CI and Deploy on: + pull_request: push: branches: - main +permissions: + contents: read + issues: write + pull-requests: write + +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true + jobs: - deploy: + ci: runs-on: ubuntu-latest + timeout-minutes: 15 steps: - name: Checkout uses: actions/checkout@v7 @@ -29,13 +40,58 @@ jobs: - name: Enforce slug-only route docs run: bash tools/check_slug_only_docs.sh + - name: Type check + run: pnpm exec tsc --noEmit + - name: Run tests run: pnpm test - name: Build check run: pnpm run build + - name: Upload pull request preview + id: preview + if: >- + github.event_name == 'pull_request' && + github.event.pull_request.head.repo.full_name == github.repository + env: + CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }} + CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }} + NO_COLOR: '1' + run: | + set -euo pipefail + output="$(pnpm exec wrangler versions upload --preview-alias "pr-${{ github.event.pull_request.number }}" 2>&1)" + printf '%s\n' "$output" + + preview_url="$(printf '%s\n' "$output" | grep -Eo 'https://[^[:space:]]+\.workers\.dev' | tail -n 1)" + if [ -z "$preview_url" ]; then + echo '::error::Wrangler did not return a Worker preview URL.' + exit 1 + fi + + echo "url=$preview_url" >> "$GITHUB_OUTPUT" + + - name: Publish pull request preview link + if: steps.preview.outcome == 'success' + env: + GH_TOKEN: ${{ github.token }} + PREVIEW_URL: ${{ steps.preview.outputs.url }} + PR_NUMBER: ${{ github.event.pull_request.number }} + run: | + set -euo pipefail + marker='' + short_sha="${GITHUB_SHA:0:7}" + body="$(printf '%s\nCloudflare Worker preview: %s\n\nUpdated for commit `%s`.' "$marker" "$PREVIEW_URL" "$short_sha")" + comment_id="$(gh api --paginate "repos/${GITHUB_REPOSITORY}/issues/${PR_NUMBER}/comments" --jq '.[] | select(.body | contains("")) | .id' | head -n 1)" + + if [ -n "$comment_id" ]; then + gh api --method PATCH "repos/${GITHUB_REPOSITORY}/issues/comments/${comment_id}" -f body="$body" >/dev/null + else + gh api --method POST "repos/${GITHUB_REPOSITORY}/issues/${PR_NUMBER}/comments" -f body="$body" >/dev/null + fi + - name: Deploy to Cloudflare Worker + if: github.event_name == 'push' && github.ref == 'refs/heads/main' env: CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }} CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }} From 0c623049bb2076684d408681e27cc9259e257102 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9D=90=93=F0=9D=90=9E=F0=9D=90=9A=F0=9D=90=A4?= =?UTF-8?q?=F0=9D=90=A8=F0=9D=90=B0=F0=9D=90=9A?= <27560638+Teakowa@users.noreply.github.com> Date: Thu, 17 Sep 2026 17:42:20 +0800 Subject: [PATCH 02/10] ci: keep PR validation aligned with existing gates --- .github/workflows/deploy-cloudflare.yml | 3 --- 1 file changed, 3 deletions(-) diff --git a/.github/workflows/deploy-cloudflare.yml b/.github/workflows/deploy-cloudflare.yml index 880081c..292d23c 100644 --- a/.github/workflows/deploy-cloudflare.yml +++ b/.github/workflows/deploy-cloudflare.yml @@ -40,9 +40,6 @@ jobs: - name: Enforce slug-only route docs run: bash tools/check_slug_only_docs.sh - - name: Type check - run: pnpm exec tsc --noEmit - - name: Run tests run: pnpm test From e6cb2b2b2080e019ca142839d886331952a27964 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9D=90=93=F0=9D=90=9E=F0=9D=90=9A=F0=9D=90=A4?= =?UTF-8?q?=F0=9D=90=A8=F0=9D=90=B0=F0=9D=90=9A?= <27560638+Teakowa@users.noreply.github.com> Date: Thu, 17 Sep 2026 17:43:38 +0800 Subject: [PATCH 03/10] ci: skip previews when PR secrets are unavailable --- .github/workflows/deploy-cloudflare.yml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/workflows/deploy-cloudflare.yml b/.github/workflows/deploy-cloudflare.yml index 292d23c..fb63a07 100644 --- a/.github/workflows/deploy-cloudflare.yml +++ b/.github/workflows/deploy-cloudflare.yml @@ -50,7 +50,8 @@ jobs: id: preview if: >- github.event_name == 'pull_request' && - github.event.pull_request.head.repo.full_name == github.repository + github.event.pull_request.head.repo.full_name == github.repository && + github.actor != 'dependabot[bot]' env: CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }} CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }} @@ -73,11 +74,12 @@ jobs: env: GH_TOKEN: ${{ github.token }} PREVIEW_URL: ${{ steps.preview.outputs.url }} + PREVIEW_SHA: ${{ github.event.pull_request.head.sha }} PR_NUMBER: ${{ github.event.pull_request.number }} run: | set -euo pipefail marker='' - short_sha="${GITHUB_SHA:0:7}" + short_sha="${PREVIEW_SHA:0:7}" body="$(printf '%s\nCloudflare Worker preview: %s\n\nUpdated for commit `%s`.' "$marker" "$PREVIEW_URL" "$short_sha")" comment_id="$(gh api --paginate "repos/${GITHUB_REPOSITORY}/issues/${PR_NUMBER}/comments" --jq '.[] | select(.body | contains("")) | .id' | head -n 1)" From feb8a65622176a210a7ec02a8cce88d4a3ec612e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9D=90=93=F0=9D=90=9E=F0=9D=90=9A=F0=9D=90=A4?= =?UTF-8?q?=F0=9D=90=A8=F0=9D=90=B0=F0=9D=90=9A?= <27560638+Teakowa@users.noreply.github.com> Date: Thu, 17 Sep 2026 17:54:18 +0800 Subject: [PATCH 04/10] ci: keep GitHub Actions validation-only --- .github/workflows/ci.yml | 45 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) create mode 100644 .github/workflows/ci.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..eba7d2a --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,45 @@ +name: CI + +on: + pull_request: + push: + branches: + - main + +permissions: + contents: read + +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true + +jobs: + ci: + runs-on: ubuntu-latest + timeout-minutes: 15 + steps: + - name: Checkout + uses: actions/checkout@v7 + + - name: Setup pnpm + uses: pnpm/action-setup@v5 + with: + version: 10 + + - name: Setup Node.js + uses: actions/setup-node@v7 + with: + node-version: 22 + cache: pnpm + + - name: Install dependencies + run: pnpm install --frozen-lockfile + + - name: Enforce slug-only route docs + run: bash tools/check_slug_only_docs.sh + + - name: Run tests + run: pnpm test + + - name: Build check + run: pnpm run build From 58ab6806d38910dd56ea3db981996346c234f93b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9D=90=93=F0=9D=90=9E=F0=9D=90=9A=F0=9D=90=A4?= =?UTF-8?q?=F0=9D=90=A8=F0=9D=90=B0=F0=9D=90=9A?= <27560638+Teakowa@users.noreply.github.com> Date: Thu, 17 Sep 2026 17:54:22 +0800 Subject: [PATCH 05/10] ci: move deployment ownership to Workers Builds --- .github/workflows/deploy-cloudflare.yml | 97 ------------------------- 1 file changed, 97 deletions(-) delete mode 100644 .github/workflows/deploy-cloudflare.yml diff --git a/.github/workflows/deploy-cloudflare.yml b/.github/workflows/deploy-cloudflare.yml deleted file mode 100644 index fb63a07..0000000 --- a/.github/workflows/deploy-cloudflare.yml +++ /dev/null @@ -1,97 +0,0 @@ -name: CI and Deploy - -on: - pull_request: - push: - branches: - - main - -permissions: - contents: read - issues: write - pull-requests: write - -concurrency: - group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} - cancel-in-progress: true - -jobs: - ci: - runs-on: ubuntu-latest - timeout-minutes: 15 - steps: - - name: Checkout - uses: actions/checkout@v7 - - - name: Setup pnpm - uses: pnpm/action-setup@v5 - with: - version: 10 - - - name: Setup Node.js - uses: actions/setup-node@v7 - with: - node-version: 22 - cache: pnpm - - - name: Install dependencies - run: pnpm install --frozen-lockfile - - - name: Enforce slug-only route docs - run: bash tools/check_slug_only_docs.sh - - - name: Run tests - run: pnpm test - - - name: Build check - run: pnpm run build - - - name: Upload pull request preview - id: preview - if: >- - github.event_name == 'pull_request' && - github.event.pull_request.head.repo.full_name == github.repository && - github.actor != 'dependabot[bot]' - env: - CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }} - CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }} - NO_COLOR: '1' - run: | - set -euo pipefail - output="$(pnpm exec wrangler versions upload --preview-alias "pr-${{ github.event.pull_request.number }}" 2>&1)" - printf '%s\n' "$output" - - preview_url="$(printf '%s\n' "$output" | grep -Eo 'https://[^[:space:]]+\.workers\.dev' | tail -n 1)" - if [ -z "$preview_url" ]; then - echo '::error::Wrangler did not return a Worker preview URL.' - exit 1 - fi - - echo "url=$preview_url" >> "$GITHUB_OUTPUT" - - - name: Publish pull request preview link - if: steps.preview.outcome == 'success' - env: - GH_TOKEN: ${{ github.token }} - PREVIEW_URL: ${{ steps.preview.outputs.url }} - PREVIEW_SHA: ${{ github.event.pull_request.head.sha }} - PR_NUMBER: ${{ github.event.pull_request.number }} - run: | - set -euo pipefail - marker='' - short_sha="${PREVIEW_SHA:0:7}" - body="$(printf '%s\nCloudflare Worker preview: %s\n\nUpdated for commit `%s`.' "$marker" "$PREVIEW_URL" "$short_sha")" - comment_id="$(gh api --paginate "repos/${GITHUB_REPOSITORY}/issues/${PR_NUMBER}/comments" --jq '.[] | select(.body | contains("")) | .id' | head -n 1)" - - if [ -n "$comment_id" ]; then - gh api --method PATCH "repos/${GITHUB_REPOSITORY}/issues/comments/${comment_id}" -f body="$body" >/dev/null - else - gh api --method POST "repos/${GITHUB_REPOSITORY}/issues/${PR_NUMBER}/comments" -f body="$body" >/dev/null - fi - - - name: Deploy to Cloudflare Worker - if: github.event_name == 'push' && github.ref == 'refs/heads/main' - env: - CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }} - CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }} - run: pnpm run deploy From 93ea65ee02c520434a9da7cec8ac1c7d832d7448 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9D=90=93=F0=9D=90=9E=F0=9D=90=9A=F0=9D=90=A4?= =?UTF-8?q?=F0=9D=90=A8=F0=9D=90=B0=F0=9D=90=9A?= <27560638+Teakowa@users.noreply.github.com> Date: Thu, 17 Sep 2026 17:54:38 +0800 Subject: [PATCH 06/10] docs: record Workers Builds deployment ownership --- AGENTS.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/AGENTS.md b/AGENTS.md index 9af6732..cf4f5fd 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -17,6 +17,7 @@ - `docs/ADR-001-architecture.md` records the converter architecture and its rationale. - `docs/SPEC-COVERAGE-REPORT.md` is an acceptance-coverage snapshot. Use it as evidence to audit, then verify the live code and tests rather than treating the dated status as authoritative. - `package.json` and `wrangler.jsonc` are the sources of truth for local commands and Worker runtime configuration. +- `.github/workflows/ci.yml` validates pull requests and `main`; Cloudflare Workers Builds owns production and preview deployments through the repository's Git integration. - `tools/check_slug_only_docs.sh` is the documentation guard for the slug-only route contract. - Keep repository-wide rules here. Put directory-specific rules in a nested `AGENTS.md` only when a directory gains constraints that do not apply elsewhere. @@ -79,6 +80,7 @@ - `pnpm build` runs the Wrangler dry-run build and is required for runtime or deployment-related changes. - `pnpm exec tsc --noEmit` is useful for TypeScript-only changes when a focused type check is sufficient. - `bash tools/check_slug_only_docs.sh` is required after changing `README.md`, `AGENTS.md`, or route documentation. +- GitHub Actions is validation-only. Do not add Cloudflare credentials or Worker deployment steps to repository workflows; production and branch-preview deployment belong to Cloudflare Workers Builds. - Before committing, run `git diff --check`, inspect the staged diff, and stage only files owned by the task. - For implementation work, commit verified task-owned changes with a concise message. Do not push, amend, rewrite history, deploy, or publish without an explicit request. From 64648225c9a557d0fed160bd3336c83a0260a025 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9D=90=93=F0=9D=90=9E=F0=9D=90=9A=F0=9D=90=A4?= =?UTF-8?q?=F0=9D=90=A8=F0=9D=90=B0=F0=9D=90=9A?= <27560638+Teakowa@users.noreply.github.com> Date: Thu, 17 Sep 2026 17:55:10 +0800 Subject: [PATCH 07/10] docs: clarify Cloudflare-native deployment ownership From 981e06f533132c773eaefea6e4c6cdee7b3b53ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9D=90=93=F0=9D=90=9E=F0=9D=90=9A=F0=9D=90=A4?= =?UTF-8?q?=F0=9D=90=A8=F0=9D=90=B0=F0=9D=90=9A?= <27560638+Teakowa@users.noreply.github.com> Date: Thu, 17 Sep 2026 17:55:24 +0800 Subject: [PATCH 08/10] docs: document Workers Builds deployment ownership --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 27a1a2b..6c7c293 100644 --- a/README.md +++ b/README.md @@ -87,7 +87,7 @@ curl -s -D - -o /dev/null -H 'If-None-Match: ""' \ ## Maintainer Note -For local development and runtime configuration, use the repository scripts and `wrangler.jsonc` as the source of truth. This README is intentionally user-focused and omits internal deployment and CI details. +For local development and runtime configuration, use the repository scripts and `wrangler.jsonc` as the source of truth. GitHub Actions validates changes; production and branch-preview deployments are owned by Cloudflare Workers Builds through the repository's Git integration. ## License & Content Ownership From b4bf2dfc2f600b582abbf6af294d2979cf7dfe48 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9D=90=93=F0=9D=90=9E=F0=9D=90=9A=F0=9D=90=A4?= =?UTF-8?q?=F0=9D=90=A8=F0=9D=90=B0=F0=9D=90=9A?= <27560638+Teakowa@users.noreply.github.com> Date: Thu, 17 Sep 2026 17:55:29 +0800 Subject: [PATCH 09/10] chore: retain Worker config for native Builds From 411a5e9bbed6f2c1cb97247433c3e0a9e88f39e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9D=90=93=F0=9D=90=9E=F0=9D=90=9A=F0=9D=90=A4?= =?UTF-8?q?=F0=9D=90=A8=F0=9D=90=B0=F0=9D=90=9A?= <27560638+Teakowa@users.noreply.github.com> Date: Thu, 17 Sep 2026 17:55:36 +0800 Subject: [PATCH 10/10] chore: retain Wrangler scripts for Workers Builds