feat: Add in-place upgrade suggestion to colab update (#36)

print an additional hint suggesting `colab update --install` when supported.

Seth Troisi committed Jun 2, 2026 at 09:46 UTC f4ef6a6727c464a011e44d80b9b25986ad977351
3 files changed +41 -2
docs/04_automation_and_utility.md
+5 -2
@@ -1,5 +1,6 @@
1 ---
2 log:
3 +2026-06-01: Improved `colab update` output. On Linux platforms, an additional message is shown recommending `colab update --install` to upgrade in place, positioned above the standard `pip`/`uv` installation command.
4 2026-05-27: Refactored `colab README` and `colab AGENT` to bundle `README.md` and `AGENTS.md` via Hatchling's `force-include` and read them using `importlib.resources` instead of `importlib.metadata`. `colab AGENT` now correctly prints `AGENTS.md`.
5 2026-05-27: Extended `colab update --install` to detect if the CLI was installed via `uv tool install` (by checking if `sys.executable` contains `/uv/`) and if so, use `uv tool install -U google-colab-cli` to upgrade.
6 2026-05-27: Updated auto-update upgrade hint to recommend `pip install --upgrade google-colab-cli` instead of `colab`, aligning with the PyPI package name.
@@ -185,8 +186,10 @@ remediation guidance) rather than silently after ~1 minute via the daemon.
186 the cache.
187 - **Notification**: If a new version is found, a non-intrusive message is
188 printed to the console with a `Run 'pip install --upgrade google-colab-cli' to
188 - update.` hint. The cached banner shown between fetches uses the generic
189 - `Run 'colab update' to update.` hint.
189 + update.` hint. On Linux platforms where `--install` self-update is supported,
190 + an additional hint `You can run 'colab update --install' to upgrade in place.`
191 + is displayed above the pip/uv install command. The cached banner shown between
192 + fetches uses the generic `Run 'colab update' to update.` hint.
193 - **Self-install (`--install`)**: An opt-in `--install` flag (default
194 `False`) makes `colab update` upgrade the CLI in place (**Linux only**).
195 It detects how the CLI was installed:
src/colab_cli/auto_update.py
+2
@@ -113,6 +113,8 @@ def announce_upgrade(
113 typer.echo(
114 f"\n[colab] A new version of Colab CLI is available: {latest} (current: {current})"
115 )
116 + if platform.system() == "Linux" and ("pip" in install_cmd or "uv" in install_cmd):
117 + typer.echo("[colab] You can run 'colab update --install' to upgrade in place.")
118 typer.echo(f"[colab] Run '{install_cmd}' to update.")
119 if show_disable_hint:
120 typer.echo(
tests/test_update.py
+34
@@ -129,8 +129,17 @@ def test_pypi_upgrade_uses_pip_hint(mocker, app_version, fake_settings, mock_pyp
129 result = runner.invoke(app, ["update"])
130 assert result.exit_code == 0
131 assert "available: 1.1.0 (current: 1.0.0)" in result.output
132 + assert "You can run 'colab update --install' to upgrade in place." in result.output
133 assert "Run 'pip install --upgrade google-colab-cli' to update." in result.output
134
135 + idx_install = result.output.find(
136 + "You can run 'colab update --install' to upgrade in place."
137 + )
138 + idx_pip = result.output.find(
139 + "Run 'pip install --upgrade google-colab-cli' to update."
140 + )
141 + assert idx_install < idx_pip
142 +
143
144 def test_pypi_upgrade_uses_uv_hint(mocker, app_version, fake_settings, mock_pypi):
145 app_version("1.0.0")
@@ -144,8 +153,33 @@ def test_pypi_upgrade_uses_uv_hint(mocker, app_version, fake_settings, mock_pypi
153 result = runner.invoke(app, ["update"])
154 assert result.exit_code == 0
155 assert "available: 1.1.0 (current: 1.0.0)" in result.output
156 + assert "You can run 'colab update --install' to upgrade in place." in result.output
157 assert "Run 'uv tool install -U google-colab-cli' to update." in result.output
158
159 + idx_install = result.output.find(
160 + "You can run 'colab update --install' to upgrade in place."
161 + )
162 + idx_uv = result.output.find("Run 'uv tool install -U google-colab-cli' to update.")
163 + assert idx_install < idx_uv
164 +
165 +
166 +def test_pypi_upgrade_uses_pip_hint_non_linux(
167 + mocker, app_version, fake_settings, mock_pypi
168 +):
169 + app_version("1.0.0")
170 + mock_pypi({"info": {"version": "1.1.0"}})
171 + fake_settings()
172 + mocker.patch("sys.executable", "/usr/bin/python")
173 + mocker.patch("colab_cli.auto_update.platform.system", return_value="Darwin")
174 +
175 + result = runner.invoke(app, ["update"])
176 + assert result.exit_code == 0
177 + assert "available: 1.1.0 (current: 1.0.0)" in result.output
178 + assert (
179 + "You can run 'colab update --install' to upgrade in place." not in result.output
180 + )
181 + assert "Run 'pip install --upgrade google-colab-cli' to update." in result.output
182 +
183
184 def test_explicit_update_omits_disable_hint(app_version, fake_settings, mock_pypi):
185 """`colab update` is explicit user opt-in; the 'how to silence' line