diff --git a/CHANGELOG-2.1.263-e.md b/CHANGELOG-2.1.263-e.md new file mode 100644 index 0000000..60e27bb --- /dev/null +++ b/CHANGELOG-2.1.263-e.md @@ -0,0 +1,30 @@ +# CHANGELOG 2.1.263-e — 手動リリース + +## 背景 + +`claude@192.168.9.65:websites/e-stay-tours.git`(SSH ユーザーを自分のアカウント名にした URL)が +`リポジトリ URL の形式が不正です` で弾かれ、理由が分かりにくかった。 +Gitea・GitHub の SSH ユーザーは常に `git` だが、それを案内していなかった。 + +## 変更 + +- **`REPO_RE`(manager の URL バリデーション)を緩和** + - `ssh://@[:port]/` … `git` 以外のユーザーも受理 + - `@:` … SCP 記法。`git@` 固定をやめて任意ユーザー可 + - `:` … ユーザー省略も受理 + - `//` で始まるパスは引き続き拒否(URL 誤入力の検出) + - ホスト allowlist(`git.allowedHosts`)は従来どおり全形式に適用 +- **エラーメッセージを具体化** + - URL 形式エラー時に受理される 3 形式と「SSH ユーザーは常に `git`」を明記 + - ホスト不許可時に許可済みホスト一覧を併記 +- **Web UI**: リポジトリ URL 入力欄の下に形式ヒントを表示 + +## 注意 + +`claude@…` のような不正ユーザーでも URL 形式としては通るようになったが、その場合の +`git clone` は Gitea 側で失敗する。失敗ログは `2.1.263-d` で追加した「ログ」ボタンで確認できる。 +正しくは `git@:/.git`。 + +## バージョン + +`version` = `2.1.263-e` / `appVersion` = `"2.1.263"`。 diff --git a/CLAUDE.md b/CLAUDE.md index 6fa86a7..f4bc610 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -57,6 +57,19 @@ Git リポジトリ(GitHub / Gitea など互換 Git 全般)をチェック ## 改修履歴 +### リポジトリ URL バリデーション緩和 + エラーメッセージ具体化(2026-09-09, v2.1.263-e) + +**症状**: `claude@192.168.9.65:...`(SSH ユーザーをアカウント名にした URL)が +`リポジトリ URL の形式が不正です` で弾かれ、原因が分かりにくい。Gitea/GitHub の SSH +ユーザーは常に `git`。 + +**修正内容**: `manager.py` の `REPO_RE` を `ssh://@host/…` / `@host:path` / +`host:path` も受理するよう緩和(`//` 始まりは拒否、ホスト allowlist は全形式に適用)。 +URL 形式エラーで受理形式と「SSH ユーザーは常に `git`」を明記、ホスト不許可で許可済み +一覧を併記。UI 入力欄にヒント追加。README の git セクションに URL 形式表を追加。 + +**対象ファイル**: `files/manager.py`, `README.md`, `Chart.yaml`, `CHANGELOG-2.1.263-e.md` + ### 失敗セッションの調査性改善(終了 Pod の保持 + ログ API + UI)(2026-09-08, v2.1.263-d) **症状**: セッション Pod がブート失敗で `Failed` になると reaper が即削除し、`kubectl logs` で diff --git a/Chart.yaml b/Chart.yaml index 42972fd..17ad3e4 100644 --- a/Chart.yaml +++ b/Chart.yaml @@ -6,7 +6,7 @@ description: >- 使い捨てコンテナで Git リポジトリ(GitHub / Gitea など)をチェックアウト・編集・ 検証・プッシュする。一定時間アイドルなセッションは自動破棄される。 type: application -version: 2.1.263-d +version: 2.1.263-e appVersion: "2.1.263" keywords: - claude-code diff --git a/README.md b/README.md index 9349331..41b49b9 100644 --- a/README.md +++ b/README.md @@ -409,6 +409,18 @@ Secret は **セッション NS** に作成されます。`credentials` モー GitHub でも Gitea でも GitLab でも、SSH か HTTPS トークンで認証できる Git ホストなら同じ仕組みで扱えます。 セッション作成時に identity 名を選ぶと、その Secret だけがそのセッション Pod にマウントされます。 +**URL の形式**(`git.identities` で選んだ鍵で認証されます): + +| 形式 | 例 | identity | +|---|---|---| +| SSH(SCP 記法) | `git@192.168.9.65:websites/e-stay-tours.git` | `ssh` 型 | +| SSH(URL 記法) | `ssh://git@192.168.9.65/websites/e-stay-tours.git` | `ssh` 型 | +| HTTPS | `https://git.cafepieters.com/websites/e-stay-tours.git` | `https-token` 型(プライベート時) | + +> SSH のユーザー名は **常に `git`** です(Gitea・GitHub 共通)。自分の Gitea アカウント名 +> (`claude@…` など)ではありません。認証は identity の鍵で行われるため、その鍵を持つ +> アカウントが対象リポジトリの参照権限を持っている必要があります。 + ### session — 使い捨てセッション | キー | 既定 | 説明 | @@ -528,6 +540,7 @@ docker buildx build --platform linux/amd64,linux/arm64 \ | バージョン | Claude Code | 変更内容 | |---|---|---| +| 2.1.263-e | 2.1.263 | リポジトリ URL バリデーションを緩和(`ssh://user@…` / `user@host:path` / `host:path` も受理。ホスト allowlist は従来どおり適用)。エラーメッセージに受理形式と「SSH ユーザーは常に `git`」の注意を明記。UI の入力欄にヒントを追加 | | 2.1.263-d | 2.1.263 | 失敗したセッションの調査性を改善。`session.reaper.terminatedRetentionSeconds`(既定 900)で終了 Pod を即削除せず保持。UI に **「ログ」ボタン**(`GET /api/sessions//log`)を追加し、`Failed` の理由(exitCode / reason)を一覧に表示。セッションブートスクリプトが URL スキームと identity 型の不一致を警告し、`git clone` の出力をそのままログに出すよう変更 | | 2.1.263-c | 2.1.263 | 修正: gateway の nginx が起動時 `pcre2_compile() failed: missing closing parenthesis` でクラッシュループ。`location ~` の正規表現に `{10}` が含まれ、nginx がブロック区切りの `{}` と解釈していた。正規表現全体をダブルクォートで囲んで解消 | | 2.1.263-b | 2.1.263 | `anthropic.authMode: credentials`(既定)を追加。`~/.claude/.credentials.json` 全体を注入し、セッション内 `claude` の自己更新に加え、クラスタ内 refresher CronJob(`refreshToken` で Secret を定期更新)でトークン失効に自動追従。→ トークンが変わっても再デプロイ不要。refresher の RBAC は認証情報 Secret 1 個の get/patch に限定 | diff --git a/files/manager.py b/files/manager.py index 6d851ed..0285fbd 100644 --- a/files/manager.py +++ b/files/manager.py @@ -105,12 +105,19 @@ ANN_PREFIX = "claude-code.cafepieters.com/" SESSION_SELECTOR = LABEL_COMPONENT + "=session," + LABEL_APP + "=claude-code" SID_RE = re.compile(r"^[a-f0-9]{10}$") -# 受け入れる repo URL: https://host/... または ssh://git@host/... または git@host:path +# 受け入れる repo URL: +# https://host[:port]/path +# ssh://[user@]host[:port]/path +# [user@]host:path (SCP 形式。Gitea/GitHub の SSH ユーザーは常に "git") +_USER = r"(?:[A-Za-z0-9_.\-]+@)?" REPO_RE = re.compile( r"^(?:https://(?P

