@maravilla-labs/cli 0.1.1 → 0.1.4
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/package.json +1 -1
- package/scripts/postinstall.js +62 -13
- package/scripts/update.js +3 -2
package/package.json
CHANGED
package/scripts/postinstall.js
CHANGED
|
@@ -37,17 +37,47 @@ function log(msg) {
|
|
|
37
37
|
console.log(`${flower()} ${msg}`);
|
|
38
38
|
}
|
|
39
39
|
|
|
40
|
+
function sleep(ms) { return new Promise(r => setTimeout(r, ms)); }
|
|
41
|
+
|
|
42
|
+
async function abortableFetch(url, opts = {}, timeoutMs = 30000) {
|
|
43
|
+
const controller = new AbortController();
|
|
44
|
+
const t = setTimeout(() => controller.abort(), timeoutMs);
|
|
45
|
+
try {
|
|
46
|
+
return await fetch(url, { ...opts, signal: controller.signal });
|
|
47
|
+
} finally {
|
|
48
|
+
clearTimeout(t);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
async function withRetries(taskFn, tries = 3, baseDelay = 1000) {
|
|
53
|
+
let attempt = 0;
|
|
54
|
+
let lastErr;
|
|
55
|
+
while (attempt < tries) {
|
|
56
|
+
try {
|
|
57
|
+
return await taskFn();
|
|
58
|
+
} catch (e) {
|
|
59
|
+
lastErr = e;
|
|
60
|
+
attempt++;
|
|
61
|
+
if (attempt >= tries) break;
|
|
62
|
+
const delay = baseDelay * Math.pow(2, attempt - 1);
|
|
63
|
+
console.log(chalk.yellow(`${flower()} retrying in ${Math.round(delay/100)/10}s...`));
|
|
64
|
+
await sleep(delay);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
throw lastErr;
|
|
68
|
+
}
|
|
69
|
+
|
|
40
70
|
async function getReleaseTag() {
|
|
41
71
|
if (VERSION && VERSION !== 'latest') return VERSION;
|
|
42
72
|
const api = `https://api.github.com/repos/${REPO}/releases/latest`;
|
|
43
|
-
const res = await
|
|
73
|
+
const res = await withRetries(() => abortableFetch(api, { headers: { 'User-Agent': 'maravilla-cli-installer' } }, 20000));
|
|
44
74
|
if (!res.ok) throw new Error(`Failed to fetch latest release: ${res.status} ${res.statusText}`);
|
|
45
75
|
const json = await res.json();
|
|
46
76
|
return json.tag_name;
|
|
47
77
|
}
|
|
48
78
|
|
|
49
79
|
async function downloadWithProgress(url, dest) {
|
|
50
|
-
const res = await
|
|
80
|
+
const res = await withRetries(() => abortableFetch(url, {}, 60000));
|
|
51
81
|
if (!res.ok) throw new Error(`Failed to download ${url}: ${res.status} ${res.statusText}`);
|
|
52
82
|
const total = Number(res.headers.get('content-length')) || 0;
|
|
53
83
|
const bar = total ? new ProgressBar(`${flower()} downloading [:bar] :percent :etas`, {
|
|
@@ -58,9 +88,14 @@ async function downloadWithProgress(url, dest) {
|
|
|
58
88
|
res.body,
|
|
59
89
|
new (class extends fs.WriteStream {
|
|
60
90
|
constructor(dest) { super(dest); }
|
|
61
|
-
write(chunk, enc, cb) {
|
|
91
|
+
write(chunk, enc, cb) {
|
|
92
|
+
if (bar) bar.tick(chunk.length);
|
|
93
|
+
else process.stdout.write('.');
|
|
94
|
+
super.write(chunk, enc, cb);
|
|
95
|
+
}
|
|
62
96
|
})(dest)
|
|
63
97
|
);
|
|
98
|
+
if (!bar) process.stdout.write('\n');
|
|
64
99
|
}
|
|
65
100
|
|
|
66
101
|
async function extractArchive(archivePath, targetDir, ext) {
|
|
@@ -104,6 +139,7 @@ function cmpSemver(a, b) {
|
|
|
104
139
|
|
|
105
140
|
async function install() {
|
|
106
141
|
const { target, ext } = resolveTarget();
|
|
142
|
+
log(`Resolving latest release tag from ${chalk.cyan(REPO)}`);
|
|
107
143
|
const tag = await getReleaseTag();
|
|
108
144
|
const art = artifactName(BIN_NAME, tag, target);
|
|
109
145
|
const assetFile = `${art}.${ext}`;
|
|
@@ -114,17 +150,30 @@ async function install() {
|
|
|
114
150
|
const tmpDir = await fs.promises.mkdtemp(path.join(os.tmpdir(), 'maravilla-cli-'));
|
|
115
151
|
const archive = path.join(tmpDir, assetFile);
|
|
116
152
|
|
|
117
|
-
//
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
153
|
+
// Decide whether to download based on current vs requested version
|
|
154
|
+
const force = process.env.MARAVILLA_FORCE_INSTALL === '1';
|
|
155
|
+
let requested = null;
|
|
156
|
+
const m = tag.match(/v(\d+\.\d+\.\d+)/);
|
|
157
|
+
if (m) requested = m[1];
|
|
158
|
+
let cur = null;
|
|
159
|
+
try { cur = await currentVersion(); } catch {}
|
|
160
|
+
|
|
161
|
+
if (!force) {
|
|
162
|
+
if (process.env.MARAVILLA_OFFLINE === '1') {
|
|
163
|
+
if (cur) {
|
|
164
|
+
if (requested && cmpSemver(cur, requested) < 0) {
|
|
165
|
+
log(chalk.yellow(`Offline mode: cannot update to v${requested}; keeping existing v${cur}`));
|
|
166
|
+
return;
|
|
167
|
+
}
|
|
168
|
+
log(chalk.yellow(`Offline mode: keeping existing v${cur}`));
|
|
125
169
|
return;
|
|
126
170
|
}
|
|
127
|
-
|
|
171
|
+
throw new Error('Offline mode: no existing binary to keep');
|
|
172
|
+
}
|
|
173
|
+
if (cur && requested && cmpSemver(cur, requested) >= 0) {
|
|
174
|
+
log(chalk.green(`Already up to date (v${cur})`));
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
128
177
|
}
|
|
129
178
|
|
|
130
179
|
log(chalk.bold(`Installing ${BIN_NAME} ${chalk.cyan(tag)} for ${chalk.cyan(target)}`));
|
|
@@ -134,7 +183,7 @@ async function install() {
|
|
|
134
183
|
// Verify checksum if SHA256SUMS is available
|
|
135
184
|
try {
|
|
136
185
|
log('Verifying checksum');
|
|
137
|
-
|
|
186
|
+
const sumsRes = await abortableFetch(sumsUrl, { headers: { 'User-Agent': 'maravilla-cli-installer' } }, 15000);
|
|
138
187
|
if (sumsRes.ok) {
|
|
139
188
|
const sumsText = await sumsRes.text();
|
|
140
189
|
const expected = sumsText
|
package/scripts/update.js
CHANGED
|
@@ -82,8 +82,9 @@ async function main() {
|
|
|
82
82
|
log(`Updating to ${chalk.cyan(tag)}`);
|
|
83
83
|
}
|
|
84
84
|
|
|
85
|
-
|
|
86
|
-
|
|
85
|
+
return await new Promise((resolve) => {
|
|
86
|
+
const env = { ...process.env, MARAVILLA_FORCE_INSTALL: '1' };
|
|
87
|
+
const child = spawn(process.execPath, [postinstall], { stdio: 'inherit', env });
|
|
87
88
|
child.on('exit', (code) => resolve(code ?? 0));
|
|
88
89
|
});
|
|
89
90
|
}
|