Skip to content

feat(cli): add auto-update checker and discovery update command#61

Open
matt-chan wants to merge 4 commits into
microsoft:mainfrom
matt-chan:feat/cli-auto-update
Open

feat(cli): add auto-update checker and discovery update command#61
matt-chan wants to merge 4 commits into
microsoft:mainfrom
matt-chan:feat/cli-auto-update

Conversation

@matt-chan
Copy link
Copy Markdown
Contributor

Summary

Adds a Copilot-CLI-style auto-update flow to the Discovery supercomputer CLI: every invocation does a daemon-thread background check (24h-throttled), and emits a single line at exit when a newer release is available. Users can run discovery update to apply it.

🔔 A new version of the Discovery CLI is available (2026-06-01)
   Current: 1633c830  ->  Latest: 749f5010
   Run uv tool upgrade discovery or discovery update to upgrade.

Design

Background check (discovery.common.auto_update)

  • Daemon thread, never blocks: every CLI invocation reads a small ~/.discovery/update-check.json cache. If it's stale (>24h) a daemon thread refreshes it asynchronously. Failures are swallowed silently.
  • Subdirectory-aware: uses the GitHub compare API and only reports an update when at least one changed file lives under utilities/supercomputer-cli/. Avoids "your CLI is out of date" noise from unrelated monorepo activity (catalog edits, doc tweaks, other utilities).
  • Idempotent notification: the at-exit notification fires once per upstream commit — re-shows only when a newer commit appears.
  • Authentication optional: the public-repo path works fully unauthenticated. The checker opportunistically uses DISCOVERY_GITHUB_TOKEN, GITHUB_TOKEN, GH_TOKEN, or gh auth token to raise the rate limit from 60/hr to 5000/hr — no setup required for most developers.

discovery update command

  • discovery update — interactive check + confirm + install
  • discovery update --check — check only (exit 0 = up to date, 3 = network/auth failure)
  • discovery update -y — install without confirmation
  • discovery update --enable / --disable — toggle persistent background checks

Installs are applied via uv tool upgrade discovery. On any failure the CLI prints a categorized hint (rate-limited → suggests GITHUB_TOKEN or gh auth login; 404 → points at DISCOVERY_UPDATE_REPO/REF; network → suggests checking proxy/DNS).

Environment toggles

Env var Effect
DISCOVERY_NO_UPDATE_CHECK=1 Skip checks for this invocation (CI-friendly)
DISCOVERY_GITHUB_TOKEN, GITHUB_TOKEN, GH_TOKEN Raise GitHub API rate limit
DISCOVERY_UPDATE_REF=<ref> Follow a non-default branch / tag / SHA
DISCOVERY_UPDATE_REPO=<owner/name> Follow a fork (RC channel, end-to-end testing)

Other surfaces

  • discovery doctor gained a section reporting cache state and any pending update.
  • The conftest installs an autouse fixture that disables network update checks so test runs never accidentally hit api.github.com.

End-to-end verification

Done against my fork (matt-chan/discovery) using DISCOVERY_UPDATE_REPO/REF to simulate "real" upstream activity without merging an extra commit to main. Flow:

  1. uv tool install discovery --from "git+https://github.com/matt-chan/discovery.git@feat/cli-auto-update#subdirectory=utilities/supercomputer-cli/discovery" → installed at 1633c830
  2. Pushed a new commit (749f5010) on the same branch
  3. discovery update --check correctly reported the new commit:
    Update available: 749f5010 (2026-06-01)
    Installed:      1633c830
    
  4. discovery update -y ran uv tool upgrade discovery, refetched the branch, installed the new SHA
  5. discovery --version then reported 0.1.0+g749f5010
  6. Re-running discovery update --check reported "You are on the latest version" (idempotency guard verified)

The no-token path was separately verified anonymously against octocat/Hello-World (200 OK, no Authorization header).

Test coverage

