Skip to content

Commit 685f67a

Browse files
authored
self upgrade CLI command
1 parent f691451 commit 685f67a

File tree

8 files changed

+763
-10
lines changed

8 files changed

+763
-10
lines changed

cli/commands/build.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
exports.command = "build";
22

3-
exports.describe = "bundle package";
3+
exports.describe = "compile sources";
44

55
exports.builder = {};
66

cli/commands/lint.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
exports.command = "lint";
22

3-
exports.describe = "lint sources";
3+
exports.describe = "run static analysis";
44

55
exports.builder = {};
66

cli/commands/upgrade.js

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
exports.command = "upgrade";
2+
3+
exports.describe = "upgrade to latest compatible";
4+
5+
exports.builder = {};
6+
7+
exports.handler = async function (options, _cleanupHooks) {
8+
const { run } = await import("../helpers/upgrade.mjs");
9+
10+
await run(options);
11+
};

cli/helpers/shell.mjs

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ export async function execShellCommand(cmd) {
55
return new Promise((resolve, reject) => {
66
exec(cmd, { env: process.env, maxBuffer: 1024 * 500 }, (error, stdout, stderr) => {
77
if (error) {
8-
return reject(error);
8+
return reject(stderr ? new Error(stderr) : error);
99
} else {
1010
return resolve(stdout.trim());
1111
}

cli/helpers/upgrade.mjs

+106
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
import semver from "semver";
2+
3+
import process from "node:process";
4+
import path from "node:path";
5+
import colors from "ansi-colors";
6+
import npmFetch from "npm-registry-fetch";
7+
import { execShellCommand } from "./shell.mjs";
8+
import { readFile } from "./io.mjs";
9+
10+
function longestCommonPrefix(a, b) {
11+
let i = 0;
12+
while (a[i] && b[i] && a[i] === b[i]) {
13+
i++;
14+
}
15+
return i;
16+
}
17+
18+
async function getCurrentVersion(name) {
19+
const result = JSON.parse(await readFile(path.resolve(process.env.INIT_CWD, "package.json")));
20+
21+
if (result.dependencies && name in result.dependencies) {
22+
return semver.minVersion(result.dependencies[name]).version;
23+
}
24+
if (result.devDependencies && name in result.devDependencies) {
25+
return semver.minVersion(result.devDependencies[name]).version;
26+
}
27+
return null;
28+
}
29+
30+
async function getLatestVersion(name) {
31+
const registry = npmFetch.pickRegistry(name, {});
32+
const url = new URL(encodeURIComponent(name), registry.endsWith("/") ? registry : `${registry}/`);
33+
34+
const stream = npmFetch.json.stream(url.href, "$*", {
35+
headers: {
36+
"user-agent": "node/${process.version}",
37+
accept: "application/vnd.npm.install-v1+json; q=1.0, application/json; q=0.8, */*",
38+
spec: name,
39+
offline: false,
40+
},
41+
});
42+
43+
const versions = {};
44+
45+
for await (const { key, value } of stream) {
46+
if (key === "versions") {
47+
Object.assign(versions, value);
48+
break;
49+
}
50+
}
51+
52+
let newest = null;
53+
54+
for (const version in versions) {
55+
const wantedEngine = versions[version].engines?.node;
56+
57+
if (wantedEngine && !semver.satisfies(process.version, wantedEngine)) {
58+
continue;
59+
}
60+
61+
if (!newest || semver.gt(version, newest)) {
62+
newest = version;
63+
}
64+
}
65+
66+
return newest;
67+
}
68+
69+
export async function run(options) {
70+
const outdated = [];
71+
72+
for (const name of ["@lastui/dependencies", "@lastui/rocker"]) {
73+
const current = await getCurrentVersion(name);
74+
if (!current) {
75+
continue;
76+
}
77+
78+
const latest = await getLatestVersion(name);
79+
80+
if (!latest) {
81+
continue;
82+
}
83+
84+
if (semver.gt(latest, current)) {
85+
outdated.push({ name, current, latest });
86+
}
87+
}
88+
89+
if (outdated.length === 0) {
90+
console.log(colors.green("Up to date"));
91+
return;
92+
}
93+
94+
for (const { name, current, latest } of outdated) {
95+
const idx = longestCommonPrefix(latest, current);
96+
97+
console.log(`Bumping ${colors.cyan.bold(name)} to ${current.substring(0, idx)}${colors.cyan(latest.substring(idx))}`);
98+
99+
await execShellCommand(
100+
`npm --prefix=${process.env.INIT_CWD} i --no-progress --package-lock-only --save-exact --no-fund --no-audit ${name}@${latest}`,
101+
);
102+
}
103+
104+
console.log(colors.green("Installing upgrade"));
105+
await execShellCommand(`npm --prefix=${process.env.INIT_CWD} ci --no-progress --ci --no-fund`);
106+
}

cli/index.js

+6-4
Original file line numberDiff line numberDiff line change
@@ -82,16 +82,18 @@ require("yargs")
8282
})
8383
.option("fix", {
8484
type: "boolean",
85-
describe: "Fix automatically fixable issues",
85+
describe: "Try to automatically fix issues",
8686
})
8787
.option("cwd", {
8888
type: "string",
89-
describe: "Override working directory",
89+
describe: "Set working directory",
9090
})
9191
.conflicts("quiet", "debug")
9292
.command(envelope(require("./commands/build.js")))
93+
.command(envelope(require("./commands/upgrade.js")))
9394
.command(envelope(require("./commands/start.js")))
9495
.command(envelope(require("./commands/test.js")))
9596
.command(envelope(require("./commands/lint.js")))
96-
.demandCommand()
97-
.help(false).argv;
97+
.showHelpOnFail(false)
98+
.strict()
99+
.help(true).argv;

0 commit comments

Comments
 (0)