fix(plugin): copy_plugin で rmtree+copytree を rsync 同期に置換#17
Merged
Conversation
ユーザが `projects/<name>` シンボリックリンク経由で `plugins/<plugin>/projects/<name>/` 内部に `cd` した状態で `devbase plugin update` を実行すると、`copy_plugin()` の `shutil.rmtree(dest); shutil.copytree(plugin_path, dest)` がプラグインディレクトリの inode ごと置換し、ユーザのシェル CWD が "宙ぶらりんの旧 inode" を掴んだままになって ファイルが消えたように見える問題があった。 `_sync_dir(src, dst)` を新設し、rmtree せずに既存ディレクトリの inode を保ったまま ファイル差分を反映する (rsync 風)。差分は: - src に存在しない dst エントリは削除 (orphan) - src の symlink は再生成 (target が変わっていれば追従) - src の dir は再帰的に同期 (両方に存在する dir は inode 維持) - src の file は shutil.copy2 で上書き これにより、ユーザの CWD が更新対象プラグイン配下にあっても、シェルは引き続き 同じ inode を参照し続け、`ll` で正しく中身を表示できる。 linked プラグインで dest が symlink の場合は従来どおり unlink + copytree。 新規インストールも従来どおり copytree。 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
前コミットの rsync 同期は inode は保つが、ユーザがプラグイン配下で編集 したファイル (`compose.yml`, `env` 等) や独自に置いたファイル (`.env`, ログ等) をそのまま上書き / 削除していた。これは旧 rmtree+copytree と 同等の data-loss セマンティクスで、UX 改善としては不十分。 挙動を以下の保守的なものに置き換える: | src | dst | content | action | |---|---|---|---| | 存在 | 無し | - | コピー (added) | | 存在 | 存在 | 同じ | no-op | | 存在 | 存在 | 違う | dst 維持、src を `<name>.new` に並置 (kept_local) | | 無し | 存在 | - | dst 残す (preserved_orphans) | これにより: - `.env` 等 upstream に無いファイルは保持 - ユーザが手で書き換えた `compose.yml` も保持、upstream 版は `compose.yml.new` として手元で diff/merge できる - 内容が変わっていなければ `.new` も生成されない (ノイズなし) - 既存の `.new` (前回の sync で残った) は次回 sync 時に再生成 (常に 最新の upstream を反映) - ファイル/dir/symlink の type mismatch は upstream 側を `.new` 経由で 並置 (ユーザ側を壊さない) `copy_plugin()` は同期後に `kept_local` / `preserved_orphans` 件数を ログ出力し、ユーザに `.new` の存在と "merge の余地" を伝える。 NOTE: `plugin.yml` もユーザ編集として扱われるため、ユーザが意図せず 触っていた場合 registry に古い version が記録され得る。実害は表示が 古くなるだけで、`.new` が並置されるので気付ける。 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
`_sync_dir` のユーザ編集保護セマンティクスにより、ユーザが 意図せず `plugin.yml` を編集していた場合に registry の version / description 表示が upstream と desync する可能性があった。 `plugin.yml` はプラグインのメタデータでありユーザ編集対象では ないため、プラグインルート直下の `plugin.yml` のみ常時 upstream で上書きする例外を `_ALWAYS_OVERWRITE_AT_ROOT` で表現する。 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
devbase plugin updateの 2 つの問題を修正:projects/<proj>シンボリックリンク経由でプラグイン配下にcd済みの状態で update を実行すると、copy_plugin()のrmtree+copytreeがディレクトリ inode を置き換え、シェルが宙ぶらりんになるcompose.ymlやenvを編集していたり、.envを作っていたりすると、update でこれらが全部消える / 上書きされる修正内容
copy_pluginの update 経路を、shutil.rmtree(dest) + shutil.copytree(src, dest)から 保守的な in-place 同期 (_sync_dir) に置き換え。同期セマンティクス (1 ファイル単位)
added<name>.newに並置kept_localpreserved_orphans例外: プラグインルート直下の
plugin.ymlはプラグインメタデータ (registry の version / description ソース) であり、ユーザ編集対象ではないため 常時 upstream で上書きする (_ALWAYS_OVERWRITE_AT_ROOT)。これにより:
compose.yml等は 消えない — upstream 版はcompose.yml.newで並置され、手で diff / merge できる.env, ログファイル等は 削除されないplugin.yml(メタデータ) は 常に upstream に同期 — registry の version 表示が古くなる経路を塞ぐ.newも作らない (ノイズなし).newは次回 sync で最新 upstream に再生成copy_plugin の挙動変化
再現エラー (修正前)
→ シェルが古い inode を握ったまま、ファイルが消えたように見える。
検証
8 シナリオを 1 つのテストで検証 (PR ローカル):
plugin.yml(upstream 変更)compose.yml(ユーザ編集).newenv(ユーザ編集).newREADME.md(内容同じ).new無しCHANGELOG.md(upstream 新規).env(ユーザ作成、upstream 無し)projects/demo/app.log(ユーザ作成)projects/demo/index.html(ユーザ編集).newTest plan
cd状態 +devbase plugin updateを実機で確認<file>.newの存在をユーザが気付き、diff / merge できることを確認plugin.ymlがユーザ編集されていても update で upstream 版に戻ることを確認やらないこと
--cleanフラグは別 PR で検討関連
git_cloneの cwd 明示 — 後続クローン失敗を防止