Skip to content

feat: PLAN32-migrate-cmd env から project.yml への変換コマンド - #107

Merged
takemi-ohama merged 3 commits into
release/PLAN32from
feature/PLAN32-migrate-cmd
Aug 22, 2026
Merged

feat: PLAN32-migrate-cmd env から project.yml への変換コマンド#107
takemi-ohama merged 3 commits into
release/PLAN32from
feature/PLAN32-migrate-cmd

Conversation

@takemi-ohama

@takemi-ohamatakemi-ohama commented Aug 22, 2026

Copy link
Copy Markdown
Contributor

Summary

env 形式のプロジェクト定義を project.yml へ変換するコマンド devbase project migrate-config を追加します。

devbase project migrate-config --dry-run # 変換結果を確認(書き換えない)
devbase project migrate-config # projects/ 配下を全件変換
devbase project migrate-config carmo uttarov2 # プロジェクトを指定
devbase project migrate-config --projects-dir repos/<repo>/<plugin>/projects

変換対象キー: GIT_USER / GIT_REPO / GIT_HOST / WORK_DIR / CONTAINER_SCALE / DEVBASE_OPEN_EDITOR

設計上の判断

  • 変換対象キーは allowlist で限定し、それ以外(ENABLE_SSHAWS_CONFIG_BASE64 など)は env にそのまま残す。配布中の定義は 3 リポジトリ 136 件あり、取りこぼしを機械的に防ぐ必要がある
  • 既存の project.yml は上書きしない。手で複数リポジトリ構成へ整えたものを壊さないため。env の旧キー掃除だけは行うので、何度実行しても同じ状態に収束する
  • 旧キーを説明していた直前のコメント行も一緒に落とす。キーだけ消すと、何を説明しているのか分からないコメントが残る
  • env ファイル自体は消さないcompose.ymlenv_file: - env で参照しており、実在しないと compose が起動時に落ちるため。空になった場合はファイルの役割を書いた雛形を残す
  • 生成した YAML はローダで検証してから書き出す。検証に通らない場合は書き出さず failed として報告する
  • --projects-dir: projects/<name> は plugin リポジトリへの symlink で、実体側が書き換わる(そこが定義の正なので意図どおり)。一方 plugin リポジトリには devbase へリンクしていない projects もあるため、ディレクトリを直接指定できるようにした

実データでの確認

--dry-run を実際の projects/(42 件)に対して実行し、gitlab.com ホストのプロジェクトが host: gitlab.com を持つこと、書き換え先が plugin リポジトリ内の実体パスになることを確認済みです。

Test plan

  • uv run pytest tests/project/test_migrate.py — 15 件(生成内容 / 旧キー削除 / コメント削除 / 空 env の扱い / 非既定 host・work_dir / open_editor: false / 任意キー未指定 / dry-run / 冪等性 / 既存 project.yml の保護 / 変換不能・env 欠落のスキップ / 検証失敗 / ディレクトリ走査 / symlink 解決)
  • uv run pytest tests/commands/test_project_migrate_config.py — 7 件(全件変換 / dry-run / プロジェクト指定 / 未知プロジェクト / 変換失敗時の終了コード / --projects-dir / 不在ディレクトリ)
  • uv run pytest — 全 1344 件 green
  • 実データ 42 プロジェクトに対する --dry-run

PLAN32 Task 4。devbase project migrate-config で旧 env 形式
(GIT_USER / GIT_REPO / GIT_HOST / WORK_DIR / CONTAINER_SCALE /
DEVBASE_OPEN_EDITOR) を project.yml へ機械的に変換する。配布中のプロジェクト
定義は 3 つの plugin リポジトリに 136 件あり、手で書き換えると取りこぼしが
混じるため。
- 変換対象キーは allowlist で限定し、それ以外 (ENABLE_SSH 等) は env に残す
- 既存の project.yml は上書きしない。手で複数 repo 構成へ整えたものを壊さず、
env の旧キー掃除だけ行うので何度実行しても同じ状態に収束する
- 旧キーを説明していた直前のコメント行も一緒に落とす。キーだけ消すと何を
説明しているか分からない行が残るため
- 全部消えて空になった env にはファイルの役割を書いた雛形を残す (compose が
env_file で参照するためファイル自体は消せない)
- 生成した YAML はローダで検証してから書き出す
- --dry-run で生成内容を確認でき、--projects-dir で devbase へリンクしていない
plugin リポジトリ内の projects も直接変換できる
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@takemi-ohama
takemi-ohamaforce-pushed the feature/PLAN32-migrate-cmd branch from 0e576b4 to 38680ffCompareAugust 22, 2026 16:21