71 new test cases in tests/test_auto_update.py and tests/test_cli_update.py, covering:

  • Cache I/O round-trip + corrupt/unknown-key tolerance
  • Opt-out (env var + persistent flag) + cache staleness math
  • Token resolution priority chain (env vars → gh auth token → none)
  • All 7 error categories (rate_limited, unauthorized, not_found, network, http_error, parse_error, ineligible)
  • Subdir filtering: ignores changes outside the CLI subdir; picks the newest CLI-touching commit when others are interleaved
  • Notification idempotency (re-fires on new commits, suppresses repeats)
  • uv missing → typed UpgradeError
  • Root-callback wiring + --version eager-callback wiring (regression test for an early bug where --version exited before the auto-update was armed)

Full suite: 427 passed (excluding 10 pre-existing failures from a missing tests/artifacts/response.json fixture, unrelated to this change). ruff check and mypy --exclude .venv clean on all changed files.

Out of scope / future work

  • Atomic rollback (discovery update --rollback) — could re-pin to the prior commit recorded in current_at_check.
  • Channels (stable / rc / beta) — DISCOVERY_UPDATE_REF is the building block; a ~/.discovery-sc-config setting could persist the choice.
  • Release notes display — the compare-API response already contains commit messages; surfacing a one-line summary in the at-exit notice would be a small follow-up.

Matt Chan and others added 3 commits June 1, 2026 11:35
Adds a Copilot-CLI-style auto-update flow to the Discovery
supercomputer CLI:

* New `discovery.common.auto_update` module:
  * Per-user cache at `~/.discovery/update-check.json` (24h throttle).
  * Daemon-thread background refresh on every invocation; never blocks
    the foreground command, never raises into user code.
  * `atexit`-driven one-line notification when a newer release exists.
  * Subdirectory-aware comparison via GitHub's compare API — only
    notifies when `utilities/supercomputer-cli/` actually changed,
    avoiding false positives from unrelated monorepo activity.
  * Discovers a GitHub token from `DISCOVERY_GITHUB_TOKEN`,
    `GITHUB_TOKEN`, `GH_TOKEN`, or `gh auth token` (the repo is
    private; without a token the check fails silently).
  * Opt-out via `DISCOVERY_NO_UPDATE_CHECK=1` (per-invocation) or
    `discovery update --disable` (persistent).

* New `discovery update` Typer command:
  * `--check` for check-only mode, `-y` for non-interactive install,
    `--enable`/`--disable` to toggle background checks.
  * Synchronous check + `uv tool upgrade discovery` to apply.
  * Distinct exit codes: 0 success, 1 generic failure, 2 bad usage,
    3 network/auth failure.

* Wiring:
  * Root callback (and the eager `--version` callback) schedule the
    background check and register the at-exit notification.
  * `discovery doctor` reports update-checker status + pending updates.
  * Update-check helpers are auto-disabled in the test suite so
    nothing accidentally hits the live GitHub API.

* Tests: 71 new test cases covering cache I/O, opt-out, staleness,
  token resolution, network failures, the notification idempotency
  guard, and the full CLI surface.

* README: documents the new command and the auth requirement.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Allows the auto-update checker to compare against a ref other than
main — useful for release-candidate channels, fork validation, and
end-to-end testing of the update flow without merging to main.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The microsoft/discovery repository is going public, so the auto-update
checker no longer needs to assume authentication is required. Changes:

* New public API `check_for_update()` raises `UpdateCheckError` with a
  categorical `reason` (rate_limited / unauthorized / not_found /
  network / http_error / parse_error / ineligible), so callers can
  emit tailored hints. The legacy `fetch_update_info()` still returns
  `None` on any failure for silent-mode callers.
* `discovery update` now prints a specific actionable hint per
  category: rate-limited users are told to set GITHUB_TOKEN or run
  `gh auth login` to raise the limit from 60/hr to 5000/hr; 404
  users are told about DISCOVERY_UPDATE_REPO/REF or force-push
  scenarios; etc. The old 'repo is private' message is gone.
