Skip to content

feat(plugin): repos/ 永続クローンによるプラグイン管理と projects/ 直接シンボリックリンク#29

Merged
takemi-ohama merged 10 commits into
release/PLAN04from
feature/PLAN04-repos-core
May 28, 2026
Merged

feat(plugin): repos/ 永続クローンによるプラグイン管理と projects/ 直接シンボリックリンク#29
takemi-ohama merged 10 commits into
release/PLAN04from
feature/PLAN04-repos-core

Conversation

@takemi-ohama
Copy link
Copy Markdown
Contributor

何のために

プラグインの install / update / uninstall フローにおいて、従来の plugins/ 中間コピー方式には以下の課題があった:

  • plugin update のたびにファイルコピーが走り、変更差分が不透明
  • plugins/ と repos/ の二重管理で整合性が崩れるケースがあった
  • clone が毎回使い捨てのため、大きなリポジトリで時間がかかる

これを解消するため、repos/ にリポジトリの永続クローンを保持し、projects/ から repos/ 内のプラグインディレクトリへ直接シンボリックリンクを張る方式に移行する。

何を

repos/ 永続クローンの導入

  • repo add で repos/ 配下に full clone を作成・永続保持 (owner--repo 形式のディレクトリ名)
  • repo refresh で既存クローンを git pull して registry.yml メタデータを同期
  • repo remove で dirty check (uncommitted changes / unpushed commits) を行い、--force なしでは削除をブロック
  • upstream tracking branch 未設定時の pull エラーに対する分かりやすいメッセージ

projects/ 直接シンボリックリンク install

  • plugin install で repos/ 内のプラグインディレクトリから projects/ へ直接シンボリックリンクを作成
  • plugins/ 中間コピー層を廃止し、copy_plugin() / _sync_dir() 等のコピー系ロジックを削除
  • 未登録リポジトリからの直接 install 時に自動 repo 登録 (auto-register) を実行
  • @ref 指定は未登録リポジトリに対して拒否し、明示的な repo add を要求

git pull ベースの update

  • plugin update を git pull ベースに変更 — clone し直しではなく差分取得
  • pull 前にプロジェクト一覧のスナップショットを取得し、リネーム・移動されたプラグインの移行検出に使用
  • update 対象を全リポジトリ一括で処理する batch refresh に対応

シンボリックリンク同期の改善

  • repos/ ベースと --link ベースの両方式に対応した相対パスリンク生成
  • 同名プロジェクトの衝突時に <project>.<owner--repo> suffix 付きリンクで解決
  • uninstall 時はリンク削除のみ — repos/ のクローンは保護

その他

  • RegisteredRepository.local_path フィールド追加 (repos/ 内のクローンパス管理)
  • PluginRegistry.get_repos_dir() 追加
  • .gitignorerepos/ を追加
  • テスト 747 行追加 (tests/plugin/test_repos_core.py)

Test plan

  • repo add → repos/ に full clone が作成される
  • plugin install → projects/ から repos/ への直接シンボリックリンクが作成される
  • plugin update → git pull で差分取得される
  • plugin uninstall → シンボリックリンクのみ削除、repos/ のクローンは残る
  • repo remove → dirty check が動作し、--force で強制削除できる
  • 同名プロジェクト衝突 → suffix 付きシンボリックリンクで共存
  • --link インストール → 従来の local symlink 動作を維持
  • 未登録リポジトリからの直接 install → 自動 repo 登録が走る
  • @ref 付き install で未登録リポジトリ → エラーメッセージで repo add を案内

takemi-ohama and others added 7 commits May 27, 2026 05:24
plugins/ 中間層を廃止し、repos/ に git clone を永続保持して
projects/ からシンボリックリンクで直接参照する構造に変更。

- models.py: RegisteredRepository に local_path フィールド追加
- registry.py: get_repos_dir() 追加
- repo_manager.py: repos/ 永続クローン、git pull refresh、dirty check 付き remove
- installer.py: repos/ ベースのシンボリックリンク install、repos/ 保護 uninstall、
  copy_plugin / _sync_dir 等のコピー系ロジック削除
- syncer.py: InstalledPlugin.path ベース走査、同名衝突時の .<owner> suffix リンク
- updater.py: git pull ベース update
- cli.py: repo remove に --force オプション追加
- .gitignore: repos/ 追加

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- installer.py / updater.py: rel_path を plugin名ではなく
  plugin_path.relative_to(devbase_root) で算出し、
  registry.yml の path が name と異なるサブディレクトリ配置に対応
- repo_manager.py: upstream未設定時に @{u}..HEAD が失敗して
  dirty=false となりデータ損失の恐れがあった問題を修正。
  upstream未設定時は dirty 扱いにして安全側に倒す
