Skip to content

feat: 環境変数ごとに同期先プロバイダーを指定可能にする (#11)#14

Merged
polidog merged 4 commits into
mainfrom
feat/11
Jun 26, 2026
Merged

feat: 環境変数ごとに同期先プロバイダーを指定可能にする (#11)#14
polidog merged 4 commits into
mainfrom
feat/11

Conversation

@polidog

@polidog polidog commented Jun 26, 2026

Copy link
Copy Markdown
Member

タスク

背景

現状の --provider vercel|github は CLI フラグで全変数に一律適用され、変数ごとに同期先を選べない。1 つの env-sync.yaml 内で「この変数は Vercel、この変数は GitHub」と混在指定できるようにした。

変更内容

  • env-sync.yamlvariables 各エントリと defaultsprovider フィールドを追加
    • 値は vercel / github、または両方へ送る配列 [vercel, github] を許可
  • 解決優先順位(高い順): 変数個別の providerdefaults.provider → CLI --provider フラグ
  • 1 回の実行で複数プロバイダーへ振り分けて同期
  • --dry-run の一覧出力に各 key の同期先プロバイダーを表示
  • 不正な provider 値(vercel/github 以外)はエラーで中止
  • README と env-sync.yaml のコメントに provider フィールドの説明と混在指定の例を追記

完了条件

  • env-sync.yaml の各変数および defaultsprovider: vercel / provider: github / provider: [vercel, github] が宣言できる。
  • 変数個別 > defaults > CLI --provider の優先順位で同期先が解決される。
  • provider をどこにも書かない既存の env-sync.yaml は従来どおり --provider フラグ(デフォルト vercel)で動作する(後方互換)。
  • 1 回の実行で Vercel 向け・GitHub 向けの変数が混在していても、それぞれ正しいプロバイダーへ同期される。
  • 不正な provider 値を指定するとエラーメッセージを出して中止する。
  • --dry-run で各 key の同期先プロバイダーが確認できる。
  • README と env-sync.yaml コメントに provider フィールドの説明・例が追記されている。
  • 追加・変更したロジックに対応するテストが追加され、go test ./... が通る。

動作確認

  • lint / build 通過
  • 追加テスト通過(ProviderVal YAML パース / provider 解決ロジック各種)
  • 既存テスト全通過(go test ./...
  • 後方互換確認(provider 未指定時は CLI --provider のデフォルト vercel)

主な実装ポイント

  • ProviderVal 型: YAML で string / []string を透過的に扱うカスタム UnmarshalYAML
  • Entry.Providers []string: 解決済みプロバイダーリストを保持
  • resolveEntriescliProvider string 引数を追加しプロバイダー解決・検証を実装
  • run() で dry-run 時に統合一覧( KEY -> vercel, github)を表示して早期終了
  • run()providerEntries マップに振り分け、登録順で各 Sync() を呼び出し

env-sync.yaml の variables 各エントリと defaults に provider フィールドを追加。
provider: vercel / github / [vercel, github] を許可し、1 回の実行で複数プロバイダーへ
振り分け同期できるようにした。解決優先順位は varConf > defaults > CLI --provider。

- ProviderVal 型を追加(YAML で string / []string 両対応)
- Entry.Providers フィールドを追加してプロバイダー情報を保持
- resolveEntries に cliProvider 引数を追加しプロバイダー解決・検証を実装
- run() で dry-run 時に統合一覧(key -> provider)を表示して早期終了
- run() でプロバイダーごとにエントリを振り分け Sync() を呼び出すよう変更
- 不正な provider 値はエラーで中止
- README と env-sync.yaml にドキュメントを追記

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

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

env-sync.yaml の各変数ごとに同期先プロバイダー(Vercel / GitHub / 両方)を指定できるようにし、1 回の実行で複数プロバイダーへ振り分けて同期できるようにする PR です。CLI の --provider から YAML 設定(変数個別 / defaults)へ解決優先順位を拡張しています。

Changes:

  • YAML スキーマに provider(string / []string)を追加し、優先順位(var > defaults > CLI)で解決
  • run() で Entry を provider ごとに振り分けて、登録順に各 Provider へ Sync
  • README / env-sync.yaml コメントに provider 混在指定の例と説明を追記、関連テストを追加

Reviewed changes

Copilot reviewed 8 out of 8 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
README.md provider フィールドの説明と混在指定例を追記
main.go provider ごとに振り分けて同期、dry-run 出力の変更
init_test.go resolveEntries の新シグネチャに追従
env-sync.yaml provider フィールドの説明コメントと例を追記
entry.go provider 解決・検証ロジックを resolveEntries に追加し Entry.Providers を導入
entry_test.go provider 解決ロジックのユニットテストを追加
config.go YAML の provider を string / []string で扱う ProviderVal を追加
config_test.go ProviderVal の YAML パーステストを追加

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

Comment thread entry.go
Comment on lines +58 to +76
// provider の解決: varConf.Provider → defaults.Provider → CLI フラグ
var providers []string
if def.Defaults.Provider != nil {
providers = def.Defaults.Provider.Values
}
if conf.Provider != nil {
providers = conf.Provider.Values
}
if len(providers) == 0 {
providers = []string{cliProvider}
}

// provider 値の検証
for _, p := range providers {
if !isRegisteredProvider(p) {
names := strings.Join(registeredProviderNames(), " / ")
return nil, fmt.Errorf("%s: 不正な provider 値 %q(%s のいずれかを指定してください)", key, p, names)
}
}
Comment thread main.go
Comment on lines +122 to +134
// ---- dry-run: 統合一覧表示して終了 ----
if opts.dryRun {
if len(entries) == 0 {
fmt.Println("登録対象がありません")
} else {
fmt.Printf("同期対象 %d 件:\n", len(entries))
for _, e := range entries {
fmt.Printf(" %s -> %s\n", e.Key, strings.Join(e.Providers, ", "))
}
}
fmt.Println("\n[dry-run] 送信しません")
return nil
}
Comment thread entry_test.go
Comment on lines +245 to +293
// provider に両方指定できる
func TestResolveEntries_ProviderBoth(t *testing.T) {
pv := &ProviderVal{Values: []string{"vercel", "github"}}
def := definition{
Variables: map[string]varConf{
"FOO": {Provider: pv},
},
}
envVars := map[string]string{"FOO": "bar"}
entries, err := resolveEntries(def, envVars, []string{"FOO"}, "vercel")
if err != nil {
t.Fatalf("resolveEntries エラー: %v", err)
}
if len(entries[0].Providers) != 2 {
t.Fatalf("Providers len = %d, want 2", len(entries[0].Providers))
}
}

// 不正な provider 値はエラーを返す
func TestResolveEntries_ProviderInvalid(t *testing.T) {
pv := &ProviderVal{Values: []string{"gitlab"}}
def := definition{
Variables: map[string]varConf{
"FOO": {Provider: pv},
},
}
envVars := map[string]string{"FOO": "bar"}
_, err := resolveEntries(def, envVars, []string{"FOO"}, "vercel")
if err == nil {
t.Error("不正な provider 値でエラーが返らなかった")
}
}

// provider が YAML に無ければ CLI フラグのデフォルトが使われる
func TestResolveEntries_ProviderFallbackToCLI(t *testing.T) {
def := definition{
Variables: map[string]varConf{
"FOO": {},
},
}
envVars := map[string]string{"FOO": "bar"}
entries, err := resolveEntries(def, envVars, []string{"FOO"}, "github")
if err != nil {
t.Fatalf("resolveEntries エラー: %v", err)
}
if len(entries[0].Providers) != 1 || entries[0].Providers[0] != "github" {
t.Errorf("Providers = %v, want [github]", entries[0].Providers)
}
}
Copilot レビューの指摘に対応:
- entry.go: [vercel, vercel] のような重複 provider を deduplicateProviders で排除し二重 Sync を防止
- entry.go: provider: [] の明示的空配列はフォールバックせずエラーを返すよう変更
- main.go: --dry-run の出力に secret と environments 情報を追加(key のみだった表示を拡充)
- entry_test.go: 重複 provider 排除・空配列エラーのテストを追加

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

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 8 out of 8 changed files in this pull request and generated 3 comments.

Comment thread entry.go
Comment on lines +58 to +76
// provider の解決: varConf.Provider → defaults.Provider → CLI フラグ
var providers []string
if def.Defaults.Provider != nil {
providers = def.Defaults.Provider.Values
}
if conf.Provider != nil {
providers = conf.Provider.Values
}
if len(providers) == 0 {
providers = []string{cliProvider}
}

// provider 値の検証
for _, p := range providers {
if !isRegisteredProvider(p) {
names := strings.Join(registeredProviderNames(), " / ")
return nil, fmt.Errorf("%s: 不正な provider 値 %q(%s のいずれかを指定してください)", key, p, names)
}
}
Comment thread main.go
Comment on lines +136 to +137
// ---- プロバイダーへ同期(登録順) ----
for _, pname := range registeredProviderNames() {
Comment thread entry_test.go
Comment on lines +278 to +293
// provider が YAML に無ければ CLI フラグのデフォルトが使われる
func TestResolveEntries_ProviderFallbackToCLI(t *testing.T) {
def := definition{
Variables: map[string]varConf{
"FOO": {},
},
}
envVars := map[string]string{"FOO": "bar"}
entries, err := resolveEntries(def, envVars, []string{"FOO"}, "github")
if err != nil {
t.Fatalf("resolveEntries エラー: %v", err)
}
if len(entries[0].Providers) != 1 || entries[0].Providers[0] != "github" {
t.Errorf("Providers = %v, want [github]", entries[0].Providers)
}
}
Copilot ラウンド 2 の指摘に対応:
- 同期対象が 0 件の場合、dry-run 以外の通常実行でも何も出力されずに正常終了していた
- 0 件チェックを dry-run 分岐の前に統合し、明示的なメッセージを出して早期 return

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

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 8 out of 8 changed files in this pull request and generated 2 comments.

Comment thread entry.go
Comment on lines +74 to +78
if len(providers) == 0 {
providers = []string{cliProvider}
}
// [vercel, vercel] のような重複指定で二重 Sync にならないよう排除する
providers = deduplicateProviders(providers)
Comment thread entry.go
Comment on lines +60 to +66
if def.Defaults.Provider != nil {
if len(def.Defaults.Provider.Values) == 0 {
names := strings.Join(registeredProviderNames(), " / ")
return nil, fmt.Errorf("defaults.provider に空配列が指定されています(%s のいずれかを指定してください)", names)
}
providers = def.Defaults.Provider.Values
}

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 8 out of 8 changed files in this pull request and generated 1 comment.

Comment thread entry.go
Comment on lines +74 to +78
if len(providers) == 0 {
providers = []string{cliProvider}
}
// [vercel, vercel] のような重複指定で二重 Sync にならないよう排除する
providers = deduplicateProviders(providers)
- provider: " " など空白のみの指定が deduplicateProviders() 後に空になった場合、
  静かに落ちずエラーを返すよう修正
- defaults.provider の不正値を、varConf による上書きの有無にかかわらず
  resolveEntries の先頭で事前検証するよう修正(不正値は即エラー)
- 各ケースのユニットテストを追加

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
@polidog polidog merged commit d0863c0 into main Jun 26, 2026
1 check passed
@polidog polidog deleted the feat/11 branch June 26, 2026 14:46
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