Conversation
- Vercel: GET /v10/projects/{id}/env で既存 key を取得し新規/更新を分類
- GitHub: secret/variable ごとに API で存在確認して新規/更新を分類
- 確認プロンプトは更新(上書き)対象がある場合のみ表示する(新規のみなら即送信)
- dry-run でもトークンがあれば新規/更新を分類して表示する
- 非対話環境で更新対象かつ --yes 未指定の場合はエラー停止し --yes を案内
- 分類ロジックは純粋関数(classifyVercelItems / classifyGitHubTasksByExistence)に切り出しユニットテストを追加
- README と printUsage を新挙動に更新
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WdeTedorn5WqsyAJt4bybB
There was a problem hiding this comment.
Pull request overview
送信前に各 provider へ問い合わせてキーの「新規/更新」を分類し、更新がある場合のみ確認プロンプトを出すことで、同期時の安全性と視認性を上げる PR です。
Changes:
- Vercel/GitHub ともに既存キーを照会して新規/更新を分類し、一覧表示に反映
- 更新がある場合のみ確認プロンプトを表示し、
--yes/-yでスキップ可能に変更 --dry-runでも(トークンがあれば)分類して表示する挙動に更新し、README/usage を追従
Reviewed changes
Copilot reviewed 6 out of 6 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| README.md | 新規/更新表示・確認条件・dry-run の挙動をドキュメント化 |
| internal/provider/vercel/vercel.go | 既存 key 取得・分類・更新時のみ確認のロジックを追加 |
| internal/provider/vercel/classify_test.go | Vercel 側の分類/件数カウント/既存取得に関するテストを追加 |
| internal/provider/github/github.go | Secret の存在確認 API 追加、分類/件数カウント、更新時のみ確認に対応 |
| internal/provider/github/classify_test.go | GitHub 側の分類/件数カウントのテストを追加 |
| cmd/env-sync/main.go | dry-run の早期 return を廃止し provider 側で dry-run 表示を行うよう変更、usage 更新 |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Comment on lines
+127
to
+142
| origBase := apiBase | ||
| // テスト用に apiBase を差し替える(package レベル変数を経由しないため直接書き換え) | ||
| // vercel.go の apiBase は const なので httptest URL を直接 projectID に組み込む | ||
| _ = origBase // suppress unused warning | ||
|
|
||
| client := &http.Client{} | ||
| // テスト用 URL を使うため vercelFetchExistingKeys を直接呼ぶには apiBase を差し替える必要がある。 | ||
| // ここでは URL 構築ロジックを検証する別の方法として httptest.Server の URL を projectID に含めた | ||
| // カスタム URL で呼ぶ(テスト用ヘルパー経由)。 | ||
| _ = client | ||
|
|
||
| // apiBase が const のため URL 組み立てのテストは httptest URL を直接呼ぶ方法で行う | ||
| u := srv.URL + "/v10/projects/test-project/env" | ||
| req, _ := http.NewRequest(http.MethodGet, u, nil) | ||
| req.Header.Set("Authorization", "Bearer test-token") | ||
| res, err := http.DefaultClient.Do(req) |
Comment on lines
+69
to
+75
| if token != "" { | ||
| existing, err := vercelFetchExistingKeys(client, token, projectID, teamID) | ||
| if err == nil { | ||
| classified = classifyVercelItems(items, existing) | ||
| } | ||
| // API 失敗時は classified = nil のまま(確認スキップしない安全側フォールバック) | ||
| } |
Comment on lines
+72
to
+75
| found, err := exists(t) | ||
| if err != nil { | ||
| return nil, fmt.Errorf("%s: 存在確認失敗: %w", t.entry.Key, err) | ||
| } |
Comment on lines
+127
to
+133
| if token != "" { | ||
| cls, err := classifyGitHubTasks(client, token, owner, repo, tasks) | ||
| if err == nil { | ||
| classified = cls | ||
| } | ||
| // API 失敗時は classified = nil のまま(安全側フォールバック) | ||
| } |
- vercel: apiBase を var 化し、TestVercelFetchExistingKeys_Success が実際に vercelFetchExistingKeys を呼んで URL/ヘッダ/ステータス/パースを検証する形に修正 - vercel: 既存 key 取得失敗時に分類スキップ理由を stderr へ警告表示 - github: 存在確認失敗時のエラー文に envScope を含めて切り分け可能に - github: 分類 API 失敗時に分類スキップ理由を stderr へ警告表示 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WdeTedorn5WqsyAJt4bybB
Comment on lines
+219
to
+221
| if res.StatusCode != http.StatusOK { | ||
| return nil, fmt.Errorf("既存 key 取得失敗: HTTP %d", res.StatusCode) | ||
| } |
| return tasks | ||
| } | ||
|
|
||
| // classifyGitHubTasksByExistence は exists 関数を使って各タスクを新規/更新に分類する純粋関数。 |
Comment on lines
+234
to
+238
| // variable: GET で存在確認 → POST or PATCH | ||
| exists, e := githubVariableExists(client, token, owner, repo, t.envScope, t.entry.Key) | ||
| if e != nil { | ||
| fmt.Printf("✗ %s -> 存在確認失敗: %s\n", t.entry.Key, e) | ||
| scope := t.envScope | ||
| if scope == "" { |
Comment on lines
+68
to
+70
| // ---- 既存 key を問い合わせて新規/更新を分類 ---- | ||
| client := &http.Client{} | ||
| var classified []classifiedVercelItem |
Comment on lines
+128
to
+130
| // ---- 既存確認して新規/更新を分類 ---- | ||
| client := &http.Client{} | ||
| var classified []githubClassifiedTask |
Comment on lines
+449
to
+479
| // githubSecretExists は GitHub Actions のシークレットが存在するかを確認する。 | ||
| func githubSecretExists(client *http.Client, token, owner, repo, envScope, name string) (bool, error) { | ||
| var apiURL string | ||
| if envScope == "" { | ||
| apiURL = fmt.Sprintf("%s/repos/%s/%s/actions/secrets/%s", | ||
| githubAPIBase, url.PathEscape(owner), url.PathEscape(repo), url.PathEscape(name)) | ||
| } else { | ||
| apiURL = fmt.Sprintf("%s/repos/%s/%s/environments/%s/secrets/%s", | ||
| githubAPIBase, url.PathEscape(owner), url.PathEscape(repo), url.PathEscape(envScope), url.PathEscape(name)) | ||
| } | ||
|
|
||
| req, err := http.NewRequest(http.MethodGet, apiURL, nil) | ||
| if err != nil { | ||
| return false, fmt.Errorf("リクエスト生成失敗: %w", err) | ||
| } | ||
| setGitHubHeaders(req, token) | ||
|
|
||
| res, err := client.Do(req) | ||
| if err != nil { | ||
| return false, fmt.Errorf("リクエスト失敗: %w", err) | ||
| } | ||
| defer res.Body.Close() | ||
|
|
||
| if res.StatusCode == http.StatusOK { | ||
| io.Copy(io.Discard, res.Body) //nolint:errcheck // drain で接続を再利用可能にする | ||
| return true, nil | ||
| } | ||
| if res.StatusCode == http.StatusNotFound { | ||
| io.Copy(io.Discard, res.Body) //nolint:errcheck // drain で接続を再利用可能にする | ||
| return false, nil | ||
| } |
- vercel: vercelFetchExistingKeys の非200エラーにレスポンスボディの詳細を含める - vercel/github: http.Client に 30s タイムアウトを設定し無期限ハングを防止 - github: variable 送信時に classified を再利用し、存在確認 API の二重呼び出しを解消(classified==nil のときのみフォールバック) - github: classifyGitHubTasksByExistence のコメントを「純粋関数」から「存在確認関数を注入」に訂正 - github: githubSecretExists の httptest テスト(repo/env スコープ・200/404/error)を追加 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WdeTedorn5WqsyAJt4bybB
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
タスク
送信前に provider へ API で問い合わせ、登録対象 key が provider 側に既に存在するか確認し、各 Entry を「新規」/「更新(上書き)」に分類する。
GET /v10/projects/{projectId}/envで既存 key 一覧を取得し key の一致で更新判定githubVariableExistsを活用、シークレットは名前確認 API (githubSecretExists) を新設⟳ KEY [更新]、+ KEY [新規])--yes/-y既存フラグで確認をスキップ(新しい --force は追加しない)--dry-run時もトークンがあれば新規/更新を分類して表示--yes未指定の場合はエラー停止し--yesを案内完了条件
--yesまたは-yを付けると確認をスキップして送信する-y未指定かつ確認が必要な場合は従来同様エラー停止し--yesを案内--dry-runの一覧出力にも新規/更新の区別が反映されるgo test ./...が通る動作確認
go vet ./.../go build ./...)classifyVercelItems/classifyGitHubTasksByExistence)go test -race ./...通過主な変更点
internal/provider/vercel/vercel.govercelFetchExistingKeys():GET /v10/projects/{id}/envで既存 key セットを取得classifyVercelItems(): items を既存 key セットと照合して新規/更新に分類する純粋関数countClassified(): 新規/更新件数を集計するヘルパーSync(): dry-run 時も分類を試みる。更新がある場合のみ確認プロンプト表示internal/provider/github/github.gogithubSecretExists(): シークレットの名前確認 APIclassifyGitHubTasksByExistence(): exists 関数を注入して分類する純粋関数(テスト容易性)classifyGitHubTasks(): 実 API を使って分類するラッパーcountGitHubClassified(): 件数集計ヘルパーSync(): 同上cmd/env-sync/main.goprintUsage(): 新挙動の説明を追加テスト追加
internal/provider/vercel/classify_test.gointernal/provider/github/classify_test.go