feat(env): PLAN03-1 PR2 devbase env import#15
Conversation
`devbase env import` を追加し、export で生成したバンドル (age 暗号化済み or 平文 tar.gz) から `.env` 群を復元できるようにする。 主な機能: - merge セマンティクス: --merge keep-existing (既定) / prefer-incoming - --replace-keys: 指定キーのみ上書き - --replace: 対象 .env を丸ごと差し替え (backup 取得) - --dry-run: 差分プレビュー (書き込みなし) - 2 フェーズ書き出し (prepare → commit) で部分適用を最小化、失敗時は backup から best-effort で rollback - --backup-dir / --keep-last N (既定 10) で backup を GC - .env.sources.yml は既定で上書きせず参照用コピーのみ、--merge-metadata で 新規 source エントリのみ追加、--no-metadata で完全無視 - 暗号化判定: gzip magic で平文 / age 暗号化を識別 - 引数バリデーション: SOURCE='-' と --passphrase-stdin の併用拒否、 --passphrase-env と --passphrase-stdin の併用拒否、--replace と --replace-keys の併用拒否 - 既定 identity: ~/.ssh/id_ed25519 → ~/.ssh/id_rsa の順で探索 テスト (tests/cli/test_env_import.py, 17 ケース): - export → import の round-trip、0600 permission 保持、LF 保持 - merge モード別の挙動 (keep-existing / prefer-incoming / replace-keys / replace)、dry-run が変更しないこと、replace 時の backup 作成 - --include-project / --no-metadata / --merge-metadata の挙動 - 未知 manifest version のバンドル拒否、--keep-last による古い backup GC - 平文バンドル import、passphrase round-trip Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
takemi-ohama
left a comment
There was a problem hiding this comment.
🤖 cross-review | round 1 | codex | REQUEST_CHANGES
上記のデータ損失、部分適用、merge セマンティクスの修正が必要です。
takemi-ohama
left a comment
There was a problem hiding this comment.
🤖 cross-review | round 1 | gemini | REQUEST_CHANGES
本PRは env import 機能を追加するものですが、バックアップの自動削除ロジックにおける安全性や、特定キー上書き時のマージ挙動に重大な問題が見つかりました。以下の指摘事項の修正をお願いします。
cross-review round 1 で指摘されたデータ損失・部分適用・merge 不整合を修正: - --replace-keys 指定外でも既存に無い incoming 新規キーは追加するように修正 (CLI help の "other keys behave like keep-existing" に整合) - _rollback で op='create' なターゲットは backup が無くても unlink し、 commit 途中失敗時の部分適用残骸を消す - _gc_backups は devbase が生成する timestamp 形式 (YYYYMMDD-HHMMSS) の ディレクトリのみを削除対象にし、--backup-dir 親に置かれた無関係なファイル /ディレクトリを誤って消さないようにする - EnvFile.parse_bytes(data) を新設し、io_import の bytes パースを 一時ファイル経由から直接パースに置き換え (I/O 削減 + 例外安全) - 上記 3 件分の回帰テストを追加 Refs: codex review #4336666744 / gemini review #4336672519
fix-pr round 1 サマリ (--defer-nit)cross-review round 1 (codex review #4336666744 / gemini review #4336672519) で指摘された全 7 件に対応しました。 重要度別件数 (独自再判定後)
修正内容 (commit 62363d7)
重複指摘の集約
いずれも対応コミットを同一 reply で全 thread に通知し、修正済み thread を Resolve しました。 テスト
CI 状態 (push 時点スナップショット)
|
takemi-ohama
left a comment
There was a problem hiding this comment.
🤖 cross-review | round 2 | gemini | REQUEST_CHANGES
ロールバック時の新規ファイル削除(sources.yml 対応)と、失敗時の一時ファイルクリーンアップの徹底を提案します。
takemi-ohama
left a comment
There was a problem hiding this comment.
🤖 cross-review | round 2 | codex | REQUEST_CHANGES
import は .env の値を変更せず復元できることが中核要件なので、merge/create 経路の parser/formatter round-trip を修正してください。あわせて CLI 追加分の completion と、バックアップディレクトリ名の衝突回避も対応が必要です。
PR #15 round 2 のクロスレビュー指摘 (codex 3 件 + gemini 2 件) に対応。 * EnvFile.parse_bytes に double-quote 内 escape の逆変換 (state machine) を 追加し、save / _format_env_bytes との round-trip を成立させる。これにより backslash / quote / 改行を含む値が import → export を経て二重エスケープ される問題を解消する。 * _plan_env_merge の create パスでは _format_env_bytes による再シリアライズを 避け、incoming_bytes をそのまま採用する。バンドル側のバイト列を完全に 保持し、parse/format に潜む副作用を確実に排除する。 * _rollback で「backup が無い = 元ファイル不在」のケースを op に関係なく unlink するように変更。op='sources-merge' で sources.yml を新規作成した ロールバックでも残骸が残らなくなる。 * _commit 失敗時に、まだ rename されていない .import.tmp ファイルを try-finally で確実に削除する。 * _make_backup_dir に microsecond + 連番フォールバックを付与し、同一秒内に import が複数回走っても backup ディレクトリが衝突しない。 * etc/devbase-completion.bash と etc/_devbase の env サブコマンド一覧に import を追加し、各オプションも補完できるようにする。 テストでは parse_bytes round-trip / create 経路の byte preservation / sources.yml rollback unlink / tmp cleanup / backup 衝突回避を検証する。 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Round 2 修正サマリ (PR #15)クロスレビュー round 2 で挙がった codex 3 件 + gemini 2 件、計 5 件にすべて対応しました。 修正コミット
対応内訳 (5/5 修正済み, deferred: 0, rejected: 0)
追加テスト (6 件)
ローカル品質チェック
|
takemi-ohama
left a comment
There was a problem hiding this comment.
🤖 cross-review | round 3 | codex | APPROVE
修正必須の指摘はありません。対象テストは uv run pytest /tmp/devbase-worktrees/pr15/tests/env /tmp/devbase-worktrees/pr15/tests/cli/test_env_export.py /tmp/devbase-worktrees/pr15/tests/cli/test_env_import.py /tmp/devbase-worktrees/pr15/tests/cli/test_prefix_resolution.py で 98 passed です。
takemi-ohama
left a comment
There was a problem hiding this comment.
🤖 cross-review | round 3 | gemini | REQUEST_CHANGES
env import における既存の .env ファイルのマージ処理とエスケープ処理に、データの正確性を損なう可能性のある問題が見つかりました。特に、マージ時に既存のコメントや空行がすべて削除されてしまう点と、シェル変数展開を防ぐための $ のエスケープが不足している点は、破壊的な影響を及ぼす可能性があるため修正が必要です。
- ``EnvFile.parse_entries`` を追加し、コメント / 空行 / kv を行単位で トークン化できるようにする (PR #15 gemini 指摘)。 - ``_merge_into_existing_bytes`` で既存 ``.env`` のコメント・空行・キー順を 保持したまま値を差し替えるよう merge 経路を変更。``EnvFile.dump_bytes`` への単純差し替えではコメントが失われていた。 - ダブルクオート値に含まれる ``$`` を ``\$`` にエスケープし、``parse_bytes`` 側でも ``\$`` を ``$`` に戻すように round-trip 対応 (シェル ``source`` 時の 意図しない変数展開を防止 / PR #15 gemini 指摘)。 - ``EnvFile.dump_bytes`` / ``EnvFile.dump_entries_bytes`` にフォーマット ロジックを集約し、``io_import._format_env_bytes`` を廃止。``EnvFile.save`` も ``dump_bytes`` 経由に統一して二重実装を解消 (PR #15 gemini 指摘)。 - 新規テスト: コメント保持マージ (prefer-incoming / keep-existing)、 ``$`` の round-trip、``EnvFile.dump_bytes`` のエスケープ仕様を追加。
ndf:fix round 3 サマリgemini round 3 の指摘 3 件 (major×2, minor×1) を全て対応しました。codex round 3 は APPROVE で対象なし。 修正コミット
対応内容
新規テスト
ローカル検証
Resolve 済み thread
deferred / rejected はなし。再レビューをお願いします。 |
takemi-ohama
left a comment
There was a problem hiding this comment.
🤖 cross-review | round 4 | codex | APPROVE
修正要求に該当する指摘はありません。
takemi-ohama
left a comment
There was a problem hiding this comment.
🤖 cross-review | round 4 | gemini | REQUEST_CHANGES
PR #15 では、.env ファイルのマージ時にコメントや空行を保持する仕組みが導入され、全体の堅牢性が向上しています。特に $ のエスケープ対応はシェル環境での安全な利用に寄与する重要な改善です。
一方で、実装面でコードの重複がいくつか見受けられます。保守性の観点から、共通処理の括り出しを検討してください。
各マージ戦略 (replace_keys / keep-existing / prefer-incoming) で個別に書かれていた new_bytes 生成と _Plan 構築を `_build_merge_plan` にまとめた。各分岐は merged / added / overwritten / skipped の計算だけを担当するように整理。 動作変更なし (PR #15 gemini round4 指摘 / 既存テスト 102 件 PASS)。
fix round 4 サマリこのラウンドの対応
レビュー結果
対応内容
ローカル品質チェック
解決済みスレッド
|
takemi-ohama
left a comment
There was a problem hiding this comment.
🤖 cross-review | round 5 | gemini | REQUEST_CHANGES
共通処理の _build_merge_plan への括り出しによりコードの見通しは改善されましたが、ファイルの存在判定に existing (パース済み KV 辞書) を使用している箇所に重大なバグがあります。コメントのみの .env ファイルに対してマージを行う際、既存のコメントが全て失われる設計になっています。
takemi-ohama
left a comment
There was a problem hiding this comment.
🤖 cross-review | round 5 | codex | REQUEST_CHANGES
既存prefix互換とコメント保持の回帰があるため、該当箇所の修正をお願いします。
- _build_merge_plan / _plan_env_merge / replace ブランチの op 判定を existing (key=value dict) の有無から target.exists() に変更。 コメント / 空行のみで構成された既存 .env が「create」と誤判定されて incoming_bytes で上書きされ既存コメントが失われる問題を修正 (PR #15 round5 codex/gemini 指摘)。 - SUBCMD_PREFIX_PREFERENCES に i: init を追加。import 追加で devbase env i が init / import の両方にマッチして ambiguous に なっていたため、既存ショートカット (devbase env i → init) を維持する (PR #15 round5 codex 指摘)。 - 回帰テスト追加: - tests/cli/test_env_import.py: コメント/空行のみの既存 .env が prefer-incoming / keep-existing / replace-keys でコメント保持される こと。--replace ブランチでも op='replace' として報告され backup が 取られること - tests/cli/test_prefix_resolution.py: devbase env i → init, devbase env im → import, devbase env in → init Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
PR #15 Round 5 修正対応サマリcross-review round 5 で挙がった指摘 (gemini critical/major/minor + codex minor x2、計 5 thread) に対応しました。実体は 2 個のバグ (バグ A は 4 thread 重複指摘): 修正コミット
バグ A: コメント / 空行のみの既存 .env が create 扱いされる (4 thread)
判定基準を 回帰テストを
バグ B:
|
| 区分 | 件数 |
|---|---|
| 対応 (critical) | 1 |
| 対応 (major) | 1 |
| 対応 (minor) | 3 |
| deferred (nit) | 0 |
| rejected | 0 |
| 合計 thread | 5 (resolve 済み) |
ローカル品質チェック
uv run pytest: 109 passeduv run python -m compileall lib bin: OKuvx ruff check --select=E9,F63,F7,F82 lib: All checks passed
CI 状態: PR にはチェックが登録されていない (gh pr checks 15 → no checks reported)。
takemi-ohama
left a comment
There was a problem hiding this comment.
🤖 cross-review | round 6 | codex | APPROVE
修正が必要な指摘はありません。
takemi-ohama
left a comment
There was a problem hiding this comment.
🤖 cross-review | round 6 | gemini | APPROVE
PR #15 の実装を確認しました。env import の機能追加、アトミックな書き換えとロールバック、コメント・空行の保持ロジックは非常に堅牢に実装されており、テストも網羅的です。既存の env export (release/PLAN03-1 で実装済み) との round-trip 整合性も確保されています。
以下の 2 点、軽微な改善提案がありますが、機能自体は完成しているため APPROVE とします。
| raise ImportError(f"環境変数 {opts.passphrase_env} が空または未設定です") | ||
| return value | ||
| if opts.passphrase_stdin: | ||
| import sys |
There was a problem hiding this comment.
[minor / メンテナンス性] import sys はファイル先頭のインポートセクションに移動することを検討してください。
| import sys | ||
| if sys.stdin.isatty(): | ||
| print("passphrase: ", end='', file=sys.stderr, flush=True) | ||
| line = sys.stdin.readline() |
There was a problem hiding this comment.
[minor / セキュリティ] TTY の場合、パスフレーズ入力時のエコーバックを避けるために getpass.getpass() の使用を検討してください。
Summary
issues/PLAN03-1.mddevbase env importサブコマンド実装 (Local + Stdio)スコープ
lib/devbase/env/io_import.py新設--merge keep-existing(既定): 既存キーを保持、新規キーのみ追加--merge prefer-incoming: バンドル側の値で上書き--replace-keys KEY,...: 指定キーのみ上書き--replace: 対象.envを丸ごと差し替え (backup 取得)--dry-runで差分プレビュー--backup-dir/--keep-last N(既定 10) で backup GC.env.sources.yml取り扱いポリシー--no-metadata: 完全無視--merge-metadata: 新規 source のみ追加 (マシン固有値は再計算)SOURCE='-'と--passphrase-stdinの併用拒否Test plan
.env完全一致--dry-runが.envを変更しないこと--replaceで backup ディレクトリが作成されること0600を維持すること--keep-last N後に古い backup が削除されていること