Skip to content

feat(cli): show version update notice#602

Open
eric-tramel wants to merge 5 commits intomainfrom
codex/issue-598-version-update-notice
Open

feat(cli): show version update notice#602
eric-tramel wants to merge 5 commits intomainfrom
codex/issue-598-version-update-notice

Conversation

@eric-tramel
Copy link
Copy Markdown
Contributor

@eric-tramel eric-tramel commented May 4, 2026

📋 Summary

Adds a PyPI-backed update notice to data-designer --version while preserving the installed version as the first output line. The notice is intentionally opportunistic: scripted/non-TTY output, local development versions, network failures, malformed PyPI data, and cache failures all skip the CTA instead of disrupting version output.

🔗 Related Issue

Fixes #598

🔄 Changes

  • Added CLI version update detection against PyPI with fail-closed behavior, DATA_DESIGNER_DISABLE_VERSION_CHECK, and DATA_DESIGNER_VERSION_CHECK_PRERELEASES.
  • Rendered the optional update notice as a compact Rich panel after the plain installed version line.
  • Skipped update-notice lookup for non-TTY stdout so data-designer --version remains script-friendly.
  • Skipped update notices for local/dev installed versions that should not compare against public PyPI releases.
  • Hardened PyPI release parsing for yanked releases, malformed release file records, malformed payloads, and invalid version strings.
  • Added package-specific, schema-versioned cache reads and atomic cache writes.
  • Selected uv tool upgrade data-designer by default, uv add --upgrade data-designer for project environments, and pipx upgrade data-designer for pipx installs.
  • Tightened upgrade-command path detection so project venvs under paths containing uv/tools are not misclassified as uv-tool installs.
  • Added packaging as a direct runtime dependency for PEP 440 version comparison.
  • Added tests for newer/current versions, lookup failure, non-TTY output, invalid/local installed versions, opt-out, cache hit/expiry/schema mismatch, malformed PyPI data, prerelease handling, and upgrade command selection.

Usage Examples

Local development checkout output, captured from this branch:

$ uv run --package data-designer data-designer --version
0.5.10.dev7+fc0365ca

Opted-out version check output, captured from this branch:

$ DATA_DESIGNER_DISABLE_VERSION_CHECK=1 uv run --package data-designer data-designer --version
0.5.10.dev7+fc0365ca

Interactive update-available rendering, captured from the CLI --version path with the installed version set to 0.5.10 and latest version set to 0.5.11 for deterministic output:

$ data-designer --version
0.5.10
╭─ 🚀 Update available ───────────────────────╮
│ New Data Designer version available: 0.5.11 │
│ Upgrade with: uv tool upgrade data-designer │
╰─────────────────────────────────────────────╯

🧪 Testing

  • make test passes (not run; interface package suite run instead)
  • Unit tests added/updated
  • E2E tests added/updated (N/A - no E2E surface changed)
  • make check-interface
  • make test-interface (675 passed)
  • uv run --package data-designer data-designer --version
  • DATA_DESIGNER_DISABLE_VERSION_CHECK=1 uv run --package data-designer data-designer --version
  • Deterministic CLI --version update-available rendering run

✅ Checklist

  • Follows commit message conventions
  • Commits are signed off (DCO)
  • Architecture docs updated (N/A - no architecture docs impacted)

Signed-off-by: Eric W. Tramel <eric.tramel@gmail.com>
@eric-tramel eric-tramel changed the title [codex] feat(cli): show version update notice feat(cli): show version update notice May 4, 2026
@eric-tramel eric-tramel marked this pull request as ready for review May 4, 2026 17:57
@eric-tramel eric-tramel requested a review from a team as a code owner May 4, 2026 17:57
Signed-off-by: Eric W. Tramel <eric.tramel@gmail.com>
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 4, 2026

PR #602 Review — feat(cli): show version update notice

Summary

Adds a PyPI-backed update notice that renders after the installed version line on data-designer --version. Includes a 0.75s-timeout HTTPS fetch to the PyPI JSON API, a 6h on-disk cache under DATA_DESIGNER_HOME, prerelease filtering, a DATA_DESIGNER_DISABLE_VERSION_CHECK opt-out, and a DATA_DESIGNER_VERSION_CHECK_PRERELEASES override. Selects between uv tool upgrade data-designer and uv add --upgrade data-designer based on sys.prefix / environment variables. Adds packaging>=25,<27 as a direct runtime dep and a new version_notice module plus unit tests.