* Module docstring + README reframe authentication as optional and
  recommended only for shared/CI environments hitting the anonymous
  rate limit.
* Live no-token check verified against octocat/Hello-World (200 OK
  with no Authorization header).
* 7 new tests covering each error classification.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@github-actions github-actions Bot added needs-human-review Awaiting human approval pr-validation-passed The pr-review workflow's validator passed. Other status checks report separately. labels Jun 1, 2026
@github-actions
Copy link
Copy Markdown

github-actions Bot commented Jun 1, 2026

✅ Automated Check Results — All checks passed

All structural, schema, content, documentation, and secret scan checks passed.

This PR is ready for human review. The maintainers have been automatically requested.

Reminder: 1 approval from a CODEOWNERS reviewer is required before this PR can be merged.

The previous `/compare/{base}...{head}` endpoint returned the full
unified-diff patch for every changed file — 70-95% of the payload was
patch text we never read. Live-measured: 99 KB for a 2-commit diff,
1.3 MB for a multi-month stale install.

Two changes:

1. **Switch endpoint** to
   `/commits?sha={ref}&path=utilities/supercomputer-cli/&per_page=1`
   which returns just the newest CLI-touching commit's metadata. The
   server already does the path filtering we used to do client-side,
   and `per_page=1` caps the response at a single commit object.
   Live: ~3 KB body regardless of how far behind the user is.

2. **HTTP ETag / If-None-Match** conditional GET. The fetcher records
   the response's ETag (bound to its URL so changes to
   DISCOVERY_UPDATE_REF / _REPO invalidate cleanly) and sends
   If-None-Match on subsequent requests. The steady-state-unchanged
   case returns 304 Not Modified — ~200 bytes body + ~1 KB headers,
   and per GitHub's docs does NOT count against the rate limit.

`UpdateCacheState` gained `etag` + `etag_url` fields (forward-
compatible: older cache files still load).
`check_for_update()` signature changed to return
`tuple[UpdateInfo, str | None]` so callers can participate in the
etag protocol; the silent-failure `fetch_update_info()` wrapper is
unchanged in shape.

Live measurements (against matt-chan/discovery on a real network):

| Scenario              | Before          | After (fresh) | After (304) |
| --------------------- | --------------- | ------------- | ----------- |
| Tiny update           |   99 KB         |   3 KB body   |   0 body    |
| Cold install          | 1.3 MB          |   3 KB body   |   0 body    |
| Annual bandwidth/user | ~36 MB - 365 MB |  ~440 KB      | (free)      |

5 new tests cover the etag protocol end-to-end (sent on URL match,
skipped on URL mismatch, 304 handled correctly, worker round-trip
preserves the etag and updates last_checked).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@matt-chan matt-chan requested a review from prashney as a code owner June 1, 2026 21:17
@github-actions github-actions Bot added pr-validation-passed The pr-review workflow's validator passed. Other status checks report separately. and removed pr-validation-passed The pr-review workflow's validator passed. Other status checks report separately. labels Jun 1, 2026
@github-actions
Copy link
Copy Markdown

github-actions Bot commented Jun 1, 2026

✅ Automated Check Results — All checks passed

All structural, schema, content, documentation, and secret scan checks passed.

This PR is ready for human review. The maintainers have been automatically requested.

Reminder: 1 approval from a CODEOWNERS reviewer is required before this PR can be merged.

@anzaman anzaman enabled auto-merge (squash) June 2, 2026 21:54
auto-merge was automatically disabled June 2, 2026 21:55

Head branch was pushed to by a user without write access

@anzaman anzaman enabled auto-merge (squash) June 2, 2026 22:01
auto-merge was automatically disabled June 2, 2026 22:05

Pull Request is not mergeable

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

Labels

needs-human-review Awaiting human approval pr-validation-passed The pr-review workflow's validator passed. Other status checks report separately.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants