feat: add npm-style update-available notice#24
Conversation
When a newer version is published, the CLI now prints a one-time upgrade hint to stderr — it never auto-updates. The notice only shows with the default --output table in an interactive terminal; it is suppressed for --output json, when piped, in CI, and under npx/npm scripts, so machine-readable stdout stays byte-clean. The npm lookup runs in a non-blocking background process (at most once a day) and never affects timing or exit codes. Opt out via CODACY_DISABLE_UPDATE_CHECK, NO_UPDATE_NOTIFIER, or --no-update-notifier. Uses update-notifier@5 (last CommonJS-compatible major). A package.json overrides entry pins the transitive got/package-json to patched, still-CommonJS versions (got@^11.8.6) to avoid CVE-2022-33987. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Code Review
This pull request introduces an npm-style "update available" notice using update-notifier that prints a non-blocking, one-time upgrade hint to stderr when a newer version is published. The notice is safely gated to only show during interactive table output, and can be disabled via several environment variables or the --no-update-notifier flag. Feedback on the changes suggests improving the unit tests in src/utils/update-check.test.ts by using Vitest's vi.stubEnv and vi.unstubAllEnvs() utilities to modify environment variables instead of mutating process.env directly, which prevents test pollution.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
| beforeEach(() => { | ||
| delete process.env.CODACY_DISABLE_UPDATE_CHECK; | ||
| }); | ||
|
|
||
| afterEach(() => { | ||
| vi.clearAllMocks(); // clears call history but keeps the default mock impl | ||
| delete process.env.CODACY_DISABLE_UPDATE_CHECK; | ||
| }); |
There was a problem hiding this comment.
To prevent environment mutations from leaking across tests, use Vitest's vi.stubEnv and vi.unstubAllEnvs() in afterEach instead of manually deleting properties from process.env.
| beforeEach(() => { | |
| delete process.env.CODACY_DISABLE_UPDATE_CHECK; | |
| }); | |
| afterEach(() => { | |
| vi.clearAllMocks(); // clears call history but keeps the default mock impl | |
| delete process.env.CODACY_DISABLE_UPDATE_CHECK; | |
| }); | |
| afterEach(() => { | |
| vi.clearAllMocks(); // clears call history but keeps the default mock impl | |
| vi.unstubAllEnvs(); | |
| }); |
References
- Use Vitest's
vi.stubEnvandvi.unstubAllEnvs()inafterEachto modify environment variables in tests, ensuring that environment mutations do not leak across tests.
There was a problem hiding this comment.
Keeping direct process.env mutation to match this repo's established test convention — every existing test that touches env (auth.test.ts, info.test.ts, issues.test.ts, and ~7 others) does the same, and none use vi.stubEnv. beforeEach/afterEach already delete the var on both sides, so there's no cross-test leak. Favoring consistency here over the (otherwise reasonable) switch.
🤖 Generated by /pr-fixup command
| }); | ||
|
|
||
| it("honors the CODACY_DISABLE_UPDATE_CHECK opt-out", () => { | ||
| process.env.CODACY_DISABLE_UPDATE_CHECK = "1"; |
There was a problem hiding this comment.
Use vi.stubEnv to set the environment variable instead of mutating process.env directly.
| process.env.CODACY_DISABLE_UPDATE_CHECK = "1"; | |
| vi.stubEnv("CODACY_DISABLE_UPDATE_CHECK", "1"); |
References
- Use Vitest's
vi.stubEnvandvi.unstubAllEnvs()inafterEachto modify environment variables in tests, ensuring that environment mutations do not leak across tests.
There was a problem hiding this comment.
Same as above — direct process.env assignment matches the repo-wide test convention, and the var is cleared in both beforeEach and afterEach. Keeping it consistent with the existing suite.
🤖 Generated by /pr-fixup command
There was a problem hiding this comment.
Pull request overview
This PR adds an npm-style “update available” notice to the Codacy Cloud CLI using update-notifier, aiming to keep stdout byte-clean for machine-readable --output json while showing a one-time upgrade hint on stderr for interactive/table usage.
Changes:
- Introduces runtime CLI name/version resolution (
src/version.ts) and an update-notifier wrapper (maybeNotifyUpdate). - Wires update checks into the CLI lifecycle via a Commander
preActionhook and adds--no-update-notifiersupport. - Adds dependency overrides to avoid a vulnerable transitive
gotversion, plus unit tests and a changeset.
Reviewed changes
Copilot reviewed 7 out of 8 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| src/version.ts | Adds a runtime single source of truth for CLI package name/version from package.json. |
| src/utils/update-check.ts | Implements maybeNotifyUpdate wrapper around update-notifier with format gating and opt-out env var. |
| src/utils/update-check.test.ts | Adds unit tests for the update-check gating/opt-out behavior with update-notifier mocked. |
| src/index.ts | Wires update-check scheduling via preAction hook; registers --no-update-notifier on root and subcommands. |
| SPECS/README.md | Adds a changelog entry describing the new update notice feature. |
| package.json | Adds update-notifier, adds overrides for patched transitive deps, and adds typings. |
| package-lock.json | Updates lockfile for new dependencies and version bump. |
| .changeset/update-available-notice.md | Declares a minor release changeset describing the new behavior and opt-outs. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| // Full opt-out: bail before constructing the notifier so no background | ||
| // network/disk lookup is even scheduled. (update-notifier also honors | ||
| // NO_UPDATE_NOTIFIER and --no-update-notifier on its own.) | ||
| if (process.env[DISABLE_ENV_VAR]) return; |
There was a problem hiding this comment.
Good catch on the wording. The behavior is actually safe: updateNotifier() runs .check() on construction, and check() returns early when the instance is disabled — which covers NO_UPDATE_NOTIFIER, --no-update-notifier, CI and NODE_ENV=test — before any disk read or network spawn (see update-notifier index.js lines 43-46, 77). So those opt-outs cost one cheap object construction and no background work. Reworded the comment to state this precisely instead of implying the early CODACY_DISABLE_UPDATE_CHECK return is the only safeguard.
🤖 Generated by /pr-fixup command
Up to standards ✅🟢 Issues
|
| Metric | Results |
|---|---|
| Complexity | 20 |
| Duplication | 0 |
AI Reviewer: first review requested successfully. AI can make mistakes. Always validate suggestions.
TIP This summary will be updated as you push new changes.
There was a problem hiding this comment.
Pull Request Overview
The PR successfully implements the update-available notice system but contains a significant divergence from the requirements regarding the output stream. The implementation currently defaults to stdout, whereas the specification and internal comments indicate stderr should be used to prevent polluting machine-readable output. Additionally, the project is currently 'not up to standards' due to missing coverage requirements for the newly introduced complexity in the update-check logic.
Two security-related findings in the package.json recommend pinning dependency versions to exact values. This is particularly relevant given the PR's intent to address CVE-2022-33987. Ensuring exact versions are used will prevent accidental upgrades to unverified or potentially compromised minor/patch versions in the future.
About this PR
- There is a systemic discrepancy between the PR description and the implementation: the notification system should target stderr to avoid interfering with stdout-based data processing, but the current configuration uses stdout.
Test suggestions
- Display notice when output format is 'table' (human-readable)
- Suppress notice when output format is 'json' (machine-readable)
- Initialize notifier even in JSON mode to warm the background cache for future calls
- Fully disable update check when CODACY_DISABLE_UPDATE_CHECK env var is set
- Verify that background check interval is configured for 24 hours
- Ensure errors during update check (e.g., registry issues) do not crash the CLI
TIP Improve review quality by adding custom instructions
TIP How was this review? Give us feedback
| "ora": "7.0.1", | ||
| "pluralize": "8.0.0" | ||
| "pluralize": "8.0.0", | ||
| "update-notifier": "^5.1.0" |
There was a problem hiding this comment.
🔴 HIGH RISK
Pin the 'update-notifier' version to ensure consistent behavior and prevent potential dependency confusion or hijacking attacks. This aligns with your current strategy of pinning critical transitive dependencies.
| "update-notifier": "^5.1.0" | |
| "update-notifier": "5.1.0" |
There was a problem hiding this comment.
Fixed — pinned update-notifier to exact 5.1.0, and the package-json/got overrides to 7.0.0/11.8.6. Matches the repo's exact-pin convention and resolves this finding.
🤖 Generated by /pr-fixup command
| // `defer` (default) prints the notice to stderr on process exit, which fires | ||
| // even though commands may call process.exit() explicitly. `isGlobal` makes | ||
| // the suggested command an `npm i -g …`, matching how this CLI is installed. | ||
| notifier.notify({ isGlobal: true }); |
There was a problem hiding this comment.
🟡 MEDIUM RISK
The PR description and internal comments state the notice is printed to stderr, but 'update-notifier' defaults to stdout. To ensure compatibility with users piping stdout for JSON processing, explicitly configure the output stream to stderr or verify if stdout is acceptable given the TTY/format gating.
There was a problem hiding this comment.
False positive: update-notifier@5 writes the notice via console.error (stderr), not stdout — see node_modules/update-notifier/index.js lines 67/166/169. It also self-suppresses entirely when process.stdout.isTTY is false (line 118), so piping stdout for JSON is doubly safe; we additionally gate on --output table. stdout stays byte-clean.
🤖 Generated by /pr-fixup command
- Pin update-notifier (5.1.0), @types/update-notifier (5.1.0), and the got/package-json overrides to exact versions, matching the repo's exact-pin convention and resolving the Codacy "variant versions" security finding (dependency-confusion risk). - Reword the opt-out comment in update-check.ts to state precisely that NO_UPDATE_NOTIFIER/--no-update-notifier/CI/NODE_ENV=test are handled by update-notifier's own disabled check (which returns before any disk or network work), rather than implying the early CODACY_DISABLE_UPDATE_CHECK return is the only safeguard. - Add .gemini/styleguide.md capturing project review conventions (direct process.env mutation in tests, stderr-only diagnostics, exact dep pins, generated-file false positives) to reduce future AI-review false positives. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Summary
update-notifier@5: when a newer version is published, the CLI prints a one-time upgrade hint to stderr on exit. It never auto-updates.--output tablein an interactive TTY. It is suppressed for--output json, when piped/non-TTY, in CI, and undernpx/npm scripts — so machine-readable stdout stays byte-clean.CODACY_DISABLE_UPDATE_CHECK,NO_UPDATE_NOTIFIER,--no-update-notifier.src/version.ts(single source of name/version, read frompackage.jsonat runtime) andsrc/utils/update-check.ts(maybeNotifyUpdate). Wiring insrc/index.tsis a thinpreActionhook plus the--no-update-notifierflag on the root and every subcommand.update-notifier@5's transitivepackage-json@6→got@9is vulnerable to CVE-2022-33987. Apackage.jsonoverridesentry pinspackage-json@^7/got@^11.8.6(patched, still CommonJS).npm audit --omit=devreports 0 vulnerabilities.This mirrors the equivalent feature in analysis-cli#70, adapted for this CLI's
tscbuild and--output table|jsonflag.Test plan
npm run check-types— cleannpm run build— cleannpm test— 409 tests pass (7 new insrc/utils/update-check.test.ts)npm ls got package-json— resolvesgot@11.8.6/package-json@7.0.0, nogot@9versionbelow the npm latest, build, then in a TTY runnode dist/index.js info(clear~/.config/configstore/update-notifier-@codacy/codacy-cloud-cli.jsonfirst, run once, wait a few seconds, run again) → boxed notice on stderrnode dist/index.js info --output json | jq .→ valid JSON, no banner on stdout🤖 Generated with Claude Code