@maravilla-labs/cli 0.1.0 → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/maravilla-update.js +12 -0
- package/package.json +3 -2
- package/scripts/postinstall.js +42 -0
- package/scripts/update.js +85 -3
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// Thin wrapper that runs the package updater
|
|
3
|
+
import { fileURLToPath } from 'url';
|
|
4
|
+
import path from 'path';
|
|
5
|
+
import { spawn } from 'child_process';
|
|
6
|
+
|
|
7
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
8
|
+
const __dirname = path.dirname(__filename);
|
|
9
|
+
const updater = path.join(__dirname, '..', 'scripts', 'update.js');
|
|
10
|
+
|
|
11
|
+
const child = spawn(process.execPath, [updater], { stdio: 'inherit' });
|
|
12
|
+
child.on('exit', (code) => process.exit(code ?? 0));
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@maravilla-labs/cli",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.1",
|
|
4
4
|
"description": "NPM wrapper for the Maravilla CLI binary; downloads the right release for your platform.",
|
|
5
5
|
"license": "SEE LICENSE IN LICENSE",
|
|
6
6
|
"author": "Maravilla Labs",
|
|
@@ -14,7 +14,8 @@
|
|
|
14
14
|
},
|
|
15
15
|
"homepage": "https://github.com/solutas/maravilla-runtime#readme",
|
|
16
16
|
"bin": {
|
|
17
|
-
"maravilla": "bin/maravilla"
|
|
17
|
+
"maravilla": "bin/maravilla",
|
|
18
|
+
"maravilla-update": "bin/maravilla-update.js"
|
|
18
19
|
},
|
|
19
20
|
"type": "module",
|
|
20
21
|
"scripts": {
|
package/scripts/postinstall.js
CHANGED
|
@@ -73,6 +73,35 @@ async function extractArchive(archivePath, targetDir, ext) {
|
|
|
73
73
|
}
|
|
74
74
|
}
|
|
75
75
|
|
|
76
|
+
async function currentVersion() {
|
|
77
|
+
return new Promise((resolve) => {
|
|
78
|
+
const child = spawn(INSTALL_PATH, ['--version'], { stdio: ['ignore', 'pipe', 'ignore'] });
|
|
79
|
+
let out = '';
|
|
80
|
+
child.stdout.on('data', (d) => (out += d.toString()));
|
|
81
|
+
child.on('error', () => resolve(null));
|
|
82
|
+
child.on('exit', (code) => {
|
|
83
|
+
if (code === 0) {
|
|
84
|
+
const v = (out || '').trim();
|
|
85
|
+
const m = v.match(/(\d+\.\d+\.\d+)/);
|
|
86
|
+
resolve(m ? m[1] : null);
|
|
87
|
+
} else {
|
|
88
|
+
resolve(null);
|
|
89
|
+
}
|
|
90
|
+
});
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function cmpSemver(a, b) {
|
|
95
|
+
if (!a || !b) return 1;
|
|
96
|
+
const pa = a.split('.').map(Number);
|
|
97
|
+
const pb = b.split('.').map(Number);
|
|
98
|
+
for (let i = 0; i < 3; i++) {
|
|
99
|
+
if (pa[i] > pb[i]) return 1;
|
|
100
|
+
if (pa[i] < pb[i]) return -1;
|
|
101
|
+
}
|
|
102
|
+
return 0;
|
|
103
|
+
}
|
|
104
|
+
|
|
76
105
|
async function install() {
|
|
77
106
|
const { target, ext } = resolveTarget();
|
|
78
107
|
const tag = await getReleaseTag();
|
|
@@ -85,6 +114,19 @@ async function install() {
|
|
|
85
114
|
const tmpDir = await fs.promises.mkdtemp(path.join(os.tmpdir(), 'maravilla-cli-'));
|
|
86
115
|
const archive = path.join(tmpDir, assetFile);
|
|
87
116
|
|
|
117
|
+
// Fast path: if installed and matches latest, skip
|
|
118
|
+
if (VERSION === 'latest') {
|
|
119
|
+
try {
|
|
120
|
+
const cur = await currentVersion();
|
|
121
|
+
const m = tag.match(/v(\d+\.\d+\.\d+)/);
|
|
122
|
+
const latest = m ? m[1] : null;
|
|
123
|
+
if (cur && latest && cmpSemver(cur, latest) >= 0) {
|
|
124
|
+
log(chalk.green(`Already up to date (v${cur})`));
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
127
|
+
} catch {}
|
|
128
|
+
}
|
|
129
|
+
|
|
88
130
|
log(chalk.bold(`Installing ${BIN_NAME} ${chalk.cyan(tag)} for ${chalk.cyan(target)}`));
|
|
89
131
|
log(`Downloading from ${url}`);
|
|
90
132
|
await downloadWithProgress(url, archive);
|
package/scripts/update.js
CHANGED
|
@@ -2,11 +2,93 @@
|
|
|
2
2
|
import { fileURLToPath } from 'url';
|
|
3
3
|
import path from 'path';
|
|
4
4
|
import { spawn } from 'child_process';
|
|
5
|
+
import fetch from 'node-fetch';
|
|
6
|
+
import chalk from 'chalk';
|
|
5
7
|
|
|
6
|
-
//
|
|
8
|
+
// Re-run postinstall only when an update is actually needed
|
|
7
9
|
const __filename = fileURLToPath(import.meta.url);
|
|
8
10
|
const __dirname = path.dirname(__filename);
|
|
9
11
|
const postinstall = path.join(__dirname, 'postinstall.js');
|
|
10
12
|
|
|
11
|
-
const
|
|
12
|
-
|
|
13
|
+
const BIN_NAME = 'maravilla';
|
|
14
|
+
const REPO = process.env.MARAVILLA_CLI_REPO || 'maravilla-labs/maravilla-cli';
|
|
15
|
+
const BIN_DIR = path.join(__dirname, '..', 'bin');
|
|
16
|
+
const EXECUTABLE = process.platform === 'win32' ? `${BIN_NAME}.exe` : BIN_NAME;
|
|
17
|
+
const INSTALL_PATH = path.join(BIN_DIR, EXECUTABLE);
|
|
18
|
+
|
|
19
|
+
function flower() {
|
|
20
|
+
return chalk.magenta('✿');
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function log(msg) {
|
|
24
|
+
console.log(`${flower()} ${msg}`);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
async function currentVersion() {
|
|
28
|
+
return new Promise((resolve) => {
|
|
29
|
+
const child = spawn(INSTALL_PATH, ['--version'], { stdio: ['ignore', 'pipe', 'ignore'] });
|
|
30
|
+
let out = '';
|
|
31
|
+
child.stdout.on('data', (d) => (out += d.toString()));
|
|
32
|
+
child.on('error', () => resolve(null));
|
|
33
|
+
child.on('exit', (code) => {
|
|
34
|
+
if (code === 0) {
|
|
35
|
+
const v = (out || '').trim();
|
|
36
|
+
// clap default prints: "maravilla X.Y.Z" — extract semver
|
|
37
|
+
const match = v.match(/(\d+\.\d+\.\d+)/);
|
|
38
|
+
resolve(match ? match[1] : null);
|
|
39
|
+
} else {
|
|
40
|
+
resolve(null);
|
|
41
|
+
}
|
|
42
|
+
});
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
async function latestVersion() {
|
|
47
|
+
const api = `https://api.github.com/repos/${REPO}/releases/latest`;
|
|
48
|
+
const res = await fetch(api, { headers: { 'User-Agent': 'maravilla-cli-updater' } });
|
|
49
|
+
if (!res.ok) throw new Error(`Failed to fetch latest release: ${res.status} ${res.statusText}`);
|
|
50
|
+
const json = await res.json();
|
|
51
|
+
const tag = json.tag_name || '';
|
|
52
|
+
// tags are like cli-vX.Y.Z
|
|
53
|
+
const m = tag.match(/v(\d+\.\d+\.\d+)/);
|
|
54
|
+
return { tag, version: m ? m[1] : null };
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function cmpSemver(a, b) {
|
|
58
|
+
if (!a || !b) return 1; // treat unknown as needs update
|
|
59
|
+
const pa = a.split('.').map(Number);
|
|
60
|
+
const pb = b.split('.').map(Number);
|
|
61
|
+
for (let i = 0; i < 3; i++) {
|
|
62
|
+
if (pa[i] > pb[i]) return 1;
|
|
63
|
+
if (pa[i] < pb[i]) return -1;
|
|
64
|
+
}
|
|
65
|
+
return 0;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
async function main() {
|
|
69
|
+
// Allow forcing a version via env (MARAVILLA_VERSION)
|
|
70
|
+
const forced = process.env.MARAVILLA_VERSION?.replace(/^v/, '') || null;
|
|
71
|
+
const cur = await currentVersion();
|
|
72
|
+
const { tag, version: latest } = await latestVersion();
|
|
73
|
+
|
|
74
|
+
if (forced) {
|
|
75
|
+
log(chalk.cyan(`Forcing update to v${forced}`));
|
|
76
|
+
} else if (cur && latest && cmpSemver(cur, latest) >= 0) {
|
|
77
|
+
log(chalk.green(`Already up to date (v${cur})`));
|
|
78
|
+
return 0;
|
|
79
|
+
} else if (cur && latest) {
|
|
80
|
+
log(`Updating from v${cur} to ${chalk.cyan(tag)}`);
|
|
81
|
+
} else {
|
|
82
|
+
log(`Updating to ${chalk.cyan(tag)}`);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
return await new Promise((resolve) => {
|
|
86
|
+
const child = spawn(process.execPath, [postinstall], { stdio: 'inherit' });
|
|
87
|
+
child.on('exit', (code) => resolve(code ?? 0));
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
main().then((code) => process.exit(code)).catch((e) => {
|
|
92
|
+
console.error(chalk.red(`${flower()} update failed: ${e.message}`));
|
|
93
|
+
process.exit(1);
|
|
94
|
+
});
|