Skip to content

feat: 複数 Vercel プロジェクト・複数 GitHub リポジトリ(モノレポ)対応 (#27)#28

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

feat: 複数 Vercel プロジェクト・複数 GitHub リポジトリ(モノレポ)対応 (#27)#28
polidog merged 4 commits into
mainfrom
feat/27

Conversation

@polidog

@polidog polidog commented Jun 27, 2026

Copy link
Copy Markdown
Member

タスク

turborepo などのモノレポ構成では 1 リポジトリに複数アプリが存在し、Vercel プロジェクトや GitHub リポジトリも複数になる場合がある。現状は単一ターゲットにしか同期できなかったため、複数ターゲットへ環境変数を配れるようにする。

変更内容

config (internal/config/appconfig.go)

  • vercel.projects: [{ name, project_id, team_id, token? }] / github.repos: [{ name, repo, token? }] を追加
  • ResolveVercelTargets(selectName) / ResolveGitHubTargets(selectName) を追加。解決済みターゲットのスライスを返す
  • token/team_id は per-target を最優先し、空ならトップレベル config・環境変数へフォールバック
  • マージ方針: project 側が非空なら global を置き換え(要素マージはしない)
  • ${VAR} / ${VAR:-default} 展開を per-target フィールドにも適用
  • 入力バリデーション: 複数定義時の project_id/repo 必須、name 必須・重複検出、projects/repos 未定義時に --vercel-project/--github-repo 指定はエラー

CLI (internal/config/config.go, internal/provider/provider.go)

  • --vercel-project <name> / --github-repo <name> を追加(特定ターゲットのみ同期 / 未指定なら全件)

provider (vercel.go, github.go)

  • Sync を複数ターゲットのループへリファクタ。ターゲットごとに「対象プロジェクト/リポジトリ」見出しと新規/更新分類を表示
  • 1 ターゲット失敗でも残りを試行し、ループ後に失敗集約して os.Exit 判定(Vercel/GitHub 統一)
  • 分類結果を再利用して既存確認 API の二重呼び出しを回避(GitHub の存在確認最適化を復元)
  • .vercel/project.json / git remote フォールバックは単一ターゲット時のみ

ドキュメント

  • README に「複数 Vercel プロジェクト / 複数 GitHub リポジトリ(モノレポ)」セクション追加、オプション表更新
  • env-sync.yaml コメント・--help usage に設定例を追記

完了条件

Vercel

  • config で複数 Vercel プロジェクトを定義できる
  • 既存の単一プロジェクト設定(project_id/team_id/token、環境変数、.vercel/project.json)が従来どおり動作
  • 定義された全プロジェクト(または --vercel-project 指定分)へ同期

GitHub

  • config で複数 GitHub リポジトリを定義できる
  • 既存の単一リポジトリ設定(github.repo、GITHUB_REPO、git remote フォールバック)が従来どおり動作
  • 定義された全リポジトリ(または --github-repo 指定分)へ同期

共通

  • 各ターゲットごとに対象名・新規/更新分類一覧を表示
  • 1 ターゲット失敗時の挙動が Vercel・GitHub で統一・集約表示
  • gcp provider の挙動は不変
  • ユニットテストあり、go test ./... green
  • README / config コメントに複数ターゲットの設定例

動作確認

  • lint / build 通過(gofmt -l 差分なし / go vet ./... / go build ./...
  • 設計・計画適合レビュー通過(Critical なし)
  • 自動テスト生成・通過(失敗集約の統合テスト追加)
  • コード品質チェック通過(Critical なし)
  • 受け入れテスト通過(dry-run で後方互換/複数対応/絞り込み/バリデーションを確認)

受け入れテスト結果(dry-run)

  • Vercel 単一後方互換 → 1 件表示 / 複数 projects 2件 → 2 件表示 / --vercel-project admin → 1 件 / 存在しない名前 → エラー
  • GitHub GITHUB_REPO のみ → 1 件 / 複数 repos 2件 → 2 件表示 / --github-repo infra → 1 件 / 重複 name → エラー

品質チェック結果

🟡 Warning(PR 進行可・既存パターン踏襲)

  • provider をまたいだ失敗集約は provider 単位にとどまる。Vercel・GitHub 両方が対象のとき、Vercel の Sync が失敗で os.Exit(1) すると GitHub が実行されない。これは本 PR 以前からの既存挙動(各 Sync が直接 os.Exit)の踏襲であり、本 PR の新規退行ではない。provider 横断での集約が必要なら別途 os.Exitrun() 側へ寄せるリファクタが必要。

🔵 Info

  • 複数ターゲット時は新規のみでも確認プロンプトを出す(安全側の意図的な挙動)
  • --provider gcp 時は --vercel-project/--github-repo を無視(GCP にモノレポ対応がないため妥当)

パフォーマンス診断(静的解析・非ブロッキング)

  • 中: Vercel の分類 API(vercelFetchExistingKeys)は upsert のため送信に使われず表示専用。確認・表示が不要な経路ではターゲット数分の API 呼び出しを省略できる余地(将来最適化)
  • GitHub の分類結果再利用・公開鍵キャッシュのターゲット単位分離・items/tasks の共有はいずれも適切
  • ループ計算量は O(ターゲット数 × 変数数) で本質的に必要なネスト

🤖 Generated with Claude Code

https://claude.ai/code/session_01MUNfaHdh77j6ZEA2v8oieH

config に vercel.projects[] / github.repos[] を追加し、1 つの定義ファイルから
複数の Vercel プロジェクト・複数の GitHub リポジトリへ一括同期できるようにした。

- config: VercelProjectConf/GitHubRepoConf 型と ResolveVercelTargets/
  ResolveGitHubTargets を追加。token/team_id は per-target を最優先し、
  空ならトップレベル config・環境変数へフォールバック
- マージ: project 側が非空なら global を置き換え
- CLI: --vercel-project / --github-repo で対象を 1 件に絞り込み
- provider: Sync を複数ターゲットのループへリファクタし、ターゲットごとに
  見出し・新規/更新分類を表示。1 ターゲット失敗でも残りを試行し、ループ後に
  失敗集約して os.Exit 判定(Vercel/GitHub 統一)
- 分類結果を再利用し既存確認 API の二重呼び出しを回避
- バリデーション: 複数定義時の project_id/repo 必須、name 必須・重複検出、
  projects/repos 未定義時の selectName 指定はエラー
- 後方互換: 単一 project_id/repo・環境変数・.vercel/project.json・git remote
  フォールバックは従来どおり単一ターゲットとして動作
- README / env-sync.yaml コメント / usage に設定例を追記
- ユニット・統合テスト追加(go test -race ./... green)

Co-Authored-By: Claude Opus 4.8 (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

モノレポ構成で「複数 Vercel プロジェクト」「複数 GitHub リポジトリ」へ 1 回の実行で環境変数を同期できるようにするため、config スキーマ拡張とターゲット解決 API、CLI 絞り込みフラグ、provider の複数ターゲット送信ループ化、関連ドキュメント/テスト追加を行う PR です。

Changes:

  • config に vercel.projects[] / github.repos[] を追加し、ResolveVercelTargets / ResolveGitHubTargets で解決済みターゲット配列を返すように変更
  • CLI に --vercel-project / --github-repo を追加し、provider 側を複数ターゲット同期のループへリファクタ
  • README / env-sync.yaml コメント / --help usage とテストを拡充(複数ターゲット挙動と失敗集約関連のテスト追加)

Reviewed changes

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

Show a summary per file
File Description
README.md 複数ターゲット(モノレポ)設定例と CLI オプション表を追記
env-sync.yaml 複数同期先は認証 config 側で定義する旨をコメント追記
cmd/env-sync/main.go --vercel-project / --github-repo の usage とスキーマ例を追記
internal/config/appconfig.go projects/repos スキーマ追加、マージ方針、ターゲット解決・env ref 展開の適用範囲拡張
internal/config/appconfig_test.go projects/repos のロード・マージ・解決・展開・バリデーションのテスト追加
internal/config/config.go --vercel-project / --github-repo のフラグパース追加
internal/config/config_test.go 新規フラグのパーステスト追加
internal/provider/provider.go provider.Options にターゲット絞り込み用フィールドを追加
internal/provider/vercel/vercel.go 複数 Vercel ターゲットの一覧表示・確認・送信ループ化、送信処理の関数分離
internal/provider/vercel/vercel_test.go syncOneVercelTarget の送信/集計/継続性テスト追加
internal/provider/github/github.go 複数 GitHub ターゲットの一覧表示・確認・送信ループ化、owner/repo 解決と送信処理の関数分離
internal/provider/github/github_integration_test.go syncOneGitHubTarget の token 反映・継続性・存在確認二重呼び出し防止のテスト追加

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

Comment on lines +242 to +247
pfields := []*string{
&cfg.Vercel.Projects[i].Token,
&cfg.Vercel.Projects[i].ProjectID,
&cfg.Vercel.Projects[i].TeamID,
}
for _, f := range pfields {
Comment on lines +256 to +260
rfields := []*string{
&cfg.GitHub.Repos[i].Token,
&cfg.GitHub.Repos[i].Repo,
}
for _, f := range rfields {
Comment on lines +95 to +99
for i, tgt := range targets {
// トークン未設定チェック(per-target)
if !opts.DryRun && tgt.Token == "" {
return fmt.Errorf("VERCEL_TOKEN が未設定です(プロジェクト %q: 環境変数 VERCEL_TOKEN または config ファイルの token で指定してください)", tgt.Name)
}
Comment on lines +162 to 166
// per-target トークンを決定
targetToken := tgt.Token
if !opts.DryRun && targetToken == "" {
return fmt.Errorf("GITHUB_TOKEN が未設定です(リポジトリ %q: 環境変数 GITHUB_TOKEN または config ファイルの token で指定してください)", tgt.Name)
}
Copilot レビュー指摘対応(ラウンド1):
- vercel.go: 複数 targets 時に token 未設定ターゲットを tokenMissing フラグで記録し、
  残りターゲットを継続。送信フェーズで失敗として集計する
- github.go: 複数 targets 時に token 未設定ターゲットを skipped フラグで記録し、
  残りターゲットを継続。送信フェーズで失敗として集計する
- 単一ターゲット時は従来通り即エラー返却

Co-Authored-By: Claude Sonnet 4.6 <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 12 out of 12 changed files in this pull request and generated 3 comments.

Comment thread internal/config/appconfig.go Outdated
return cfg.GitHub.Repo
}

// resolveVercelToken はトークンの解決優先順位(per-target > top-level config > 環境変数)を実装する。
Comment thread internal/config/appconfig.go Outdated
return cfg.ResolveVercelToken()
}

// resolveVercelTeamID はチーム ID の解決優先順位(per-target > top-level config > 環境変数)を実装する。
Comment thread internal/config/appconfig.go Outdated
return cfg.ResolveVercelTeamID()
}

// resolveGitHubToken はトークンの解決優先順位(per-target > top-level config > 環境変数)を実装する。
Copilot レビュー指摘対応(ラウンド2):
- resolveVercelToken / resolveVercelTeamID / resolveGitHubToken のコメントが
  「per-target > top-level config > 環境変数」と誤記していたのを
  「per-target > 環境変数 > top-level config」(実装と一致)に修正

Co-Authored-By: Claude Sonnet 4.6 <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 12 out of 12 changed files in this pull request and generated 4 comments.

Comment thread internal/config/appconfig.go Outdated
Comment on lines +372 to +377
// name の必須チェックと重複チェック(selectName の有無によらず常に実施)
if err := validateVercelProjectNames(cfg.Vercel.Projects); err != nil {
return nil, err
}

var targets []VercelTarget
Comment thread internal/config/appconfig.go Outdated
Comment on lines +431 to +436
// name の必須チェックと重複チェック(selectName の有無によらず常に実施)
if err := validateGitHubRepoNames(cfg.GitHub.Repos); err != nil {
return nil, err
}

var targets []GitHubTarget
Comment thread internal/provider/vercel/vercel.go Outdated
Comment on lines 179 to 183
if len(targets) > 1 {
fmt.Printf("上記を Vercel の %d プロジェクトに登録します(既存は上書き)。続行しますか? (y/N) ", len(targets))
} else {
fmt.Print("上記を Vercel に登録します(既存は上書き)。続行しますか? (y/N) ")
}
Comment thread internal/provider/github/github.go Outdated
Comment on lines 244 to 248
if len(resolved) > 1 {
fmt.Printf("上記を GitHub の %d リポジトリに登録します(既存は上書き)。続行しますか? (y/N) ", len(resolved))
} else {
fmt.Print("上記を GitHub に登録します。続行しますか? (y/N) ")
}
Copilot レビュー指摘対応(ラウンド3):
- appconfig.go: validateVercelProjectConfs / validateGitHubRepoConfs を追加し、
  projects/repos が定義されている場合は絞り込み後の件数に関わらず
  全エントリの project_id / repo を必須チェックする
  (--vercel-project / --github-repo で 1 件に絞っても .vercel/project.json や
  git remote フォールバックが動かないようにする)
- vercel.go / github.go: 送信フェーズの複数ターゲット表示・全体完了メッセージを
  activeCount / activeResolved(スキップ除外数)で判定するよう修正
- 確認プロンプトの件数表示を実際に送信されるターゲット数に合わせる
- 新規バリデーションのテストを追加(project_id 空・repo 空、絞り込み時も全エントリ検証)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MUNfaHdh77j6ZEA2v8oieH
@polidog polidog merged commit 3594fba into main Jun 27, 2026
1 check passed
@polidog polidog deleted the feat/27 branch June 27, 2026 08: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