feat(cli): add auto version checking and update prompt#29
Conversation
Introduce a structured, version-based documentation system for KDM CLI commands. This addresses issue KDM-cli#25 by creating a dedicated docs/ directory with versioned command documentation. Structure: docs/v1.2.1/README.md — Version overview and command index docs/v1.2.1/show/README.md — show (runners, pods, containers, minikube) docs/v1.2.1/health/README.md — health (pods, containers, all) docs/v1.2.1/watch/README.md — watch (live monitoring) docs/v1.2.1/logs/README.md — logs (container/pod logs) docs/v1.2.1/config/README.md — config (setup, set, list, clear) Each command doc includes: overview, syntax, parameters, examples, expected output, use cases, and common errors. Version info is included in every document for traceability.
- Create new version-check utility that fetches the latest version from npm registry - Compares installed version with latest published version - Displays a non-blocking, color-coded update notification when a newer version is available - Shows update type (Major/Minor/New) with current vs latest version info - Provides direct update command: npm install -g kdm-cli@latest - Silently fails if the registry is unreachable (non-intrusive) - Runs asynchronously after command execution to avoid slowing down the CLI Addresses issue KDM-cli#26.
📝 WalkthroughWalkthroughAdds a non-blocking version-check: a new utility reads the installed kdm-cli version from package.json, fetches kdm-cli@latest from the npm registry, compares semantic versions, and logs an update message when behind. The root command invokes this check asynchronously after CLI execution. ChangesAuto-update version check
|
| Check name | Status | Explanation | Resolution |
|---|---|---|---|
| Docstring Coverage | Docstring coverage is 60.00% which is insufficient. The required threshold is 80.00%. | Write docstrings for the functions missing them to satisfy the coverage threshold. |
✅ Passed checks (4 passed)
| Check name | Status | Explanation |
|---|---|---|
| Description Check | ✅ Passed | Check skipped - CodeRabbit’s high-level summary is enabled. |
| Title check | ✅ Passed | The title clearly and specifically describes the main change: adding automatic version checking with an update prompt to the CLI on startup. |
| Linked Issues check | ✅ Passed | Check skipped because no linked issues were found for this pull request. |
| Out of Scope Changes check | ✅ Passed | Check skipped because no linked issues were found for this pull request. |
✏️ Tip: You can configure your own custom pre-merge checks in the settings.
✨ Finishing Touches
🧪 Generate unit tests (beta)
- Create PR with unit tests
Comment @coderabbitai help to get the list of available commands and usage tips.
There was a problem hiding this comment.
Actionable comments posted: 4
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@src/commands/root.ts`:
- Around line 80-81: The update check call checkForUpdates() is never reached in
the zero-argument branch because that branch calls process.exit(...); fix by
ensuring checkForUpdates() runs before terminating: either invoke
checkForUpdates() (non-blocking) prior to the process.exit call in the no-args
branch or replace the immediate process.exit(...) with a return so the flow
continues to the shared post-command teardown that calls checkForUpdates();
update the no-args handling code path (where process.exit is called) to
reference the same logic used for other branches so checkForUpdates() always
runs.
In `@src/utils/version-check.ts`:
- Around line 20-68: Add unit tests that exercise compareSemver and
getUpdateType across all branches: for compareSemver test major/minor/patch
equality and v-prefixed inputs (expect 'lt'/'gt'/'eq'); for getUpdateType verify
it returns 'major', 'minor', 'patch' (or empty string for equal) by providing
installed/latest combos that differ by major, minor, patch and with/without
leading "v"; also add tests for checkForUpdates to simulate fetch responses
(mock successful JSON with a newer version to verify console output uses the
correct typeLabel and version strings, and mock a failing fetch or non-ok
response to ensure the function silently returns) — target the functions
compareSemver, getUpdateType, and checkForUpdates when adding these focused
tests.
- Around line 38-40: The code that compares installed vs latest versions
mis-destructures the minor component: it reuses index 0 for i2 and l2 so minor
bumps are treated as patch; update the destructuring for the minor comparison in
the version-check logic (the lines that currently read const [i2] =
installed.replace(/^v/, '').split('.').map(Number); and const [l2] =
latest.replace(/^v/, '').split('.').map(Number);) to pull the second element
(minor) instead (e.g., use const [, i2] = ... and const [, l2] = ... or index
into the array) so that 1.1.0 -> 1.2.0 correctly returns 'minor'.
- Around line 46-47: The fetch in checkForUpdates() (src/utils/version-check.ts)
needs a timeout so a stalled npm-registry request doesn't keep the event loop
alive; update the call to use an AbortSignal via AbortSignal.timeout(timeoutMs)
and pass the signal to fetch (ensuring you abort/handle the thrown
DOMException), and ensure the early-return/error logging paths remain intact in
the checkForUpdates() function; then add vitest tests for checkForUpdates() that
simulate a hanging fetch (to assert the timeout triggers) and a failing response
(non-ok) to cover error handling.
🪄 Autofix (Beta)
❌ Autofix failed (check again to retry)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro Plus
Run ID: 30d3daaf-c928-4d8c-a830-54b53536667e
⛔ Files ignored due to path filters (6)
docs/v1.2.1/README.mdis excluded by none and included by nonedocs/v1.2.1/config/README.mdis excluded by none and included by nonedocs/v1.2.1/health/README.mdis excluded by none and included by nonedocs/v1.2.1/logs/README.mdis excluded by none and included by nonedocs/v1.2.1/show/README.mdis excluded by none and included by nonedocs/v1.2.1/watch/README.mdis excluded by none and included by none
📒 Files selected for processing (2)
src/commands/root.tssrc/utils/version-check.ts
| // Non-blocking version check (fires after command execution) | ||
| checkForUpdates(); |
There was a problem hiding this comment.
Update check is unreachable on the zero-argument flow.
Because Line 74 calls process.exit(...) in the no-args branch, Lines 80–81 never execute there.
Suggested control-flow adjustment
const run = async () => {
if (!process.argv.slice(2).length) {
showWelcomeBanner('1.1.0');
@@
} finally {
program.outputHelp();
- process.exit(hadError ? 1 : 0);
+ process.exitCode = hadError ? 1 : 0;
}
+ void checkForUpdates();
+ return;
}
program.parse(process.argv);
- checkForUpdates();
+ void checkForUpdates();
};🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/commands/root.ts` around lines 80 - 81, The update check call
checkForUpdates() is never reached in the zero-argument branch because that
branch calls process.exit(...); fix by ensuring checkForUpdates() runs before
terminating: either invoke checkForUpdates() (non-blocking) prior to the
process.exit call in the no-args branch or replace the immediate
process.exit(...) with a return so the flow continues to the shared post-command
teardown that calls checkForUpdates(); update the no-args handling code path
(where process.exit is called) to reference the same logic used for other
branches so checkForUpdates() always runs.
| // Compare two semver strings; returns 'lt' if a < b, 'gt' if a > b, 'eq' if equal | ||
| function compareSemver(a: string, b: string): 'lt' | 'gt' | 'eq' { | ||
| const parse = (v: string) => v.replace(/^v/, '').split('.').map(Number); | ||
| const [a1, a2, a3] = parse(a); | ||
| const [b1, b2, b3] = parse(b); | ||
| if (a1 !== b1) return a1 < b1 ? 'lt' : 'gt'; | ||
| if (a2 !== b2) return a2 < b2 ? 'lt' : 'gt'; | ||
| if (a3 !== b3) return a3 < b3 ? 'lt' : 'gt'; | ||
| return 'eq'; | ||
| } | ||
|
|
||
| // Determine update type for messaging | ||
| function getUpdateType(installed: string, latest: string): string { | ||
| const cmp = compareSemver(installed, latest); | ||
| if (cmp === 'eq') return ''; | ||
| const [i1] = installed.replace(/^v/, '').split('.').map(Number); | ||
| const [l1] = latest.replace(/^v/, '').split('.').map(Number); | ||
| if (l1 > i1) return 'major'; | ||
| const [i2] = installed.replace(/^v/, '').split('.').map(Number); | ||
| const [l2] = latest.replace(/^v/, '').split('.').map(Number); | ||
| if (l2 > i2) return 'minor'; | ||
| return 'patch'; | ||
| } | ||
|
|
||
| export async function checkForUpdates(): Promise<void> { | ||
| try { | ||
| const response = await fetch('https://registry.npmjs.org/kdm-cli/latest'); | ||
| if (!response.ok) return; | ||
|
|
||
| const data = await response.json() as { version: string }; | ||
| const latestVersion = data.version; | ||
| const installedVersion = getInstalledVersion(); | ||
|
|
||
| const cmp = compareSemver(installedVersion, latestVersion); | ||
| if (cmp === 'lt') { | ||
| const updateType = getUpdateType(installedVersion, latestVersion); | ||
| const typeLabel = updateType === 'major' ? 'Major' : updateType === 'minor' ? 'Minor' : 'New'; | ||
|
|
||
| console.log(); | ||
| console.log(chalk.bold(chalk.yellow(` ${typeLabel} update available!`))); | ||
| console.log(chalk.white(` Current Version : v${installedVersion}`)); | ||
| console.log(chalk.white(` Latest Version : v${latestVersion}`)); | ||
| console.log(chalk.cyan(` Update using : npm install -g kdm-cli@latest`)); | ||
| console.log(); | ||
| } | ||
| } catch { | ||
| // Silently fail — version check is non-critical | ||
| } | ||
| } |
There was a problem hiding this comment.
🧹 Nitpick | 🔵 Trivial | ⚡ Quick win
Please add focused tests for version comparison and update labeling paths.
This file introduces branch-heavy logic (compareSemver, getUpdateType, silent-fail behavior) and should be covered to avoid regressions.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/utils/version-check.ts` around lines 20 - 68, Add unit tests that
exercise compareSemver and getUpdateType across all branches: for compareSemver
test major/minor/patch equality and v-prefixed inputs (expect 'lt'/'gt'/'eq');
for getUpdateType verify it returns 'major', 'minor', 'patch' (or empty string
for equal) by providing installed/latest combos that differ by major, minor,
patch and with/without leading "v"; also add tests for checkForUpdates to
simulate fetch responses (mock successful JSON with a newer version to verify
console output uses the correct typeLabel and version strings, and mock a
failing fetch or non-ok response to ensure the function silently returns) —
target the functions compareSemver, getUpdateType, and checkForUpdates when
adding these focused tests.
utkarsh232005
left a comment
There was a problem hiding this comment.
You pr has 2 commits by which it is merging two different prs merge just create fresh pr or force push your current pr branch changes
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
|
Note Autofix is a beta feature. Expect some limitations and changes as we gather feedback and continue to improve it. An unexpected error occurred while generating fixes: Not Found - https://docs.github.com/rest/git/refs#get-a-reference |
There was a problem hiding this comment.
Actionable comments posted: 3
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@src/utils/version-check.ts`:
- Around line 57-58: The code casts the registry response to `{ version: string
}` and assigns `const data`/`latestVersion` without validating the shape; update
the logic in version-check utilities (the code around `const data = await
response.json() as { version: string };` and `const latestVersion =
data.version;`) to perform runtime validation: parse the JSON into an unknown,
assert that `data` is an object and that `data.version` exists and is a string
(e.g., typeof check), and handle invalid responses by logging a clear error and
returning a safe fallback (or throwing a descriptive error) instead of relying
on the cast. Ensure the function's return type and error handling reflect this
change.
- Around line 32-48: Remove the duplicate/overlapping getUpdateType declaration
and keep the corrected implementation: the version that uses compareSemver,
parses installed and latest with default numeric parts (e.g., const [i1 = 0, i2
= 0], const [l1 = 0, l2 = 0]), returns 'major' if l1>i1, 'minor' if l2>i2,
'patch' otherwise, and returns '' when compareSemver(...) === 'eq'; ensure only
one getUpdateType function (referenced symbol: getUpdateType) remains and the
stray earlier/extra braces and trailing return are removed so the file compiles.
- Around line 10-18: Remove the duplicate/incomplete getUpdateType declaration
and keep only the complete implementation named getUpdateType (the version that
correctly destructures major/minor with defaults and returns 'minor' when
appropriate); delete the orphaned earlier lines so the file compiles. In
checkForUpdates, add a runtime guard that validates the registry response by
checking typeof data.version === 'string' and early-return if not. Add vitest
fixtures/tests covering compareSemver, getUpdateType, and checkForUpdates
including tests for timeout behavior, error states, and invalid registry
responses to ensure coverage.
🪄 Autofix (Beta)
❌ Autofix failed (check again to retry)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro Plus
Run ID: 2e12211c-4c70-494b-8145-7f22c5f3c35d
📒 Files selected for processing (1)
src/utils/version-check.ts
| function getInstalledVersion(): string { | ||
| const pkgPath = join(__dirname, '..', '..', 'package.json'); | ||
| try { | ||
| const pkg = JSON.parse(readFileSync(pkgPath, 'utf-8')); | ||
| return pkg.version || '0.0.0'; | ||
| } catch { | ||
| return '0.0.0'; | ||
| } | ||
| } |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Description: Check build configuration and deployment structure
# Check for bundler config
fd -t f -e json -e js -e ts 'vite.config|webpack.config|esbuild|tsup.config|build.config' --exec cat {} \;
# Check package.json build script and bin entry
jq '.scripts.build, .bin' package.json
# Check if dist/ structure matches src/
if [ -d "dist" ]; then
echo "=== dist/ structure ==="
tree -L 3 dist/ 2>/dev/null || find dist/ -type f | head -20
fiRepository: KDM-cli/kdm-cli
Length of output: 126
🏁 Script executed:
#!/bin/bash
# Check repository root structure
echo "=== Repository root structure ==="
ls -la | grep -E "^d|^-.*\.(json|js|ts|sh)$"
# Check if bin/ directory exists
echo ""
echo "=== Checking bin/ directory ==="
if [ -d "bin" ]; then
ls -la bin/
echo ""
echo "Content of bin/kdm.js (first 30 lines):"
head -30 bin/kdm.js
else
echo "bin/ directory does not exist"
fi
# Check if dist/ directory exists
echo ""
echo "=== Checking dist/ directory ==="
if [ -d "dist" ]; then
ls -la dist/
find dist/ -type f | head -10
else
echo "dist/ directory does not exist"
fi
# Find actual output of the build
echo ""
echo "=== Searching for .js output files ==="
find . -name "*.js" -type f ! -path "*/node_modules/*" ! -path "*/.git/*" | head -20Repository: KDM-cli/kdm-cli
Length of output: 1155
🏁 Script executed:
#!/bin/bash
# Read the version-check.ts file
cat -n src/utils/version-check.tsRepository: KDM-cli/kdm-cli
Length of output: 3412
Remove duplicate getUpdateType function declaration—this is a syntax error blocking compilation.
Lines 32–37 and 47–48 contain the incomplete first version; lines 38–46 are the corrected implementation. Delete lines 32–37 and the orphaned lines 47–48. Keep only the complete version (lines 38–46) which correctly destructures both major and minor versions with defaults and includes the 'minor' return case.
Additionally:
- Line 57: No runtime validation that
data.versionis a string. If the registry returns unexpected data,latestVersioncould be undefined. Add a check:if (typeof data.version !== 'string') return; - Test coverage: Add vitest fixtures for
compareSemver,getUpdateType, andcheckForUpdates(including timeout, error states, and registry response validation).
The __dirname-based path resolution is correct for the tsup + bin shim setup (outputs to dist/, so dist/utils/../../package.json resolves to repo root).
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/utils/version-check.ts` around lines 10 - 18, Remove the
duplicate/incomplete getUpdateType declaration and keep only the complete
implementation named getUpdateType (the version that correctly destructures
major/minor with defaults and returns 'minor' when appropriate); delete the
orphaned earlier lines so the file compiles. In checkForUpdates, add a runtime
guard that validates the registry response by checking typeof data.version ===
'string' and early-return if not. Add vitest fixtures/tests covering
compareSemver, getUpdateType, and checkForUpdates including tests for timeout
behavior, error states, and invalid registry responses to ensure coverage.
| function getUpdateType(installed: string, latest: string): string { | ||
| const cmp = compareSemver(installed, latest); | ||
| if (cmp === 'eq') return ''; | ||
| const [i1] = installed.replace(/^v/, '').split('.').map(Number); | ||
| const [l1] = latest.replace(/^v/, '').split('.').map(Number); | ||
| if (l1 > i1) return 'major'; | ||
| function getUpdateType(installed: string, latest: string): string { | ||
| const cmp = compareSemver(installed, latest); | ||
| if (cmp === 'eq') return ''; | ||
| const [i1 = 0, i2 = 0] = installed.replace(/^v/, '').split('.').map(Number); | ||
| const [l1 = 0, l2 = 0] = latest.replace(/^v/, '').split('.').map(Number); | ||
| if (l1 > i1) return 'major'; | ||
| if (l2 > i2) return 'minor'; | ||
| return 'patch'; | ||
| } | ||
| return 'patch'; | ||
| } |
There was a problem hiding this comment.
Critical: Duplicate function declaration prevents compilation.
Lines 32–48 contain overlapping getUpdateType declarations. Line 38 starts a second function definition before the first closes, creating invalid syntax. This appears to be a merge artifact where the corrected version (lines 38–46) was added but the original (lines 32–37, 47–48) wasn't removed.
🔧 Proposed fix: remove the duplicate and keep only the corrected version
-// Determine update type for messaging
-function getUpdateType(installed: string, latest: string): string {
- const cmp = compareSemver(installed, latest);
- if (cmp === 'eq') return '';
- const [i1] = installed.replace(/^v/, '').split('.').map(Number);
- const [l1] = latest.replace(/^v/, '').split('.').map(Number);
- if (l1 > i1) return 'major';
+// Determine update type for messaging
function getUpdateType(installed: string, latest: string): string {
const cmp = compareSemver(installed, latest);
if (cmp === 'eq') return '';
const [i1 = 0, i2 = 0] = installed.replace(/^v/, '').split('.').map(Number);
const [l1 = 0, l2 = 0] = latest.replace(/^v/, '').split('.').map(Number);
if (l1 > i1) return 'major';
if (l2 > i2) return 'minor';
return 'patch';
}
- return 'patch';
-}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| function getUpdateType(installed: string, latest: string): string { | |
| const cmp = compareSemver(installed, latest); | |
| if (cmp === 'eq') return ''; | |
| const [i1] = installed.replace(/^v/, '').split('.').map(Number); | |
| const [l1] = latest.replace(/^v/, '').split('.').map(Number); | |
| if (l1 > i1) return 'major'; | |
| function getUpdateType(installed: string, latest: string): string { | |
| const cmp = compareSemver(installed, latest); | |
| if (cmp === 'eq') return ''; | |
| const [i1 = 0, i2 = 0] = installed.replace(/^v/, '').split('.').map(Number); | |
| const [l1 = 0, l2 = 0] = latest.replace(/^v/, '').split('.').map(Number); | |
| if (l1 > i1) return 'major'; | |
| if (l2 > i2) return 'minor'; | |
| return 'patch'; | |
| } | |
| return 'patch'; | |
| } | |
| function getUpdateType(installed: string, latest: string): string { | |
| const cmp = compareSemver(installed, latest); | |
| if (cmp === 'eq') return ''; | |
| const [i1 = 0, i2 = 0] = installed.replace(/^v/, '').split('.').map(Number); | |
| const [l1 = 0, l2 = 0] = latest.replace(/^v/, '').split('.').map(Number); | |
| if (l1 > i1) return 'major'; | |
| if (l2 > i2) return 'minor'; | |
| return 'patch'; | |
| } |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/utils/version-check.ts` around lines 32 - 48, Remove the
duplicate/overlapping getUpdateType declaration and keep the corrected
implementation: the version that uses compareSemver, parses installed and latest
with default numeric parts (e.g., const [i1 = 0, i2 = 0], const [l1 = 0, l2 =
0]), returns 'major' if l1>i1, 'minor' if l2>i2, 'patch' otherwise, and returns
'' when compareSemver(...) === 'eq'; ensure only one getUpdateType function
(referenced symbol: getUpdateType) remains and the stray earlier/extra braces
and trailing return are removed so the file compiles.
| const data = await response.json() as { version: string }; | ||
| const latestVersion = data.version; |
There was a problem hiding this comment.
Add runtime validation for the npm registry response.
The response is cast to { version: string } but not validated. If the registry returns malformed JSON (missing .version, wrong type, or unexpected structure), the code will attempt to compare undefined or throw during access. While the outer try-catch prevents a crash, explicit validation improves security posture and makes failure modes clear.
🛡️ Proposed defensive check
const data = await response.json() as { version: string };
+ if (!data || typeof data.version !== 'string') return;
const latestVersion = data.version;🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/utils/version-check.ts` around lines 57 - 58, The code casts the registry
response to `{ version: string }` and assigns `const data`/`latestVersion`
without validating the shape; update the logic in version-check utilities (the
code around `const data = await response.json() as { version: string };` and
`const latestVersion = data.version;`) to perform runtime validation: parse the
JSON into an unknown, assert that `data` is an object and that `data.version`
exists and is a string (e.g., typeof check), and handle invalid responses by
logging a clear error and returning a safe fallback (or throwing a descriptive
error) instead of relying on the cast. Ensure the function's return type and
error handling reflect this change.
|
Note Autofix is a beta feature. Expect some limitations and changes as we gather feedback and continue to improve it. An unexpected error occurred while generating fixes: Cannot open a stacked PR on a forked repository. |
feat(cli): add auto version checking and update prompt
This PR implements automatic version checking when the CLI starts, addressing issue #26.
What's New
src/utils/version-check.ts— fetches the latest version from the npm registry and compares it with the installed versionnpm install -g kdm-cli@latestcommandExample Output
When a newer version is available:
Files Changed
src/utils/version-check.ts— new file (version check utility)src/commands/root.ts— integrated version check into CLI entry pointSummary by CodeRabbit