Fixes #598.

Findings

Correctness

  • get_update_notice always prints version first_version_callback echoes the installed version before importing the notice modules, so the core --version contract is preserved even if the notice path fails. Good.
  • Yanked-release heuristic edge case (version_notice.py:139-142): _is_yanked_release returns True when release_files is [] (no files) — reasonable, but it returns False when the list contains any non-dict entry (because isinstance(..., dict) and ... short-circuits to False, causing all(...) to be False). Non-dict entries shouldn't occur in practice, but the inversion is subtle; consider any(is_valid_and_not_yanked) framing instead.
  • Dev / local-version installs trigger spurious notices: a user with e.g. 0.6.1.dev0+gabc1234 has is_prerelease == True, so the check enables prereleases and then compares the local version to PyPI's latest prerelease. Version("0.6.1.dev0+abc") < Version("0.6.1rc1") is true, so editable developers will get nagged to upgrade their own working tree. Minor, but worth a one-line skip for versions with local segments:
    if installed.local is not None:
        return None
  • Upgrade-command heuristic is uv-only: users who installed via pipx install data-designer or pip install --user data-designer will see uv tool upgrade data-designer or uv add --upgrade data-designer, both of which won't work for them. If cross-installer support matters, consider a pipx prefix check (".local/pipx/venvs" in parts) or a generic fallback like pip install -U data-designer. Acceptable given the uv-centric positioning, but noting it.
  • Cache key lacks a schema version: cache_data stores checked_at/include_prereleases/latest_version but no schema_version or package_name. If the key semantics ever change, stale caches will be silently misread. Low risk right now; a one-field bump field is cheap insurance.
  • Cache file write is non-atomic (_write_cached_version): write_text on the final path means a concurrent data-designer --version could read a half-written file. _read_cached_version swallows json.JSONDecodeError so it's safe, but a tmp-file+rename would avoid the wasted network roundtrip. Optional.

Performance / UX

  • 0.75s synchronous block on first invocation (and every 6h thereafter) before --version returns. The installed version is printed first, so the user sees output immediately, but the process doesn't exit for up to 750ms. Consider a shorter default (e.g. 0.5s) or an env-var override for CI environments that see thousands of --version calls.
  • No stdout/TTY gating: the notice prints via Rich even when stdout is piped (e.g. data-designer --version | cat). Version-checkers in scripts will get a Rich panel in their output on upgrade-available days. A sys.stdout.isatty() or not sys.stdout.isatty() skip in _version_callback would make this safer for scripting.
  • Fail-closed is appropriate: all failure modes (timeout, HTTP error, invalid JSON, InvalidVersion) return None and print nothing. Good.

Style / conventions

  • Absolute imports, from __future__ import annotations, full type annotations — all match the style guide.
  • Deferred imports inside _version_callback keep the fast path clean. Matches the lazy_heavy_imports posture in AGENTS.md.
  • Private helpers are underscore-prefixed and module-local; public surface is just UpdateNotice, get_update_notice, select_upgrade_command.
  • Mapping[str, str] and Callable[[], float] injection for environ / now make the tests clean without monkeypatching globals.

Tests

  • Coverage of core branches is thorough: newer version, current version, fetch failure, opt-out, fresh cache, prerelease filtering (both opt-in paths), and upgrade-command selection.
  • Gaps to consider:
    • No test for yanked-release filtering (the _is_yanked_release path).
    • No test for an invalid installed_version (e.g., "garbage") — the early InvalidVersion return is untested.
    • No test for cache-expired → refetch (opposite of the fresh-cache test).
    • No test for the PyPI payload being malformed at the top level (releases missing / not a dict).
    • test_app_version_prints_installed_data_designer_version only asserts result.output == "0.6.0\n"; stable against regressions, but the "version-first ordering" invariant isn't explicitly tested with a non-None notice in cli/main.py's own tests (covered in test_version_notice.py separately).

Security

  • Uses HTTPS to pypi.org with Accept: application/json and a named User-Agent; no credentials sent. Fine.
  • No shell expansion / subprocess — upgrade command is a suggestion string only, never executed. Good.
  • Cache file written under DATA_DESIGNER_HOME (user home by default). No symlink follow attacks beyond what pathlib.write_text already has.

Verdict