- syncer.py: collision suffix を owner のみ → owner--repo に変更し、
  同一 owner の複数 repo で同名 project が衝突する問題を修正。
  既存 symlink 存在チェックも追加

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- updater: git pull 前に旧 plugin の projects をスナップショットし、
  pull 後の migration で旧ディレクトリが消えても移行先を検出可能に
- updater: name 指定の update でも同一 repo の全 installed plugin の
  metadata (version/path) を pull 後に再読み込みして整合性を維持
- repo_manager: repo add で clone 後の registry.yml parse や名前衝突
  失敗時に clone_dir を自動削除し、リトライ時の詰まりを防止
- syncer: path.split('/') を Path().parts に変更 (OS 非依存化)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- installer.py: user/repo:plugin-name 形式で未登録リポジトリを指定した際に
  自動で repo add を実行し、既存の直接指定形式を維持 (codex round 3 major)
- updater.py: _update_repo_plugins の未使用引数 repo_local_path を削除 (gemini round 3 minor)
- repo_manager.py: git_clone を try/except ブロック内に移動し、
  部分 clone 失敗時もディレクトリを自動クリーンアップ (gemini round 3 minor)

全 210 テスト PASSED

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- installer.py: 未登録リポジトリの自動登録時に @ref を明示的に拒否
  (永続 clone はデフォルトブランチを追跡するため、pinned ref と矛盾する)
- repo_manager.py: refresh_repository で git pull 後に installed plugin の
  metadata (version/path) を再計算し sync_projects() を実行
- repo_manager.py: _git_pull で upstream tracking branch の有無を事前検査し、
  未設定時に具体的な修正手順を含むエラーメッセージを返す

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…ch refresh効率化

- installer.py:131 — @ref 拒否時の未定義変数 `url` を `repo_url` に修正 (NameError 解消)
- repo_manager.py:133 — _git_pull の upstream 未設定エラーで detached HEAD/remote未設定を個別判定、remote名を動的取得
- repo_manager.py:379 — refresh_repository に sync パラメータ追加、batch refresh 時は最後に1回だけ sync_projects 実行
- installer.py:223 — legacy repo (local_path 未設定) の自動移行: 初回 install 時に永続 clone を作成して local_path を設定
- テスト追加: @ref 拒否の PluginError テスト、legacy repo migration テスト (計 212 tests PASSED)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown
Contributor Author

@takemi-ohama takemi-ohama left a comment

Choose a reason for hiding this comment

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

🤖 cross-review | round 6 | gemini | REQUEST_CHANGES

NameErrorの修正、detached HEAD/upstream未設定時のエラーメッセージ改善、およびバッチ処理の高速化を確認しました。レガシーリポジトリの自動移行ロジックにおいて、移行時にリポジトリ内のプラグインリスト(cache)が更新されない点などの改善を提案します。

Comment thread lib/devbase/plugin/installer.py
Comment thread lib/devbase/plugin/repo_manager.py Outdated
Comment thread lib/devbase/plugin/repo_manager.py
Copy link
Copy Markdown
Contributor Author

@takemi-ohama takemi-ohama left a comment

Choose a reason for hiding this comment

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

🤖 cross-review | round 6 | codex | REQUEST_CHANGES

レガシーrepo移行は、clone内容の検証が完了するまで plugins.yml を更新しないようにしてください。

Comment thread lib/devbase/plugin/installer.py
- installer.py: レガシーrepo移行時に parse_registry_yml で検証してから
  plugins.yml へ保存するように変更。plugins リストも registry.yml から
  最新情報を取得して更新 (major x2 対応)
- repo_manager.py: 複数リモート時に origin を優先選択 (minor)
- repo_manager.py: detached HEAD エラーに具体的な復帰コマンド例を追加 (minor)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@takemi-ohama
Copy link
Copy Markdown
Contributor Author

fix summary (round 6 review)

commit: e5ed764
対応: 4件 (major: 2, minor: 2, nit: 0)
deferred: 0件
rejected: 0件

修正内容

# severity file 内容
1 major installer.py レガシー移行時に parse_registry_yml で検証してから plugins.yml 保存。plugins リストも registry.yml から最新化
2 major installer.py 同上 (codex 指摘: 検証前の永続化防止)
3 minor repo_manager.py 複数リモート時に origin を優先選択
4 minor repo_manager.py detached HEAD エラーに具体的な復帰コマンド例を追加

テスト

全 212 テスト PASS (既存テスト + PLAN04 テスト 42件)

- installer.py: _install_from_repo 内の print() を logger.info() に統一
- repo_manager.py: add_repository で SSH/HTTPS 形式の URL バリアント重複を
  _url_to_repos_dirname 正規化により検知し、RepositoryError で拒否

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@takemi-ohama
Copy link
Copy Markdown
Contributor Author

deferred nit 対応 (commit 49e5f82)

対応: 2件 (minor: 1, nit: 1)

修正内容

# severity file 内容
1 minor installer.py _install_from_repo 内の print()logger.info() に統一。ファイル内の他箇所は既に logger を使用しており、一貫性を確保。
2 nit repo_manager.py add_repository で SSH/HTTPS URL バリアント(git@github.com:owner/repo.git vs https://github.com/owner/repo.git)の重複登録を _url_to_repos_dirname 正規化で検知し、RepositoryError で拒否するガードを追加。

テスト結果

  • 全 212 テスト PASS

Copy link
Copy Markdown
Contributor Author

@takemi-ohama takemi-ohama left a comment

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

永続 clone を基準にした install/refresh で、ユーザー指定 ref と repo URL の識別、refresh 時の rename migration が壊れないようにしてください。

Comment thread lib/devbase/plugin/installer.py Outdated
Comment thread lib/devbase/plugin/repo_manager.py
Comment thread lib/devbase/plugin/repo_manager.py
Copy link
Copy Markdown
Contributor Author

@takemi-ohama takemi-ohama left a comment

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

永続クローン方式への移行により、インストールや更新の効率が向上していることを確認しました。一方で、マイグレーション時のスナップショット欠如や、登録済みリポジトリにおける @ref 指定の扱いについて、いくつか改善が必要な点があります。

Comment thread lib/devbase/plugin/repo_manager.py
Comment thread lib/devbase/plugin/installer.py
Comment thread lib/devbase/plugin/repo_manager.py
Comment thread lib/devbase/plugin/updater.py
…・print→logger・migration テスト

- 登録済みrepoへの @ref 指定を PluginError で拒否(codex + gemini 指摘)
- _url_to_repos_dirname に host を含め、異なるホストの同名 repo の衝突を防止
- refresh_repository で git pull 前に _snapshot_plugin_projects を取得し
  _update_repo_plugins に渡すことで、pull 後のディレクトリ変更時も移行可能に
- repo_manager.py の残存 print() を logger.info() に統一
- _migrate_removed_plugin / _snapshot_plugin_projects のテスト追加(3件)
- refresh の pre_pull_projects 受け渡し検証テスト追加

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@takemi-ohama
Copy link
Copy Markdown
Contributor Author

fix summary (round 1 review)

commit: ebb33e0
対応: 7件 (major: 5, minor: 2, nit: 0)
deferred: 0件
rejected: 0件
重複指摘: 2組 (codex/gemini が同じ箇所を別 thread で指摘 — 全 thread に reply + Resolve 済み)

修正内容

# severity file 内容
1 major installer.py 登録済みリポジトリに対する @ref 指定を PluginError で拒否 (codex + gemini 重複指摘)
2 major repo_manager.py _url_to_repos_dirname に host を含め host--owner--repo 形式に変更。異なるホストの同名 repo の衝突を防止
3 major repo_manager.py refresh_repository で git pull 前に _snapshot_plugin_projects を取得し _update_repo_plugins に渡すことでディレクトリ変更時の移行を修正 (codex + gemini 重複指摘)
4 major test_repos_core.py _migrate_removed_plugin / _snapshot_plugin_projects / refresh の pre_pull snapshot テスト追加 (6件)
5 minor repo_manager.py 残存 print()logger.info() に統一 (show_repositories, add_repository, refresh_repository)

テスト結果

全 221 テスト PASS (前回 212 + 新規 9件)

  • TestUrlToReposDirname に host 分離・SSH/HTTPS 一致テスト追加 (2件)
  • TestInstallPlugin に登録済み repo での @ref 拒否テスト追加 (1件)
  • TestSnapshotPluginProjects (2件)
  • TestMigrateRemovedPlugin (3件)
  • TestRefreshWithPrePullSnapshot (1件)

Copy link
Copy Markdown
Contributor Author

@takemi-ohama takemi-ohama left a comment

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

リポジトリディレクトリ名の衝突回避(hostname 追加)、および git pull 時のプラグイン移行ロジックの改善を確認しました。

  • _url_to_repos_dirname への host 追加により、異なるホストの同名 repo の衝突が防止されています。既存の local_path を持つレポジトリの互換性も維持されています。
  • refresh_repository における pre_pull_projects のスナップショット取得により、リポジトリ構成変更を伴う更新時でも、インストール済みプロジェクトの追跡と移行が確実に行われるようになっています。
  • detached HEAD や upstream 未設定時のエラーメッセージ改善、および @ref 指定の適切な拒否により、UX が向上しています。

全テストのパスを確認しました。

Copy link
Copy Markdown
Contributor Author

@takemi-ohama takemi-ohama left a comment

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 | APPROVE

修正が必要な新規指摘はありません。

Sign up for free to 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