1- """env カテゴリの TUI 操作フロー (PLAN31_2 PR3)。
1+ """env カテゴリの TUI 操作フロー (PLAN31_2 PR3 → メニュー再構成 )。
22
3- ``devbase env`` の全サブコマンド (init/list/set/get/delete/edit/sync/project/
4- export/import) をトップ階層メニューから実行できるようにする。引数収集は
5- ``tui.menu`` のヘルパで CLI parser (cli.py ``_add_env_parser``) と同じ属性値を
6- 集め、``tui.dispatch.dispatch_group`` 経由で既存ハンドラ ``cmd_env`` へ委譲する
7- (plan 2.3 契約表 / ロジック二重実装なし)。
3+ TUI では参照・対話系の操作のみに絞り、メニュー階層を浅くする:
4+
5+ - 変数一覧はスコープ選択の中間プロンプトを挟まず、グローバル一覧のみを
6+ 即実行する。プロジェクト単位の一覧は TUI から除外する (CLI で実行)。
7+ - キー単位の get/set/delete と export/import も TUI から除外する (CLI で実行。
8+ 値の変更は ``edit`` ($EDITOR) と ``project`` (対話設定) で代替できる)。
9+
10+ 引数収集は ``tui.menu`` のヘルパで CLI parser (cli.py ``_add_env_parser``) と
11+ 同じ属性値を集め、``tui.dispatch.dispatch_group`` 経由で既存ハンドラ
12+ ``cmd_env`` へ委譲する (ロジック二重実装なし)。
813
914project スコープ依存の扱い (plan 3.3):
10- - ``set --project`` / ``project`` / ``list`` (プロジェクトを含む表示範囲) /
11- ``get`` (プロジェクト取得) は CWD (環境変数 ``PWD``) のプロジェクト
12- ディレクトリで動くため、先にプロジェクト選択メニューで対象を選ばせて
13- chdir + ``PWD`` 差し替えしてからハンドラを呼び、実行後は必ず元へ復帰する
15+ - ``project`` (対話設定) は CWD (環境変数 ``PWD``) のプロジェクトディレクトリで
16+ 動くため、先にプロジェクト選択メニューで対象を選ばせて chdir + ``PWD``
17+ 差し替えしてからハンドラを呼び、実行後は必ず元へ復帰する
1418 (``_run_in_project``)。``cmd_env_*`` は ``os.environ.get('PWD', os.getcwd())``
1519 で現在地を判定するため、``os.chdir`` だけでなく ``PWD`` も併せて切り替える。
16- - ``edit`` は plan 3.3 で CWD スコープとされているが、実装 (``cmd_env_edit``) は
17- 常に ``$DEVBASE_ROOT/.env`` を開くグローバル操作のため、プロジェクト選択は
18- 行わない (plan 表と実装の乖離。parser / 実装を正とする)。
19-
20- 破壊的操作 ``delete`` は実行前に確認する (plan 3.4)。
21-
22- export/import は引数が多いため TUI では主要引数 (``dest`` / ``source``) のみ
23- 収集し、残りは CLI parser の既定値と同一の属性を明示的に渡す (既定値の乖離を
24- 防ぐ。細かい制御が必要な場合は CLI を使う想定)。
20+ - ``edit`` は常に ``$DEVBASE_ROOT/.env`` を開くグローバル操作のため、
21+ プロジェクト選択は行わない。
2522
2623中止系の伝搬 (Ctrl-C / Esc / ``_ARG_CANCEL``) は ``tui.flow`` のナビ規約に従う。
2724"""
4239
4340logger = get_logger (__name__ )
4441
45- # env カテゴリで選べる操作 (表示順 = ハイライト既定順)。参照系の list を先頭に
46- # 置き、Enter 連打で安全な一覧表示へ到達できるようにする。各 value は cmd_env の
47- # サブコマンド名。
42+ # env カテゴリで選べる操作 (表示順 = ハイライト既定順)。参照系のグローバル一覧を
43+ # 先頭に置き、Enter 連打で安全な一覧表示へ到達できるようにする (中間プロンプト
44+ # なしで即実行)。プロジェクト単位の一覧と get/set/delete/export/import は
45+ # TUI から除外 (CLI で実行)。
4846_ENV_OPS : list [tuple [str , str ]] = [
49- ("変数一覧 (list)" , "list" ),
50- ("値の取得 (get)" , "get" ),
51- ("変数の設定 (set)" , "set" ),
52- ("変数の削除 (delete)" , "delete" ),
47+ ("変数一覧 (グローバル)" , "list-global" ),
5348 ("エディタで編集 (edit)" , "edit" ),
5449 ("認証情報の再同期 (sync)" , "sync" ),
5550 ("プロジェクト変数の対話設定 (project)" , "project" ),
5651 ("初期セットアップ (init)" , "init" ),
57- ("暗号化バンドルへエクスポート (export)" , "export" ),
58- ("バンドルからインポート (import)" , "import" ),
5952]
6053
6154# 中止系番兵は flow と同一オブジェクトを再公開する (呼び出し側・テストの契約)。
@@ -136,153 +129,10 @@ def _run_in_project(devbase_root: Path, project_name: str, fn):
136129 os .environ ["PWD" ] = old_pwd
137130
138131
139- def _collect_assignment ():
140- """``env set`` の KEY=VALUE を収集する。
141-
142- 形式エラー (``=`` 無し / キー名空) は ``cmd_env_set`` でも弾かれるが、TUI では
143- 実行前に再入力を促す。戻り値: 入力文字列 / ``MENU_BACK`` (Esc → サブメニューへ
144- 戻る) / ``None`` (Ctrl-C → 全体中止)。
145- """
146- while True :
147- raw = menu .text ("設定する変数 (KEY=VALUE 形式)" , allow_empty = False )
148- if raw is None or raw is menu .MENU_BACK :
149- return raw # None=Ctrl-C 全体中止 / MENU_BACK=Esc 戻る
150- if "=" not in raw or not raw .partition ("=" )[0 ].strip ():
151- logger .error ("形式: KEY=VALUE (キー名は必須)" )
152- continue
153- return raw
154-
155-
156- def _export_default_attrs () -> dict :
157- """``env export`` の CLI parser 既定値 (cli.py:246-279) と同一の属性セット。
158-
159- TUI で収集しない引数も Namespace に明示的に載せ、CLI 実行と完全に同じ属性で
160- ハンドラを呼ぶ (getattr 既定値とのズレを防ぐ)。list は呼び出しごとに新規生成。
161- """
162- return {
163- "include_projects" : None ,
164- "exclude_projects" : [],
165- "no_global" : False ,
166- "no_metadata" : False ,
167- "recipients" : [],
168- "passphrase_env" : None ,
169- "passphrase_stdin" : False ,
170- "force_unencrypted" : False ,
171- "unsafe_allow_unencrypted_bucket" : False ,
172- }
173-
174-
175- def _import_default_attrs () -> dict :
176- """``env import`` の CLI parser 既定値 (cli.py:281-328) と同一の属性セット。"""
177- return {
178- "merge" : "keep-existing" ,
179- "replace_keys" : "" ,
180- "replace" : False ,
181- "dry_run" : False ,
182- "identities" : [],
183- "passphrase_env" : None ,
184- "passphrase_stdin" : False ,
185- "include_projects" : None ,
186- "exclude_projects" : [],
187- "no_global" : False ,
188- "no_metadata" : False ,
189- "merge_metadata" : False ,
190- "backup_dir" : None ,
191- "keep_last" : 10 ,
192- }
193-
194-
195- def _select_scoped_project (devbase_root : Path , message : str , choices ):
196- """スコープ選択 + プロジェクトスコープなら対象プロジェクトも選ぶ共通フロー。
197-
198- list/set/get が共有する「グローバル or プロジェクトを選び、プロジェクトを
199- 含むスコープなら対象名も選ぶ」の前半 2 プロンプト。``(scope, name)`` を返す
200- (グローバルのみのとき ``name`` は ``None``)。中止系は flow 例外で伝搬する。
201- """
202- scope = flow .need (menu .select (f"{ message } { menu .HINT_BACK } :" ,
203- choices , back = True , search = False ))
204- name = None
205- if scope != "global" :
206- name = flow .need (_select_project (devbase_root ))
207- return scope , name
208-
209-
210132# ---------------------------------------------------------------------------
211133# 各操作の引数収集 + dispatch (plan 2.3 契約)
212134# ---------------------------------------------------------------------------
213135
214- def _op_list (devbase_root : Path ):
215- """``env list``: 表示範囲を収集して一覧表示する。
216-
217- ハンドラ (``cmd_env_list``) は CWD (PWD) が projects/ 配下のときだけ
218- プロジェクト .env を表示するため、プロジェクトを含む表示範囲
219- (「グローバル + プロジェクト」「プロジェクトのみ」) は対象プロジェクトを
220- 選ばせて chdir + ``PWD`` 切替後に実行する (plan 3.3 / codex round3 指摘。
221- TUI は通常 DEVBASE_ROOT で動くので、切替なしではプロジェクト分が表示
222- されない)。「グローバルのみ」だけが切替なしで実行できる。
223- """
224- scope , name = _select_scoped_project (
225- devbase_root , "表示範囲を選択" ,
226- [("グローバル + プロジェクト" , "both" ),
227- ("グローバルのみ (--global)" , "global" ),
228- ("プロジェクトのみ (--project)" , "project" )])
229-
230- # --reveal / --keys は CLI 既定 (False = 機密値は伏せ字・通常表示) で実行する
231- # (非破壊操作の確認プロンプト廃止)。必要な場合は CLI を使う想定。
232- attrs = {"global_only" : scope == "global" ,
233- "project_only" : scope == "project" ,
234- "reveal" : False , "keys_only" : False }
235- if name is None :
236- return _dispatch (devbase_root , "list" , ** attrs )
237- return _run_in_project (devbase_root , name ,
238- lambda : _dispatch (devbase_root , "list" , ** attrs ))
239-
240-
241- def _op_set (devbase_root : Path ):
242- """``env set``: 設定先 (グローバル / プロジェクト) と KEY=VALUE を収集して設定する。
243-
244- プロジェクト設定 (--project) は対象を選ばせて chdir してから実行する (plan 3.3)。
245- """
246- _ , name = _select_scoped_project (
247- devbase_root , "設定先を選択" ,
248- [("グローバル ($DEVBASE_ROOT/.env)" , "global" ),
249- ("プロジェクト (projects/<name>/.env, --project)" , "project" )])
250- assignment = flow .need (_collect_assignment ())
251-
252- if name is None :
253- return _dispatch (devbase_root , "set" , assignment = assignment , project = False )
254- return _run_in_project (
255- devbase_root , name ,
256- lambda : _dispatch (devbase_root , "set" , assignment = assignment , project = True ))
257-
258-
259- def _op_get (devbase_root : Path ):
260- """``env get``: 取得元 (グローバル / プロジェクト) と変数名を収集して値を表示する。
261-
262- ``cmd_env_get`` はグローバル .env に無いキーを CWD (PWD) のプロジェクト .env へ
263- フォールバックして探すが、TUI は常に DEVBASE_ROOT で動くため、そのままでは
264- プロジェクト固有キーを取得できない。list/set と同様に取得元を選ばせ、
265- プロジェクト選択時は chdir + ``PWD`` 切替後に実行する (codex round2 指摘)。
266- """
267- _ , name = _select_scoped_project (
268- devbase_root , "取得元を選択" ,
269- [("グローバル ($DEVBASE_ROOT/.env)" , "global" ),
270- ("プロジェクト (グローバルに無ければ projects/<name>/.env)" , "project" )])
271- key = flow .need (menu .text ("取得する変数名" , allow_empty = False ))
272-
273- if name is None :
274- return _dispatch (devbase_root , "get" , key = key )
275- return _run_in_project (devbase_root , name ,
276- lambda : _dispatch (devbase_root , "get" , key = key ))
277-
278-
279- def _op_delete (devbase_root : Path ):
280- key = flow .need (menu .text ("削除する変数名" , allow_empty = False ))
281- # 破壊的操作のため実行前に確認する (plan 3.4)。拒否 / Esc は実行せず戻る。
282- flow .confirm_or_back (f"変数 '{ key } ' をグローバル .env から削除しますか?" )
283- return _dispatch (devbase_root , "delete" , key = key )
284-
285-
286136def _op_project (devbase_root : Path ):
287137 # プロジェクト固有変数の対話設定。projects/ 配下で動く CWD スコープ操作の
288138 # ため、対象を選ばせて chdir してから実行する (plan 3.3)。
@@ -291,40 +141,21 @@ def _op_project(devbase_root: Path):
291141 lambda : _dispatch (devbase_root , "project" ))
292142
293143
294- def _op_export (devbase_root : Path ):
295- # 主要引数 dest のみ収集。空入力は parser 既定 (./devbase-env-<TS>.dbenv)。
296- dest = flow .need (menu .path (
297- "出力先パス (空で既定: ./devbase-env-<タイムスタンプ>.dbenv)" ,
298- allow_empty = True ))
299- return _dispatch (devbase_root , "export" , dest = (dest or None ),
300- ** _export_default_attrs ())
301-
302-
303- def _op_import (devbase_root : Path ):
304- # 主要引数 source のみ収集 (必須 positional)。merge は parser 既定の
305- # keep-existing (既存キー優先) で安全側。既存 .env はハンドラ側で
306- # バックアップされる。
307- source = flow .need (menu .path ("インポートするバンドルのパス" , allow_empty = False ))
308- return _dispatch (devbase_root , "import" , source = source ,
309- ** _import_default_attrs ())
310-
311-
312144_OP_HANDLERS = {
145+ # グローバル一覧は引数収集なしで即実行 (chdir 不要)。--reveal/--keys は
146+ # CLI 既定の False (伏せ字・通常表示)。
313147 # sync は引数なしで即実行 (ソースファイルから認証情報を再同期する)。
314148 # edit も引数なし。$DEVBASE_ROOT/.env を $EDITOR で開くグローバル操作のため
315149 # chdir しない (plan 3.3 は CWD スコープとするが実装を正とする)。
316150 # init は --reset なし (CLI 既定) で即実行。セットアップ済みなら
317151 # cmd_env_init が案内を出して安全に終了し、やり直しは CLI --reset を使う。
152+ "list-global" : lambda root : _dispatch (root , "list" , global_only = True ,
153+ project_only = False ,
154+ reveal = False , keys_only = False ),
318155 "sync" : lambda root : _dispatch (root , "sync" ),
319156 "edit" : lambda root : _dispatch (root , "edit" ),
320157 "init" : lambda root : _dispatch (root , "init" , reset = False ),
321- "list" : _op_list ,
322- "set" : _op_set ,
323- "get" : _op_get ,
324- "delete" : _op_delete ,
325158 "project" : _op_project ,
326- "export" : _op_export ,
327- "import" : _op_import ,
328159}
329160
330161
0 commit comments