Approve with minor comments. The implementation is clean, well-tested, and defensive in the right places (fail-closed, opt-out, version-first echo, deferred imports). The issues above are mostly polish:

  • Skip notice for local-version dev installs (recommended).
  • Skip notice when stdout is not a TTY (recommended — avoids breaking scripts parsing --version).
  • Reconsider the uv-only upgrade suggestion if pipx/pip users are in scope.
  • Add tests for yanked releases, invalid installed versions, and cache expiry.
  • Consider a schema-version field in the cache payload.

None of these block merging.

@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps Bot commented May 4, 2026

Greptile Summary

This PR adds a TTY-gated, PyPI-backed update notice to data-designer --version. The installed version is always emitted first as plain text, followed (only on interactive TTYs) by an optional Rich panel showing the latest version and the appropriate upgrade command. Network failures, cache errors, invalid/local installed versions, and non-TTY contexts all skip the notice without affecting the version output.

The implementation is well-structured and carefully hardened: atomic cache writes with {pid}.tmp → rename, schema-versioned cache records, a tight _has_direct_child_path heuristic that checks adjacency and position in the path (correctly resolving the previous set()-based concern), and full environment-variable escape hatches. Test coverage is thorough across all the stated failure modes.

Confidence Score: 5/5

Safe to merge — no correctness or security issues found.

No P0 or P1 findings. The new code is well-tested, fail-closed, and the path-adjacency heuristic in _has_direct_child_path correctly addresses the previous set()-based concern. All edge cases (invalid versions, local builds, yanked releases, cache races, network timeouts) are handled.

No files require special attention.

Important Files Changed

Filename Overview
packages/data-designer/src/data_designer/cli/version_notice.py New module implementing PyPI version check with cache, env-var opt-out, prerelease handling, and upgrade command detection — all well-guarded against network/parse failures.
packages/data-designer/src/data_designer/cli/main.py _version_callback extended to show update notice only on TTY; bare-except used intentionally to keep version output usable if notice lookup fails.
packages/data-designer/src/data_designer/cli/ui.py Adds print_update_notice() rendering a Rich Panel with version and upgrade command; straightforward addition to the existing ui helpers.
packages/data-designer/tests/cli/test_version_notice.py Comprehensive test suite covering newer/current/invalid versions, opt-out, cache hit/expiry/schema mismatch, prerelease handling, malformed payloads, and all upgrade command heuristics.
packages/data-designer/tests/cli/test_main.py Existing version tests updated; four new integration tests added covering TTY/non-TTY paths, notice rendering, and lookup failure graceful degradation.
packages/data-designer/pyproject.toml Adds packaging>=25,<27 as a direct runtime dependency for PEP 440 version comparison.

Sequence Diagram

sequenceDiagram
    participant User
    participant CLI as data-designer --version
    participant TTY as isatty() check
    participant Cache as version-check.json
    participant PyPI as PyPI JSON API
    participant UI as Rich Panel

    User->>CLI: data-designer --version
    CLI->>CLI: importlib.metadata.version()
    CLI->>User: echo installed_version (plain text)
    CLI->>TTY: stdout.isatty()?
    alt not TTY (pipe/script)
        TTY-->>CLI: False
        CLI->>User: Exit (no notice)
    else TTY (interactive terminal)
        TTY-->>CLI: True
        CLI->>CLI: check DISABLE_VERSION_CHECK env
        CLI->>Cache: read version-check.json
        alt cache fresh (< 6h)
            Cache-->>CLI: cached latest_version
        else cache miss/expired
            CLI->>PyPI: GET /pypi/data-designer/json (0.75s timeout)
            PyPI-->>CLI: releases payload
            CLI->>Cache: atomic write (pid.tmp → rename)
        end
        CLI->>CLI: latest > installed?
        alt newer version available
            CLI->>CLI: select_upgrade_command()
            CLI->>UI: print_update_notice(latest, cmd)
            UI->>User: Rich Panel (update CTA)
        end
        CLI->>User: Exit
    end
Loading

Reviews (4): Last reviewed commit: "Merge branch 'main' into codex/issue-598..." | Re-trigger Greptile

Comment thread packages/data-designer/src/data_designer/cli/version_notice.py Outdated
Signed-off-by: Eric W. Tramel <eric.tramel@gmail.com>
Signed-off-by: Eric W. Tramel <eric.tramel@gmail.com>
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.

feat(cli): show update notice when a newer Data Designer version is available

1 participant