|
| 1 | + |
| 2 | +# devbase build / devbase up の仕様再考 |
| 3 | + |
| 4 | +`devbase build` と `devbase up` の build 機構が混乱してきたので、再ビルドの意味を |
| 5 | +統一する。中心となるのは「キャッシュをどう扱うか」を以下の 3 モードに整理し、 |
| 6 | +`rebuild` / `up` をその上に集約することである。 |
| 7 | + |
| 8 | +## 用語 |
| 9 | +- **親 image (base image)**: `FROM devbase-*` で参照されるベースイメージ |
| 10 | + (`devbase-base` など)。 |
| 11 | +- **子 image (project image)**: compose の build 対象となるプロジェクトイメージ。 |
| 12 | + |
| 13 | +## build の 3 モード |
| 14 | + |
| 15 | +| 指定 | 子 image | 親 image | |
| 16 | +|---|---|---| |
| 17 | +| `devbase build` | キャッシュビルド(常に実行) | キャッシュビルド(常に実行) | |
| 18 | +| `devbase build --no-cache` | 無条件で no-cache | 無条件で no-cache | |
| 19 | +| `devbase build --expires=n` | n 日以上古ければ no-cache、未満なら再ビルドしない | 親の作成日で独立に同判定 | |
| 20 | + |
| 21 | +- `--expires=n` の `n` のデフォルトは 7。 |
| 22 | +- `--expires=n` で **project が n 日未満なら再ビルドを実行しない**(既存イメージを |
| 23 | + そのまま使う)。n 日以上のときだけ no-cache で作り直す。 |
| 24 | +- `--expires=n` における親 image の判定は、**親 image 自身の作成日**で行う |
| 25 | + (親が n 日以上古ければ親も no-cache、未満なら親はキャッシュ利用)。 |
| 26 | + つまり「子は古いが親は新しい」場合、親はキャッシュを使い子だけ no-cache で |
| 27 | + 作り直す。 |
| 28 | +- `--no-cache` と `--expires` は併用しない (意味が矛盾するため)。`--no-cache` は |
| 29 | + 無条件、`--expires` は条件付き、と明確に分ける。 |
| 30 | + |
| 31 | +## rebuild / up |
| 32 | + |
| 33 | +- `devbase rebuild` は `devbase build --expires=7` のシノニム |
| 34 | + (デフォルトの 7 日で判定する)。 |
| 35 | +- `devbase up` 時は、`devbase rebuild` 相当の build を実行する。 |
| 36 | + |
| 37 | +## 補足: image-only サービス (公開イメージ) の扱い |
| 38 | + |
| 39 | +`build:` を持たず `image:` のみのサービスについては、作成日 (`Created`) が |
| 40 | +upstream のビルド時刻であってローカルの鮮度を表さないため、`--expires` 判定は |
| 41 | +**前回 pull 日時** (touch-file の mtime) を基準とし、n 日以上経過していれば |
| 42 | +`docker pull` で再取得する。 |
| 43 | + |
| 44 | +--- |
| 45 | + |
| 46 | +# 実装プラン |
| 47 | + |
| 48 | +## 現状の構造 (混乱の原因) |
| 49 | + |
| 50 | +再ビルドの概念が 3 つの別実装に散らばっている: |
| 51 | + |
| 52 | +1. **shell `cmd_build`** (`bin/devbase`): base 検出 + 2 段ビルドの機械的処理。 |
| 53 | + 現状フラグは `--no-cache` (両方 no-cache) / `--project-no-cache` (base は |
| 54 | + cache・project は no-cache)。 |
| 55 | +2. **Python `cmd_rebuild`** (`container.py`): 素の `docker compose build --no-cache`。 |
| 56 | + base 処理も期限判定もしない → 仕様から最も乖離。 |
| 57 | +3. **Python `_ensure_images` → `_rebuild_if_stale` → `_base_image_is_fresh`** |
| 58 | + (`up` 経路): 作成日による期限判定 + base 独立判定を持ち、結果に応じて shell |
| 59 | + `cmd_build` を `--no-cache` / `--project-no-cache` で呼ぶ。 |
| 60 | + |
| 61 | +期限判定 (RFC3339 作成日のパース、base の独立判定) は **③ に既に実装済み**。 |
| 62 | +shell では日付パースが困難なため、判定は Python に集約し、shell は機械的 3 モード |
| 63 | +に徹する、という方針で統一する。 |
| 64 | + |
| 65 | +## 設計方針 |
| 66 | + |
| 67 | +- **shell `cmd_build`** は機械的な 3 モードのみを担う (現状維持): |
| 68 | + デフォルト (cache) / `--no-cache` (両方 no-cache) / `--project-no-cache` |
| 69 | + (base cache・project no-cache)。`--expires` の日付判定は持たせない。 |
| 70 | +- **`--expires=n` の解決は Python に集約**する。project image / base image の |
| 71 | + 作成日を inspect し、上表のどのモードで shell を呼ぶかを決める共通リゾルバを |
| 72 | + 用意し、`build --expires` / `rebuild` / `up` の 3 経路から再利用する。 |
| 73 | + 既存の `_rebuild_if_stale` / `_base_image_is_fresh` / `_get_image_age_days` を |
| 74 | + この共通リゾルバに一般化する。 |
| 75 | + |
| 76 | +## 変更点 |
| 77 | + |
| 78 | +### 1. 共通リゾルバの導入 (`container.py`) |
| 79 | +- `_rebuild_if_stale` を一般化した `_resolve_build_mode(expires, image_name, |
| 80 | + inspect_json, dev_service)` を作り、戻り値に応じて `_run_build()` を呼ぶ。 |
| 81 | + - project が期限内 → no-op (cache 利用、再ビルド不要) |
| 82 | + - project が期限超過 + base 期限内 → `_run_build(project_no_cache=True)` |
| 83 | + - project が期限超過 + base 期限超過/判定不能 → `_run_build(no_cache=True)` |
| 84 | + |
| 85 | +### 2. `cmd_rebuild` を `build --expires=7` のシノニムに (`container.py`) |
| 86 | +- 現状の `docker compose build --no-cache` 直呼びを廃止。 |
| 87 | +- compose config から dev サービスを解決し、共通リゾルバを `expires=7` |
| 88 | + (= `_image_max_age_days()`) で呼ぶ実装に置き換える。 |
| 89 | +- これにより base 2 段ビルドと期限判定が rebuild にも効くようになる。 |
| 90 | + |
| 91 | +### 3. `devbase build` に `--expires` / `--no-cache` を追加 |
| 92 | +- **CLI**: `_add_build_subparser` (`cli.py`) に `--no-cache` / `--expires N` |
| 93 | + (`nargs='?'`, default なし、`--expires` 単独時は 7) を追加。 |
| 94 | +- **shell ルーティング** (`bin/devbase`): top-level `build` は現状 shell `cmd_build` |
| 95 | + へ直行している。`--expires` 付き呼び出しは日付判定が必要なため Python 経路へ |
| 96 | + 振り分ける (例: `build --expires` を検出したら `run_python build ...` に回す)。 |
| 97 | + `--no-cache` / 引数なしは従来どおり shell `cmd_build` で機械処理してよい。 |
| 98 | + ※ build の CWD 依存 (PLAN06) は、Python が `bash bin/devbase build` を |
| 99 | + 呼び戻す既存の `_run_build` 経路で担保されている (wrapper が既に cd 済み)。 |
| 100 | + |
| 101 | +### 4. `up` 経路を rebuild に集約 (`container.py`) |
| 102 | +- `_ensure_images` の「build 定義あり」分岐 (`_rebuild_if_stale`) を、2 で作る |
| 103 | + rebuild 相当 (= 共通リゾルバ) に寄せる。`up` = `rebuild` 相当を実体化する。 |
| 104 | +- ただし `up` は rebuild に無い責務 (イメージ未存在時の build/pull、image-only |
| 105 | + サービスの期限切れ pull) も持つため、それらは `_ensure_images` 側に残す。 |
| 106 | + 「build 定義あり + イメージ存在」のケースのみ共通リゾルバへ委譲する。 |
| 107 | + |
| 108 | +### 5. ドキュメント / テスト |
| 109 | +- `docs/user/cli-reference.md`: build の 3 モード表、`rebuild` = `build |
| 110 | + --expires=7`、`up` = `rebuild` 相当を反映。 |
| 111 | +- `CHANGELOG.md`: 仕様統一を追記。 |
| 112 | +- テスト: |
| 113 | + - `test_base_image_staleness.py` の `--base-cache` 否定アサーション 3 箇所 |
| 114 | + (存在しない引数名の見張り) を削除し、`--project-no-cache` の肯定検証に統一。 |
| 115 | + - `--expires` の境界 (n 日未満=cache / n 日以上=no-cache)、`rebuild` = |
| 116 | + `build --expires=7` のシノニム、`up` 経路が共通リゾルバを通ることのテストを追加。 |
| 117 | + |
| 118 | +## 影響範囲 |
| 119 | +- `bin/devbase` (cmd_build ルーティング) |
| 120 | +- `lib/devbase/cli.py` (build parser に `--no-cache` / `--expires`) |
| 121 | +- `lib/devbase/commands/container.py` (共通リゾルバ・cmd_rebuild・_ensure_images) |
| 122 | +- `docs/user/cli-reference.md`, `CHANGELOG.md` |
| 123 | +- `tests/cli/test_base_image_staleness.py` |
| 124 | + |
| 125 | +## 進め方 (推奨ステップ) |
| 126 | +1. 共通リゾルバ抽出 + `cmd_rebuild` 置換 (内部のみ、CLI 変更なし) — テスト先行。 |
| 127 | +2. `build --expires` / `--no-cache` の CLI + shell ルーティング追加。 |
| 128 | +3. `up` 経路 (`_ensure_images`) をリゾルバへ集約。 |
| 129 | +4. docs / CHANGELOG / テスト整理。 |
0 commit comments