feat: add Codespaces secrets CLI management script - #448
Conversation
GitHub Codespaces シークレットのリポジトリ紐付けを CLI で管理するスクリプトを追加。 - script/codespaces-secrets.sh: シークレット管理スクリプト - .claude/commands/codespaces-secrets.md: Claude コマンドドキュメント 機能: - list: シークレットと紐付けリポジトリを表示 - repos add/remove: 管理対象リポジトリの追加・削除 - sync: 設定ファイルのリポジトリを全シークレットに一括紐付け - diff: 設定と現在の状態の差分を表示 - init: 現在の設定からファイルを初期化 設定ファイルは ~/.config/codespaces-secrets/repos.txt に保存(Git管理外) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
|
Warning Rate limit exceeded
⌛ How to resolve this issue?After the wait time has elapsed, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout. Please see our FAQ for further information. 📝 WalkthroughWalkthroughThis pull request introduces a new CLI tool for managing GitHub Codespaces secrets and their repository bindings. It includes documentation describing the command interface and a shell script implementing the functionality, which enables users to list secrets, manage repository associations, synchronize configurations, and initialize settings via command-line operations. Changes
Sequence Diagram(s)sequenceDiagram
participant User
participant CLI as codespaces-secrets.sh
participant GH as gh CLI
participant API as GitHub API
participant Config as Local repos.txt
User->>CLI: sync [secret-name]
activate CLI
CLI->>GH: check authentication
GH-->>CLI: auth confirmed
CLI->>Config: read configured repos
Config-->>CLI: repo list
CLI->>GH: fetch repo IDs for repos
activate GH
GH->>API: resolve repo names
API-->>GH: repo IDs
GH-->>CLI: aggregated IDs
deactivate GH
CLI->>API: PUT selected_repository_ids
API-->>CLI: sync confirmed
deactivate CLI
CLI->>User: success message
sequenceDiagram
participant User
participant CLI as codespaces-secrets.sh
participant API as GitHub API
participant Config as Local repos.txt
User->>CLI: init
activate CLI
CLI->>API: fetch all secrets
API-->>CLI: secrets list
loop For each secret
CLI->>API: get linked repositories
API-->>CLI: repo IDs
end
CLI->>CLI: deduplicate repos
CLI->>Config: write repos.txt
Config-->>CLI: file saved
deactivate CLI
CLI->>User: initialization complete
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Suggested labels
Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
zsh スクリプトのため shellcheck の除外リストに追加 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
There was a problem hiding this comment.
Actionable comments posted: 4
🤖 Fix all issues with AI agents
In @.claude/commands/codespaces-secrets.md:
- Around line 61-75: Add a language identifier to the fenced code block that
contains the example terminal output (the block starting with "=== Codespaces
シークレット一覧 ===" and the subsequent secret list) to satisfy MD040; replace the
opening triple backticks with a language-tagged fence such as ```text or
```console so the block becomes a recognized text/console code block.
In `@script/codespaces-secrets.sh`:
- Line 1: CI fails because ShellCheck doesn't support zsh; fix by converting the
script to POSIX/bash: change the shebang to use bash (/usr/bin/env bash) and
replace the zsh-specific parameter expansions ${0:A:h} and ${0:t} (used around
the variables on lines referencing script dir/name) with POSIX equivalents that
set SCRIPT_DIR by resolving the directory (cd "$(dirname "$0")" && pwd) and
SCRIPT_NAME using basename "$0"; alternatively, if you prefer keeping zsh,
update the ShellCheck CI filter to exclude script/codespaces-secrets.sh from
linting so the pipeline no longer runs ShellCheck on this file.
- Around line 317-319: The cmd_diff flow uses secrets=$(get_all_secrets) and
then iterates over secrets without checking for emptiness, so if get_all_secrets
returns empty the loop runs once with a blank name; modify cmd_diff to guard
after calling get_all_secrets by checking if secrets is empty (e.g., empty
string or zero-length array) and skip the diff loop or return early when there
are no secrets to process; reference the variables/function names secrets,
get_all_secrets and the cmd_diff routine when making the change.
- Around line 274-297: Guard cmd_init by checking get_all_secrets output before
entering the read loop and avoid printing a blank line when all_repos is empty:
after calling get_all_secrets, test if "$secrets" is non-empty before the while
IFS= read -r secret loop (skip the loop entirely if empty) and only feed printf
'%s\n' "${all_repos[@]}" into the redirect when all_repos has elements (use a
length check on the all_repos array); keep the header lines but ensure no blank
repo line is written to REPOS_FILE. Use the existing symbols get_all_secrets,
get_secret_repos, all_repos, REPOS_FILE and the cmd_init context to locate where
to add these guards.
🧹 Nitpick comments (3)
script/codespaces-secrets.sh (3)
164-171: Regex metacharacters in repo names are not escaped insedandgrep.
${repo//\//\\/}only escapes/, but repo names contain.which is a regex wildcard. For example,grep -qx "owner/repo.name"would also matchowner/repoXname. Similarly, thesedpattern could match unintended lines.Since this is a personal config tool with controlled input, the practical risk is low, but for correctness:
Proposed fix using `grep -Fqx` for literal matching and a safer sed approach
- if grep -qx "$repo" "$REPOS_FILE" 2>/dev/null; then + if grep -Fqx "$repo" "$REPOS_FILE" 2>/dev/null; thenFor the
removesubcommand, also use fixed-string grep and considergrep -Fxvinstead ofsed:- if grep -qx "$repo" "$REPOS_FILE" 2>/dev/null; then - # macOS と Linux の両方で動作するsed - if [[ "$(uname)" == "Darwin" ]]; then - sed -i '' "/^${repo//\//\\/}$/d" "$REPOS_FILE" - else - sed -i "/^${repo//\//\\/}$/d" "$REPOS_FILE" - fi + if grep -Fqx "$repo" "$REPOS_FILE" 2>/dev/null; then + grep -Fxv "$repo" "$REPOS_FILE" > "$REPOS_FILE.tmp" && mv "$REPOS_FILE.tmp" "$REPOS_FILE"This also eliminates the macOS/Linux
sed -iportability dance.
145-148: Samegrep -qxregex issue as inremove— use-Ffor literal match.- if grep -qx "$repo" "$REPOS_FILE" 2>/dev/null; then + if grep -Fqx "$repo" "$REPOS_FILE" 2>/dev/null; then
243-249: Sync failure is silently swallowed — consider propagating a non-zero exit.When the API PUT fails for a secret, the script prints "FAILED" but continues and ends with "同期完了" (sync complete) + exit 0. For a CLI tool, it would be helpful to track failures and exit non-zero so callers (or the user) know something went wrong.
| ``` | ||
| === Codespaces シークレット一覧 === | ||
|
|
||
| LINEAR_API_KEY | ||
| - Elu-co-jp/project-a | ||
| - keito4/my-project | ||
|
|
||
| NODE_AUTH_TOKEN | ||
| - Elu-co-jp/project-a | ||
|
|
||
| OP_SERVICE_ACCOUNT_TOKEN | ||
| - Elu-co-jp/project-a | ||
| - Elu-co-jp/project-b | ||
| - keito4/my-project | ||
| ``` |
There was a problem hiding this comment.
Add a language identifier to the fenced code block to fix the markdownlint warning.
Per MD040, fenced code blocks should specify a language. This is example terminal output, so text or console would be appropriate:
-```
+=```text
=== Codespaces シークレット一覧 ===🧰 Tools
🪛 markdownlint-cli2 (0.20.0)
[warning] 61-61: Fenced code blocks should have a language specified
(MD040, fenced-code-language)
🤖 Prompt for AI Agents
In @.claude/commands/codespaces-secrets.md around lines 61 - 75, Add a language
identifier to the fenced code block that contains the example terminal output
(the block starting with "=== Codespaces シークレット一覧 ===" and the subsequent secret
list) to satisfy MD040; replace the opening triple backticks with a
language-tagged fence such as ```text or ```console so the block becomes a
recognized text/console code block.
| @@ -0,0 +1,421 @@ | |||
| #!/usr/bin/env zsh | |||
There was a problem hiding this comment.
CI failure: zsh shebang is not supported by ShellCheck.
The pipeline fails because ShellCheck does not support zsh scripts. Options:
- Exclude this file from the ShellCheck CI step (e.g., add it to the grep filter).
- Convert to bash — the only zsh-specific features used are
${0:A:h}and${0:t}(lines 20–21), which can be replaced withSCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"andSCRIPT_NAME="$(basename "$0")".
🧰 Tools
🪛 GitHub Actions: CI
[error] 1-1: ShellCheck SC1071: ShellCheck only supports sh/bash/dash/ksh scripts. The script uses a zsh shebang ('#!/usr/bin/env zsh'), which is not supported. Command: find script -name '*.sh' -type f | grep -v 'import.sh|export.sh|credentials.sh|brew-deps.sh|/lib/|credentials/providers/' | xargs -r shellcheck -x
🤖 Prompt for AI Agents
In `@script/codespaces-secrets.sh` at line 1, CI fails because ShellCheck doesn't
support zsh; fix by converting the script to POSIX/bash: change the shebang to
use bash (/usr/bin/env bash) and replace the zsh-specific parameter expansions
${0:A:h} and ${0:t} (used around the variables on lines referencing script
dir/name) with POSIX equivalents that set SCRIPT_DIR by resolving the directory
(cd "$(dirname "$0")" && pwd) and SCRIPT_NAME using basename "$0";
alternatively, if you prefer keeping zsh, update the ShellCheck CI filter to
exclude script/codespaces-secrets.sh from linting so the pipeline no longer runs
ShellCheck on this file.
| local all_repos=() | ||
| local secrets _repos | ||
| secrets=$(get_all_secrets) | ||
|
|
||
| while IFS= read -r secret; do | ||
| _repos=$(get_secret_repos "$secret") | ||
| if [[ -n "$_repos" ]]; then | ||
| while IFS= read -r repo; do | ||
| all_repos+=("$repo") | ||
| done <<< "$_repos" | ||
| fi | ||
| done <<< "$secrets" | ||
|
|
||
| # 重複を除去してファイルに書き込み | ||
| { | ||
| echo "# Codespaces Secrets - 管理対象リポジトリ" | ||
| echo "# このファイルは Git 管理外です" | ||
| echo "# 1行1リポジトリ(owner/repo 形式)" | ||
| echo "#" | ||
| echo "# 使用方法:" | ||
| echo "# ./script/codespaces-secrets.sh sync - 全シークレットにこのリストを紐付け" | ||
| echo "#" | ||
| printf '%s\n' "${all_repos[@]}" | sort -u | ||
| } > "$REPOS_FILE" |
There was a problem hiding this comment.
cmd_init doesn't guard against empty secrets, and printf on an empty array prints a blank line.
If there are no secrets, get_all_secrets returns an empty string, but the while read loop at line 278 still executes once with an empty $secret, making a bogus API call. Additionally, if all_repos is empty, printf '%s\n' "${all_repos[@]}" (line 296) writes an empty line into the config file.
Proposed fix
secrets=$(get_all_secrets)
+ if [[ -z "$secrets" ]]; then
+ output::warning "シークレットが見つかりません"
+ return 0
+ fi
+
while IFS= read -r secret; doAnd guard the write:
- printf '%s\n' "${all_repos[@]}" | sort -u
+ if [[ ${`#all_repos`[@]} -gt 0 ]]; then
+ printf '%s\n' "${all_repos[@]}" | sort -u
+ fi
} > "$REPOS_FILE"🤖 Prompt for AI Agents
In `@script/codespaces-secrets.sh` around lines 274 - 297, Guard cmd_init by
checking get_all_secrets output before entering the read loop and avoid printing
a blank line when all_repos is empty: after calling get_all_secrets, test if
"$secrets" is non-empty before the while IFS= read -r secret loop (skip the loop
entirely if empty) and only feed printf '%s\n' "${all_repos[@]}" into the
redirect when all_repos has elements (use a length check on the all_repos
array); keep the header lines but ensure no blank repo line is written to
REPOS_FILE. Use the existing symbols get_all_secrets, get_secret_repos,
all_repos, REPOS_FILE and the cmd_init context to locate where to add these
guards.
| local secrets current_repos missing_repos extra_repos | ||
| secrets=$(get_all_secrets) | ||
|
|
There was a problem hiding this comment.
cmd_diff doesn't guard against empty secrets either.
Same issue as cmd_init — if get_all_secrets returns empty, the loop executes once with a blank secret name. Add a guard:
secrets=$(get_all_secrets)
+ if [[ -z "$secrets" ]]; then
+ output::warning "シークレットが見つかりません"
+ return 0
+ fi
+
while IFS= read -r secret; do📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| local secrets current_repos missing_repos extra_repos | |
| secrets=$(get_all_secrets) | |
| local secrets current_repos missing_repos extra_repos | |
| secrets=$(get_all_secrets) | |
| if [[ -z "$secrets" ]]; then | |
| output::warning "シークレットが見つかりません" | |
| return 0 | |
| fi | |
🤖 Prompt for AI Agents
In `@script/codespaces-secrets.sh` around lines 317 - 319, The cmd_diff flow uses
secrets=$(get_all_secrets) and then iterates over secrets without checking for
emptiness, so if get_all_secrets returns empty the loop runs once with a blank
name; modify cmd_diff to guard after calling get_all_secrets by checking if
secrets is empty (e.g., empty string or zero-length array) and skip the diff
loop or return early when there are no secrets to process; reference the
variables/function names secrets, get_all_secrets and the cmd_diff routine when
making the change.
PR レビュー結果総合評価✅ 承認可能 (Approve with minor suggestions) CI は全て成功しており、コード品質、機能性、セキュリティの観点で大きな問題はありません。以下、改善提案と軽微な指摘を記載します。 コード品質 (Code Quality)✅ 良い点
|
シークレット名とリポジトリ名を汎用的なサンプルに変更 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
PR Review - Codespaces Secrets CLI Management概要GitHub Codespaces シークレットのリポジトリ紐付けをCLIで管理する新しいスクリプトの追加です。GUIを使わずに設定ファイルベースで管理できる実用的なツールです。 ✅ 良い点1. 明確な目的と実装
2. 充実したコマンド体系
3. コード品質
4. ドキュメント
🔍 改善提案1. テストカバレッジの不足 (Critical)問題:
影響:
推奨対応: # test/integration/codespaces-secrets.bats を作成
@test "codespaces-secrets: help displays usage" {
run ./script/codespaces-secrets.sh help
[ "$status" -eq 0 ]
[[ "$output" =~ "Usage:" ]]
}
@test "codespaces-secrets: repos file created when adding repo" {
# モック環境でのテスト
}
@test "codespaces-secrets: diff command shows differences" {
# 差分検出のロジックをテスト
}2. ShellCheck除外の理由が不明問題: 推奨対応:
3. エラーハンドリングの強化問題箇所 (script/codespaces-secrets.sh:235-241): 推奨対応: 4. セキュリティ考慮事項問題: 推奨対応: ensure_config_dir() {
if [[ ! -d "$CONFIG_DIR" ]]; then
mkdir -p "$CONFIG_DIR"
chmod 700 "$CONFIG_DIR" # 所有者のみアクセス可能
output::info "設定ディレクトリを作成しました: $CONFIG_DIR"
fi
}
ensure_repos_file() {
ensure_config_dir
if [[ ! -f "$REPOS_FILE" ]]; then
touch "$REPOS_FILE"
chmod 600 "$REPOS_FILE" # 所有者のみ読み書き可能
output::info "リポジトリ設定ファイルを作成しました: $REPOS_FILE"
fi
}5. cmd_diff のエッジケース問題箇所 (script/codespaces-secrets.sh:329-330): 推奨対応: 6. Release Type要件への適合確認:
📊 コード品質メトリクス
🎯 推奨アクション必須対応 (CI Redを防ぐため)
推奨対応 (品質向上)
📝 総評機能的には優れたツールですが、テストカバレッジの不足がCLAUDE.mdの品質基準に抵触しています。 統合テストを追加し、ShellCheckの問題を解決すれば、マージ可能な状態になります。 推奨判定現時点では Request Changes ですが、テスト追加後は Approve に変更できます。 参考情報
|
|
🎉 This PR is included in version 1.66.0 🎉 The release is available on GitHub release Your semantic-release bot 📦🚀 |
Summary
~/.config/codespaces-secrets/repos.txtに保存(Git管理外)機能
listreposrepos add <repo>repos remove <repo>repos editsyncsync <secret>diffinit使用例
ワークフロー
./script/codespaces-secrets.sh init- 現在の紐付け状態をファイルに保存./script/codespaces-secrets.sh repos add owner/repo- 新しいリポジトリを追加./script/codespaces-secrets.sh diff- 同期が必要か確認./script/codespaces-secrets.sh sync- 全シークレットに一括同期Test plan
listコマンドでシークレット一覧が表示されることを確認repos addでリポジトリが追加されることを確認syncで全シークレットにリポジトリが紐付けられることを確認diffで差分が表示されることを確認initで設定ファイルが作成されることを確認🤖 Generated with Claude Code
Summary by CodeRabbit
New Features
Documentation