From 85544668be468e8bd3e1fbcae9dbaa787089c506 Mon Sep 17 00:00:00 2001 From: satyaborg Date: Thu, 18 Jun 2026 10:25:32 +1000 Subject: [PATCH] feat!: replace upgrade command with update --- .../{upgrade-command.md => update-command.md} | 50 +++--- README.md | 2 +- devloop | 42 ++--- scripts/devloop_test.sh | 151 ++++++++++-------- 4 files changed, 130 insertions(+), 115 deletions(-) rename .devloop/specs/{upgrade-command.md => update-command.md} (58%) diff --git a/.devloop/specs/upgrade-command.md b/.devloop/specs/update-command.md similarity index 58% rename from .devloop/specs/upgrade-command.md rename to .devloop/specs/update-command.md index 19f532e..e9c522d 100644 --- a/.devloop/specs/upgrade-command.md +++ b/.devloop/specs/update-command.md @@ -5,60 +5,60 @@ created: 2026-06-16 pr: null --- -# Devloop Upgrade Command -Add an explicit upgrade command and an interactive startup prompt so installed Devloop users can move to the latest release without uninstalling first. +# Devloop Update Command +Add an explicit update command and an interactive startup prompt so installed Devloop users can move to the latest release without uninstalling first. ```mermaid flowchart LR Current["Installed Devloop"] --> Check["Check latest release"] - Check --> Upgrade["Run existing remote installer"] - Upgrade --> Result["Current symlink points at latest release"] + Check --> Update["Run existing remote installer"] + Update --> Result["Current symlink points at latest release"] ``` ## Problem Updating an already installed Devloop currently requires remembering the uninstall script and rerunning the curl installer manually. This hurts maintainers and users whenever a new release is available, because the update path is outside the CLI they already use. ## Outcome -Users can run `devloop upgrade` to install the latest released Devloop. When a user opens the interactive Devloop entry point and a newer release exists, Devloop prompts once in that session to upgrade or skip before continuing. +Users can run `devloop update` to install the latest released Devloop. When a user opens the interactive Devloop entry point and a newer release exists, Devloop prompts once in that session to update or skip before continuing. ## Scope - In: `devloop`, `scripts/install.remote.sh`, `README.md`, `scripts/devloop_test.sh`, and generated site install parity if the remote installer contract changes. -- In: `devloop upgrade`, help/usage text, release version comparison, interactive prompt behavior for `devloop` with no args and `devloop menu`. +- In: `devloop update`, help/usage text, release version comparison, interactive prompt behavior for `devloop` with no args and `devloop menu`. - In: test fixtures that avoid real network calls by using existing `DEVLOOP_GITHUB_API_URL`, `DEVLOOP_RELEASE_BASE_URL`, `DEVLOOP_INSTALL_DIR`, and `DEVLOOP_BIN_DIR` overrides. -- Out: background update daemons, automatic upgrades without confirmation, package manager integration, self-updating from git `main`, and update prompts for non-interactive or scripted commands. +- Out: background update daemons, automatic installs without confirmation, package manager integration, self-updating from git `main`, and update prompts for non-interactive or scripted commands. ## Behavior ### Happy path -1. User runs `devloop upgrade`. +1. User runs `devloop update`. 2. Devloop resolves the latest released version using the same release metadata source as the remote installer. 3. If the latest version is newer than the current `VERSION`, Devloop invokes the bundled remote installer flow to download, verify, install, relink `devloop`, and refresh bundled skills. 4. User sees output that names the current version, target version, and completed install. -5. User later runs `devloop --version` and sees the upgraded version. +5. User later runs `devloop --version` and sees the updated version. ### Edge cases -- Already current: `devloop upgrade` exits 0, prints that the current version is already latest, and does not reinstall. -- Version check fails: `devloop upgrade` exits nonzero with a concise error that the latest version could not be resolved. +- Already current: `devloop update` exits 0, prints that the current version is already latest, and does not reinstall. +- Version check fails: `devloop update` exits nonzero with a concise error that the latest version could not be resolved. - Check finds an invalid version: Devloop exits nonzero and reports the invalid version instead of installing. -- Non-interactive command: `devloop `, `devloop status`, `devloop clean`, `devloop continue`, `devloop doctor`, `devloop spec`, `devloop --version`, and `devloop --help` do not perform an automatic upgrade check or prompt. -- Interactive startup has newer version: `devloop` with no args and `devloop menu` prompt before showing the menu; accepting runs the same upgrade flow as `devloop upgrade`, and declining continues normally. +- Non-interactive command: `devloop `, `devloop status`, `devloop clean`, `devloop continue`, `devloop doctor`, `devloop spec`, `devloop --version`, and `devloop --help` do not perform an automatic update check or prompt. +- Interactive startup has newer version: `devloop` with no args and `devloop menu` prompt before showing the menu; accepting runs the same update flow as `devloop update`, and declining continues normally. - Interactive startup is current or check fails: Devloop does not block the menu; failures are quiet or dim informational output, not fatal. - Prompt environment has no TTY: Devloop skips the automatic prompt. -- Source checkout install: the command still upgrades to the latest released archive using the remote installer, replacing the active symlink just like the curl install path. +- Source checkout install: the command still updates to the latest released archive using the remote installer, replacing the active symlink just like the curl install path. ## Acceptance criteria -1. `devloop upgrade` appears in plain help, TUI help, README command table, and command dispatch. -2. `devloop upgrade` installs a newer release through the existing remote installer behavior when fixture metadata reports a greater version. -3. `devloop upgrade` exits 0 without reinstalling when the latest fixture version equals the current `VERSION`. -4. `devloop upgrade` exits nonzero with a clear error when the latest version cannot be resolved. -5. `devloop` with no args prompts to upgrade before the menu when fixture metadata reports a greater version and stdin/stdout are TTYs. -6. `devloop menu` prompts to upgrade before the menu when fixture metadata reports a greater version and stdin/stdout are TTYs. +1. `devloop update` appears in plain help, TUI help, README command table, and command dispatch. +2. `devloop update` installs a newer release through the existing remote installer behavior when fixture metadata reports a greater version. +3. `devloop update` exits 0 without reinstalling when the latest fixture version equals the current `VERSION`. +4. `devloop update` exits nonzero with a clear error when the latest version cannot be resolved. +5. `devloop` with no args prompts to update before the menu when fixture metadata reports a greater version and stdin/stdout are TTYs. +6. `devloop menu` prompts to update before the menu when fixture metadata reports a greater version and stdin/stdout are TTYs. 7. Declining the automatic prompt continues to the normal interactive menu without changing the installed symlink. -8. Non-interactive commands and commands with explicit work do not run the automatic upgrade check. -9. The implementation has Bash test coverage in `scripts/devloop_test.sh` for upgrade command success, already-current behavior, resolver failure, prompt accept, prompt decline, and prompt skip paths. +8. Non-interactive commands and commands with explicit work do not run the automatic update check. +9. The implementation has Bash test coverage in `scripts/devloop_test.sh` for update command success, already-current behavior, resolver failure, prompt accept, prompt decline, and prompt skip paths. 10. The implementation preserves the existing remote installer checksum verification and skill installation behavior. ## Test plan -- Red: Add failing Bash tests in `scripts/devloop_test.sh` for `devloop upgrade` using local release fixtures and for automatic prompt routing on interactive entry points. +- Red: Add failing Bash tests in `scripts/devloop_test.sh` for `devloop update` using local release fixtures and for automatic prompt routing on interactive entry points. - Green: `bash scripts/devloop_test.sh` - Full: `bash scripts/devloop_test.sh` - Coverage: `bash scripts/devloop_test.sh` must keep the existing project function coverage gate passing at 100%. @@ -67,10 +67,10 @@ Users can run `devloop upgrade` to install the latest released Devloop. When a u - Must: keep Devloop as a Bash CLI and reuse the release archive, checksum, install root, bin dir, and skill install conventions already present in `scripts/install.remote.sh`. - Must: honor existing test override environment variables so tests do not hit GitHub or `devloop.sh`. - Must: avoid blocking scripted commands on network checks or prompts. -- Must: compare semantic versions using a deterministic Bash-compatible helper before deciding that an upgrade is available. +- Must: compare semantic versions using a deterministic Bash-compatible helper before deciding that an update is available. - Avoid: introducing Python, Node, Homebrew, cron, launch agents, telemetry, or a new package distribution path. - Avoid: duplicating archive download and checksum installation logic in `devloop` when the remote installer can perform the install. - Existing convention: `VERSION` is the single local version source, `scripts/devloop_test.sh` is the shell test suite, and user-visible CLI changes are documented in `README.md`. ## Notes -Use the current remote installer as the installation engine. A minimal shape is to add shared helpers in `devloop` that resolve the latest version, compare it with `DEVLOOP_VERSION`, and call `$ROOT_DIR/scripts/install.remote.sh --yes --version ` when upgrading. The existing remote installer already supports `DEVLOOP_GITHUB_API_URL`, `DEVLOOP_RELEASE_BASE_URL`, `DEVLOOP_INSTALL_DIR`, `DEVLOOP_BIN_DIR`, `--yes`, archive verification, and skill installation, which should keep the implementation small. \ No newline at end of file +Use the current remote installer as the installation engine. A minimal shape is to add shared helpers in `devloop` that resolve the latest version, compare it with `DEVLOOP_VERSION`, and call `$ROOT_DIR/scripts/install.remote.sh --yes --version ` when updating. The existing remote installer already supports `DEVLOOP_GITHUB_API_URL`, `DEVLOOP_RELEASE_BASE_URL`, `DEVLOOP_INSTALL_DIR`, `DEVLOOP_BIN_DIR`, `--yes`, archive verification, and skill installation, which should keep the implementation small. diff --git a/README.md b/README.md index decc350..297a009 100644 --- a/README.md +++ b/README.md @@ -39,7 +39,7 @@ Uninstall with `./scripts/uninstall.sh` (`--dry-run` to preview). | `devloop spec "..."` | Have an agent interview you and write a spec | | `devloop ` | Run a spec | | `devloop --create-pr ` | Run a spec and maintain a draft PR (requires `gh`) | -| `devloop upgrade` | Install the latest released Devloop | +| `devloop update` | Install the latest released Devloop | | `devloop continue` | Resume a tracked run | | `devloop status` | Show run status | | `devloop clean` | Remove run artifacts | diff --git a/devloop b/devloop index 5002664..140b901 100755 --- a/devloop +++ b/devloop @@ -35,7 +35,7 @@ UI_DIM_COLOR="244" UI_BORDER_COLOR="141" UI_BACK=false UI_NOTICE="" -DEVLOOP_UPGRADE_PROMPTED=false +DEVLOOP_UPDATE_PROMPTED=false EVENT_IDS=() EVENT_TITLES=() @@ -88,9 +88,9 @@ main() { return 0 fi - if [ "${1:-}" = "upgrade" ]; then + if [ "${1:-}" = "update" ]; then shift - upgrade_command "$@" + update_command "$@" return $? fi @@ -102,7 +102,7 @@ main() { if [ "${1:-}" = "menu" ]; then shift - if [ "$#" -eq 0 ]; then maybe_prompt_upgrade; fi + if [ "$#" -eq 0 ]; then maybe_prompt_update; fi interactive_menu "$@" return $? fi @@ -143,7 +143,7 @@ main() { if [ "$#" -eq 0 ] || has_arg "-h" "$@" || has_arg "--help" "$@"; then if [ "$#" -eq 0 ] && [ "$USE_TUI" = true ]; then - maybe_prompt_upgrade + maybe_prompt_update interactive_menu return $? fi @@ -181,7 +181,7 @@ Usage: Common commands: devloop doctor - devloop upgrade + devloop update devloop reports devloop status devloop clean @@ -220,7 +220,7 @@ welcome_tui() { printf ' devloop [options] [max=5]\n\n' gum style --foreground "$UI_ACCENT_COLOR" --bold "Common commands" printf ' %-42s %s\n' "devloop doctor" "check required tools" - printf ' %-42s %s\n' "devloop upgrade" "install the latest release" + printf ' %-42s %s\n' "devloop update" "install the latest release" printf ' %-42s %s\n' "devloop reports" "open previous run reports" printf ' %-42s %s\n' "devloop status" "summarize tracked runs" printf ' %-42s %s\n' "devloop clean" "show safe cleanup candidates" @@ -386,7 +386,7 @@ devloop_resolve_latest_version() { devloop_normalize_version "$version" } -devloop_upgrade_install() { +devloop_update_install() { local latest="$1" local current="$2" local installer="$ROOT_DIR/scripts/install.remote.sh" @@ -394,20 +394,20 @@ devloop_upgrade_install() { printf 'error: remote installer not found: %s\n' "$installer" >&2 return 1 fi - printf 'upgrading devloop %s -> %s\n' "$current" "$latest" + printf 'updating devloop %s -> %s\n' "$current" "$latest" bash "$installer" --yes --version "$latest" } -upgrade_command() { +update_command() { local resolved latest current if [ "$#" -gt 0 ]; then case "$1" in -h|--help) - printf '%s\n' "usage: devloop upgrade" + printf '%s\n' "usage: devloop update" return 0 ;; *) - printf '%s\n' "usage: devloop upgrade" >&2 + printf '%s\n' "usage: devloop update" >&2 return 2 ;; esac @@ -423,29 +423,29 @@ upgrade_command() { printf 'devloop %s is already latest\n' "$current" return 0 fi - devloop_upgrade_install "$latest" "$current" + devloop_update_install "$latest" "$current" } devloop_prompt_tty_ready() { [ -t 0 ] && [ -t 1 ] } -maybe_prompt_upgrade() { +maybe_prompt_update() { local resolved latest current - if [ "$DEVLOOP_UPGRADE_PROMPTED" = true ]; then return 0; fi - DEVLOOP_UPGRADE_PROMPTED=true + if [ "$DEVLOOP_UPDATE_PROMPTED" = true ]; then return 0; fi + DEVLOOP_UPDATE_PROMPTED=true devloop_prompt_tty_ready || return 0 current="$(devloop_normalize_version "$DEVLOOP_VERSION" 2>/dev/null || printf '%s\n' "$DEVLOOP_VERSION")" resolved="$(devloop_resolve_latest_version 2>/dev/null)" || return 0 latest="$resolved" devloop_version_gt "$latest" "$current" || return 0 - printf 'upgrade available: devloop %s -> %s\n' "$current" "$latest" >&2 - if ui_confirm "Upgrade Devloop to $latest now?"; then - if ! devloop_upgrade_install "$latest" "$current"; then - UI_NOTICE="upgrade failed; continuing without upgrade" + printf 'update available: devloop %s -> %s\n' "$current" "$latest" >&2 + if ui_confirm "Update Devloop to $latest now?"; then + if ! devloop_update_install "$latest" "$current"; then + UI_NOTICE="update failed; continuing without update" fi else - UI_NOTICE="upgrade skipped" + UI_NOTICE="update skipped" fi } diff --git a/scripts/devloop_test.sh b/scripts/devloop_test.sh index a289c16..3a299ce 100755 --- a/scripts/devloop_test.sh +++ b/scripts/devloop_test.sh @@ -56,7 +56,9 @@ contains "$help" "devloop doctor" "help" contains "$help" "devloop reports" "help" contains "$help" "devloop status" "help" contains "$help" "devloop clean" "help" -contains "$help" "devloop upgrade" "help" +contains "$help" "devloop update" "help" +obsolete_update_command="up""grade" +not_contains "$help" "devloop $obsolete_update_command" "help" contains "$help" "--create-pr" "help" contains "$help" "draft PR during the loop" "help" contains "$help" "--no-shell" "help" @@ -80,7 +82,8 @@ contains "$readme_text" "curl -fsSL https://devloop.sh/install | bash" "README r contains "$readme_text" "git clone https://github.com/satyaborg/devloop.git" "README source install" contains "$readme_text" "cd devloop" "README source install" contains "$readme_text" "./scripts/install.sh" "README source install" -contains "$readme_text" "\`devloop upgrade\`" "README command table" +contains "$readme_text" "\`devloop update\`" "README command table" +not_contains "$readme_text" "\`devloop $obsolete_update_command\`" "README command table" ok "README install docs" skill_path="$("$REPO_ROOT/devloop" spec --skill-path)" @@ -338,7 +341,8 @@ gum() { return 0; } old_use_tui="$USE_TUI" USE_TUI=true tui_help="$(welcome_tui)" -contains "$tui_help" "devloop upgrade" "TUI help" +contains "$tui_help" "devloop update" "TUI help" +not_contains "$tui_help" "devloop $obsolete_update_command" "TUI help" USE_TUI="$old_use_tui" unset -f gum @@ -433,7 +437,7 @@ devloop_version_gt 1.2.3 1.2.3-alpha.1 || fail "devloop version comparison rejec devloop_version_gt 1.2.3-alpha.2 1.2.3-alpha.1 || fail "devloop version comparison rejected prerelease identifier" if devloop_version_gt 1.2.3 1.2.3; then fail "devloop version comparison accepted equal version"; fi if devloop_version_gt 1.2.3-alpha.1 1.2.3; then fail "devloop version comparison accepted prerelease over release"; fi -if devloop_prompt_tty_ready; then fail "upgrade prompt tty check accepted non-tty test shell"; fi +if devloop_prompt_tty_ready; then fail "update prompt tty check accepted non-tty test shell"; fi frontmatter_text=$'---\ntype: fix!\nslug: "Chat Retry"\nbreaking: true\nempty: null\n---\n# Title' equals "$(frontmatter_value type "$frontmatter_text")" "fix!" "frontmatter type" @@ -637,26 +641,26 @@ if ! menu_default_output="$( )"; then fail "menu default choice failed"; fi equals "$menu_default_output" "create" "menu starts with create spec" if ! menu_dispatch_output="$( - maybe_prompt_upgrade() { printf '%s\n' "prompt"; } + maybe_prompt_update() { printf '%s\n' "prompt"; } interactive_menu() { printf '%s\n' "menu"; } USE_TUI=true main menu -)"; then fail "menu dispatch upgrade prompt failed"; fi +)"; then fail "menu dispatch update prompt failed"; fi equals "$menu_dispatch_output" $'prompt\nmenu' "menu dispatch prompts before menu" if ! no_arg_dispatch_output="$( - maybe_prompt_upgrade() { printf '%s\n' "prompt"; } + maybe_prompt_update() { printf '%s\n' "prompt"; } interactive_menu() { printf '%s\n' "menu"; } USE_TUI=true main -)"; then fail "no-arg dispatch upgrade prompt failed"; fi +)"; then fail "no-arg dispatch update prompt failed"; fi equals "$no_arg_dispatch_output" $'prompt\nmenu' "no-arg dispatch prompts before menu" if ! explicit_work_output="$( - maybe_prompt_upgrade() { printf '%s\n' "prompt"; } + maybe_prompt_update() { printf '%s\n' "prompt"; } run_command() { printf 'run'; } USE_TUI=true main "$criteria_file" )"; then fail "explicit work dispatch failed"; fi -equals "$explicit_work_output" "run" "explicit work skips upgrade prompt" +equals "$explicit_work_output" "run" "explicit work skips update prompt" if ! create_spec_output="$( ui_choose() { printf '%s\n' "Codex"; } ui_input() { printf '%s\n' "unexpected input"; return 1; } @@ -871,51 +875,62 @@ for tool in gum fzf codex claude; do done remote_path="$remote_tool_bin:/usr/bin:/bin:/usr/sbin:/sbin" -upgrade_root="$work/upgrade-root" -upgrade_bin="$work/upgrade-bin" -upgrade_home="$work/upgrade-home" -if ! upgrade_output="$( - HOME="$upgrade_home" PATH="$remote_path" DEVLOOP_GITHUB_API_URL="file://$latest_api_file" DEVLOOP_RELEASE_BASE_URL="$remote_release_base" DEVLOOP_INSTALL_DIR="$upgrade_root" DEVLOOP_BIN_DIR="$upgrade_bin" \ - main upgrade 2>&1 +update_root="$work/update-root" +update_bin="$work/update-bin" +update_home="$work/update-home" +if ! update_output="$( + HOME="$update_home" PATH="$remote_path" DEVLOOP_GITHUB_API_URL="file://$latest_api_file" DEVLOOP_RELEASE_BASE_URL="$remote_release_base" DEVLOOP_INSTALL_DIR="$update_root" DEVLOOP_BIN_DIR="$update_bin" \ + main update 2>&1 )"; then - printf '%s\n' "$upgrade_output" >&2 - fail "devloop upgrade failed" + printf '%s\n' "$update_output" >&2 + fail "devloop update failed" fi -contains "$upgrade_output" "upgrading devloop $version -> $remote_version" "devloop upgrade" -contains "$upgrade_output" "verified checksum" "devloop upgrade checksum" -contains "$upgrade_output" "devloop $remote_version installed" "devloop upgrade install" -[[ -L "$upgrade_bin/devloop" ]] || fail "devloop upgrade did not create symlink" -equals "$(readlink "$upgrade_bin/devloop")" "$upgrade_root/$remote_version/devloop" "devloop upgrade symlink target" -equals "$("$upgrade_bin/devloop" --version)" "devloop $remote_version" "devloop upgrade installed version" -[[ -f "$upgrade_home/.agents/skills/devloop-review/.devloop-checksum" ]] || fail "devloop upgrade did not install Codex skills" -[[ -f "$upgrade_home/.claude/skills/devloop-review/.devloop-checksum" ]] || fail "devloop upgrade did not install Claude skills" -ok "devloop upgrade installs newer release" +contains "$update_output" "updating devloop $version -> $remote_version" "devloop update" +contains "$update_output" "verified checksum" "devloop update checksum" +contains "$update_output" "devloop $remote_version installed" "devloop update install" +[[ -L "$update_bin/devloop" ]] || fail "devloop update did not create symlink" +equals "$(readlink "$update_bin/devloop")" "$update_root/$remote_version/devloop" "devloop update symlink target" +equals "$("$update_bin/devloop" --version)" "devloop $remote_version" "devloop update installed version" +[[ -f "$update_home/.agents/skills/devloop-review/.devloop-checksum" ]] || fail "devloop update did not install Codex skills" +[[ -f "$update_home/.claude/skills/devloop-review/.devloop-checksum" ]] || fail "devloop update did not install Claude skills" +ok "devloop update installs newer release" current_api_file="$work/current-release.json" printf '{"tag_name":"v%s"}\n' "$version" > "$current_api_file" -current_upgrade_root="$work/current-upgrade-root" -current_upgrade_bin="$work/current-upgrade-bin" -if ! current_upgrade_output="$( - HOME="$work/current-upgrade-home" PATH="$remote_path" DEVLOOP_GITHUB_API_URL="file://$current_api_file" DEVLOOP_RELEASE_BASE_URL="$remote_release_base" DEVLOOP_INSTALL_DIR="$current_upgrade_root" DEVLOOP_BIN_DIR="$current_upgrade_bin" \ - main upgrade 2>&1 +current_update_root="$work/current-update-root" +current_update_bin="$work/current-update-bin" +if ! current_update_output="$( + HOME="$work/current-update-home" PATH="$remote_path" DEVLOOP_GITHUB_API_URL="file://$current_api_file" DEVLOOP_RELEASE_BASE_URL="$remote_release_base" DEVLOOP_INSTALL_DIR="$current_update_root" DEVLOOP_BIN_DIR="$current_update_bin" \ + main update 2>&1 )"; then - printf '%s\n' "$current_upgrade_output" >&2 - fail "devloop upgrade current version failed" + printf '%s\n' "$current_update_output" >&2 + fail "devloop update current version failed" fi -contains "$current_upgrade_output" "devloop $version is already latest" "devloop upgrade current" -[[ ! -e "$current_upgrade_bin/devloop" ]] || fail "devloop upgrade reinstalled already-current version" -ok "devloop upgrade already current" +contains "$current_update_output" "devloop $version is already latest" "devloop update current" +[[ ! -e "$current_update_bin/devloop" ]] || fail "devloop update reinstalled already-current version" +ok "devloop update already current" if resolver_failure_output="$( HOME="$work/resolver-failure-home" PATH="$remote_path" DEVLOOP_GITHUB_API_URL="file://$work/missing-latest.json" DEVLOOP_RELEASE_BASE_URL="$remote_release_base" DEVLOOP_INSTALL_DIR="$work/resolver-failure-root" DEVLOOP_BIN_DIR="$work/resolver-failure-bin" \ - main upgrade 2>&1 + main update 2>&1 )"; then printf '%s\n' "$resolver_failure_output" >&2 - fail "devloop upgrade accepted missing latest version" + fail "devloop update accepted missing latest version" fi -contains "$resolver_failure_output" "could not resolve latest Devloop release" "devloop upgrade resolver failure" +contains "$resolver_failure_output" "could not resolve latest Devloop release" "devloop update resolver failure" [[ ! -e "$work/resolver-failure-bin/devloop" ]] || fail "resolver failure created devloop symlink" -ok "devloop upgrade resolver failure" +ok "devloop update resolver failure" + +if obsolete_command_output="$( + HOME="$work/obsolete-home" PATH="$remote_path" DEVLOOP_GITHUB_API_URL="file://$latest_api_file" DEVLOOP_RELEASE_BASE_URL="$remote_release_base" DEVLOOP_INSTALL_DIR="$work/obsolete-root" DEVLOOP_BIN_DIR="$work/obsolete-bin" \ + main "$obsolete_update_command" 2>&1 +)"; then + printf '%s\n' "$obsolete_command_output" >&2 + fail "obsolete update command still accepted" +fi +contains "$obsolete_command_output" "usage: devloop" "obsolete update command removed" +not_contains "$obsolete_command_output" "updating devloop" "obsolete update command removed" +ok "obsolete update command removed" prompt_accept_root="$work/prompt-accept-root" prompt_accept_bin="$work/prompt-accept-bin" @@ -924,18 +939,18 @@ if ! prompt_accept_output="$( devloop_prompt_tty_ready() { return 0; } ui_confirm() { return 0; } interactive_menu() { printf '%s\n' "menu after prompt"; } - DEVLOOP_UPGRADE_PROMPTED=false + DEVLOOP_UPDATE_PROMPTED=false USE_TUI=true HOME="$prompt_accept_home" PATH="$remote_path" DEVLOOP_GITHUB_API_URL="file://$latest_api_file" DEVLOOP_RELEASE_BASE_URL="$remote_release_base" DEVLOOP_INSTALL_DIR="$prompt_accept_root" DEVLOOP_BIN_DIR="$prompt_accept_bin" \ main menu 2>&1 )"; then printf '%s\n' "$prompt_accept_output" >&2 - fail "automatic upgrade prompt accept failed" + fail "automatic update prompt accept failed" fi -contains "$prompt_accept_output" "upgrade available: devloop $version -> $remote_version" "automatic upgrade prompt accept" -contains "$prompt_accept_output" "menu after prompt" "automatic upgrade prompt accept menu" -equals "$(readlink "$prompt_accept_bin/devloop")" "$prompt_accept_root/$remote_version/devloop" "automatic upgrade prompt accept symlink" -ok "automatic upgrade prompt accept" +contains "$prompt_accept_output" "update available: devloop $version -> $remote_version" "automatic update prompt accept" +contains "$prompt_accept_output" "menu after prompt" "automatic update prompt accept menu" +equals "$(readlink "$prompt_accept_bin/devloop")" "$prompt_accept_root/$remote_version/devloop" "automatic update prompt accept symlink" +ok "automatic update prompt accept" prompt_decline_root="$work/prompt-decline-root" prompt_decline_bin="$work/prompt-decline-bin" @@ -946,60 +961,60 @@ if ! prompt_decline_output="$( devloop_prompt_tty_ready() { return 0; } ui_confirm() { return 1; } interactive_menu() { printf '%s\n' "menu after decline"; } - DEVLOOP_UPGRADE_PROMPTED=false + DEVLOOP_UPDATE_PROMPTED=false USE_TUI=true HOME="$work/prompt-decline-home" PATH="$remote_path" DEVLOOP_GITHUB_API_URL="file://$latest_api_file" DEVLOOP_RELEASE_BASE_URL="$remote_release_base" DEVLOOP_INSTALL_DIR="$prompt_decline_root" DEVLOOP_BIN_DIR="$prompt_decline_bin" \ main menu 2>&1 )"; then printf '%s\n' "$prompt_decline_output" >&2 - fail "automatic upgrade prompt decline failed" + fail "automatic update prompt decline failed" fi -contains "$prompt_decline_output" "upgrade available: devloop $version -> $remote_version" "automatic upgrade prompt decline" -contains "$prompt_decline_output" "menu after decline" "automatic upgrade prompt decline menu" -equals "$(readlink "$prompt_decline_bin/devloop")" "$prompt_decline_target" "automatic upgrade prompt decline symlink" -[[ ! -e "$prompt_decline_root/$remote_version" ]] || fail "automatic upgrade prompt decline installed release" -ok "automatic upgrade prompt decline" +contains "$prompt_decline_output" "update available: devloop $version -> $remote_version" "automatic update prompt decline" +contains "$prompt_decline_output" "menu after decline" "automatic update prompt decline menu" +equals "$(readlink "$prompt_decline_bin/devloop")" "$prompt_decline_target" "automatic update prompt decline symlink" +[[ ! -e "$prompt_decline_root/$remote_version" ]] || fail "automatic update prompt decline installed release" +ok "automatic update prompt decline" prompt_skip_root="$work/prompt-skip-root" prompt_skip_bin="$work/prompt-skip-bin" if ! prompt_skip_output="$( - DEVLOOP_UPGRADE_PROMPTED=false + DEVLOOP_UPDATE_PROMPTED=false HOME="$work/prompt-skip-home" PATH="$remote_path" DEVLOOP_GITHUB_API_URL="file://$latest_api_file" DEVLOOP_RELEASE_BASE_URL="$remote_release_base" DEVLOOP_INSTALL_DIR="$prompt_skip_root" DEVLOOP_BIN_DIR="$prompt_skip_bin" \ - maybe_prompt_upgrade 2>&1 + maybe_prompt_update 2>&1 )"; then printf '%s\n' "$prompt_skip_output" >&2 - fail "automatic upgrade prompt non-tty skip failed" + fail "automatic update prompt non-tty skip failed" fi -equals "$prompt_skip_output" "" "automatic upgrade prompt non-tty skip" -[[ ! -e "$prompt_skip_bin/devloop" ]] || fail "automatic upgrade prompt non-tty skip installed release" +equals "$prompt_skip_output" "" "automatic update prompt non-tty skip" +[[ ! -e "$prompt_skip_bin/devloop" ]] || fail "automatic update prompt non-tty skip installed release" if ! prompt_current_output="$( devloop_prompt_tty_ready() { return 0; } ui_confirm() { printf '%s\n' "unexpected prompt"; return 1; } - DEVLOOP_UPGRADE_PROMPTED=false + DEVLOOP_UPDATE_PROMPTED=false HOME="$work/prompt-current-home" PATH="$remote_path" DEVLOOP_GITHUB_API_URL="file://$current_api_file" DEVLOOP_RELEASE_BASE_URL="$remote_release_base" DEVLOOP_INSTALL_DIR="$work/prompt-current-root" DEVLOOP_BIN_DIR="$work/prompt-current-bin" \ - maybe_prompt_upgrade 2>&1 + maybe_prompt_update 2>&1 )"; then printf '%s\n' "$prompt_current_output" >&2 - fail "automatic upgrade prompt current skip failed" + fail "automatic update prompt current skip failed" fi -equals "$prompt_current_output" "" "automatic upgrade prompt current skip" +equals "$prompt_current_output" "" "automatic update prompt current skip" if ! prompt_failure_output="$( devloop_prompt_tty_ready() { return 0; } ui_confirm() { printf '%s\n' "unexpected prompt"; return 1; } interactive_menu() { printf '%s\n' "menu after failed check"; } - DEVLOOP_UPGRADE_PROMPTED=false + DEVLOOP_UPDATE_PROMPTED=false USE_TUI=true HOME="$work/prompt-failure-home" PATH="$remote_path" DEVLOOP_GITHUB_API_URL="file://$work/missing-prompt-latest.json" DEVLOOP_RELEASE_BASE_URL="$remote_release_base" DEVLOOP_INSTALL_DIR="$work/prompt-failure-root" DEVLOOP_BIN_DIR="$work/prompt-failure-bin" \ main menu 2>&1 )"; then printf '%s\n' "$prompt_failure_output" >&2 - fail "automatic upgrade prompt resolver skip failed" + fail "automatic update prompt resolver skip failed" fi -contains "$prompt_failure_output" "menu after failed check" "automatic upgrade prompt resolver skip" -not_contains "$prompt_failure_output" "unexpected prompt" "automatic upgrade prompt resolver skip" -ok "automatic upgrade prompt skip paths" +contains "$prompt_failure_output" "menu after failed check" "automatic update prompt resolver skip" +not_contains "$prompt_failure_output" "unexpected prompt" "automatic update prompt resolver skip" +ok "automatic update prompt skip paths" remote_home="$work/remote-home" remote_install_output="$(