Skip to content

feat: GCP Secret Manager プロバイダーを追加 (#16)#21

Merged
polidog merged 3 commits into
mainfrom
feat/16
Jun 26, 2026
Merged

feat: GCP Secret Manager プロバイダーを追加 (#16)#21
polidog merged 3 commits into
mainfrom
feat/16

Conversation

@polidog

@polidog polidog commented Jun 26, 2026

Copy link
Copy Markdown
Member

タスク

  • internal/provider/gcp/gcp.gogcpProvider struct を実装し、Provider interface(Name() string, Sync(opts Options, entries []Entry) error)を満たす
  • init() 関数内で RegisterProvider("gcp", ...) を呼び出し自己登録
  • cmd/env-sync/main.go_ "github.com/ptyhard/env-sync/internal/provider/gcp" を blank import して registry に登録
  • 認証: Application Default Credentials(ADC)を使用
  • 必須環境変数: GCP_PROJECT_ID(Secret Manager の対象 GCP プロジェクト ID)
  • Entry.Secret == true の場合は Secret Manager に secret として作成・更新(バージョン追加)
  • Entry.Secret == false のエントリはスキップしてログ出力
  • Entry.Environments が指定された場合、labels {"environment": "<env>"} を secret に付与
  • opts.DryRun == true の場合は API 呼び出しをスキップしてログのみ出力
  • go.mod / go.sumcloud.google.com/go/secretmanager 依存を追加
  • env-sync.yaml サンプルに provider: gcp の使用例を追記

完了条件

  • env-sync.yamlprovider: gcp または provider: [vercel, gcp] と設定したとき、GCP Secret Manager に環境変数が同期される
  • GCP_PROJECT_ID が未設定の場合、明示的なエラーメッセージが表示される
  • Entry.Secret == false のエントリはスキップされ、警告ログが出力される
  • --dry-run フラグ使用時は API 呼び出しなしで対象キー一覧がログ出力される
  • go test ./... がすべてパスする(internal/provider/gcp/gcp_test.go を含む)
  • go build ./... がエラーなしで通る

動作確認

  • lint / build 通過 (go build ./...)
  • 自動テスト生成・通過 (go test ./... 全 7 パッケージ PASS)

品質チェック結果

特記事項なし

テスト内容

internal/provider/gcp/gcp_test.go に以下のテストを追加:

  • TestBuildLabels_*: labels 組み立てロジックのユニットテスト(空・1件・複数)
  • TestSyncSecret_*: secret の新規作成・既存更新・エラーハンドリングのテスト
  • TestSync_MissingProjectID: GCP_PROJECT_ID 未設定時のエラー確認
  • TestSync_DryRun: dry-run 時の API 非呼び出し確認
  • TestSync_SkipsNonSecretEntries: secret=false エントリのスキップ確認
  • TestSync_SecretCreatedAndVersionAdded: 正常系 E2E テスト
  • TestGCPProvider_Registered: registry への自動登録確認

実装方針

  • secretManagerClient インターフェースを定義してテスタビリティを確保(export_test.goSwapClientFunc を公開)
  • realClient ラッパーが本物の *secretmanager.Client を wrap してインターフェースに適合させる
  • newClientFunc 変数でクライアント生成をテストで差し替え可能にする
  • secret が存在しない場合は CreateSecret(labels 付き)→ AddSecretVersion
  • secret が既存の場合は UpdateSecret(environments ある時のみ)→ AddSecretVersion

- internal/provider/gcp/gcp.go: gcpProvider を実装し Provider interface を満たす
- init() で RegisterProvider("gcp", ...) を呼び自己登録する
- 認証: Application Default Credentials (ADC) を使用
- 必須環境変数: GCP_PROJECT_ID
- Entry.Secret == false のエントリはスキップしてログ出力
- Entry.Environments が指定された場合、labels {"environment": "<env>"} を付与
- opts.DryRun == true の場合は API 呼び出しをスキップしてログのみ出力
- cmd/env-sync/main.go に blank import を追加
- go.mod / go.sum に cloud.google.com/go/secretmanager 依存を追加
- env-sync.yaml サンプルに provider: gcp の使用例を追記

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MUNfaHdh77j6ZEA2v8oieH

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

GCP Secret Manager へ .env の値を同期できるようにするため、新しい gcp provider を追加し、registry 登録・依存追加・サンプル設定を更新する PR です。

Changes:

  • internal/provider/gcp に Secret Manager 同期ロジック(作成/ラベル更新/バージョン追加)を実装し、自動登録を追加
  • GCP provider のユニット/結合テストを追加し、依存関係(Secret Manager SDK など)を go.mod/go.sum に追加
  • cmd/env-sync で gcp provider を blank import し、env-sync.yaml サンプルに使用例を追記

Reviewed changes

Copilot reviewed 6 out of 7 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
internal/provider/gcp/gcp.go GCP Secret Manager への同期処理(secret の作成/更新、labels、バージョン追加)を追加
internal/provider/gcp/gcp_test.go buildLabels / syncSecret / Sync のテストを追加
internal/provider/gcp/export_test.go テスト用の差し替え・エクスポート補助を追加
cmd/env-sync/main.go gcp provider を registry 登録するための blank import を追加
env-sync.yaml provider: gcp の利用例と説明を追記
go.mod Secret Manager SDK 等の依存を追加
go.sum 依存追加に伴うチェックサム更新

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

return nil
}

ctx := context.Background()
Comment on lines +163 to +174
} else if len(labels) > 0 {
// 既存 secret の labels を更新する
if _, err = client.UpdateSecret(ctx, &secretmanagerpb.UpdateSecretRequest{
Secret: &secretmanagerpb.Secret{
Name: secretName,
Labels: labels,
},
UpdateMask: &fieldmaskpb.FieldMask{Paths: []string{"labels"}},
}); err != nil {
return fmt.Errorf("Secret のラベル更新に失敗: %s", err)
}
}
Comment thread env-sync.yaml Outdated
# ※ GitHub の named environment は事前に作成が必要
#
# provider: vercel|github|[vercel, github]
# provider: vercel|github|gcp|[vercel, gcp]
Comment thread env-sync.yaml
# 必須環境変数: GCP_PROJECT_ID(対象 GCP プロジェクト ID)
# 認証: Application Default Credentials(GOOGLE_APPLICATION_CREDENTIALS または ADC 自動解決)
# secret: true のエントリのみ同期(false はスキップ)
# environments に値を設定すると、Secret に labels {"environment": "<env>"} が付与される
Comment thread cmd/env-sync/main.go
Comment on lines +45 to 47
_ "github.com/ptyhard/env-sync/internal/provider/gcp"
_ "github.com/ptyhard/env-sync/internal/provider/github"
_ "github.com/ptyhard/env-sync/internal/provider/vercel"
- GCP provider に確認プロンプト(--yes フラグ未指定時)を追加し、vercel/github と挙動を統一
- env-sync.yaml のコメントに複数 provider 指定例([vercel, github])と environments 複数時の挙動を追記
- cmd/env-sync/main.go の usage に GCP_PROJECT_ID / ADC 認証説明を追加

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MUNfaHdh77j6ZEA2v8oieH

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