@takemi-ohamatakemi-ohama left a comment

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤖 cross-review | round 1 | codex | REQUEST_CHANGES

移行のデータ保全とプロジェクト単位の失敗隔離を修正してください。

Comment threadlib/devbase/project/migrate.py
Comment threadlib/devbase/project/migrate.py Outdated
Comment threadlib/devbase/project/migrate.py

@takemi-ohamatakemi-ohama left a comment

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤖 cross-review | round 1 | gemini | REQUEST_CHANGES

自動マイグレーションの堅牢性(エラーハンドリング)に 1 点懸念があります。レガシーな env ファイルから予期せぬ文字列が流れてきた場合でも、一括移行のバッチ処理全体がクラッシュしないように例外処理を調整してください。

Comment threadlib/devbase/project/migrate.py
cross-review round 1 (codex / gemini) の指摘対応。
- 既存 project.yml が壊れている場合は load_project_config で検出し、env を
一切変更せず failed として返す。旧キーは唯一の復旧元であり、設定を読めない
状態で掃除すると構成が完全に失われるため
- project.yml / env を同一ディレクトリの一時ファイルへ書いて os.replace する
atomic write に変更。ディスクフルや中断で truncate されると「壊れた
project.yml + 旧キーの無い env」から復旧できなくなる。symlink 自体を
置き換えないよう realpath 解決し、既存ファイルのパーミッションを引き継ぐ
- migrate_project を薄いガードで包み、OSError / UnicodeDecodeError /
yaml.YAMLError / ConfigError を failed の MigrationResult に畳む。136 件の
一括移行で 1 件の I/O・デコード失敗が全体を止めないようにする
- _load_yaml は yaml.YAMLError を ConfigError にラップする。env に
GIT_REPO="carmo のような閉じられていない引用符があると生成 YAML が壊れ、
その 1 件で一括移行がクラッシュしていた
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@takemi-ohama

Copy link
Copy Markdown
ContributorAuthor

🔧 /ndf:fix サマリ (round 1)

対応件数: critical=0 / major=4 / minor=0 (合計 4 件)
deferred: 0 件 / rejected: 0 件
commit: 8f2860a
CI: NONE (このリポジトリはこのブランチに対する GitHub Actions チェックなし)

codex 3 件・gemini 1 件すべて独自に再判定した結果、いずれも実害のある major と判断し全件修正しました。

詳細

#指摘元箇所対応
1codex migrate.py:78既存 project.yml が壊れていても env の旧キー (復旧元) を消してしまうload_project_config で先に検証し、失敗時は env を一切変更せず failed を返す
2codex migrate.py:100直接 write_text は truncate されうる / 順序も危険同一 dir の一時ファイル + fsync + os.replaceatomic write に変更。project.yml 永続化後にのみ env を掃除
3codex migrate.py:731 件の I/O・デコード失敗で 136 件の一括移行が中断migrate_project をガードで包み OSError / UnicodeDecodeError / yaml.YAMLError / ConfigErrorfailed に畳んで継続
4gemini migrate.py:95閉じ引用符欠けの env で yaml.YAMLError が捕捉されずクラッシュ_load_yamlConfigError にラップ (ローダと同じ挙動) + #3 のガードで二重に防御

atomic write では追加で symlink 自体を置き換えない (os.path.realpath 解決) / 既存ファイルの mode を引き継ぐ (mkstemp の 0600 のままにしない) 点も入れています。

検証

  • uv run pytest1348 passed (回帰テスト 4 本追加)
    • test_broken_existing_project_yml_keeps_env_untouched
    • test_writes_are_atomic
    • test_unreadable_env_is_reported_as_failed
    • test_broken_yaml_from_env_fails_only_that_project
  • 実データ dry-run: devbase project migrate-config --dry-runmigrated=42 (exit 0、挙動変化なし)

@codex@gemini 再レビューをお願いします。

