diff --git a/CHANGELOG.md b/CHANGELOG.md index 236e9ba..44f8602 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,10 @@ ## Unreleased +- Make bare `dyro update` check, confirm, and install (same path as + `update now`). `update check` remains check-only; `--yes` skips the + confirmation on both `update` and `update now`. + ## 0.6.6 - 2026-08-13 - Launch any installed coding tool from `dyro start` / `open`, not only a diff --git a/README.de.md b/README.de.md index 52ec141..de9a0cc 100644 --- a/README.de.md +++ b/README.de.md @@ -278,9 +278,9 @@ Erstinstallation bleibt opt-in über Setup oder Interaktive Starts mit `dyro`, `dyro home` oder `dyro start` prüfen höchstens einmal pro lokalem Tag den offiziellen PyPI-Endpunkt. Fehler blockieren den Arbeitsbereich nicht. Standardmäßig bleibt jede Aktualisierung bestätigt: ```bash -dyro update # entspricht dyro update check -dyro update check -dyro update now +dyro update # prüfen, bestätigen, dann installieren (entspricht dyro update now) +dyro update check # nur prüfen +dyro update now # Alias von dyro update dyro update auto on # automatische Patch-Updates aktivieren dyro update auto off dyro update disable diff --git a/README.es.md b/README.es.md index c4e9d69..90ad642 100644 --- a/README.es.md +++ b/README.es.md @@ -278,9 +278,9 @@ primera instalación sigue siendo opt-in vía setup o Los inicios interactivos con `dyro`, `dyro home` o `dyro start` consultan el endpoint oficial de PyPI como máximo una vez al día local. Los errores nunca bloquean el espacio de trabajo y, por defecto, cada actualización requiere confirmación: ```bash -dyro update # equivalente a dyro update check -dyro update check -dyro update now +dyro update # comprobar, confirmar e instalar (equivalente a dyro update now) +dyro update check # solo comprobar +dyro update now # alias de dyro update dyro update auto on # activa actualizaciones automáticas de parches dyro update auto off dyro update disable diff --git a/README.fr.md b/README.fr.md index 0edbcb8..781e210 100644 --- a/README.fr.md +++ b/README.fr.md @@ -278,9 +278,9 @@ obsolète. La première installation reste opt-in via setup ou Les lancements interactifs `dyro`, `dyro home` ou `dyro start` interrogent le point officiel PyPI au plus une fois par jour local. Un échec ne bloque jamais l’espace de travail et chaque mise à jour reste soumise à confirmation par défaut : ```bash -dyro update # équivalent à dyro update check -dyro update check -dyro update now +dyro update # vérifier, confirmer, puis installer (équivalent à dyro update now) +dyro update check # vérification seulement +dyro update now # alias de dyro update dyro update auto on # active les mises à jour correctives automatiques dyro update auto off dyro update disable diff --git a/README.ko.md b/README.ko.md index 6ccb7ba..120fcb9 100644 --- a/README.ko.md +++ b/README.ko.md @@ -276,9 +276,9 @@ python3 -m pip install --user --upgrade dyro 대화형 `dyro`, `dyro home`, `dyro start` 실행은 현지 날짜 기준 하루에 한 번만 공식 PyPI 엔드포인트를 확인합니다. 실패해도 작업 공간 진입을 막지 않으며 기본적으로 업데이트 전 확인을 요청합니다. ```bash -dyro update # dyro update check 와 동일 -dyro update check -dyro update now +dyro update # 확인 후 설치 (dyro update now 와 동일) +dyro update check # 검사만 +dyro update now # dyro update 별칭 dyro update auto on # 패치 버전 자동 업데이트 사용 dyro update auto off dyro update disable diff --git a/README.md b/README.md index 793ee25..92896e0 100644 --- a/README.md +++ b/README.md @@ -294,9 +294,9 @@ PyPI endpoint at most once per local day. A failed or slow check never blocks workspace entry. Updates remain confirmation-first by default: ```bash -dyro update # same as: dyro update check -dyro update check -dyro update now +dyro update # check, confirm, then install (same as: dyro update now) +dyro update check # check only +dyro update now # alias of dyro update dyro update auto on # opt in to patch-only automatic updates dyro update auto off dyro update disable # also disables automatic updates diff --git a/README.pt-BR.md b/README.pt-BR.md index 76b2d9d..fd542ac 100644 --- a/README.pt-BR.md +++ b/README.pt-BR.md @@ -278,9 +278,9 @@ primeira instalação continua opt-in via setup ou Execuções interativas de `dyro`, `dyro home` ou `dyro start` consultam o endpoint oficial do PyPI no máximo uma vez por dia local. Falhas nunca bloqueiam o workspace e, por padrão, toda atualização exige confirmação: ```bash -dyro update # equivalente a dyro update check -dyro update check -dyro update now +dyro update # verificar, confirmar e instalar (equivalente a dyro update now) +dyro update check # apenas verificar +dyro update now # alias de dyro update dyro update auto on # ativa atualizações automáticas de patch dyro update auto off dyro update disable diff --git a/README.ru.md b/README.ru.md index de6294d..06c4ae4 100644 --- a/README.ru.md +++ b/README.ru.md @@ -278,9 +278,9 @@ python3 -m pip install --user --upgrade dyro Интерактивные запуски `dyro`, `dyro home` и `dyro start` проверяют официальный PyPI не чаще одного раза за локальный день. Ошибка не блокирует вход в рабочую область, а по умолчанию обновление требует подтверждения: ```bash -dyro update # то же, что dyro update check -dyro update check -dyro update now +dyro update # проверить, подтвердить и установить (то же, что dyro update now) +dyro update check # только проверка +dyro update now # псевдоним dyro update dyro update auto on # включить автообновление patch-версий dyro update auto off dyro update disable diff --git a/README.zh-CN.md b/README.zh-CN.md index 74e699a..c851638 100644 --- a/README.zh-CN.md +++ b/README.zh-CN.md @@ -288,9 +288,9 @@ python3 -m pip install --user --upgrade dyro 交互运行 `dyro`、`dyro home` 或 `dyro start` 时,Dyro 每个本地自然日最多访问一次官方 PyPI;断网、超时或状态目录不可写都不会阻塞进入工作区。默认仍由用户确认更新: ```bash -dyro update # 等价于 dyro update check -dyro update check -dyro update now +dyro update # 检查后确认安装(等价于 dyro update now) +dyro update check # 仅检查 +dyro update now # dyro update 的别名 dyro update auto on # 主动开启补丁版本自动更新 dyro update auto off dyro update disable # 同时关闭自动更新 diff --git a/docs/updates.md b/docs/updates.md index 26a731f..cf55d4e 100644 --- a/docs/updates.md +++ b/docs/updates.md @@ -20,8 +20,8 @@ automatic check. Use an explicit command to retry or to control the saved preference: ```bash -dyro update # same as: dyro update check -dyro update check +dyro update # check, confirm, then install (same as: dyro update now) +dyro update check # check only dyro update enable dyro update disable ``` @@ -44,9 +44,9 @@ filter). ## Installing an update -`dyro update now` fetches the latest stable `X.Y.Z` version, shows the exact -plan, and asks before writing. For automation, review the plan first and then -use `dyro update now --yes`. Dyro recognizes its active `uv tool` or `pipx` +`dyro update` (or the `now` alias) fetches the latest stable `X.Y.Z` version, +shows the exact plan, and asks before writing. For automation, review the plan +first and then use `dyro update --yes`. Dyro recognizes its active `uv tool` or `pipx` environment and otherwise uses the active Python interpreter's `pip`. A normal virtual environment without pip falls back to `uv pip --python` when uv is available. Commands are fixed argument arrays, never shell strings or @@ -58,7 +58,7 @@ command succeeds. Interactive `dyro setup` can install the Skill during personal preferences (preview in the plan, applied only after confirmation). After a successful -`dyro update now` (or patch auto-update), Dyro best-effort syncs an **already +`dyro update` (or patch auto-update), Dyro best-effort syncs an **already managed** Skill via the fresh `dyro` entry point. On interactive `dyro` / `dyro home` / `dyro start` launches, an **outdated** managed Skill is repaired automatically. First-time install is never forced on upgrade or @@ -87,5 +87,5 @@ dyro update auto off This preference is off by default. When enabled, only a higher patch within the same major and minor line can install automatically, such as `0.5.5` to `0.5.6`. A minor or major change still produces a notice and requires -`dyro update now`. An installation failure is reported but does not prevent the +`dyro update`. An installation failure is reported but does not prevent the current Dyro launch from continuing. diff --git a/src/dyro/cli.py b/src/dyro/cli.py index f3a6b1d..4c3c670 100644 --- a/src/dyro/cli.py +++ b/src/dyro/cli.py @@ -1946,7 +1946,7 @@ def cmd_update_check(args: argparse.Namespace) -> None: result = _explicit_update_check() _print_update_result(result) if result.kind != UpdateKind.NONE: - print("运行 dyro update now 可确认并完成更新。") + print("运行 dyro update 可确认并完成更新。") def cmd_update_now(args: argparse.Namespace) -> None: @@ -1954,8 +1954,9 @@ def cmd_update_now(args: argparse.Namespace) -> None: _print_update_result(result) if result.kind == UpdateKind.NONE: return + yes = bool(getattr(args, "yes", False)) if ( - not args.yes + not yes and not args.dry_run and not (sys.stdin.isatty() and sys.stdout.isatty()) ): @@ -1964,7 +1965,7 @@ def cmd_update_now(args: argparse.Namespace) -> None: ) updated = perform_update( result.latest_version, - yes=args.yes, + yes=yes, dry_run=args.dry_run, ) if updated: @@ -3791,14 +3792,19 @@ def build_parser() -> argparse.ArgumentParser: integration_uninstall_parser.set_defaults(func=cmd_integration_uninstall) update = sub.add_parser( "update", - help="检测并安全更新 Dyro(无子命令时等价于 update check)", + help="检测并安全更新 Dyro(无子命令时确认后安装)", + ) + update.add_argument( + "--yes", action="store_true", help="确认执行已展示的更新命令" ) update_sub = update.add_subparsers(dest="update_command", required=False) - update.set_defaults(func=cmd_update_check) + update.set_defaults(func=cmd_update_now) update_sub.add_parser( - "check", help="立即检查官方 PyPI 的最新稳定版本(等价于 dyro update)" + "check", help="仅检查官方 PyPI 的最新稳定版本,不安装" ).set_defaults(func=cmd_update_check) - update_now = update_sub.add_parser("now", help="显示计划并更新到最新稳定版本") + update_now = update_sub.add_parser( + "now", help="显示计划并更新到最新稳定版本(等价于 dyro update)" + ) update_now.add_argument( "--yes", action="store_true", help="确认执行已展示的更新命令" ) @@ -4451,14 +4457,14 @@ def _maybe_run_daily_update(*, install=None) -> bool: ) except (DyroError, OSError) as exc: print(f"自动更新失败:{exc}") - print("本次启动继续使用当前版本;稍后可运行 dyro update now 重试。") + print("本次启动继续使用当前版本;稍后可运行 dyro update 重试。") return False if updated: print("自动更新完成;本次启动继续运行,下次将使用新版本。") _refresh_skill_via_new_cli() return True return False - print("运行 dyro update now 可一键确认更新;今天不再重复提示。") + print("运行 dyro update 可确认并完成更新;今天不再重复提示。") return False diff --git a/tests/test_updates.py b/tests/test_updates.py index fbcd96c..2384a56 100644 --- a/tests/test_updates.py +++ b/tests/test_updates.py @@ -544,7 +544,8 @@ def test_patch_auto_update_is_opt_in_and_minor_updates_only_notify(self) -> None install.assert_not_called() refresh_skill.assert_not_called() self.assertFalse(refreshed) - self.assertIn("dyro update now", output.getvalue()) + self.assertIn("dyro update", output.getvalue()) + self.assertNotIn("dyro update now", output.getvalue()) def test_daily_check_failure_never_blocks_or_prints(self) -> None: from dyro.cli import _maybe_run_daily_update @@ -709,7 +710,7 @@ def test_update_now_refreshes_skill_only_when_update_succeeds(self) -> None: main(["update", "now", "--yes"]) refresh_skip.assert_not_called() - def test_bare_update_is_equivalent_to_update_check(self) -> None: + def test_bare_update_confirms_and_installs(self) -> None: from dyro.cli import main result = UpdateResult( @@ -719,20 +720,66 @@ def test_bare_update_is_equivalent_to_update_check(self) -> None: kind=UpdateKind.PATCH, ) with tempfile.TemporaryDirectory(prefix="dyro-update-bare-") as tmp: - for argv in (["update"], ["update", "check"]): + for argv in (["update", "--yes"], ["update", "now", "--yes"]): output = StringIO() with ( patch.dict(os.environ, {"DYRO_HOME": tmp}), patch("dyro.cli.check_for_update", return_value=result) as check, - patch("dyro.cli.perform_update") as install, + patch("dyro.cli.perform_update", return_value=True) as install, + patch("dyro.cli._refresh_skill_via_new_cli") as refresh, redirect_stdout(output), ): main(argv) check.assert_called_once() - install.assert_not_called() + install.assert_called_once_with("0.5.6", yes=True, dry_run=False) + refresh.assert_called_once_with() text = output.getvalue() self.assertIn("发现 Dyro 0.5.6", text) - self.assertIn("dyro update now", text) + self.assertNotIn("运行 dyro update", text) + + def test_update_check_does_not_install(self) -> None: + from dyro.cli import main + + result = UpdateResult( + checked=True, + current_version="0.5.5", + latest_version="0.5.6", + kind=UpdateKind.PATCH, + ) + output = StringIO() + with tempfile.TemporaryDirectory(prefix="dyro-update-check-") as tmp: + with ( + patch.dict(os.environ, {"DYRO_HOME": tmp}), + patch("dyro.cli.check_for_update", return_value=result) as check, + patch("dyro.cli.perform_update") as install, + redirect_stdout(output), + ): + main(["update", "check"]) + check.assert_called_once() + install.assert_not_called() + text = output.getvalue() + self.assertIn("发现 Dyro 0.5.6", text) + self.assertIn("运行 dyro update 可确认并完成更新。", text) + + def test_bare_update_without_yes_rejects_noninteractive(self) -> None: + from dyro.cli import cmd_update_now + + result = UpdateResult( + checked=True, + current_version="0.5.5", + latest_version="0.5.6", + kind=UpdateKind.PATCH, + ) + with ( + patch("dyro.cli.check_for_update", return_value=result), + patch("dyro.cli.perform_update") as install, + patch("sys.stdin.isatty", return_value=False), + patch("sys.stdout.isatty", return_value=False), + redirect_stdout(StringIO()), + ): + with self.assertRaisesRegex(DyroError, "非交互环境"): + cmd_update_now(argparse.Namespace(yes=False, dry_run=False)) + install.assert_not_called() def test_update_commands_work_without_a_workspace(self) -> None: from dyro.cli import main