Copilot reviewed 6 out of 7 changed files in this pull request and generated 2 comments.

Comment thread internal/provider/gcp/gcp.go Outdated
if !config.IsTTY(os.Stdin) {
return fmt.Errorf("対話できない環境です。確認をスキップするには --yes を付けてください")
}
fmt.Print("上記を GCP Secret Manager に登録します(既存は上書き)。続行しますか? (y/N) ")
Comment thread internal/provider/gcp/gcp.go Outdated
Comment on lines +173 to +179
if _, err = client.CreateSecret(ctx, &secretmanagerpb.CreateSecretRequest{
Parent: parent,
SecretId: e.Key,
Secret: secret,
}); err != nil {
return fmt.Errorf("Secret の作成に失敗: %s", err)
}
- 確認プロンプトの文言を「既存は上書き」から「新しいバージョンとして追加」に修正
  (Secret Manager はバージョン追加であり上書きではないため)
- CreateSecret の AlreadyExists エラーを吸収し、並行実行・再実行時の競合を安全に処理

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MUNfaHdh77j6ZEA2v8oieH

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

Copilot reviewed 6 out of 7 changed files in this pull request and generated 1 comment.

Comment on lines +185 to +196
} else if len(labels) > 0 {
// 既存 secret の labels を更新する
if _, err = client.UpdateSecret(ctx, &secretmanagerpb.UpdateSecretRequest{
Secret: &secretmanagerpb.Secret{
Name: secretName,
Labels: labels,
},
UpdateMask: &fieldmaskpb.FieldMask{Paths: []string{"labels"}},
}); err != nil {
return fmt.Errorf("Secret のラベル更新に失敗: %s", err)
}
}
@polidog polidog merged commit 9b497a9 into main Jun 26, 2026
2 checks passed
@polidog polidog deleted the feat/16 branch June 26, 2026 23:18
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