Skip to content

feat: Cloudflare Workers を同期先プロバイダーとして追加 - #44

Merged
polidog merged 3 commits into
mainfrom
feat/cloudflare-workers-provider
Jul 26, 2026
Merged

feat: Cloudflare Workers を同期先プロバイダーとして追加#44
polidog merged 3 commits into
mainfrom
feat/cloudflare-workers-provider

Conversation

@polidog

@polidog polidog commented Jul 26, 2026

Copy link
Copy Markdown
Member

概要

env-sync の同期先に Cloudflare Workers を追加しました。既存の provider 抽象(registry + Provider インターフェース)にそのまま乗る形で internal/provider/cloudflare/ を新規追加しています。

CLOUDFLARE_API_TOKEN=xxxxx CLOUDFLARE_ACCOUNT_ID=yyyyy \
  env-sync --provider cloudflare --env .env.production

主な設計判断

1. Worker Secrets のみ同期し、平文 vars はスキップする

Cloudflare Workers の環境変数は「Secrets」と「平文 vars」の 2 種類ありますが、本 provider は Secrets のみを同期します。

平文 vars は wrangler 設定の [vars] セクションが所有しており、API やダッシュボードで設定した値は次の wrangler deploy で上書きされて消えます。同期しても黙って失われるため、secret: false のエントリは理由を添えて警告・スキップします(Secrets はデプロイをまたいで保持されます)。

2. environments は別の Worker スクリプトに対応づける

wrangler は [env.staging]<script>-staging という別の Worker としてデプロイします。この慣習に合わせ、宣言した環境ごとに対応するスクリプトへ書き込みます。

宣言 送信先 Worker
environments 省略 my-worker
environments: [staging] my-worker-staging
environments: [production, staging] my-worker-productionmy-worker-staging

本番 Worker がベーススクリプトの場合は、認証 config の cloudflare.environments でマッピングを上書きできます。

3. スクリプト名は wrangler 設定からフォールバック解決する

CLOUDFLARE_SCRIPT_NAME → config の cloudflare.scriptwrangler.jsonc / wrangler.json / wrangler.tomlname の順で解決します。Vercel の .vercel/project.json フォールバックと対称的な設計です。

JSONC のコメント除去と TOML のトップレベル name 抽出は依存を増やさない簡易実装で、文字列リテラル内やテーブルヘッダ以降を拾わないことをテストで担保しています。

