Skip to content

feat(cli): add specify self check and self upgrade stub#2316

Merged
mnriem merged 7 commits intogithub:mainfrom
chordpli:feat/2282-upgrade-check
Apr 22, 2026
Merged

feat(cli): add specify self check and self upgrade stub#2316
mnriem merged 7 commits intogithub:mainfrom
chordpli:feat/2282-upgrade-check

Conversation

@chordpli
Copy link
Copy Markdown
Contributor

@chordpli chordpli commented Apr 22, 2026

Closes #2282.

Summary

This PR introduces a new specify self Typer sub-app, following the direction in the issue discussion.

  • specify self check performs a read-only lookup of the latest GitHub release, compares it with the installed specify-cli version, and prints either an update verdict or a graceful fallback message. When a newer release exists, it also prints a copy-pasteable uv tool install --force --from git+…@<tag> reinstall command.
  • specify self upgrade is 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

  • Uses GH_TOKEN / GITHUB_TOKEN automatically when present to avoid anonymous GitHub API rate limits.
  • Single outbound attempt, 5-second timeout, no retries, no caching, no install-method detection, and no machine-readable output mode in this release.
  • Failure vocabulary is intentionally small and fixed: offline or timeout, rate limited (try setting GH_TOKEN or GITHUB_TOKEN), and HTTP <code>.
  • Unexpected internal exceptions are not swallowed; they propagate so real bugs surface instead of being silently converted into user-facing success output.
  • Tests mock urllib.request.urlopen, so the feature test suite performs zero real network traffic.

Example output

specify self check — up to date
Up to date: 0.7.5.dev0
specify self check — newer release available (mocked example)
Update available: 0.7.4 → 0.9.0

To upgrade:
  uv tool install specify-cli --force \
    --from git+https://github.com/github/spec-kit.git@v0.9.0
specify self upgrade
specify self upgrade is not implemented yet.
Run 'specify self check' to see whether a newer release is available.
Actual self-upgrade is planned as follow-up work.

Verification

  • uv run pytest -q tests/test_upgrade.py
  • uv run pytest -q
  • uvx ruff check src/specify_cli/__init__.py
  • Manual smoke on macOS:
    • uv run specify self --help
    • uv run specify self check --help
    • uv run specify self upgrade --help
    • uv run specify self check online
    • uv run specify self check offline
    • GH_TOKEN="SENTINEL-TOKEN-VALUE" uv run specify self check 2>&1 | grep SENTINEL-TOKEN-VALUE returns no output
    • uv run specify self upgrade prints the exact three-line stub and exits 0

Notes

  • Console(highlight=False) is included to avoid Rich auto-highlighting plain numeric output, which was causing existing plain-text CLI tests to fail.
  • Destructive specify self upgrade behavior is intentionally left for follow-up work.

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.
Copilot AI review requested due to automatic review settings April 22, 2026 15:59
@chordpli chordpli requested a review from mnriem as a code owner April 22, 2026 15:59
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 check to query the latest GitHub release tag and print upgrade/reinstall guidance.
  • Adds specify self upgrade as a fixed-output stub (no network, exits 0).
  • Adds a dedicated test module that mocks urllib.request.urlopen to 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.

Comment thread src/specify_cli/__init__.py
Comment thread src/specify_cli/__init__.py Outdated
Comment thread tests/test_upgrade.py Outdated
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment thread src/specify_cli/__init__.py Outdated
Comment thread tests/test_upgrade.py
Comment thread src/specify_cli/__init__.py Outdated
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment on lines +42 to 43
from packaging.version import InvalidVersion, Version
from typing import Any, Optional
Copy link

Copilot AI Apr 22, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This module declares PEP 723 script metadata (# /// script dependencies list). Since this change adds an import-time dependency on packaging (and _fetch_latest_release_tag also relies on pyyaml/etc already), running the file as a standalone script via uvx specify-cli.py ... / uv tool install --from specify-cli.py ... can fail if those deps aren’t included. Consider updating the script dependencies = [...] list to include packaging (and ensure it matches all import-time requirements), or remove the script metadata if it’s no longer supported.

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in e5102ac by aligning the PEP 723 dependency list with the current import-time requirements.

Comment thread src/specify_cli/__init__.py
Comment thread src/specify_cli/__init__.py Outdated
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment thread src/specify_cli/__init__.py
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment thread src/specify_cli/__init__.py Outdated
Comment thread src/specify_cli/__init__.py Outdated
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment thread src/specify_cli/__init__.py
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

@mnriem mnriem merged commit c5c2013 into github:main Apr 22, 2026
18 of 19 checks passed
@mnriem
Copy link
Copy Markdown
Collaborator

mnriem commented Apr 22, 2026

Thank you!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Feature]: Allow update to @latest release

3 participants