Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,4 @@ plugins/*/
projects/*
!projects/.gitkeep
.env.sources.yml
issues/
.cache/
339 changes: 339 additions & 0 deletions issues/PLAN03-1.md

Large diffs are not rendered by default.

400 changes: 400 additions & 0 deletions issues/PLAN04_plugin-repo-persistence.md

Large diffs are not rendered by default.

256 changes: 256 additions & 0 deletions issues/PLAN06_project-subcommand.md

Large diffs are not rendered by default.

15 changes: 15 additions & 0 deletions issues/i04.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# 開発AIエージェント向け指示書

* このドキュメントは開発AIエージェント向けの指示書です。
* チャットで「docs/cmd01.mdのx番を実行してください」と言われたらこのファイルを読み、見出しに書いてある番号の内容を実行してください。
* aiからのレポートはこのファイルに書き込むのではなく、issues/にファイルを作って出力してください

# 1. pluginsの .git保持
* 現在、devbase plugin installでは、対象となるpluginをplugins/ディレクトリに格納しています。
* しかし、これではgitリポジトリの情報(.git)を引き継がないため、プラグインの修正や機能追加が非常にやりづらいです。
* そこで、devbase plugin repo add https://github.com/devbasex/devbase-samples.git でリポジトリを登録した際、repos/ディレクトリにgit cloneする仕様に変更して下さい
* devbase plugin install はこのリポジトリからprojectsフォルダにシンボリックリンクを張ることになります
* 必然的にpluginsディレクトリは廃止となるはずです
* シンボリックリンクなので、プラグインの変更commit/pushは、repos/のリポジトリをそのままcommit/pushするだけになります。
* ただし、.envなどの機密情報や.docker-compose.scale.ymlなどの一時ファイルは厳密に.gitignoreする必要があります。
* planを作成してissues/に保存してください。
Empty file added issues/i05.md
Empty file.
81 changes: 81 additions & 0 deletions issues/i06.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
# i06: `devbase project` サブコマンド導入 — プロジェクト名指定起動 + 一覧選択

## 背景

現在 `devbase up` は CWD のディレクトリ名 (または `COMPOSE_PROJECT_NAME`) でプロジェクトを特定する。
そのため、プロジェクトを起動するには毎回 `cd projects/<name>` してから `devbase up` する必要がある。

また `devbase container up` というコマンド名は Docker コンテナの操作という実装詳細を露出しており、
ユーザが意図する「プロジェクトを立ち上げる」という操作とずれている。

## 提案

### 1. `devbase project up <project_name>` でプロジェクト起動

```bash
# 現状: cd が必要
cd projects/adminer
devbase up

# 新方式: どこからでもプロジェクト名で起動
devbase project up adminer
```

- `projects/` ディレクトリ内のシンボリックリンク名をプロジェクト名として解決する
- 引数省略時は現状と同じく CWD ベースにフォールバック

### 2. `devbase project list` でプロジェクト一覧 + 選択式起動

```bash
devbase project list
# → プロジェクト一覧を表示

devbase project list --interactive
# → 一覧から選択して起動 (inquirer / simple_term_menu 等)
```

表示例:
```
NAME PLUGIN STATUS
adminer adminer running
carmo carmo-web stopped
carmo.takemi personal stopped (※ PLAN04 衝突 suffix)
```

### 3. コマンド体系のリネーム

| 現状 | 新方式 | 備考 |
|---|---|---|
| `devbase container up` | `devbase project up [name]` | プロジェクト名引数を追加 |
| `devbase container down` | `devbase project down [name]` | 同上 |
| `devbase container ps` | `devbase project ps` / `devbase project list` | 一覧と統合 |
| `devbase container login` | `devbase project login [name]` | 同上 |
| `devbase container logs` | `devbase project logs [name]` | 同上 |
| `devbase container scale` | `devbase project scale [name]` | 同上 |
| `devbase container build` | `devbase project build [name]` | 同上 |
| `devbase up [name]` (ショートカット) | `devbase project up [name]` のシノニム | 引数なし時は CWD フォールバック |
| `devbase list` (ショートカット) | `devbase project list` のシノニム | |
| `devbase container` (エイリアス `ct`) | 非推奨化 → 将来削除 | 移行期間中はエイリアスとして残す |

## シノニム (トップレベルショートカット)

トップレベルコマンドは `devbase project *` のシノニムとして同じ引数を受け入れる:

| ショートカット | 実体 |
|---|---|
| `devbase up [name]` | `devbase project up [name]` |
| `devbase down [name]` | `devbase project down [name]` |
| `devbase list` | `devbase project list` |
| `devbase ps` | `devbase project ps` |
| `devbase login [name]` | `devbase project login [name]` |
| `devbase build [name]` | `devbase project build [name]` |
| `devbase scale [name] N` | `devbase project scale [name] N` |

## 後方互換性

- `devbase container *` は非推奨 warning を出しつつ `devbase project *` に委譲
- 移行期間 (1〜2 リリース) 後に `container` サブコマンドを削除

## 依存

- PLAN04 (repos/ 永続クローン) の同名衝突 suffix が `project list` の表示に影響するため、PLAN04 完了後が望ましい
82 changes: 82 additions & 0 deletions issues/i28_docker-init-zombie-reap.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
# i28: Docker コンテナのゾンビプロセス蓄積を `init: true` 注入で解消する

## 関連リンク

- Issue: https://github.com/devbasex/devbase/issues/28
- 関連 issue: https://github.com/devbasex/ai-plugins/issues/21

## 概要

`generate_scaled_compose()` が生成する各サービスに `init: true` を `setdefault` で注入し、
docker がコンテナ PID 1 に tini を挿入するようにする。これにより orphan プロセスが自動 reap され、
ゾンビ (`<defunct>`) の蓄積が解消される。

## 問題・背景

- devbase コンテナの PID 1 は entrypoint の `tail -f /dev/null` であり、SIGCHLD を受けても orphan を reap しない。
- このため `nohup ... & disown` で起動・終了したプロセスがゾンビ化して蓄積する。
- 特に `ndf:cross-review` skill は codex/gemini CLI を `nohup & disown` で起動し `monitor.py` で監視するため、
以下の二次被害が出る:
1. ゾンビに対しても `kill -0` が成功し、`monitor.py` が「実行中」と誤判定 → hard timeout (420s) まで待たされる
2. PID 1 が reap しないためコンテナ再起動まで蓄積し続ける
- Docker Compose の `init: true` は 20KB の軽量 init (tini) を PID 1 として挿入し、シグナル転送 + ゾンビ reap を行う。
`PR_SET_CHILD_SUBREAPER` より単純で確実。

### 注入箇所が `generate_scaled_compose()` で十分な根拠

`devbase up` (`cmd_up`) は **常に** `generate_scaled_compose()` を呼び、生成した
`.docker-compose.scale.yml` **単独** で `docker compose up` する (`lib/devbase/commands/container.py:175,179`)。
scale=1 でも同経路を通るため、ここへの注入で全 `devbase up` ケースを網羅できる。
ベース `compose.yml` テンプレート側の変更は不要。

## 修正対象

- `lib/devbase/volume/compose.py` — `generate_scaled_compose()` の 2 つのサービス生成ループ
- `tests/volume/test_compose.py` (新規) — `init` 注入のユニットテスト
- `tests/volume/__init__.py` (新規) — テストパッケージ初期化

## タスク分解

### Task 1: dev インスタンスへの `init` 注入

- **対象ファイル:** `lib/devbase/volume/compose.py`
- **変更内容:** dev インスタンス複製ループ (`for i in range(1, scale + 1)` 内) で
`service.setdefault('init', True)` を追加する。`setdefault` のため、ユーザーが明示的に
`init: false` を指定していれば尊重して上書きしない。

### Task 2: non-dev サービスへの `init` 注入

- **対象ファイル:** `lib/devbase/volume/compose.py`
- **変更内容:** non-dev サービス複製ループ (`for service_name, service_config in services.items()` 内) で
`copied.setdefault('init', True)` を追加する。mysql / valkey 等にも tini を挿入し安全側に倒す。

### Task 3: ユニットテスト追加

- **対象ファイル:** `tests/volume/test_compose.py` (新規), `tests/volume/__init__.py` (新規)
- **変更内容:** 一時 `compose.yml` を用意して `generate_scaled_compose()` を呼び、生成された
`.docker-compose.scale.yml` を読み戻して以下を検証する:
- dev-1 (および scale>1 の各 dev-i) に `init: true` が付く
- non-dev サービス (例: mysql) に `init: true` が付く
- 明示的に `init: false` を指定したサービスは `false` のまま (setdefault の尊重)

## 影響範囲

- `devbase up` / `devbase scale` 経由で生成される全コンテナ。挙動は「PID 1 が tini になる」のみで、
entrypoint (`tail -f /dev/null`) は tini の子プロセスとして従来どおり動作する。後方互換。
- `init: false` を明示指定済みのプロジェクトには影響しない。

## テスト計画

- [ ] `pytest tests/volume/test_compose.py` が通る (init 注入 / false 尊重)
- [ ] 既存テストにリグレッションがない (`pytest tests/`)
- [ ] (手動) `devbase up && devbase login` 後 `ps -p 1 -o comm=` が `tini` を返す
- [ ] (手動) `nohup sleep 1 & disown; sleep 3; ps aux | grep 'Z.*defunct'` がゾンビを出さない

## PR 計画 (単一 PR)

| 項目 | 値 |
|---|---|
| 種別 | 単一 PR (release ブランチ不要) |
| branch 名 | `fix/i28-docker-init-zombie-reap` |
| base | `main` |
| 根拠 | 変更は 1 実装ファイル + テストのみ・結合度低・依存タスクなし (差分 ~60 行) |
122 changes: 122 additions & 0 deletions issues/i29_list-tui-simple-term-menu.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
# `devbase list` 対話選択の TUI 化 設計書

- 日付: 2026-06-07
- 対象: `devbase list` / `devbase project list --interactive` の対話選択 UI
- 関連: PR #39 (対話選択をデフォルト化), PR #40 (status 集計修正)

## 1. 目的 / 背景

現在 `devbase list`(デフォルトで対話モード)は stdlib `input()` ベースで、
`[1] name (plugin, status)` の番号一覧を表示し番号入力で 1 件選択 → `project up` を起動する。
矢印キーによる行移動がなく、視認性・操作性が低い。

本変更では、CLI 用の著名ライブラリ **simple-term-menu** を導入し、以下を実現する。

- ↑↓ 矢印キーによる行移動
- 番号(先頭 9 件のショートカット)による該当行への即ジャンプ&選択
- `/` によるインクリメンタル検索(38 件規模での実用的な絞り込み)

「自作せず著名ライブラリを使う」というユーザ方針に従う。
(現コードのコメントにある「`simple_term_menu` 等の外部依存を増やさず」という旧方針を本変更で転換する。)

## 2. 対象環境 / 制約

- macOS / Linux のみ対応(Windows ネイティブ端末は対象外)。
simple-term-menu は Unix 系専用であり本制約と一致する。
- 実行経路: `bin/devbase` → `uv run --project "$DEVBASE_ROOT" python -m devbase.cli`。
依存は `pyproject.toml` + `uv.lock` 管理で `.venv` に解決されるため、追加のブートストラップは不要。
- プロジェクト数は実運用で数十件(現状 38 件)。多桁番号の任意行直接ジャンプは
どの著名ライブラリもネイティブ非対応であり、`[1-9]` ショートカット + `/` 検索で代替する。

## 3. 変更範囲

- `pyproject.toml`: `dependencies` に `simple-term-menu>=1.6` を追加。`uv lock` で `uv.lock` 更新。
- `lib/devbase/commands/project.py`: `_interactive_select_and_up` を TUI 化。
- `list_projects` / `_print_table` / `cmd_project_list` のディスパッチ・非 TTY 判定は現状維持。
- `tests/cli/test_project_list.py`: 主経路を TUI ラッパ注入に更新 + 追加ケース。

`commands/status.py` 等の状態集計ロジックには手を入れない。

## 4. 詳細設計

### 4.1 関数構成

`commands/project.py` に以下を導入する。

- `_build_menu_entries(rows) -> tuple[list[str], list[str]]`
桁揃えした表示文字列リストと、それに対応する name リストを返す純粋関数。
先頭 9 件には simple-term-menu のショートカット記法 `[1]`〜`[9]` を付与する。
STATUS は色付け(4.3 参照)。テスト容易性のため副作用なし。
- `_show_menu(rows) -> int | None`
`TerminalMenu` を構築し `show()` の結果(選択 index / 中止時 None)を返す薄いラッパ。
テストはこの関数を monkeypatch して選択を注入する(TerminalMenu 自体は起動しない)。
- `_fallback_select(rows) -> int | None`
現行 `input()` 番号入力ロジックを関数として温存。選択 index / 中止 None を返す。
- `_interactive_select_and_up(rows) -> int`
上記を統合。simple-term-menu の import 可否で経路分岐し、選択 index から
`cmd_project up <name>` を起動(現状と同じ委譲)。

### 4.2 TerminalMenu の挙動

- 各行: `NAME PLUGIN STATUS`(桁揃え)。先頭 9 件は `[n]` ショートカット付き。
- 設定:
- `cycle_cursor=True`(端で循環)
- `clear_screen=False`(スクロールバックを汚さない)
- 検索キー `/`、`show_search_hint=True`
- `status_bar` に操作ヒント(矢印 / 番号 / `/` 検索 / Enter / ESC)
- キー操作:
- ↑↓: 行移動
- `1`〜`9`: 該当行へジャンプ(先頭 9 件)
- `/`: 名前のインクリメンタル検索
- Enter: 確定 → `cmd_project up <name>`
- ESC / `q`: 中止(`show()` が None を返す → 戻り値 0)

### 4.3 STATUS 色付け

- `running (N containers)` 系 = 緑、`stopped` = 灰、`unknown` = 既定色、で視認性を上げる。
- リスク: simple-term-menu はメニュー項目内の ANSI エスケープで表示幅計算や
ハイライトバーがずれる場合がある。
- 方針: **実装時に実機(Unix TTY)で検証**し、
- 桁揃え・ハイライト・検索が破綻しない → 色付きで採用
- 破綻する → STATUS はプレーンに自動デグレード(機能優先・色は諦める)
- 色付けの有無に関わらず矢印 / 番号 / 検索の核機能を最優先で保証する。
- 非 TTY フォールバックの `_print_table` は従来どおりプレーン(パイプ安全)。

### 4.4 フォールバック / 堅牢性

- 非 TTY(stdin/stdout いずれかが非 TTY): 既存 `isatty` ガードで `_print_table` 表示(現状維持)。
- `import simple_term_menu` 失敗時: `logger.warning` の上で `_fallback_select`(現行 input 方式)へ。
→ simple-term-menu 未同期環境でも従来どおり番号入力で選択可能。
- 非対話(EOFError)/ Ctrl+C / 空入力 / 範囲外: 現行と同じ中止・再入力挙動を維持。

## 5. テスト設計

`tests/cli/test_project_list.py` を更新する。

- 主経路(TUI):
- `_show_menu` を monkeypatch して index を返す → `cmd_project up <name>` が正しい name で呼ばれる。
- `_show_menu` が None → 中止(戻り値 0、`cmd_project` 未呼出)。
- `_build_menu_entries`: 先頭 9 件に `[1-9]` 付与 / 10 件目以降は無印 / name 対応が一致。
- フォールバック経路:
- `import simple_term_menu` を失敗させ(`monkeypatch` で ImportError 注入)、
`builtins.input` 経由で選択 → `cmd_project up` 起動(既存 input テストをこちらに移設)。
- 非 TTY: 既存の table フォールバックテストを維持。

色付けの ANSI 有無はユニットテストでは検証しづらいため、`_build_menu_entries` は
「色付け関数を差し替え可能」または「name 抽出は色と独立」に設計し、テストは name/index の
対応とショートカット付与に集中する。色の見た目は実機検証(手動)で確認する。

## 6. 受け入れ基準

- `devbase list`(TTY)で矢印上下移動・`[1-9]` ジャンプ・`/` 検索・Enter で `up` 起動ができる。
- ESC / `q` で何も起動せず終了(戻り値 0)。
- 非 TTY(`devbase list | cat` 等)で従来どおりプレーンなテーブルが出る。
- simple-term-menu 不在でも番号入力で選択できる(フォールバック)。
- 既存・追加テストが green。

## 7. 非対象(YAGNI)

- 複数選択 / 一括 up。
- Windows ネイティブ対応。
- 多桁番号の任意行直接ジャンプ(`/` 検索で代替)。
- preview pane(将来拡張余地として残すが本変更では実装しない)。
Loading