対応範囲

  • Sync(新規/更新の分類・確認プロンプト・dry-run)
  • prune(対象スクリプトの Worker Secrets のみ削除)
  • validate サブコマンド(GET のみの読み取り専用診断、401/403/404 の推定原因表示)
  • setup サブコマンドの対話プロンプト
  • モノレポ対応(cloudflare.scripts[] + --cloudflare-script
  • i18n(英日カタログ)
  • ドキュメント(README 英日・env-sync.yamldocs/architecture.md・usage)

テスト

gofmt / go vet / go test -race ./... すべて通過。544 件(従来 449 → 95 件追加)。

  • 純粋関数: スクリプト名解決・タスク展開・prune 計算・エラーボディ解析・wrangler 設定パース
  • 統合テスト(httptest): PUT/GET/DELETE のパス・認証ヘッダ・ボディ形式、success:false の扱い、prune、dry-run が書き込まないこと
  • config: ResolveCloudflareTargets の解決優先順位とバリデーション

実バイナリでの手動確認も実施済み(wrangler.toml からの名前解決、環境ごとの展開、config によるマッピング上書き、validate の各出力)。

補足

--dry-run でも既存シークレット取得のため GET を 1 回投げます(新規/更新の分類を表示するため)。Vercel provider と同じ挙動で、書き込みは行いません。

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features
    • Added Cloudflare Workers sync for Worker Secrets (syncs only secret: true; secret: false is warned and skipped).
    • Supports multiple Worker scripts, environment-based script mapping, and script selection via --cloudflare-script.
    • Added validate --provider cloudflare for read-only connectivity and configuration diagnostics.
  • Documentation
    • Updated English/Japanese READMEs, CLI help, configuration schema/templates, and architecture notes with Cloudflare setup, flags, and environment variables.
  • Tests
    • Added unit and integration test coverage for Cloudflare sync and validation flows.

Worker Secrets を REST API で同期する cloudflare provider を追加する。

- Sync: PUT /accounts/{id}/workers/scripts/{name}/secrets で登録し、
  既存一覧の GET で新規/更新を分類。prune は DELETE で削除する
- secret: false(平文 vars)は警告のうえスキップする。平文 vars は
  wrangler 設定の [vars] が所有しており、API で設定しても次の
  wrangler deploy で上書きされて消えるため
- environments は別 Worker スクリプト <script>-<環境名> に解決する
  (wrangler が [env.staging] を my-worker-staging としてデプロイする
  慣習に合わせる)。config の cloudflare.environments で上書き可能
- スクリプト名の解決順: CLOUDFLARE_SCRIPT_NAME > config の
  cloudflare.script > wrangler.jsonc/json/toml の name
- validate サブコマンド対応(GET のみの読み取り専用診断)
- setup サブコマンドに Cloudflare の対話プロンプトを追加
- モノレポ向けに cloudflare.scripts[] と --cloudflare-script を追加
- README(英日)・env-sync.yaml・docs/architecture.md・usage を更新

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Jul 26, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 1e5a7550-0eec-4e81-9c60-0b5261437ea1

📥 Commits

Reviewing files that changed from the base of the PR and between acbaeee and 3cc40c5.

📒 Files selected for processing (2)
  • internal/i18n/catalog_en.go
  • internal/provider/cloudflare/cloudflare_validate_test.go
🚧 Files skipped from review as they are similar to previous changes (1)
  • internal/i18n/catalog_en.go

📝 Walkthrough

Walkthrough

This change adds Cloudflare Workers as a synchronization provider, including configuration resolution, secret-only synchronization, pruning, validation, CLI integration, localization, setup support, tests, and English/Japanese documentation.

Changes

Cloudflare Workers support

Layer / File(s) Summary
Configuration and setup contracts
internal/config/..., internal/provider/provider.go, internal/i18n/keys.go
Adds Cloudflare target models, credential precedence, environment mappings, --cloudflare-script, interactive setup prompts, YAML generation, and validation tests.
Cloudflare secret synchronization
internal/provider/cloudflare/...
Registers the provider, resolves Worker scripts, synchronizes secrets through Cloudflare APIs, supports dry-run and pruning, and tests API, Wrangler fallback, and task expansion logic.
Cloudflare validation diagnostics
internal/provider/cloudflare/cloudflare_validate.go, internal/provider/cloudflare/cloudflare_validate_test.go, internal/i18n/catalog_*
Adds read-only reachability checks, source reporting, missing-field skips, HTTP cause diagnostics, localized messages, and validation tests.
CLI wiring and documentation
cmd/env-sync/main.go, README*, env-sync.yaml, docs/architecture.md, internal/i18n/catalog_*, internal/provider/provider_test.go
Registers Cloudflare, documents configuration and commands, updates schemas and environment variables, and extends provider registry coverage.

Estimated code review effort: 4 (Complex) | ~60 minutes

Sequence Diagram(s)

sequenceDiagram
  participant CLI
  participant CloudflareProvider
  participant CloudflareAPI
  CLI->>CloudflareProvider: Run sync or validate
  CloudflareProvider->>CloudflareAPI: Resolve or inspect Worker secrets
  CloudflareAPI-->>CloudflareProvider: Return names or HTTP status
  CloudflareProvider->>CloudflareAPI: PUT, DELETE, or GET requests
  CloudflareProvider-->>CLI: Report results and exit status
Loading
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 52.38% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed Cloudflare Workers を同期先プロバイダーとして追加する変更内容を簡潔に正しく要約しています。
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/cloudflare-workers-provider

Comment @coderabbitai help to get the list of available commands.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds Cloudflare Workers as a new sync destination provider for env-sync, integrating it into the existing provider registry/Provider interface and extending config, CLI flags, docs, i18n, and tests to support syncing Worker Secrets (secrets-only) with optional multi-script (monorepo) targeting.

Changes:

  • Add a new internal/provider/cloudflare provider implementing sync/prune and a read-only validate subcommand flow.
  • Extend config/CLI to support Cloudflare targets (including monorepo cloudflare.scripts[] and --cloudflare-script filtering) plus setup wizard prompts/output.
  • Update documentation, architecture notes, schema comments, and i18n catalogs; add extensive unit/integration tests for Cloudflare behavior.

Reviewed changes

Copilot reviewed 20 out of 20 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
README.md Documents Cloudflare Workers provider usage, behavior (secrets-only), and configuration options.
README.ja.md Japanese documentation updates for Cloudflare Workers provider usage and config.
internal/provider/provider.go Adds CloudflareScript option for monorepo target filtering.
internal/provider/provider_test.go Ensures Cloudflare provider is registered in the provider registry.
internal/provider/cloudflare/cloudflare.go Implements Cloudflare Workers Secrets sync/prune logic, wrangler fallback, and response handling.
internal/provider/cloudflare/cloudflare_validate.go Adds Cloudflare read-only validation (reachability/auth) via GET-only checks.
internal/provider/cloudflare/cloudflare_validate_test.go Tests validate output, exit behavior, and GET-only enforcement.
internal/provider/cloudflare/cloudflare_test.go Unit tests for pure helpers (name resolution, task expansion, parsing helpers, URL building).
internal/provider/cloudflare/cloudflare_integration_test.go Integration tests with httptest verifying request paths/methods and dry-run/prune behavior.
internal/i18n/keys.go Adds i18n message keys for Cloudflare config/setup/provider/validate flows.
internal/i18n/catalog_ja.go Adds Japanese translations and usage text for Cloudflare features and flags.
internal/i18n/catalog_en.go Adds English translations and usage text for Cloudflare features and flags.
internal/config/setup.go Extends interactive setup to optionally collect Cloudflare config and emit YAML.
internal/config/setup_test.go Adds tests for Cloudflare setup YAML generation and setup flow behaviors.
internal/config/config.go Parses --cloudflare-script CLI flag into provider options.
internal/config/appconfig.go Adds Cloudflare config schema, env/config resolution, and monorepo target resolution/validation.
internal/config/appconfig_test.go Tests Cloudflare target resolution precedence, filtering, and validations.
env-sync.yaml Updates schema comments and examples to include Cloudflare Workers semantics.
docs/architecture.md Updates architecture documentation to include Cloudflare provider and dependency notes.
cmd/env-sync/main.go Registers Cloudflare provider and updates command docs/comments for new provider.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +212 to +231
// トークン未設定チェック(per-target)
// 単一ターゲット時は即エラー返却。複数ターゲット時は失敗として記録して残りを継続する。
if !opts.DryRun && tgt.APIToken == "" {
if len(targets) == 1 {
return fmt.Errorf("%s", i18n.T(i18n.MsgCloudflareTokenMissingScript, tgt.Name))
}
fmt.Fprint(os.Stderr, i18n.T(i18n.MsgCloudflareTokenSkipScript, tgt.Name))
resolved = append(resolved, resolvedTarget{label: label, skipped: true})
continue
}
// script / accountId は dry-run でも解決できていないと表示すべき対象が定まらないためエラーにする。
if tgt.Script == "" {
return fmt.Errorf("%s", i18n.T(i18n.MsgCloudflareScriptMissing))
}
if !opts.DryRun && tgt.AccountID == "" {
return fmt.Errorf("%s", i18n.T(i18n.MsgCloudflareAccountIDMissing))
}

tasks := expandCloudflareTasks(secretEntries, tgt.Script, tgt.Environments)
scripts := taskScripts(tasks)
Comment thread internal/provider/provider_test.go Outdated
Comment thread README.md Outdated
| `GITHUB_TOKEN` | Yes (GitHub) | GitHub access token (not required for dry-run) |
| `GITHUB_REPO` | – (GitHub) | `owner/repo` format. Auto-detected from config file or `git remote origin` if unset |
| `CLOUDFLARE_API_TOKEN` | Yes (Cloudflare) | API token with the Workers Scripts:Edit permission (not required for dry-run) |
| `CLOUDFLARE_ACCOUNT_ID` | Yes (Cloudflare) | Target account ID. Falls back to `cloudflare.account_id` in the config file |
Comment thread README.ja.md Outdated
| `GITHUB_TOKEN` | ◯(GitHub) | GitHub アクセストークン(dry-run 時は不要) |
| `GITHUB_REPO` | –(GitHub) | `owner/repo` 形式。未指定なら config ファイルまたは `git remote origin` から自動取得 |
| `CLOUDFLARE_API_TOKEN` | ◯(Cloudflare) | Workers Scripts:Edit 権限を持つ API トークン(dry-run 時は不要) |
| `CLOUDFLARE_ACCOUNT_ID` | ◯(Cloudflare) | 対象アカウント ID。未指定なら config ファイルの `cloudflare.account_id` |
複数ターゲットのうち 1 つが API トークン未設定でスキップされた場合、
tasks を持たない resolvedTarget を積んでいたため送信フェーズの
totalNG に 0 が加算され、ターゲットを丸ごと同期できなかったのに
他ターゲットが成功すれば exit 0 になっていた。

スキップ時にも tasks を展開して件数を保持し、失敗として集計する。
syncOsExit を差し替え可能にして、この挙動を検証するテストを追加した
(修正前のコードでは exit 0 となりテストが落ちることを確認済み)。

あわせて PR レビューの指摘に対応:
- provider_test.go: cloudflare も検証するようになったテスト名を
  TestRegistry_ProvidersRegistered へリネーム
- README(英日): CLOUDFLARE_ACCOUNT_ID は dry-run 時には必須でない
  (ただし新規/更新の判定は表示されない)ことを明記

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (3)
internal/provider/cloudflare/cloudflare.go (1)

399-434: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

No context.Context is plumbed through the Cloudflare HTTP helpers, so every request uses http.NewRequest and trips the enabled noctx linter. Requests are bounded only by client.Timeout and cannot be cancelled.

  • internal/provider/cloudflare/cloudflare.go#L399-L434: accept a ctx in syncOneTarget and deleteSecrets and switch Lines 403 and 434 to http.NewRequestWithContext; do the same for fetchSecretNamesForScript at Line 477 and pass the context down from Sync.
  • internal/provider/cloudflare/cloudflare_validate.go#L128-L133: accept a ctx in checkAccess, use http.NewRequestWithContext at Line 129, and pass it from Validate.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/provider/cloudflare/cloudflare.go` around lines 399 - 434, Add
context.Context parameters to syncOneTarget, deleteSecrets,
fetchSecretNamesForScript, and checkAccess; pass the context from Sync and
Validate through all callers. Replace each helper’s http.NewRequest call with
http.NewRequestWithContext using that context, including the request creation
sites in cloudflare.go and cloudflare_validate.go.

Source: Linters/SAST tools

internal/provider/cloudflare/cloudflare_integration_test.go (1)

349-367: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Guard syncOsExit in every Sync test.

Tests that don't call captureSyncOsExit (this one, TestSync_PrunesUndefinedSecrets, TestSync_UsesWranglerFallbackForScriptName) will invoke the real os.Exit(1) and abort the whole test binary if any request unexpectedly fails, masking the actual assertion. Adding captureSyncOsExit(t) makes the failure observable.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/provider/cloudflare/cloudflare_integration_test.go` around lines 349
- 367, In every affected Sync test—TestSync_SkipsPlainVarsAndSyncsSecrets,
TestSync_PrunesUndefinedSecrets, and
TestSync_UsesWranglerFallbackForScriptName—call captureSyncOsExit(t) before
invoking cloudflareProvider.Sync so unexpected request failures are intercepted
instead of terminating the test process.
internal/i18n/catalog_ja.go (1)

361-362: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Drop the dead Cloudflare URL-build i18n keys. secretsURL has no failure path, and MsgCloudflareURLBuildFailOut / MsgCloudflareURLBuildFailInternal have no call sites; remove these entries from both catalogs and internal/i18n/keys.go.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/i18n/catalog_ja.go` around lines 361 - 362, Remove the unused
MsgCloudflareURLBuildFailOut and MsgCloudflareURLBuildFailInternal definitions
from both translation catalogs and from the key declarations in
internal/i18n/keys.go, leaving all remaining Cloudflare messages unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@internal/i18n/catalog_en.go`:
- Around line 228-233: Update the first line of MsgInitYAMLHeader to list GCP
and Cloudflare Workers alongside the existing platforms, matching the Japanese
catalog wording while preserving the remaining YAML documentation.

In `@internal/provider/cloudflare/cloudflare_validate_test.go`:
- Around line 203-206: Fix the token leak assertion in the validation test by
using a distinctive token value that cannot match the printed `token` label,
then assert that this exact value is absent from `got` while retaining the
expected `[set]` check. Update the existing test logic around the
strings.Contains calls without changing Validate behavior.

---

Nitpick comments:
In `@internal/i18n/catalog_ja.go`:
- Around line 361-362: Remove the unused MsgCloudflareURLBuildFailOut and
MsgCloudflareURLBuildFailInternal definitions from both translation catalogs and
from the key declarations in internal/i18n/keys.go, leaving all remaining
Cloudflare messages unchanged.

In `@internal/provider/cloudflare/cloudflare_integration_test.go`:
- Around line 349-367: In every affected Sync
test—TestSync_SkipsPlainVarsAndSyncsSecrets, TestSync_PrunesUndefinedSecrets,
and TestSync_UsesWranglerFallbackForScriptName—call captureSyncOsExit(t) before
invoking cloudflareProvider.Sync so unexpected request failures are intercepted
instead of terminating the test process.

In `@internal/provider/cloudflare/cloudflare.go`:
- Around line 399-434: Add context.Context parameters to syncOneTarget,
deleteSecrets, fetchSecretNamesForScript, and checkAccess; pass the context from
Sync and Validate through all callers. Replace each helper’s http.NewRequest
call with http.NewRequestWithContext using that context, including the request
creation sites in cloudflare.go and cloudflare_validate.go.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: c7e77f32-9297-4437-bf5b-069242c44fa2

📥 Commits

Reviewing files that changed from the base of the PR and between 6385e3f and acbaeee.

📒 Files selected for processing (20)
  • README.ja.md
  • README.md
  • cmd/env-sync/main.go
  • docs/architecture.md
  • env-sync.yaml
  • internal/config/appconfig.go
  • internal/config/appconfig_test.go
  • internal/config/config.go
  • internal/config/setup.go
  • internal/config/setup_test.go
  • internal/i18n/catalog_en.go
  • internal/i18n/catalog_ja.go
  • internal/i18n/keys.go
  • internal/provider/cloudflare/cloudflare.go
  • internal/provider/cloudflare/cloudflare_integration_test.go
  • internal/provider/cloudflare/cloudflare_test.go
  • internal/provider/cloudflare/cloudflare_validate.go
  • internal/provider/cloudflare/cloudflare_validate_test.go
  • internal/provider/provider.go
  • internal/provider/provider_test.go

Comment thread internal/i18n/catalog_en.go
Comment thread internal/provider/cloudflare/cloudflare_validate_test.go
PR レビュー(CodeRabbit)の指摘に対応する。

- catalog_en.go: MsgInitYAMLHeader の 1 行目が "Vercel / GitHub Actions"
  のままで、本文や日本語カタログと食い違っていたため GCP / Cloudflare
  Workers を追記して英日を揃えた
- cloudflare_validate_test.go: トークン漏洩の検証が機能していなかった。
  値 "tok" が出力ラベル "token" の部分文字列であるため第 1 条件が常に真、
  "[set]" は必ず出力されるため第 2 条件が常に偽となり、AND が恒偽で
  アサーションが一度も発火しなかった。衝突しないトークン値に変え、
  「値が出力されないこと」と「マスクラベルが出ること」を個別に検証する
  (意図的にトークンを出力させるとテストが落ちることを確認済み)

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@polidog
polidog merged commit 7633d40 into main Jul 26, 2026
2 checks passed
@polidog
polidog deleted the feat/cloudflare-workers-provider branch July 26, 2026 23:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants