@atlas-coder/atlas-agent 0.2.0 → 0.2.2
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 +89 -21
package/package.json
CHANGED
package/scripts/postinstall.js
CHANGED
|
@@ -1,6 +1,15 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
// Postinstall: download the platform-specific binary from GitHub Releases.
|
|
3
|
-
//
|
|
3
|
+
//
|
|
4
|
+
// Robustness upgrades vs the original:
|
|
5
|
+
// * Retries transient network errors and 5xx responses (3 attempts, jittered
|
|
6
|
+
// exponential backoff) so a flaky network doesn't break a fresh install.
|
|
7
|
+
// * Treats 404 distinctly: surfaces a clear "release not found" error
|
|
8
|
+
// instead of a generic 404, telling the user to upgrade the wrapper or
|
|
9
|
+
// re-trigger the GitHub release workflow.
|
|
10
|
+
// * Streams the response to disk (same as before) so a 100MB+ download
|
|
11
|
+
// doesn't buffer in memory.
|
|
12
|
+
|
|
4
13
|
const fs = require('fs');
|
|
5
14
|
const path = require('path');
|
|
6
15
|
const https = require('https');
|
|
@@ -16,55 +25,114 @@ const REPO = 'Omerfaruk-aydn/Atlas-Agent';
|
|
|
16
25
|
const VERSION = require('../package.json').version;
|
|
17
26
|
const TAG = `v${VERSION}`;
|
|
18
27
|
|
|
28
|
+
const MAX_ATTEMPTS = 3;
|
|
29
|
+
const BASE_DELAY_MS = 500;
|
|
30
|
+
|
|
19
31
|
const binDir = path.join(__dirname, '..', 'bin');
|
|
20
32
|
fs.mkdirSync(binDir, { recursive: true });
|
|
21
33
|
const dest = path.join(binDir, assetName);
|
|
22
34
|
|
|
23
|
-
function
|
|
35
|
+
function sleep(ms) {
|
|
36
|
+
return new Promise((r) => setTimeout(r, ms));
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function download(url, redirectsLeft, attempt) {
|
|
24
40
|
if (redirectsLeft == null) redirectsLeft = 5;
|
|
41
|
+
if (attempt == null) attempt = 1;
|
|
25
42
|
return new Promise((resolve, reject) => {
|
|
26
43
|
const req = https.get(url, { headers: { 'User-Agent': 'atlas-agent-installer' } }, (res) => {
|
|
27
|
-
// Follow redirects
|
|
28
44
|
if ([301, 302, 303, 307, 308].includes(res.statusCode)) {
|
|
29
45
|
if (redirectsLeft <= 0) return reject(new Error('Too many redirects for ' + url));
|
|
30
46
|
const next = res.headers.location;
|
|
31
47
|
if (!next) return reject(new Error('Redirect with no Location header'));
|
|
32
48
|
res.resume();
|
|
33
|
-
return resolve(download(new URL(next, url).toString(), redirectsLeft - 1));
|
|
49
|
+
return resolve(download(new URL(next, url).toString(), redirectsLeft - 1, attempt));
|
|
50
|
+
}
|
|
51
|
+
if (res.statusCode === 404) {
|
|
52
|
+
res.resume();
|
|
53
|
+
return reject(new Error(
|
|
54
|
+
`HTTP 404: release ${TAG} has no ${assetName} asset. ` +
|
|
55
|
+
`The npm wrapper is at v${VERSION} but no matching GitHub release exists. ` +
|
|
56
|
+
`Either upgrade the wrapper (npm i -g @atlas-coder/atlas-agent@latest) ` +
|
|
57
|
+
`or re-run the Atlas Agent release workflow on GitHub.`
|
|
58
|
+
));
|
|
59
|
+
}
|
|
60
|
+
if (res.statusCode >= 500 && attempt < MAX_ATTEMPTS) {
|
|
61
|
+
res.resume();
|
|
62
|
+
const delay = BASE_DELAY_MS * Math.pow(2, attempt - 1) + Math.floor(Math.random() * 250);
|
|
63
|
+
console.warn(
|
|
64
|
+
`atlas-agent installer: HTTP ${res.statusCode} from ${url} ` +
|
|
65
|
+
`(attempt ${attempt}/${MAX_ATTEMPTS}), retrying in ${delay}ms`
|
|
66
|
+
);
|
|
67
|
+
return sleep(delay).then(() => resolve(download(url, redirectsLeft, attempt + 1)));
|
|
34
68
|
}
|
|
35
69
|
if (res.statusCode !== 200) {
|
|
70
|
+
res.resume();
|
|
36
71
|
return reject(new Error('HTTP ' + res.statusCode + ' for ' + url));
|
|
37
72
|
}
|
|
38
73
|
const tmp = dest + '.part';
|
|
39
74
|
const out = fs.createWriteStream(tmp);
|
|
40
75
|
res.pipe(out);
|
|
41
|
-
out.on('finish', () =>
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
76
|
+
out.on('finish', () =>
|
|
77
|
+
out.close(() => {
|
|
78
|
+
try {
|
|
79
|
+
fs.renameSync(tmp, dest);
|
|
80
|
+
resolve();
|
|
81
|
+
} catch (e) {
|
|
82
|
+
reject(e);
|
|
83
|
+
}
|
|
84
|
+
})
|
|
85
|
+
);
|
|
45
86
|
out.on('error', reject);
|
|
46
|
-
res.on('error',
|
|
87
|
+
res.on('error', (err) => {
|
|
88
|
+
// Connection-level errors (ECONNRESET, ETIMEDOUT, ENOTFOUND...) — retry.
|
|
89
|
+
if (attempt < MAX_ATTEMPTS) {
|
|
90
|
+
const delay = BASE_DELAY_MS * Math.pow(2, attempt - 1) + Math.floor(Math.random() * 250);
|
|
91
|
+
console.warn(
|
|
92
|
+
`atlas-agent installer: ${err.message} (attempt ${attempt}/${MAX_ATTEMPTS}), ` +
|
|
93
|
+
`retrying in ${delay}ms`
|
|
94
|
+
);
|
|
95
|
+
sleep(delay).then(() => resolve(download(url, redirectsLeft, attempt + 1)));
|
|
96
|
+
} else {
|
|
97
|
+
reject(err);
|
|
98
|
+
}
|
|
99
|
+
});
|
|
100
|
+
});
|
|
101
|
+
req.on('error', (err) => {
|
|
102
|
+
if (attempt < MAX_ATTEMPTS) {
|
|
103
|
+
const delay = BASE_DELAY_MS * Math.pow(2, attempt - 1) + Math.floor(Math.random() * 250);
|
|
104
|
+
console.warn(
|
|
105
|
+
`atlas-agent installer: ${err.message} (attempt ${attempt}/${MAX_ATTEMPTS}), ` +
|
|
106
|
+
`retrying in ${delay}ms`
|
|
107
|
+
);
|
|
108
|
+
sleep(delay).then(() => resolve(download(url, redirectsLeft, attempt + 1)));
|
|
109
|
+
} else {
|
|
110
|
+
reject(err);
|
|
111
|
+
}
|
|
112
|
+
});
|
|
113
|
+
req.setTimeout(60000, () => {
|
|
114
|
+
req.destroy(new Error('Download timeout (60s)'));
|
|
47
115
|
});
|
|
48
|
-
req.on('error', reject);
|
|
49
|
-
req.setTimeout(60000, () => { req.destroy(new Error('Download timeout (60s)')); });
|
|
50
116
|
});
|
|
51
117
|
}
|
|
52
118
|
|
|
53
119
|
(async () => {
|
|
54
120
|
const url = `https://github.com/${REPO}/releases/download/${TAG}/${assetName}`;
|
|
55
121
|
console.log(`Atlas Agent postinstall: downloading ${assetName} from ${url}`);
|
|
56
|
-
|
|
122
|
+
try {
|
|
123
|
+
await download(url);
|
|
124
|
+
} catch (err) {
|
|
125
|
+
console.error('Atlas Agent postinstall FAILED: ' + err.message);
|
|
126
|
+
console.error('URL: ' + url);
|
|
127
|
+
console.error('You can manually download it from:');
|
|
128
|
+
console.error(' https://github.com/' + REPO + '/releases/tag/' + TAG);
|
|
129
|
+
console.error('and place it at:');
|
|
130
|
+
console.error(' ' + dest);
|
|
131
|
+
process.exit(1);
|
|
132
|
+
}
|
|
57
133
|
if (process.platform !== 'win32') {
|
|
58
134
|
fs.chmodSync(dest, 0o755);
|
|
59
135
|
}
|
|
60
136
|
const size = (fs.statSync(dest).size / 1024 / 1024).toFixed(1);
|
|
61
137
|
console.log(`Atlas Agent postinstall: installed ${assetName} (${size} MB)`);
|
|
62
|
-
})()
|
|
63
|
-
console.error('Atlas Agent postinstall FAILED: ' + err.message);
|
|
64
|
-
console.error('URL: https://github.com/' + REPO + '/releases/download/' + TAG + '/' + assetName);
|
|
65
|
-
console.error('You can manually download it from:');
|
|
66
|
-
console.error(' https://github.com/' + REPO + '/releases/tag/' + TAG);
|
|
67
|
-
console.error('and place it at:');
|
|
68
|
-
console.error(' ' + dest);
|
|
69
|
-
process.exit(1);
|
|
70
|
-
});
|
|
138
|
+
})();
|