@tomasko/mb 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/README.md +39 -4
- package/bin/mb.js +25 -17
- package/install.js +16 -108
- package/lib.js +143 -0
- package/package.json +2 -1
package/README.md
CHANGED
|
@@ -12,10 +12,14 @@ mb tool install bobzhang/zip
|
|
|
12
12
|
|
|
13
13
|
## How it works
|
|
14
14
|
|
|
15
|
-
The package itself contains only a small launcher. On install,
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
15
|
+
The package itself contains only a small launcher. On install, `install.js`
|
|
16
|
+
downloads the prebuilt `mb` binary for the current platform from GitHub
|
|
17
|
+
Releases and writes it to `bin/mb`. The published `bin` entry (`bin/mb.js`)
|
|
18
|
+
then spawns it, forwarding arguments, standard IO and the exit code.
|
|
19
|
+
|
|
20
|
+
The postinstall download is best-effort, and the launcher downloads the
|
|
21
|
+
binary on first run if it is missing (for example when installing with
|
|
22
|
+
`--ignore-scripts`, or when the release was not available yet).
|
|
19
23
|
|
|
20
24
|
Supported targets:
|
|
21
25
|
|
|
@@ -31,6 +35,34 @@ ships a `darwin-x86_64` target, so no such build can be produced.
|
|
|
31
35
|
Assets are produced by `.github/workflows/release.yml` when a `v*` tag is
|
|
32
36
|
pushed.
|
|
33
37
|
|
|
38
|
+
## Install scripts are blocked by default
|
|
39
|
+
|
|
40
|
+
Recent npm (12+) and pnpm (10+) versions do **not** run a dependency's install
|
|
41
|
+
scripts unless the package is approved, so `postinstall` is skipped and
|
|
42
|
+
`bin/mb` is missing right after install:
|
|
43
|
+
|
|
44
|
+
```
|
|
45
|
+
npm warn install-scripts 1 package had install scripts blocked because they are not covered by allowScripts:
|
|
46
|
+
npm warn install-scripts @tomasko/mb@0.1.3 (postinstall: node install.js)
|
|
47
|
+
mb: native binary not found at .../node_modules/@tomasko/mb/bin/mb
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
Nothing is broken: `bin/mb.js` downloads the binary on first run. To have it
|
|
51
|
+
downloaded at install time instead, approve the build once:
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
npm install-scripts approve @tomasko/mb # npm 12+ (writes `allowScripts`)
|
|
55
|
+
pnpm approve-builds # pnpm 10+
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
Either way, re-run the download manually with:
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
npm rebuild @tomasko/mb
|
|
62
|
+
# or
|
|
63
|
+
pnpm rebuild @tomasko/mb
|
|
64
|
+
```
|
|
65
|
+
|
|
34
66
|
## Overrides
|
|
35
67
|
|
|
36
68
|
| Variable | Purpose |
|
|
@@ -47,7 +79,10 @@ From this directory:
|
|
|
47
79
|
|
|
48
80
|
```bash
|
|
49
81
|
npm publish --access public
|
|
82
|
+
# or
|
|
83
|
+
pnpm publish --access public
|
|
50
84
|
```
|
|
51
85
|
|
|
86
|
+
`--access public` is required for scoped packages such as `@tomasko/mb`.
|
|
52
87
|
Change `name`, `repository` and `homepage` in `package.json` first if you
|
|
53
88
|
publish under a different scope or repository.
|
package/bin/mb.js
CHANGED
|
@@ -1,27 +1,35 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
//
|
|
2
|
+
// Launcher for the native `mb` binary. Downloads it on first run if the
|
|
3
|
+
// postinstall step did not (e.g. `--ignore-scripts`), then forwards all
|
|
4
|
+
// arguments, stdio and the exit code.
|
|
3
5
|
"use strict";
|
|
4
6
|
|
|
5
7
|
const { spawnSync } = require("child_process");
|
|
6
|
-
const
|
|
7
|
-
const path = require("path");
|
|
8
|
+
const { ensureBinary, pkg } = require("../lib");
|
|
8
9
|
|
|
9
|
-
|
|
10
|
-
|
|
10
|
+
async function main() {
|
|
11
|
+
let binary;
|
|
12
|
+
try {
|
|
13
|
+
binary = await ensureBinary({
|
|
14
|
+
log: (line) => process.stderr.write(line + "\n"),
|
|
15
|
+
});
|
|
16
|
+
} catch (err) {
|
|
17
|
+
console.error(`mb: ${err.message}`);
|
|
18
|
+
console.error(
|
|
19
|
+
`mb: run \`npm rebuild ${pkg.name}\` to retry, or set MB_DOWNLOAD_BASE ` +
|
|
20
|
+
`(or MB_REPO/MB_VERSION) to a mirror.`
|
|
21
|
+
);
|
|
22
|
+
process.exit(1);
|
|
23
|
+
}
|
|
11
24
|
|
|
12
|
-
|
|
13
|
-
console.error(
|
|
14
|
-
`mb: native binary not found at ${binary}\n` +
|
|
15
|
-
"mb: reinstall it with `npm rebuild mb` (or `npm install -g mb --force`)."
|
|
16
|
-
);
|
|
17
|
-
process.exit(1);
|
|
18
|
-
}
|
|
25
|
+
const result = spawnSync(binary, process.argv.slice(2), { stdio: "inherit" });
|
|
19
26
|
|
|
20
|
-
|
|
27
|
+
if (result.error) {
|
|
28
|
+
console.error(`mb: failed to launch ${binary}: ${result.error.message}`);
|
|
29
|
+
process.exit(1);
|
|
30
|
+
}
|
|
21
31
|
|
|
22
|
-
|
|
23
|
-
console.error(`mb: failed to launch ${binary}: ${result.error.message}`);
|
|
24
|
-
process.exit(1);
|
|
32
|
+
process.exit(typeof result.status === "number" ? result.status : 1);
|
|
25
33
|
}
|
|
26
34
|
|
|
27
|
-
|
|
35
|
+
main();
|
package/install.js
CHANGED
|
@@ -1,117 +1,25 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
//
|
|
3
|
-
// Releases and drops it next to this package's launcher (`bin/mb.js`).
|
|
2
|
+
// Postinstall hook: download the prebuilt `mb` binary for this platform.
|
|
4
3
|
//
|
|
5
|
-
//
|
|
6
|
-
//
|
|
7
|
-
// MB_VERSION release version (default: package.json version)
|
|
8
|
-
// MB_DOWNLOAD_BASE full base URL (overrides MB_REPO/MB_VERSION)
|
|
9
|
-
// MB_SKIP_DOWNLOAD set to skip fetching
|
|
4
|
+
// This is best-effort: if the download fails the install still succeeds, and
|
|
5
|
+
// `bin/mb.js` retries on first run. Set MB_SKIP_DOWNLOAD=1 to opt out.
|
|
10
6
|
"use strict";
|
|
11
7
|
|
|
12
|
-
const
|
|
13
|
-
const path = require("path");
|
|
14
|
-
const https = require("https");
|
|
15
|
-
const http = require("http");
|
|
8
|
+
const { ensureBinary, pkg } = require("./lib");
|
|
16
9
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
"linux-x64": "linux-x86_64",
|
|
21
|
-
"linux-arm64": "linux-aarch64",
|
|
22
|
-
"darwin-arm64": "darwin-aarch64",
|
|
23
|
-
};
|
|
24
|
-
|
|
25
|
-
function repoSlug(repo) {
|
|
26
|
-
if (!repo) return null;
|
|
27
|
-
const url = typeof repo === "string" ? repo : repo.url || "";
|
|
28
|
-
const m = url.match(/github\.com[/:]([^/]+)\/([^/.]+)/);
|
|
29
|
-
return m ? `${m[1]}/${m[2]}` : null;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
function target() {
|
|
33
|
-
return TARGETS[`${process.platform}-${process.arch}`];
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
function downloadUrl(targetName) {
|
|
37
|
-
if (process.env.MB_DOWNLOAD_BASE) {
|
|
38
|
-
return `${process.env.MB_DOWNLOAD_BASE.replace(/\/$/, "")}/mb-${targetName}`;
|
|
39
|
-
}
|
|
40
|
-
const repo = process.env.MB_REPO || repoSlug(pkg.repository) || "tomasky/mb";
|
|
41
|
-
const version = process.env.MB_VERSION || pkg.version;
|
|
42
|
-
return `https://github.com/${repo}/releases/download/v${version}/mb-${targetName}`;
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
function fetch(url, redirects) {
|
|
46
|
-
return new Promise((resolve, reject) => {
|
|
47
|
-
if (redirects > 10) return reject(new Error("too many redirects"));
|
|
48
|
-
const client = url.startsWith("http:") ? http : https;
|
|
49
|
-
const req = client.get(
|
|
50
|
-
url,
|
|
51
|
-
{ headers: { "user-agent": `mb-npm/${pkg.version}` } },
|
|
52
|
-
(res) => {
|
|
53
|
-
const { statusCode, headers } = res;
|
|
54
|
-
if (statusCode >= 300 && statusCode < 400 && headers.location) {
|
|
55
|
-
res.resume();
|
|
56
|
-
const next = new URL(headers.location, url).toString();
|
|
57
|
-
resolve(fetch(next, (redirects || 0) + 1));
|
|
58
|
-
return;
|
|
59
|
-
}
|
|
60
|
-
if (statusCode !== 200) {
|
|
61
|
-
res.resume();
|
|
62
|
-
reject(new Error(`GET ${url} -> HTTP ${statusCode}`));
|
|
63
|
-
return;
|
|
64
|
-
}
|
|
65
|
-
resolve(res);
|
|
66
|
-
}
|
|
67
|
-
);
|
|
68
|
-
req.on("error", reject);
|
|
69
|
-
});
|
|
10
|
+
if (process.env.MB_SKIP_DOWNLOAD) {
|
|
11
|
+
console.log("mb: MB_SKIP_DOWNLOAD set, skipping binary download");
|
|
12
|
+
process.exit(0);
|
|
70
13
|
}
|
|
71
14
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
throw new Error(
|
|
81
|
-
`unsupported platform ${process.platform}-${process.arch}; mb ` +
|
|
82
|
-
`supports linux-x64, linux-arm64 and darwin-arm64 (the MoonBit ` +
|
|
83
|
-
`toolchain no longer ships a darwin-x64 target)`
|
|
15
|
+
ensureBinary({ log: console.log })
|
|
16
|
+
.then(() => process.exit(0))
|
|
17
|
+
.catch((err) => {
|
|
18
|
+
console.warn(`mb: could not download the native binary: ${err.message}`);
|
|
19
|
+
console.warn(
|
|
20
|
+
`mb: it will be downloaded on first run instead. If that also fails, ` +
|
|
21
|
+
`set MB_DOWNLOAD_BASE (or MB_REPO/MB_VERSION) to a mirror and run ` +
|
|
22
|
+
`\`npm rebuild ${pkg.name}\`.`
|
|
84
23
|
);
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
const ext = process.platform === "win32" ? ".exe" : "";
|
|
88
|
-
const dest = path.join(__dirname, "bin", "mb" + ext);
|
|
89
|
-
fs.mkdirSync(path.dirname(dest), { recursive: true });
|
|
90
|
-
|
|
91
|
-
if (fs.existsSync(dest) && !process.env.MB_FORCE_DOWNLOAD) {
|
|
92
|
-
console.log(`mb: ${dest} already present, skipping download`);
|
|
93
|
-
return;
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
const url = downloadUrl(targetName);
|
|
97
|
-
console.log(`mb: downloading ${url}`);
|
|
98
|
-
const res = await fetch(url, 0);
|
|
99
|
-
await new Promise((resolve, reject) => {
|
|
100
|
-
const out = fs.createWriteStream(dest);
|
|
101
|
-
res.pipe(out);
|
|
102
|
-
out.on("finish", () => out.close(resolve));
|
|
103
|
-
out.on("error", reject);
|
|
104
|
-
res.on("error", reject);
|
|
24
|
+
process.exit(0);
|
|
105
25
|
});
|
|
106
|
-
fs.chmodSync(dest, 0o755);
|
|
107
|
-
console.log(`mb: installed ${dest}`);
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
main().catch((err) => {
|
|
111
|
-
console.error(`mb: failed to install the native binary: ${err.message}`);
|
|
112
|
-
console.error(
|
|
113
|
-
"mb: set MB_DOWNLOAD_BASE (or MB_REPO/MB_VERSION) to point at a mirror, " +
|
|
114
|
-
"or set MB_SKIP_DOWNLOAD=1 to skip."
|
|
115
|
-
);
|
|
116
|
-
process.exit(1);
|
|
117
|
-
});
|
package/lib.js
ADDED
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
// Shared install logic for the npm wrapper: works out which prebuilt `mb`
|
|
4
|
+
// binary to use and downloads it from GitHub Releases on demand.
|
|
5
|
+
//
|
|
6
|
+
// Overridable for forks / private mirrors:
|
|
7
|
+
// MB_REPO owner/repo (default: parsed from package.json)
|
|
8
|
+
// MB_VERSION release version (default: package.json version)
|
|
9
|
+
// MB_DOWNLOAD_BASE full base URL (overrides MB_REPO/MB_VERSION)
|
|
10
|
+
// MB_FORCE_DOWNLOAD re-download even if the binary is present
|
|
11
|
+
// MB_SKIP_DOWNLOAD never download
|
|
12
|
+
|
|
13
|
+
const fs = require("fs");
|
|
14
|
+
const path = require("path");
|
|
15
|
+
const https = require("https");
|
|
16
|
+
const http = require("http");
|
|
17
|
+
|
|
18
|
+
const pkg = require("./package.json");
|
|
19
|
+
|
|
20
|
+
// Node `platform-arch` -> MoonBit release target. Intel macOS is absent on
|
|
21
|
+
// purpose: the MoonBit toolchain no longer ships a darwin-x86_64 target.
|
|
22
|
+
const TARGETS = {
|
|
23
|
+
"linux-x64": "linux-x86_64",
|
|
24
|
+
"linux-arm64": "linux-aarch64",
|
|
25
|
+
"darwin-arm64": "darwin-aarch64",
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
function repoSlug(repo) {
|
|
29
|
+
if (!repo) return null;
|
|
30
|
+
const url = typeof repo === "string" ? repo : repo.url || "";
|
|
31
|
+
const m = url.match(/github\.com[/:]([^/]+)\/([^/.]+)/);
|
|
32
|
+
return m ? `${m[1]}/${m[2]}` : null;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function targetName() {
|
|
36
|
+
return TARGETS[`${process.platform}-${process.arch}`];
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function downloadUrl(target) {
|
|
40
|
+
if (process.env.MB_DOWNLOAD_BASE) {
|
|
41
|
+
return `${process.env.MB_DOWNLOAD_BASE.replace(/\/$/, "")}/mb-${target}`;
|
|
42
|
+
}
|
|
43
|
+
const repo = process.env.MB_REPO || repoSlug(pkg.repository) || "tomasky/mb";
|
|
44
|
+
const version = process.env.MB_VERSION || pkg.version;
|
|
45
|
+
return `https://github.com/${repo}/releases/download/v${version}/mb-${target}`;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function binaryPath() {
|
|
49
|
+
return path.join(
|
|
50
|
+
__dirname,
|
|
51
|
+
"bin",
|
|
52
|
+
process.platform === "win32" ? "mb.exe" : "mb"
|
|
53
|
+
);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function isInstalled() {
|
|
57
|
+
try {
|
|
58
|
+
fs.accessSync(binaryPath(), fs.constants.X_OK);
|
|
59
|
+
return true;
|
|
60
|
+
} catch {
|
|
61
|
+
return false;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function fetch(url, redirects) {
|
|
66
|
+
return new Promise((resolve, reject) => {
|
|
67
|
+
if (redirects > 10) return reject(new Error("too many redirects"));
|
|
68
|
+
const client = url.startsWith("http:") ? http : https;
|
|
69
|
+
const req = client.get(
|
|
70
|
+
url,
|
|
71
|
+
{ headers: { "user-agent": `mb-npm/${pkg.version}` } },
|
|
72
|
+
(res) => {
|
|
73
|
+
const { statusCode, headers } = res;
|
|
74
|
+
if (statusCode >= 300 && statusCode < 400 && headers.location) {
|
|
75
|
+
res.resume();
|
|
76
|
+
const next = new URL(headers.location, url).toString();
|
|
77
|
+
resolve(fetch(next, (redirects || 0) + 1));
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
if (statusCode !== 200) {
|
|
81
|
+
res.resume();
|
|
82
|
+
reject(new Error(`GET ${url} -> HTTP ${statusCode}`));
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
resolve(res);
|
|
86
|
+
}
|
|
87
|
+
);
|
|
88
|
+
req.on("error", reject);
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
async function download(target, dest) {
|
|
93
|
+
const url = downloadUrl(target);
|
|
94
|
+
const res = await fetch(url, 0);
|
|
95
|
+
fs.mkdirSync(path.dirname(dest), { recursive: true });
|
|
96
|
+
await new Promise((resolve, reject) => {
|
|
97
|
+
const out = fs.createWriteStream(dest);
|
|
98
|
+
res.pipe(out);
|
|
99
|
+
out.on("finish", () => out.close(resolve));
|
|
100
|
+
out.on("error", reject);
|
|
101
|
+
res.on("error", reject);
|
|
102
|
+
});
|
|
103
|
+
fs.chmodSync(dest, 0o755);
|
|
104
|
+
return url;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// Resolves to a ready-to-run binary path, downloading it when missing.
|
|
108
|
+
async function ensureBinary({ force = false, log = () => {} } = {}) {
|
|
109
|
+
const dest = binaryPath();
|
|
110
|
+
|
|
111
|
+
if (!force && !process.env.MB_FORCE_DOWNLOAD && isInstalled()) {
|
|
112
|
+
return dest;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
if (process.env.MB_SKIP_DOWNLOAD) {
|
|
116
|
+
if (isInstalled()) return dest;
|
|
117
|
+
throw new Error("MB_SKIP_DOWNLOAD is set but no binary is installed");
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
const target = targetName();
|
|
121
|
+
if (!target) {
|
|
122
|
+
throw new Error(
|
|
123
|
+
`unsupported platform ${process.platform}-${process.arch}; mb ` +
|
|
124
|
+
`supports linux-x64, linux-arm64 and darwin-arm64 (the MoonBit ` +
|
|
125
|
+
`toolchain no longer ships a darwin-x64 target)`
|
|
126
|
+
);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
log(`mb: downloading ${downloadUrl(target)}`);
|
|
130
|
+
await download(target, dest);
|
|
131
|
+
log(`mb: installed ${dest}`);
|
|
132
|
+
return dest;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
module.exports = {
|
|
136
|
+
pkg,
|
|
137
|
+
TARGETS,
|
|
138
|
+
targetName,
|
|
139
|
+
downloadUrl,
|
|
140
|
+
binaryPath,
|
|
141
|
+
isInstalled,
|
|
142
|
+
ensureBinary,
|
|
143
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tomasko/mb",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.3",
|
|
4
4
|
"description": "A MoonBit-native, uv-style toolchain and package manager: manage MoonBit toolchains and install tools from mooncakes.io.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"bin": {
|
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
"files": [
|
|
13
13
|
"bin/mb.js",
|
|
14
14
|
"install.js",
|
|
15
|
+
"lib.js",
|
|
15
16
|
"README.md"
|
|
16
17
|
],
|
|
17
18
|
"engines": {
|