|
1
|
#!/usr/bin/env node |
|
2
|
/** |
|
3
|
* scuttlectl npm wrapper. |
|
4
|
* Downloads the appropriate scuttlectl binary for the current platform |
|
5
|
* and executes it, passing all arguments through. |
|
6
|
*/ |
|
7
|
"use strict"; |
|
8
|
|
|
9
|
const { execFileSync } = require("child_process"); |
|
10
|
const { createWriteStream, existsSync, mkdirSync, chmodSync } = require("fs"); |
|
11
|
const { get } = require("https"); |
|
12
|
const { join } = require("path"); |
|
13
|
const { createGunzip } = require("zlib"); |
|
14
|
const { Extract } = require("tar"); |
|
15
|
|
|
16
|
const REPO = "ConflictHQ/scuttlebot"; |
|
17
|
const BIN_DIR = join(__dirname, "..", "dist"); |
|
18
|
const BIN_PATH = join(BIN_DIR, process.platform === "win32" ? "scuttlectl.exe" : "scuttlectl"); |
|
19
|
|
|
20
|
function platformSuffix() { |
|
21
|
const os = process.platform === "darwin" ? "darwin" : "linux"; |
|
22
|
const arch = process.arch === "arm64" ? "arm64" : "x86_64"; |
|
23
|
return `${os}-${arch}`; |
|
24
|
} |
|
25
|
|
|
26
|
async function fetchLatestVersion() { |
|
27
|
return new Promise((resolve, reject) => { |
|
28
|
get( |
|
29
|
`https://api.github.com/repos/${REPO}/releases/latest`, |
|
30
|
{ headers: { "User-Agent": "scuttlectl-npm" } }, |
|
31
|
(res) => { |
|
32
|
let data = ""; |
|
33
|
res.on("data", (c) => (data += c)); |
|
34
|
res.on("end", () => { |
|
35
|
try { |
|
36
|
resolve(JSON.parse(data).tag_name); |
|
37
|
} catch (e) { |
|
38
|
reject(e); |
|
39
|
} |
|
40
|
}); |
|
41
|
} |
|
42
|
).on("error", reject); |
|
43
|
}); |
|
44
|
} |
|
45
|
|
|
46
|
async function download(url, dest) { |
|
47
|
return new Promise((resolve, reject) => { |
|
48
|
get(url, { headers: { "User-Agent": "scuttlectl-npm" } }, (res) => { |
|
49
|
if (res.statusCode === 302 || res.statusCode === 301) { |
|
50
|
return download(res.headers.location, dest).then(resolve).catch(reject); |
|
51
|
} |
|
52
|
mkdirSync(BIN_DIR, { recursive: true }); |
|
53
|
const out = createWriteStream(dest); |
|
54
|
res.pipe(out); |
|
55
|
out.on("finish", resolve); |
|
56
|
out.on("error", reject); |
|
57
|
}).on("error", reject); |
|
58
|
}); |
|
59
|
} |
|
60
|
|
|
61
|
async function ensureBinary() { |
|
62
|
if (existsSync(BIN_PATH)) return; |
|
63
|
|
|
64
|
const version = await fetchLatestVersion(); |
|
65
|
const suffix = platformSuffix(); |
|
66
|
const asset = `scuttlectl-${version}-${suffix}.tar.gz`; |
|
67
|
const url = `https://github.com/${REPO}/releases/download/${version}/${asset}`; |
|
68
|
const tarPath = join(BIN_DIR, asset); |
|
69
|
|
|
70
|
process.stderr.write(`Downloading scuttlectl ${version}...\n`); |
|
71
|
await download(url, tarPath); |
|
72
|
|
|
73
|
await new Promise((resolve, reject) => { |
|
74
|
require("fs") |
|
75
|
.createReadStream(tarPath) |
|
76
|
.pipe(createGunzip()) |
|
77
|
.pipe(Extract({ cwd: BIN_DIR, strip: 0 })) |
|
78
|
.on("finish", resolve) |
|
79
|
.on("error", reject); |
|
80
|
}); |
|
81
|
|
|
82
|
chmodSync(BIN_PATH, 0o755); |
|
83
|
require("fs").unlinkSync(tarPath); |
|
84
|
} |
|
85
|
|
|
86
|
ensureBinary() |
|
87
|
.then(() => { |
|
88
|
execFileSync(BIN_PATH, process.argv.slice(2), { stdio: "inherit" }); |
|
89
|
}) |
|
90
|
.catch((err) => { |
|
91
|
process.stderr.write(`scuttlectl: ${err.message}\n`); |
|
92
|
process.exit(1); |
|
93
|
}); |
|
94
|
|