Skip to content
Draft
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
18 changes: 18 additions & 0 deletions docs/user/cli-reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -404,6 +404,24 @@ devbase plugin info <name>
devbase plugin sync
```

### `devbase plugin migrate`

旧形式 (`plugins/<name>` へのコピー) でインストールされたプラグインを、`repos/` 配下の永続クローンへ移行します。`install` / `update` 実行時にも自動で呼び出されるため、通常は手動実行不要です。

```
devbase plugin migrate
```

移行の挙動:

| 状況 | 動作 |
|---|---|
| コピーがクローンと一致 | 旧コピーを削除し `repos/` へ移行 (migrated) |
| コピーにローカル変更あり | 旧コピーを `plugins/<name>.bak` として保全 (preserved、手動で reconcile) |
| 移行できない (ソース未登録 等) | スキップしてエラーを表示 (skipped) |

`--link` でインストールしたプラグインは移行対象外です。

### `devbase plugin repo add`

プラグインリポジトリを登録します。
Expand Down
3 changes: 3 additions & 0 deletions lib/devbase/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,9 @@ def _add_plugin_parser(subparsers):

pl_sub.add_parser('sync', help='Resync project symlinks')

pl_sub.add_parser('migrate',
help='Migrate legacy plugins/ installs to repos/ clones')

# Plugin repo sub-subcommands
pl_repo = pl_sub.add_parser('repo', help='Manage plugin repositories')
pl_repo_sub = pl_repo.add_subparsers(dest='repo_command')
Expand Down
32 changes: 32 additions & 0 deletions lib/devbase/commands/plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
from devbase.plugin.updater import update_plugin
from devbase.plugin.info import show_plugin_info, show_available_plugins
from devbase.plugin.syncer import sync_projects
from devbase.plugin.migrator import migrate
from devbase.plugin.repo_manager import (
add_repository,
remove_repository,
Expand All @@ -34,6 +35,7 @@ def cmd_plugin(devbase_root: Path, args) -> int:
'update': lambda: cmd_plugin_update(devbase_root, getattr(args, 'name', None)),
'info': lambda: cmd_plugin_info(devbase_root, getattr(args, 'name', '')),
'sync': lambda: cmd_sync(devbase_root),
'migrate': lambda: cmd_plugin_migrate(devbase_root),
'repo': lambda: cmd_repo(devbase_root, args),
}

Expand Down Expand Up @@ -138,6 +140,36 @@ def cmd_sync(devbase_root: Path) -> int:
return 0


def cmd_plugin_migrate(devbase_root: Path) -> int:
"""Migrate legacy plugins/ copy installs to repos/ persistent clones"""
registry = PluginRegistry(devbase_root)
try:
result = migrate(registry)
except DevbaseError as e:
logger.error("%s", e)
return 1

if not (result.migrated or result.preserved or result.skipped):
logger.info("No legacy plugins/ installs to migrate.")
return 0

if result.migrated:
logger.info("Migrated %d plugin(s) to repos/: %s",
len(result.migrated), ", ".join(result.migrated))
if result.preserved:
logger.warning(
"Preserved %d plugin(s) with local changes as plugins/<name>.bak "
"(reconcile manually): %s",
len(result.preserved), ", ".join(result.preserved))
if result.skipped:
logger.warning("Could not migrate %d plugin(s): %s",
len(result.skipped), ", ".join(result.skipped))
for err in result.errors:
logger.warning(" %s", err)
return 1
return 0


def cmd_repo(devbase_root: Path, args) -> int:
"""Dispatch repo subcommands"""
registry = PluginRegistry(devbase_root)
Expand Down
25 changes: 25 additions & 0 deletions lib/devbase/plugin/installer.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,29 @@ def resolve_repo_url(repo: str) -> str:
return f"https://github.com/{repo}.git"


def _auto_migrate(registry: PluginRegistry) -> None:
"""Migrate any legacy plugins/ copy installs to repos/ before proceeding.

Triggered on the first install/update after upgrading to repos/-based
plugin management so users do not have to run `devbase plugin migrate`
manually. No-op when nothing legacy remains.
"""
from .migrator import migrate, needs_migration
if not needs_migration(registry):
return
logger.info("Legacy plugins/ installs detected — migrating to repos/...")
result = migrate(registry)
if result.migrated:
logger.info(" Migrated: %s", ", ".join(result.migrated))
if result.preserved:
logger.warning(
" Preserved with local changes (plugins/<name>.bak): %s",
", ".join(result.preserved),
)
if result.skipped:
logger.warning(" Could not migrate: %s", ", ".join(result.skipped))


def install_plugin(
registry: PluginRegistry,
source_str: str,
Expand All @@ -91,6 +114,8 @@ def install_plugin(

Raises PluginError on failure.
"""
_auto_migrate(registry)

source = PluginSource.parse(source_str, link=link)
plugins_dir = registry.get_plugins_dir()

Expand Down
Loading