Skip to content

Commit 6d9f38c

Browse files
committed
feat: add update checker and upgrade command with auto-detect package manager
1 parent 344da5b commit 6d9f38c

File tree

4 files changed

+157
-2
lines changed

4 files changed

+157
-2
lines changed

CHANGELOG.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,14 @@ All notable changes to this project will be documented in this file.
55
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
66
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
77

8+
## [0.5.2] - 2026-01-04
9+
10+
### Added
11+
12+
- **Update Checker**: Automatic version check on CLI startup with update notification
13+
- **Upgrade Command**: New `lito upgrade` command to check and install latest version
14+
- **Auto-detect Package Manager**: Upgrade uses pnpm, yarn, or npm based on availability
15+
816
## [0.5.1] - 2026-01-04
917

1018
### Changed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@litodocs/cli",
3-
"version": "0.5.1",
3+
"version": "0.5.2",
44
"description": "Beautiful documentation sites from Markdown. Fast, simple, and open-source.",
55
"main": "src/index.js",
66
"type": "module",

src/cli.js

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,16 +7,20 @@ import {
77
templateListCommand,
88
templateCacheCommand,
99
} from "./commands/template.js";
10+
import { checkForUpdates, upgradeCommand } from "./core/update-check.js";
1011

1112
export async function cli() {
1213
const program = new Command();
1314

15+
// Check for updates in the background (non-blocking)
16+
checkForUpdates();
17+
1418
program
1519
.name("lito")
1620
.description(
1721
"Beautiful documentation sites from Markdown. Fast, simple, and open-source."
1822
)
19-
.version("0.5.1");
23+
.version("0.5.2");
2024

2125
program
2226
.command("build")
@@ -107,6 +111,11 @@ export async function cli() {
107111
.option("--clear", "Clear all cached templates")
108112
.action(templateCacheCommand);
109113

114+
program
115+
.command("upgrade")
116+
.description("Check for updates and upgrade to the latest version")
117+
.action(upgradeCommand);
118+
110119
try {
111120
await program.parseAsync(process.argv);
112121
} catch (error) {

src/core/update-check.js

Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
import { exec } from 'child_process';
2+
import { promisify } from 'util';
3+
import pc from 'picocolors';
4+
import { confirm } from '@clack/prompts';
5+
6+
const execAsync = promisify(exec);
7+
8+
const PACKAGE_NAME = '@litodocs/cli';
9+
10+
/**
11+
* Get the current installed version from package.json
12+
*/
13+
export function getCurrentVersion() {
14+
return '0.5.2';
15+
}
16+
17+
/**
18+
* Fetch the latest version from npm registry
19+
*/
20+
async function getLatestVersion() {
21+
try {
22+
const { stdout } = await execAsync(`npm view ${PACKAGE_NAME} version`, {
23+
timeout: 5000,
24+
});
25+
return stdout.trim();
26+
} catch {
27+
return null;
28+
}
29+
}
30+
31+
/**
32+
* Compare two semver versions
33+
* Returns: 1 if v1 > v2, -1 if v1 < v2, 0 if equal
34+
*/
35+
function compareVersions(v1, v2) {
36+
const parts1 = v1.split('.').map(Number);
37+
const parts2 = v2.split('.').map(Number);
38+
39+
for (let i = 0; i < 3; i++) {
40+
const p1 = parts1[i] || 0;
41+
const p2 = parts2[i] || 0;
42+
if (p1 > p2) return 1;
43+
if (p1 < p2) return -1;
44+
}
45+
return 0;
46+
}
47+
48+
/**
49+
* Upgrade the package using the detected package manager
50+
*/
51+
async function upgradePackage() {
52+
const packageManagers = ['pnpm', 'yarn', 'npm'];
53+
54+
for (const pm of packageManagers) {
55+
try {
56+
await execAsync(`${pm} --version`, { timeout: 2000 });
57+
58+
console.log(pc.cyan(`\nUpgrading using ${pm}...`));
59+
60+
const command = pm === 'yarn'
61+
? `yarn global add ${PACKAGE_NAME}@latest`
62+
: `${pm} install -g ${PACKAGE_NAME}@latest`;
63+
64+
const { stdout, stderr } = await execAsync(command, { timeout: 60000 });
65+
66+
if (stdout) console.log(stdout);
67+
68+
console.log(pc.green(`\n✓ Successfully upgraded ${PACKAGE_NAME}!`));
69+
console.log(pc.dim('Please restart your terminal or run the command again.\n'));
70+
return true;
71+
} catch {
72+
continue;
73+
}
74+
}
75+
76+
console.log(pc.yellow('\nCould not auto-upgrade. Please run manually:'));
77+
console.log(pc.cyan(` npm install -g ${PACKAGE_NAME}@latest\n`));
78+
return false;
79+
}
80+
81+
/**
82+
* Check for updates and prompt user to upgrade
83+
*/
84+
export async function checkForUpdates() {
85+
try {
86+
const currentVersion = getCurrentVersion();
87+
const latestVersion = await getLatestVersion();
88+
89+
if (!latestVersion) {
90+
return; // Silently fail if we can't check
91+
}
92+
93+
if (compareVersions(latestVersion, currentVersion) > 0) {
94+
console.log('');
95+
console.log(pc.yellow('╭─────────────────────────────────────────────────╮'));
96+
console.log(pc.yellow('│') + ' Update available! ' + pc.dim(`${currentVersion}`) + ' → ' + pc.green(`${latestVersion}`) + pc.yellow(' │'));
97+
console.log(pc.yellow('│') + pc.dim(` Run: lito upgrade`) + pc.yellow(' │'));
98+
console.log(pc.yellow('╰─────────────────────────────────────────────────╯'));
99+
console.log('');
100+
}
101+
} catch {
102+
// Silently fail - don't interrupt the user's workflow
103+
}
104+
}
105+
106+
/**
107+
* Upgrade command handler
108+
*/
109+
export async function upgradeCommand() {
110+
console.log(pc.cyan('\n🔍 Checking for updates...\n'));
111+
112+
const currentVersion = getCurrentVersion();
113+
const latestVersion = await getLatestVersion();
114+
115+
if (!latestVersion) {
116+
console.log(pc.yellow('Could not check for updates. Please check your internet connection.\n'));
117+
return;
118+
}
119+
120+
console.log(` Current version: ${pc.dim(currentVersion)}`);
121+
console.log(` Latest version: ${pc.green(latestVersion)}\n`);
122+
123+
if (compareVersions(latestVersion, currentVersion) <= 0) {
124+
console.log(pc.green('✓ You are already on the latest version!\n'));
125+
return;
126+
}
127+
128+
const shouldUpgrade = await confirm({
129+
message: `Upgrade from ${currentVersion} to ${latestVersion}?`,
130+
initialValue: true,
131+
});
132+
133+
if (shouldUpgrade) {
134+
await upgradePackage();
135+
} else {
136+
console.log(pc.dim('\nUpgrade cancelled.\n'));
137+
}
138+
}

0 commit comments

Comments
 (0)