@takemi-ohamatakemi-ohama left a comment

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤖 cross-review | round 2 | codex | REQUEST_CHANGES

YAML 文字列スカラーの暗黙型変換を防ぎ、旧 env で有効なリポジトリ名を欠落なく移行してください。

Comment threadlib/devbase/project/migrate.py Outdated

@takemi-ohamatakemi-ohama left a comment

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤖 cross-review | round 2 | gemini | APPROVE

  • 移行対象のキーを allowlist で限定し、既存の project.yml を保護するフェイルセーフな設計が移行スクリプトとして適切です。
  • os.replace によるアトミックな書き込みや、パース失敗時に当該プロジェクトのみを failed に倒して後続を止めないエラーハンドリングが実装されており、バッチ処理として堅牢です。
  • env が空になった場合もファイルを残す処理(compose の制約への配慮)や、symlink 先の実体を正しく解決する仕組みがテストと共に正しく実装されていることを確認しました。

GIT_REPO=123 / GIT_REPO=on のように旧 env では有効な文字列が、生成した
project.yml では YAML 1.1 の暗黙タグで int / bool / date として読まれ、
ローダの「文字列で指定してください」に当たって移行が失敗していた。
host / owner / repo / work_dir は yaml.safe_dump にスカラー出力を任せ、
引用が必要な値だけを引用する。carmo-web のような通常の値は素のままなので、
既に移行済みのファイルと生成物の見た目は変わらない。
併せて、GIT_REPO="carmo のように env 側の引用符が閉じていない値は、
引用符込みのリポジトリ名として通ってしまわないよう malformed な env として
failed に倒す。
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@takemi-ohama

Copy link
Copy Markdown
ContributorAuthor

🔧 /ndf:fix サマリ (round 2)

対応件数: critical=0 / major=1 / minor=0 (合計 1 件)
deferred: 0 件 / rejected: 0 件
commit: d4ee1b9
CI: NONE (このブランチに対する GitHub Actions チェックなし)

round 2 の未解決指摘は codex の 1 件のみ (gemini は APPROVE)。独自に再判定した結果、GIT_REPO=123 / on / 2026-08-22 などで実際に移行が failed になる major と確認し修正しました。

詳細

#指摘元箇所対応
1codex migrate.py:293旧 env では有効な文字列 (GIT_REPO=123 / on) が YAML の暗黙型で int・bool になり移行失敗host / owner / repo / work_diryaml.safe_dump でスカラー出力し、引用が必要な値だけ引用

生成物の形式は変わりません。 最小引用なので carmo-web / volareinc / /work/carmo のような通常の値は素のまま出力され、既に移行済みの 8 プロジェクトの project.yml と同一の見た目になります (test_plain_values_are_written_without_quotes で固定)。

引用化の副作用として GIT_REPO="carmo (閉じ引用符欠け) が repo: '"carmo' として YAML 的に valid になってしまうため、round 1 の gemini 指摘で failed に倒していた挙動を保つガード (移行対象の文字列値に引用符が残っていたら malformed な env として failed) を併せて追加しています。

検証

  • uv run pytest1350 passed (回帰テスト 2 本追加)
    • test_string_values_that_look_like_yaml_scalars_stay_strings
    • test_plain_values_are_written_without_quotes
  • 実データ dry-run: devbase project migrate-config --dry-runalready=39 / skipped=3 (failed なし)

@codex@gemini 再レビューをお願いします。

@takemi-ohamatakemi-ohama left a comment

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤖 cross-review | round 3 | codex | APPROVE

追加の修正必須事項はありません。

@takemi-ohamatakemi-ohama left a comment

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤖 cross-review | round 3 | gemini | APPROVE

変更内容を確認しました。スキーマの厳密な検証(型チェックや制御文字の排除)、移行ツールの安全なファイル書き換え(アトミック操作)、コンテナ側でのシェルスクリプトの堅牢なパース処理など、全体的に手堅く実装されており、テストも十分に書かれているため指摘事項はありません。

@takemi-ohama
takemi-ohama marked this pull request as ready for review August 22, 2026 16:50
@takemi-ohama
takemi-ohama merged commit cd22257 into release/PLAN32Aug 22, 2026
@takemi-ohama
takemi-ohama deleted the feature/PLAN32-migrate-cmd branch August 22, 2026 16:50
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@takemi-ohama