Commit f1fa837
feat(env): PLAN03-1 PR5 ドキュメント追加 + import/export リファクタ (#20)
* refactor(env): PLAN03-1 PR5 env export/import モジュールを整理
- 711 行に肥大化していた `io_import.py` を以下の 3 モジュールに分割する。
公開 API (`ImportOptions`, `import_bundle`, `ImportError`) は維持し、
テストの `_read_passphrase` 直接 import や `getpass` パッチも継続して動く:
- `io_import.py` (209 行): 引数検証 / 復号判定 / 全体オーケストレーション
- `_import_merge.py`: `Plan` データクラス、`plan_env_merge` / `plan_sources`、
既存コメント・空行を保持した merge ロジック、ログ整形
- `_import_atomic.py`: 2 フェーズ書き込み (`backup_existing` → `write_atomic`
→ `commit`)、`gc_backups`、ロールバック
- export / import で重複していた共通 helper を `io_common.py` に集約する:
- `read_passphrase()` — env / stdin 入力、tty 時の getpass エコー抑止
- `resolve_recipient_specs()` / `resolve_identity_specs()` — 省略時の
`~/.ssh/id_ed25519(.pub)` → `id_rsa(.pub)` fallback
- `write_secure_bytes()` — `os.open(O_WRONLY|O_CREAT|O_TRUNC, mode=0o600)` で
TOCTOU を避けてセキュアにバイト列を書き出す共通実装。`storage.LocalBackend`
と `_import_atomic` から呼び出す
- `_plan_env_merge()` の 4 段ネスト if/elif を 4 つの小さな関数
(`_plan_replace` / `_plan_replace_keys` / `_plan_keep_existing`
/ `_plan_prefer_incoming`) に分割し、`plan_env_merge` 本体はモード選択のみに
簡素化する
- `storage.LocalBackend.write_bytes` を `io_common.write_secure_bytes`
への薄いラッパに置き換え、重複していた `os.open` + chmod パターンを削除
- `io_export.py` (185 → 168 行) の `_read_passphrase` / `_resolve_recipients`
は `io_common` への delegation に置き換え、`encrypt_payload` / `validate_options`
の helper 関数に export 本体のロジックを分解
- `_commit()` 移動に伴い、テスト 3 件の `monkeypatch.setattr(_io_import.os, 'replace', ...)`
パッチ先を `_import_atomic.os` に更新。`log_plans` 移動に伴う caplog の
logger 名も `devbase.env._import_merge` に追従
リファクタの動機:
- io_import.py が PR1〜PR3 を通じて 711 行まで肥大化し、merge 計画 / atomic 書き込み
/ orchestration が同居して読みづらかった
- io_export と io_import で `_read_passphrase` / 既定鍵 fallback / セキュア書き込みが
ほぼ重複していた
挙動の変更は無く、全 136 テストが引き続き green を維持する。
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* docs(env): PLAN03-1 PR5 env export/import 利用者向けガイドを追加
`docs/user/env-export-import.md` (456 行) を新設し、以下を網羅する:
- 対象ファイル一覧 (global / projects/*/.env / .env.sources.yml) と
公開可能な雛形 (`projects/*/env`) を含めない設計理由
- クイックスタート (既定鍵での export → 別マシンでの import)
- バンドル構造 (manifest.yml の sha256 検証、version 互換ポリシー)
- age 暗号化: recipient / identity / passphrase の 3 方式、対応鍵種別表、
ssh-ecdsa 非対応への対処 (`age-keygen` / `ssh-keygen -t ed25519`)、
既定鍵 (`~/.ssh/id_ed25519` → `id_rsa`)
- 入出力先: ローカル / stdio / S3。S3 の SSE 強制と
`--unsafe-allow-unencrypted-bucket`、`DEVBASE_S3_*` 環境変数
- export / import の全オプション表、merge モード (keep-existing /
prefer-incoming / --replace-keys / --replace) の動作比較
- `.env.sources.yml` の取り扱い (既定スキップ + 参照用コピー、
`--merge-metadata` での新規エントリ追加)
- 2 フェーズ書き込み + backup + ロールバックの仕組み、
`--keep-last N` での GC、ACID 非保証の注意
- 典型ワークフロー 4 件 (別マシン移行 / 定期バックアップ / S3 チーム共有 / CI 配布)
- トラブルシューティング 8 件
加えて以下のリンクを追加:
- `README.md`: 「利用者向け」ドキュメント表に env-export-import.md への
リンクを追加し、`env` グループの説明に export / import を併記
- `docs/user/environment-variables.md`: 「別マシンへの移行 / バックアップ」
節を新設して env-export-import.md へ誘導、ベストプラクティスに追記
- `CHANGELOG.md` (Unreleased): docs 追加とリファクタリングを記載
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(env): warning ログの文面矛盾を解消 (unsafe フラグ続行時)
`_verify_bucket_encryption` で `--unsafe-allow-unencrypted-bucket` 指定時に
「export を中止します。(unsafe フラグにより続行)」という矛盾した警告が出ていた。
問題説明 (problem) と対処案内 (guidance) を分離し、warning は problem のみ、
StorageError raise 時は問題+対処案内を出すよう統一。
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>1 parent 9c419fc commit f1fa837
11 files changed
Lines changed: 1279 additions & 702 deletions
File tree
- docs/user
- lib/devbase/env
- tests/cli
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
10 | 10 | | |
11 | 11 | | |
12 | 12 | | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
13 | 16 | | |
14 | 17 | | |
15 | 18 | | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
16 | 22 | | |
17 | 23 | | |
18 | 24 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
76 | 76 | | |
77 | 77 | | |
78 | 78 | | |
79 | | - | |
| 79 | + | |
80 | 80 | | |
81 | 81 | | |
82 | 82 | | |
| |||
106 | 106 | | |
107 | 107 | | |
108 | 108 | | |
| 109 | + | |
109 | 110 | | |
110 | 111 | | |
111 | 112 | | |
| |||
Large diffs are not rendered by default.
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
240 | 240 | | |
241 | 241 | | |
242 | 242 | | |
| 243 | + | |
| 244 | + | |
| 245 | + | |
| 246 | + | |
| 247 | + | |
| 248 | + | |
| 249 | + | |
| 250 | + | |
| 251 | + | |
| 252 | + | |
| 253 | + | |
| 254 | + | |
243 | 255 | | |
244 | 256 | | |
245 | 257 | | |
246 | 258 | | |
247 | 259 | | |
248 | 260 | | |
| 261 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
| 92 | + | |
| 93 | + | |
| 94 | + | |
| 95 | + | |
| 96 | + | |
| 97 | + | |
| 98 | + | |
| 99 | + | |
| 100 | + | |
| 101 | + | |
| 102 | + | |
| 103 | + | |
| 104 | + | |
| 105 | + | |
| 106 | + | |
| 107 | + | |
| 108 | + | |
| 109 | + | |
| 110 | + | |
| 111 | + | |
| 112 | + | |
| 113 | + | |
| 114 | + | |
| 115 | + | |
| 116 | + | |
| 117 | + | |
| 118 | + | |
| 119 | + | |
| 120 | + | |
| 121 | + | |
| 122 | + | |
| 123 | + | |
| 124 | + | |
| 125 | + | |
| 126 | + | |
| 127 | + | |
| 128 | + | |
| 129 | + | |
| 130 | + | |
| 131 | + | |
| 132 | + | |
| 133 | + | |
| 134 | + | |
| 135 | + | |
| 136 | + | |
| 137 | + | |
| 138 | + | |
| 139 | + | |
| 140 | + | |
| 141 | + | |
| 142 | + | |
| 143 | + | |
| 144 | + | |
| 145 | + | |
| 146 | + | |
| 147 | + | |
| 148 | + | |
| 149 | + | |
| 150 | + | |
| 151 | + | |
| 152 | + | |
| 153 | + | |
| 154 | + | |
| 155 | + | |
| 156 | + | |
| 157 | + | |
| 158 | + | |
| 159 | + | |
| 160 | + | |
| 161 | + | |
| 162 | + | |
| 163 | + | |
| 164 | + | |
| 165 | + | |
| 166 | + | |
| 167 | + | |
| 168 | + | |
| 169 | + | |
| 170 | + | |
| 171 | + | |
| 172 | + | |
| 173 | + | |
| 174 | + | |
| 175 | + | |
| 176 | + | |
| 177 | + | |
| 178 | + | |
| 179 | + | |
| 180 | + | |
| 181 | + | |
| 182 | + | |
| 183 | + | |
| 184 | + | |
| 185 | + | |
| 186 | + | |
| 187 | + | |
| 188 | + | |
| 189 | + | |
| 190 | + | |
| 191 | + | |
| 192 | + | |
| 193 | + | |
| 194 | + | |
| 195 | + | |
| 196 | + | |
| 197 | + | |
| 198 | + | |
| 199 | + | |
| 200 | + | |
| 201 | + | |
| 202 | + | |
| 203 | + | |
| 204 | + | |
| 205 | + | |
| 206 | + | |
| 207 | + | |
| 208 | + | |
0 commit comments