[A-Za-z0-9.\-]+)(?::\d+)?/[\w./\-~%]+" - r"|ssh://git@(?P

[A-Za-z0-9.\-]+)(?::\d+)?/[\w./\-~%]+" - r"|git@(?P

[A-Za-z0-9.\-]+):[\w./\-~%]+)$" + r"|ssh://" + _USER + r"(?P

[A-Za-z0-9.\-]+)(?::\d+)?/[\w./\-~%]+" + r"|" + _USER + r"(?P

[A-Za-z0-9.\-]+):(?!//)[\w./\-~%]+)$" ) +REPO_HELP = ("URL の形式: https:////.git / " + "ssh://git@//.git / git@:/.git " + "(Gitea・GitHub の SSH ユーザーは常に 'git'。あなたのアカウント名ではありません)") REF_RE = re.compile(r"^[\w./\-]{1,200}$") # -------------------------------------------------------------------------- @@ -411,12 +418,13 @@ def create_session(repo, ref, identity_name, owner): repo = (repo or "").strip() ref = (ref or "main").strip() if not REPO_RE.match(repo): - return None, "リポジトリ URL の形式が不正です" + return None, "リポジトリ URL の形式が不正です。" + REPO_HELP if not REF_RE.match(ref): - return None, "ref の形式が不正です" + return None, "ref(ブランチ / タグ)の形式が不正です" host = repo_host(repo) if not GIT_ALLOW_ARBITRARY and host not in GIT_ALLOWED_HOSTS: - return None, "許可されていない Git ホストです: " + str(host) + return None, ("許可されていない Git ホストです: %s(許可済み: %s)" + % (host, ", ".join(GIT_ALLOWED_HOSTS) or "なし")) if identity_name and identity_name not in GIT_IDENTITIES: return None, "未知の git identity: " + identity_name if active_session_count() >= MAX_SESSIONS: @@ -570,7 +578,8 @@ pre#logbox{white-space:pre-wrap;background:#111;color:#eee;padding:.8rem;border-