feat(cli): add specify self check and self upgrade stub#2316
Conversation
Introduce a new `specify self` Typer sub-app with two subcommands. `specify self check` performs a read-only lookup against the GitHub Releases API, compares the installed version to the latest tag with PEP 440 semantics, and prints one of four verdicts (newer-available, up-to-date, indeterminate, graceful-failure). When a newer stable release is available, the output includes a copy-pasteable `uv tool install --force --from git+...@<tag>` reinstall command. `GH_TOKEN` / `GITHUB_TOKEN` is attached as a bearer credential when set so users behind shared IPs escape the anonymous 60/hour rate limit. `specify self upgrade` is a documented non-destructive stub in this release: three-line guidance output, exit 0, no outbound call, no install-method detection. The real destructive implementation is planned as follow-up work. Failure categorization is a fixed three-entry enum (offline or timeout, rate limited, HTTP <code>). Anything outside those three categories propagates as a non-zero exit so bugs surface instead of being silently swallowed. No machine-readable output, no retries, no caching in this release — see issue github#2282 discussion. Tests mock `urllib.request.urlopen`; the suite performs zero real network calls. Full regression suite: 1586 passed.
Rich's default `highlight=True` applies ANSI color to detected patterns (integers, version strings, paths) whenever stdout is deemed a TTY. This caused intermittent failures in existing pytest assertions in tests/test_cli_version.py and tests/test_extensions.py::TestExtensionRemoveCLI that compare plain-text output without passing through `strip_ansi()`. Setting `Console(highlight=False)` globally makes all CLI output deterministic and fixes the flake without modifying the affected tests. The numeric cyan highlighting was not a documented part of the CLI visual contract.
There was a problem hiding this comment.
Pull request overview
Adds a new specify self CLI surface for checking whether a newer released version is available, and reserves a future self upgrade command as a non-destructive stub.
Changes:
- Introduces
specify self checkto query the latest GitHub release tag and print upgrade/reinstall guidance. - Adds
specify self upgradeas a fixed-output stub (no network, exits 0). - Adds a dedicated test module that mocks
urllib.request.urlopento keep the suite network-isolated.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 3 comments.
| File | Description |
|---|---|
| tests/test_upgrade.py | Adds tests for version comparison, tag normalization, failure categorization, token header behavior, and the self upgrade stub output/network isolation. |
| src/specify_cli/init.py | Implements release lookup + comparison helpers and wires the new self Typer sub-app into the CLI. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 2 out of 2 changed files in this pull request and generated 3 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 2 out of 2 changed files in this pull request and generated 3 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 2 out of 2 changed files in this pull request and generated no new comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
There was a problem hiding this comment.
Copilot's findings
Tip
Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
- Files reviewed: 2/2 changed files
- Comments generated: 0 new
|
Thank you! |
|
Feel free to bring it the rest of the way! |
|
Thanks for the review and for merging this. I’d like to follow up with a Phase 2 PR for the actual |
|
I have reopened the associated issue that was closed |
Replace the v0.7.5 reserved stub from github#2316 with an actually working self-upgrade command. Detects the install method (uv tool / pipx / uvx-ephemeral / source-checkout / unsupported) via a 3-tier ladder (path-prefix match -> editable-install marker -> PATH+registry reconciliation), runs the appropriate installer subprocess, and verifies the result by spawning a child `specify --version`. - Bare `specify self upgrade` executes immediately, matching the `pip install -U` / `uv tool upgrade` / `npm update` convention. - `--dry-run` prints the preview (method, current, target, argv) and exits 0 without launching any subprocess. - `--tag vX.Y.Z[suffix]` pins a specific release tag (validated against `^v\d+\.\d+\.\d+(?:[a-z0-9.+\-]*)?$`; rejects bare `latest`, branch names, and hash refs). - Token scrubbing: GH_TOKEN / GITHUB_TOKEN are never forwarded to the installer subprocess environment or surfaced in argv. - Exit codes: 0 (success / no-op-success), 1 (resolution failure), 2 (verification mismatch), 3 (installer missing), 124 (subprocess timeout when SPECIFY_UPGRADE_TIMEOUT_SECS is set), other (installer exit propagated verbatim). - uvx-ephemeral runs and source checkouts emit path-specific guidance and exit 0 instead of running an installer. Documentation: README and docs/upgrade.md now lead with the self-management commands as the recommended path; manual `uv tool install --force` / `pipx install --force` remain as fallback for older installs and explicit-control users. docs/installation.md adds a "Stay current" pointer. Closes the unresolved portion of github#2282.
Replace the v0.7.5 reserved stub from github#2316 with an actually working self-upgrade command. Detects the install method (uv tool / pipx / uvx-ephemeral / source-checkout / unsupported) via a 3-tier ladder (path-prefix match -> editable-install marker -> PATH+registry reconciliation), runs the appropriate installer subprocess, and verifies the result by spawning a child `specify --version`. - Bare `specify self upgrade` executes immediately, matching the `pip install -U` / `uv tool upgrade` / `npm update` convention. - `--dry-run` prints the preview (method, current, target, argv) and exits 0 without launching any subprocess. - `--tag vX.Y.Z[suffix]` pins a specific release tag (validated against `^v\d+\.\d+\.\d+(?:[a-z0-9.+\-]*)?$`; rejects bare `latest`, branch names, and hash refs). - Token scrubbing: GH_TOKEN / GITHUB_TOKEN are never forwarded to the installer subprocess environment or surfaced in argv. - Exit codes: 0 (success / no-op-success), 1 (resolution failure), 2 (verification mismatch), 3 (installer missing), 124 (subprocess timeout when SPECIFY_UPGRADE_TIMEOUT_SECS is set), other (installer exit propagated verbatim). - uvx-ephemeral runs and source checkouts emit path-specific guidance and exit 0 instead of running an installer. Documentation: README and docs/upgrade.md now lead with the self-management commands as the recommended path; manual `uv tool install --force` / `pipx install --force` remain as fallback for older installs and explicit-control users. docs/installation.md adds a "Stay current" pointer. Closes the unresolved portion of github#2282.
Closes #2282.
Summary
This PR introduces a new
specify selfTyper sub-app, following the direction in the issue discussion.specify self checkperforms a read-only lookup of the latest GitHub release, compares it with the installedspecify-cliversion, and prints either an update verdict or a graceful fallback message. When a newer release exists, it also prints a copy-pasteableuv tool install --force --from git+…@<tag>reinstall command.specify self upgradeis introduced as a reserved, non-destructive stub in this release. It prints a fixed three-line guidance message and exits 0. Actual self-upgrade remains out of scope for this PR.Design Notes
GH_TOKEN/GITHUB_TOKENautomatically when present to avoid anonymous GitHub API rate limits.offline or timeout,rate limited (try setting GH_TOKEN or GITHUB_TOKEN), andHTTP <code>.urllib.request.urlopen, so the feature test suite performs zero real network traffic.Example output
specify self check— up to datespecify self check— newer release available (mocked example)specify self upgradeVerification
uv run pytest -q tests/test_upgrade.pyuv run pytest -quvx ruff check src/specify_cli/__init__.pyuv run specify self --helpuv run specify self check --helpuv run specify self upgrade --helpuv run specify self checkonlineuv run specify self checkofflineGH_TOKEN="SENTINEL-TOKEN-VALUE" uv run specify self check 2>&1 | grep SENTINEL-TOKEN-VALUEreturns no outputuv run specify self upgradeprints the exact three-line stub and exits 0Notes
Console(highlight=False)is included to avoid Rich auto-highlighting plain numeric output, which was causing existing plain-text CLI tests to fail.specify self upgradebehavior is intentionally left for follow-up work.