@maravilla-labs/cli 0.1.1 → 0.1.3

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@maravilla-labs/cli",
3
- "version": "0.1.1",
3
+ "version": "0.1.3",
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",
@@ -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 fetch(api, { headers: { 'User-Agent': 'maravilla-cli-installer' } });
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 fetch(url);
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) { if (bar) bar.tick(chunk.length); super.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}`;
@@ -134,7 +170,7 @@ async function install() {
134
170
  // Verify checksum if SHA256SUMS is available
135
171
  try {
136
172
  log('Verifying checksum');
137
- const sumsRes = await fetch(sumsUrl, { headers: { 'User-Agent': 'maravilla-cli-installer' } });
173
+ const sumsRes = await abortableFetch(sumsUrl, { headers: { 'User-Agent': 'maravilla-cli-installer' } }, 15000);
138
174
  if (sumsRes.ok) {
139
175
  const sumsText = await sumsRes.text();
140
176
  const expected = sumsText