Skip to content

feat: validate サブコマンドで設定・接続を診断 (#34)#38

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

feat: validate サブコマンドで設定・接続を診断 (#34)#38
polidog merged 4 commits into
mainfrom
feat/34

Conversation

@polidog

@polidog polidog commented Jun 29, 2026

Copy link
Copy Markdown
Member

タスク

env-sync 実行時に出る警告(既存 key 取得失敗: HTTP 404: Project not found. 等)の原因(teamId 未設定 / token のスコープ違い / projectId 不一致 など)を、設定・認証・API 到達性を読み取り専用で診断する validate サブコマンドで self-service で切り分けられるようにする。

完了条件

  • env-sync validate で解決された Vercel target(name / projectId / teamId / token 有無・取得元)が一覧表示される
  • token は値が表示されずマスクされる(完全マスク)
  • Vercel API への到達確認が行われ、成功時は OK、失敗時は HTTP ステータスと推定原因が表示される
  • teamId 未設定でチーム配下プロジェクトにアクセスして 404 になるケースで「teamId 未設定の可能性」が原因候補として提示される
  • validate は環境変数を一切登録・変更しない(GET のみ・読み取り専用)
  • env-sync --helpvalidate の説明が追加されている
  • GitHub プロバイダー対象時は repo / token の有効性も読み取り専用で検証する

実装概要

  • Validator オプショナルインターフェースprovider.go に追加。各 provider が自分の検証を実装・出力する設計(provider 抽象を壊さず、未実装 provider は自動的に「未対応」スキップ)
  • Vercel: GET /v10/projects/{id}/env で到達確認。404→teamId未設定/projectId不一致、401→token無効、403→スコープ不足の推定原因を提示
  • GitHub: GET /repos/{owner}/{repo} で repo/token の有効性を検証
  • token / projectId / teamId / repo の解決値と取得元(env / config / .vercel/project.json / git remote)を表示。token は完全マスク
  • 書き込み API(POST/PUT/PATCH/DELETE)は一切呼ばない

動作確認

  • lint / build 通過(go vet / gofmt / go build clean)
  • 設計・計画適合レビュー通過(Critical なし。Warning 3件は本PRで修正済み)
  • 自動テスト生成・通過(統合テスト:httptest で 200/404/401/403、GET-only 検証、source トラッキング)
  • コード品質チェック通過(Critical なし)
  • 受け入れテスト通過(実バイナリで Vercel 403 / GitHub 401 の到達確認・推定原因・token マスク・exit code・日本語/英語表示を確認)

品質チェック結果

reviewer 指摘の Warning 3件はすべて本PRで解消済み:

  • 未使用ヘルパー GetWd/Chdir 削除
  • 未使用 i18n キー MsgValidateDefWarn/MsgValidateEnvWarn 削除
  • 出力ライタを vercel/github で stdoutWriter パターンに統一

🔵 Info(非ブロッキング・据え置き):

  • validate 未対応 provider(gcp)は exit 0 でスキップ(CI gate 用途では将来 non-zero 化を検討余地)
  • 言語解決ブロックが init/setup/validate で重複(既存パターン踏襲。将来ヘルパー抽出の余地)

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

  • 🟡 syncOneVercelTarget が env 変数ごとに直列 POST(本PRスコープ外の既存 sync 経路。一括 upsert で改善余地)
  • 🔵 ターゲットループの直列実行(ターゲット数が増えた場合のみ並列化の効果。現状規模では早すぎる最適化)
  • validate 実装自体にパフォーマンス上の問題なし(http.Client はループ外で1回生成、正規表現はパッケージ変数でコンパイル済み)

🤖 Generated with Claude Code

env-sync 実行時の警告(既存 key 取得失敗: HTTP 404 等)の原因を
self-service で切り分けられるよう、読み取り専用の validate サブコマンドを追加する。

- Validator オプショナルインターフェースを追加し、各 provider が自分の検証を実装・出力
- Vercel: GET /v10/projects/{id}/env で到達確認。404→teamId未設定/projectId不一致、
  401→token無効、403→スコープ不足の推定原因を提示
- GitHub: GET /repos/{owner}/{repo} で repo/token の有効性を検証
- token / projectId / teamId / repo の解決値と取得元(env / config / project.json /
  git remote)を表示。token は完全マスク(値非表示)
- 書き込み API(POST/PUT/PATCH/DELETE)は一切呼ばない(GET のみ)
- env-sync --help(i18n en/ja)に validate の説明を追加

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

Adds a new read-only validate subcommand to env-sync so users can self-diagnose common configuration/auth/reachability causes behind Vercel/GitHub sync warnings (e.g., missing teamId, invalid token, wrong project/repo), without performing any writes.

Changes:

  • Introduces an optional provider.Validator interface and implements Validate() for Vercel and GitHub providers.
  • Adds target “resolved value + source” reporting (env/config/project.json/git remote) and API reachability checks (GET-only) with status-based suspected causes.
  • Adds i18n keys/catalog entries, updates --help usage text, and adds extensive tests (including httptest-based GET-only verification).

Reviewed changes

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

Show a summary per file
File Description
internal/provider/vercel/vercel.go Refactors .vercel/project.json fallback into a helper and adds Vercel API reachability helper.
internal/provider/vercel/vercel_validate.go Implements Vercel Validate() output and GET-only API check flow.
internal/provider/vercel/vercel_validate_test.go Adds httptest-based coverage for Vercel validate and helper behavior.
internal/provider/provider.go Adds optional Validator interface alongside Provider.
internal/provider/provider_test.go Tests optional-interface behavior via type assertions.
internal/provider/github/github_validate.go Implements GitHub Validate() output and GET-only repo reachability checks.
internal/provider/github/github_validate_test.go Adds httptest-based coverage for GitHub validate and helper behavior.
internal/i18n/keys.go Adds i18n keys for validate output.
internal/i18n/catalog_ja.go Adds Japanese catalog strings and updates usage text to mention validate.
internal/i18n/catalog_en.go Adds English catalog strings and updates usage text to mention validate.
internal/config/appconfig.go Adds “value + source” resolution fields/methods for Vercel/GitHub targets.
internal/config/appconfig_test.go Adds tests for the new “with source” resolution behavior.
cmd/env-sync/validate.go Adds CLI entrypoint for validate and dispatch to provider validators.
cmd/env-sync/validate_test.go Adds integration-ish tests for validate help and behavior without def/env files.
cmd/env-sync/main.go Routes the new validate subcommand and applies the same language prescan logic.

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

Comment thread cmd/env-sync/validate.go
Comment on lines +18 to +20
func runValidate(args []string, printUsage func()) error {
opts := config.ParseFlags(args, printUsage, func() {})

Comment on lines +511 to +513
defer res.Body.Close()
d := parseErrorBody(res.Body)
return res.StatusCode, d, nil
Comment on lines +73 to +78
// token / projectId が未設定なら API 確認をスキップ
if tgt.Token == "" || tgt.ProjectID == "" {
fmt.Fprint(stdoutWriter, i18n.T(i18n.MsgValidateTokenUnsetSkip))
ngCount++
continue
}
Comment on lines +66 to +71
// repo 表示
if resolveErr != nil {
fmt.Fprintf(githubStdoutWriter, " repo : %s (%s)\n", i18n.T(i18n.MsgValidateSourceUnset), githubSourceLabel(repoSrc))
fmt.Fprint(githubStdoutWriter, i18n.T(i18n.MsgValidateTokenUnsetSkip))
ngCount++
continue
- validate --version でバージョン情報が表示されない問題を修正
  (runValidate に printVersion を渡すよう変更)
- vercelCheckAccess で 2xx 時もレスポンスボディ全量読みしていた問題を修正
  (2xx 時は io.Discard でドレインのみに変更)
- Vercel validate で projectId 未設定時も token 未設定メッセージを出していた問題を修正
  (MsgValidateProjectIDUnsetSkip キーを追加し、原因別にメッセージを分離)
- GitHub validate で repo 解決失敗時も token 未設定メッセージを出していた問題を修正
  (MsgValidateRepoUnresolvableSkip キーを追加)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YNh5eprge9f7terW2bFujE

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

Comment on lines +46 to +50
targetLabel := tgt.ProjectID
if tgt.Name != "" {
targetLabel = tgt.Name
}
fmt.Fprint(stdoutWriter, i18n.T(i18n.MsgValidateHeader, targetLabel))
Comment on lines +60 to +64
if tgt.ProjectID == "" {
fmt.Fprintf(stdoutWriter, " projectId : %s (%s)\n", i18n.T(i18n.MsgValidateSourceUnset), vercelSourceLabel(tgt.ProjectIDSource))
} else {
fmt.Fprint(stdoutWriter, i18n.T(i18n.MsgValidateVercelProjectID, tgt.ProjectID, vercelSourceLabel(tgt.ProjectIDSource)))
}
Comment thread internal/provider/github/github_validate.go
- Vercel validate: name/projectId 両方未設定時のヘッダが空文字になる問題を修正
  (targetLabel に MsgValidateSourceUnset をフォールバック)
- Vercel validate: projectId 未設定時の表示が "(unset) ((unset))" と二重カッコになる問題を修正
  (MsgValidateVercelProjectID テンプレートで統一)
- GitHub validate: repo 解決失敗時の表示が "(unset) ((unset))" と二重カッコになる問題を修正
  (MsgValidateGitHubRepo テンプレートで統一)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YNh5eprge9f7terW2bFujE

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

Comment on lines +472 to +484
if readErr == nil {
var pj projectJSON
if err := json.Unmarshal(pjText, &pj); err != nil {
return false, fmt.Errorf("%s", i18n.T(i18n.MsgVercelProjectJSONParseFail, err))
}
targets[0].ProjectID = pj.ProjectID
targets[0].ProjectIDSource = "project_json"
if targets[0].TeamID == "" && pj.OrgID != "" {
targets[0].TeamID = pj.OrgID
targets[0].TeamIDSource = "project_json"
}
return true, nil
}
Comment thread internal/i18n/keys.go Outdated

// ----- Validate サブコマンド -----

// MsgValidateHeader は validate の対象プロバイダーヘッダ(書式: プロバイダー名)。
Comment thread internal/provider/vercel/vercel_validate.go Outdated
Comment thread internal/provider/github/github_validate.go Outdated
- applyProjectJSONFallback: project.json に projectId が無い場合も
  ProjectIDSource を "project_json" に設定していた問題を修正
  (pj.ProjectID != "" のときのみ source を更新するよう条件追加)
- MsgValidateHeader のコメントを実際の用途(ターゲットラベル)に修正
  (「プロバイダー名」→「ターゲットラベル = name / projectId / owner-repo」)
- vercel/github validate の HTTP タイムアウトを 30*time.Second の直書きから
  各パッケージの httpTimeout 定数を使うよう変更(不要な time import も削除)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YNh5eprge9f7terW2bFujE
@polidog polidog merged commit bad8771 into main Jun 29, 2026
1 check passed
@polidog polidog deleted the feat/34 branch June 29, 2026 07:13
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