@bigconfig/bb 0.1.0
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 +73 -0
- package/bin/bb.js +408 -0
- package/package.json +24 -0
package/README.md
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
# @bigconfig/bb
|
|
2
|
+
|
|
3
|
+
Run [babashka](https://babashka.org) (`bb`) without installing anything first.
|
|
4
|
+
On its first invocation this package downloads a pinned babashka binary **and**
|
|
5
|
+
an Eclipse Temurin JDK into a shared user cache, then forwards every argument
|
|
6
|
+
to `bb`.
|
|
7
|
+
|
|
8
|
+
## Usage
|
|
9
|
+
|
|
10
|
+
```sh
|
|
11
|
+
npx @bigconfig/bb tasks # -> bb tasks
|
|
12
|
+
npx @bigconfig/bb <args...> # -> bb <args...>
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
All arguments (including flags) are passed through verbatim, and `bb` runs in
|
|
16
|
+
your current working directory, so it picks up the local `bb.edn`.
|
|
17
|
+
|
|
18
|
+
## What happens on first run
|
|
19
|
+
|
|
20
|
+
1. The host OS/CPU are resolved to the matching babashka release asset and
|
|
21
|
+
Adoptium API parameters.
|
|
22
|
+
2. babashka is downloaded from its GitHub releases and cached.
|
|
23
|
+
3. A Temurin JDK is downloaded from the Adoptium API and cached.
|
|
24
|
+
4. `bb` is launched with `JAVA_HOME` / `PATH` pointing at the cached JDK — the
|
|
25
|
+
environment change applies **only** to the `bb` subprocess, nothing
|
|
26
|
+
system-wide.
|
|
27
|
+
|
|
28
|
+
Subsequent runs reuse the cache and start immediately.
|
|
29
|
+
|
|
30
|
+
## git (Linux only)
|
|
31
|
+
|
|
32
|
+
On Linux, if `git` is not on `PATH`, it is installed via the system package
|
|
33
|
+
manager (`apt-get`, `dnf`, `yum`, `zypper`, `pacman`, or `apk`), using `sudo`
|
|
34
|
+
when not running as root. This is **skipped** when git is already present, and
|
|
35
|
+
is a **no-op on macOS/Windows**. Unlike babashka/JDK, this modifies the system
|
|
36
|
+
and may prompt for a sudo password; in non-interactive environments without
|
|
37
|
+
passwordless sudo it will fail with an actionable message — pre-install git to
|
|
38
|
+
avoid this entirely.
|
|
39
|
+
|
|
40
|
+
## Cache location
|
|
41
|
+
|
|
42
|
+
A single shared directory, reused across all projects:
|
|
43
|
+
|
|
44
|
+
| Platform | Path |
|
|
45
|
+
| ------------- | -------------------------------------------- |
|
|
46
|
+
| macOS / Linux | `$XDG_CACHE_HOME` or `~/.cache` → `bigconfig-bb/` |
|
|
47
|
+
| Windows | `%LOCALAPPDATA%` → `bigconfig-bb/` |
|
|
48
|
+
|
|
49
|
+
Delete that directory to force a clean reinstall.
|
|
50
|
+
|
|
51
|
+
## Configuration
|
|
52
|
+
|
|
53
|
+
| Env var | Default | Effect |
|
|
54
|
+
| ------------- | ---------- | --------------------------------------- |
|
|
55
|
+
| `BB_VERSION` | `1.12.196` | babashka release version to install |
|
|
56
|
+
| `JDK_VERSION` | `21` | Temurin feature version (e.g. `17`, `21`, `25`) |
|
|
57
|
+
|
|
58
|
+
## Supported platforms
|
|
59
|
+
|
|
60
|
+
macOS arm64, macOS x64, Linux x64, Linux arm64, Windows x64.
|
|
61
|
+
|
|
62
|
+
Notes:
|
|
63
|
+
|
|
64
|
+
- Linux x64 uses babashka's glibc build (may not run on musl distros such as
|
|
65
|
+
Alpine). Linux arm64 uses babashka's static build, which runs on both glibc
|
|
66
|
+
and musl.
|
|
67
|
+
- Extraction uses the system `tar` (present on macOS, Linux, and Windows
|
|
68
|
+
10+); Windows falls back to PowerShell `Expand-Archive` for `.zip` if `tar`
|
|
69
|
+
is unavailable.
|
|
70
|
+
|
|
71
|
+
## Requirements
|
|
72
|
+
|
|
73
|
+
Node.js >= 18.
|
package/bin/bb.js
ADDED
|
@@ -0,0 +1,408 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
// @bigconfig/bb — bootstraps babashka + a Temurin JDK on first use, then
|
|
5
|
+
// forwards all arguments to `bb`. Single-file launcher, no build step.
|
|
6
|
+
|
|
7
|
+
const fs = require('fs');
|
|
8
|
+
const os = require('os');
|
|
9
|
+
const path = require('path');
|
|
10
|
+
const { spawn, spawnSync } = require('child_process');
|
|
11
|
+
const { Readable } = require('stream');
|
|
12
|
+
const { pipeline } = require('stream/promises');
|
|
13
|
+
|
|
14
|
+
// Pinned, known-good versions. Overridable via env.
|
|
15
|
+
const DEFAULT_BB_VERSION = process.env.BB_VERSION || '1.12.196';
|
|
16
|
+
const DEFAULT_JDK_VERSION = process.env.JDK_VERSION || '21';
|
|
17
|
+
|
|
18
|
+
const TAG = '[@bigconfig/bb]';
|
|
19
|
+
|
|
20
|
+
function log(msg) {
|
|
21
|
+
// stderr so stdout stays clean for bb's own output.
|
|
22
|
+
process.stderr.write(`${TAG} ${msg}\n`);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// --- Platform resolution -------------------------------------------------
|
|
26
|
+
|
|
27
|
+
// Maps the host OS/arch to babashka release asset + Adoptium API parameters.
|
|
28
|
+
function resolvePlatform() {
|
|
29
|
+
const plat = process.platform; // 'darwin' | 'linux' | 'win32'
|
|
30
|
+
const arch = process.arch; // 'arm64' | 'x64'
|
|
31
|
+
const exeSuffix = plat === 'win32' ? '.exe' : '';
|
|
32
|
+
|
|
33
|
+
let bbOs;
|
|
34
|
+
let jdkOs;
|
|
35
|
+
let archiveExt;
|
|
36
|
+
if (plat === 'darwin') {
|
|
37
|
+
bbOs = 'macos';
|
|
38
|
+
jdkOs = 'mac';
|
|
39
|
+
archiveExt = 'tar.gz';
|
|
40
|
+
} else if (plat === 'linux') {
|
|
41
|
+
bbOs = 'linux';
|
|
42
|
+
jdkOs = 'linux';
|
|
43
|
+
archiveExt = 'tar.gz';
|
|
44
|
+
} else if (plat === 'win32') {
|
|
45
|
+
bbOs = 'windows';
|
|
46
|
+
jdkOs = 'windows';
|
|
47
|
+
archiveExt = 'zip';
|
|
48
|
+
} else {
|
|
49
|
+
throw new Error(`Unsupported OS: ${plat}`);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
let bbArch;
|
|
53
|
+
let jdkArch;
|
|
54
|
+
if (arch === 'arm64') {
|
|
55
|
+
bbArch = 'aarch64';
|
|
56
|
+
jdkArch = 'aarch64';
|
|
57
|
+
} else if (arch === 'x64') {
|
|
58
|
+
bbArch = 'amd64';
|
|
59
|
+
jdkArch = 'x64';
|
|
60
|
+
} else {
|
|
61
|
+
throw new Error(`Unsupported CPU architecture: ${arch}`);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// babashka ships only a *static* (musl) build for Linux arm64 — there is no
|
|
65
|
+
// dynamic linux-aarch64 asset. The static build also runs on glibc.
|
|
66
|
+
const bbArchToken =
|
|
67
|
+
plat === 'linux' && arch === 'arm64' ? 'aarch64-static' : bbArch;
|
|
68
|
+
|
|
69
|
+
if (plat === 'win32' && arch === 'arm64') {
|
|
70
|
+
throw new Error('babashka has no prebuilt Windows arm64 binary');
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
return {
|
|
74
|
+
exeSuffix,
|
|
75
|
+
archiveExt,
|
|
76
|
+
jdkOs,
|
|
77
|
+
jdkArch,
|
|
78
|
+
bbAssetName(version) {
|
|
79
|
+
return `babashka-${version}-${bbOs}-${bbArchToken}.${archiveExt}`;
|
|
80
|
+
},
|
|
81
|
+
jdkArchiveName: `jdk.${archiveExt}`,
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function cacheRoot() {
|
|
86
|
+
const base =
|
|
87
|
+
process.platform === 'win32'
|
|
88
|
+
? process.env.LOCALAPPDATA || path.join(os.homedir(), 'AppData', 'Local')
|
|
89
|
+
: process.env.XDG_CACHE_HOME || path.join(os.homedir(), '.cache');
|
|
90
|
+
return path.join(base, 'bigconfig-bb');
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// --- Filesystem helpers --------------------------------------------------
|
|
94
|
+
|
|
95
|
+
function rmrf(target) {
|
|
96
|
+
fs.rmSync(target, { recursive: true, force: true });
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// Runs `install(tmpDir)` into a private temp dir, then atomically renames it
|
|
100
|
+
// into place. Concurrent first-runs race on the rename; the loser is discarded
|
|
101
|
+
// so the final dir is never left half-written.
|
|
102
|
+
async function installOnce(finalDir, install) {
|
|
103
|
+
if (fs.existsSync(finalDir)) return;
|
|
104
|
+
const tmp = `${finalDir}.tmp-${process.pid}-${Date.now()}`;
|
|
105
|
+
fs.mkdirSync(tmp, { recursive: true });
|
|
106
|
+
try {
|
|
107
|
+
await install(tmp);
|
|
108
|
+
if (fs.existsSync(finalDir)) {
|
|
109
|
+
rmrf(tmp); // another process won the race
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
fs.mkdirSync(path.dirname(finalDir), { recursive: true });
|
|
113
|
+
try {
|
|
114
|
+
fs.renameSync(tmp, finalDir);
|
|
115
|
+
} catch (err) {
|
|
116
|
+
if (fs.existsSync(finalDir)) {
|
|
117
|
+
rmrf(tmp);
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
throw err;
|
|
121
|
+
}
|
|
122
|
+
} catch (err) {
|
|
123
|
+
rmrf(tmp);
|
|
124
|
+
throw err;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
async function download(url, destFile) {
|
|
129
|
+
const res = await fetch(url, {
|
|
130
|
+
redirect: 'follow',
|
|
131
|
+
headers: { 'user-agent': '@bigconfig/bb' },
|
|
132
|
+
});
|
|
133
|
+
if (!res.ok || !res.body) {
|
|
134
|
+
throw new Error(
|
|
135
|
+
`Download failed (HTTP ${res.status} ${res.statusText})\n ${url}`
|
|
136
|
+
);
|
|
137
|
+
}
|
|
138
|
+
await fs.promises.mkdir(path.dirname(destFile), { recursive: true });
|
|
139
|
+
await pipeline(Readable.fromWeb(res.body), fs.createWriteStream(destFile));
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// Extracts .tar.gz / .zip. System `tar` (GNU on Linux, bsdtar on macOS &
|
|
143
|
+
// Windows 10+) auto-detects gzip and handles zip; PowerShell is a Windows
|
|
144
|
+
// fallback when `tar` is absent.
|
|
145
|
+
function extract(archive, destDir) {
|
|
146
|
+
fs.mkdirSync(destDir, { recursive: true });
|
|
147
|
+
let r = spawnSync('tar', ['-xf', archive, '-C', destDir], {
|
|
148
|
+
stdio: ['ignore', 'inherit', 'inherit'],
|
|
149
|
+
});
|
|
150
|
+
if (r.error && r.error.code === 'ENOENT') {
|
|
151
|
+
if (process.platform === 'win32' && archive.endsWith('.zip')) {
|
|
152
|
+
r = spawnSync(
|
|
153
|
+
'powershell',
|
|
154
|
+
[
|
|
155
|
+
'-NoProfile',
|
|
156
|
+
'-Command',
|
|
157
|
+
`Expand-Archive -LiteralPath '${archive}' -DestinationPath '${destDir}' -Force`,
|
|
158
|
+
],
|
|
159
|
+
{ stdio: ['ignore', 'inherit', 'inherit'] }
|
|
160
|
+
);
|
|
161
|
+
if (r.status !== 0) {
|
|
162
|
+
throw new Error(`Failed to extract ${archive} (PowerShell fallback)`);
|
|
163
|
+
}
|
|
164
|
+
return;
|
|
165
|
+
}
|
|
166
|
+
throw new Error(`'tar' not found on PATH; cannot extract ${archive}`);
|
|
167
|
+
}
|
|
168
|
+
if (r.status !== 0) {
|
|
169
|
+
throw new Error(`Failed to extract ${archive} (tar exit ${r.status})`);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// Locates JAVA_HOME inside an extracted JDK. Layout differs per OS
|
|
174
|
+
// (linux: <root>/bin/java, macOS: <root>/Contents/Home/bin/java).
|
|
175
|
+
function findJavaHome(root, exeSuffix) {
|
|
176
|
+
const javaRel = path.join('bin', `java${exeSuffix}`);
|
|
177
|
+
const stack = [root];
|
|
178
|
+
while (stack.length) {
|
|
179
|
+
const dir = stack.pop();
|
|
180
|
+
if (fs.existsSync(path.join(dir, javaRel))) return dir;
|
|
181
|
+
let entries;
|
|
182
|
+
try {
|
|
183
|
+
entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
184
|
+
} catch {
|
|
185
|
+
continue;
|
|
186
|
+
}
|
|
187
|
+
for (const e of entries) {
|
|
188
|
+
if (e.isDirectory()) stack.push(path.join(dir, e.name));
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
return null;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
// --- Bootstrap steps -----------------------------------------------------
|
|
195
|
+
|
|
196
|
+
async function ensureBabashka(p) {
|
|
197
|
+
const version = DEFAULT_BB_VERSION;
|
|
198
|
+
const finalDir = path.join(cacheRoot(), 'bb', version);
|
|
199
|
+
const bbPath = path.join(finalDir, `bb${p.exeSuffix}`);
|
|
200
|
+
const asset = p.bbAssetName(version);
|
|
201
|
+
|
|
202
|
+
await installOnce(finalDir, async (tmp) => {
|
|
203
|
+
const archive = path.join(tmp, asset);
|
|
204
|
+
const url = `https://github.com/babashka/babashka/releases/download/v${version}/${asset}`;
|
|
205
|
+
log(`Installing babashka ${version} (set BB_VERSION to override)...`);
|
|
206
|
+
try {
|
|
207
|
+
await download(url, archive);
|
|
208
|
+
} catch (err) {
|
|
209
|
+
throw new Error(`${err.message}\n (override with BB_VERSION=<version>)`);
|
|
210
|
+
}
|
|
211
|
+
extract(archive, tmp);
|
|
212
|
+
fs.unlinkSync(archive);
|
|
213
|
+
const exe = path.join(tmp, `bb${p.exeSuffix}`);
|
|
214
|
+
if (!fs.existsSync(exe)) {
|
|
215
|
+
throw new Error('babashka binary not found after extraction');
|
|
216
|
+
}
|
|
217
|
+
if (process.platform !== 'win32') fs.chmodSync(exe, 0o755);
|
|
218
|
+
});
|
|
219
|
+
|
|
220
|
+
if (!fs.existsSync(bbPath)) {
|
|
221
|
+
throw new Error(
|
|
222
|
+
`babashka cache looks corrupt; remove ${finalDir} and retry`
|
|
223
|
+
);
|
|
224
|
+
}
|
|
225
|
+
if (process.platform !== 'win32') {
|
|
226
|
+
try {
|
|
227
|
+
fs.chmodSync(bbPath, 0o755);
|
|
228
|
+
} catch {
|
|
229
|
+
/* already executable */
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
return bbPath;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
async function ensureJdk(p) {
|
|
236
|
+
const feature = DEFAULT_JDK_VERSION;
|
|
237
|
+
const finalDir = path.join(cacheRoot(), 'jdk', feature);
|
|
238
|
+
const marker = path.join(finalDir, '.javahome');
|
|
239
|
+
|
|
240
|
+
await installOnce(finalDir, async (tmp) => {
|
|
241
|
+
const archive = path.join(tmp, p.jdkArchiveName);
|
|
242
|
+
const url =
|
|
243
|
+
`https://api.adoptium.net/v3/binary/latest/${feature}/ga/` +
|
|
244
|
+
`${p.jdkOs}/${p.jdkArch}/jdk/hotspot/normal/eclipse`;
|
|
245
|
+
log(`Installing Temurin JDK ${feature} (set JDK_VERSION to override)...`);
|
|
246
|
+
try {
|
|
247
|
+
await download(url, archive);
|
|
248
|
+
} catch (err) {
|
|
249
|
+
throw new Error(`${err.message}\n (override with JDK_VERSION=<feature>)`);
|
|
250
|
+
}
|
|
251
|
+
extract(archive, tmp);
|
|
252
|
+
fs.unlinkSync(archive);
|
|
253
|
+
const home = findJavaHome(tmp, p.exeSuffix);
|
|
254
|
+
if (!home) throw new Error('could not locate java in extracted JDK');
|
|
255
|
+
// Path relative to tmp stays valid after tmp is renamed to finalDir.
|
|
256
|
+
fs.writeFileSync(path.join(tmp, '.javahome'), path.relative(tmp, home));
|
|
257
|
+
});
|
|
258
|
+
|
|
259
|
+
let javaHome = null;
|
|
260
|
+
try {
|
|
261
|
+
javaHome = path.join(finalDir, fs.readFileSync(marker, 'utf8').trim());
|
|
262
|
+
} catch {
|
|
263
|
+
javaHome = null;
|
|
264
|
+
}
|
|
265
|
+
if (
|
|
266
|
+
!javaHome ||
|
|
267
|
+
!fs.existsSync(path.join(javaHome, 'bin', `java${p.exeSuffix}`))
|
|
268
|
+
) {
|
|
269
|
+
javaHome = findJavaHome(finalDir, p.exeSuffix);
|
|
270
|
+
}
|
|
271
|
+
if (!javaHome) {
|
|
272
|
+
throw new Error(`JDK cache looks corrupt; remove ${finalDir} and retry`);
|
|
273
|
+
}
|
|
274
|
+
return javaHome;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
// --- git (Linux only) ----------------------------------------------------
|
|
278
|
+
|
|
279
|
+
function commandWorks(cmd, args) {
|
|
280
|
+
const r = spawnSync(cmd, args, { stdio: 'ignore' });
|
|
281
|
+
return !r.error && r.status === 0;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
// ENOENT sets r.error; any exit code otherwise means the binary exists.
|
|
285
|
+
function binExists(cmd) {
|
|
286
|
+
return !spawnSync(cmd, ['--version'], { stdio: 'ignore' }).error;
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
// On Linux, install git via the system package manager if it is missing.
|
|
290
|
+
// Skipped when git is already on PATH; a no-op on macOS/Windows.
|
|
291
|
+
function ensureGit() {
|
|
292
|
+
if (process.platform !== 'linux') return;
|
|
293
|
+
if (commandWorks('git', ['--version'])) return;
|
|
294
|
+
|
|
295
|
+
const isRoot =
|
|
296
|
+
typeof process.getuid === 'function' && process.getuid() === 0;
|
|
297
|
+
const sudo = isRoot ? [] : binExists('sudo') ? ['sudo'] : null;
|
|
298
|
+
if (sudo === null) {
|
|
299
|
+
throw new Error(
|
|
300
|
+
'git is missing and cannot be installed: not running as root and ' +
|
|
301
|
+
'`sudo` is unavailable.\n' +
|
|
302
|
+
' Install git manually (e.g. `apt-get install git`) and re-run.'
|
|
303
|
+
);
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
// First match wins; `soft` lists step indexes allowed to fail (e.g.
|
|
307
|
+
// `apt-get update`, which is non-fatal if package lists already exist).
|
|
308
|
+
const managers = [
|
|
309
|
+
{
|
|
310
|
+
bin: 'apt-get',
|
|
311
|
+
steps: [
|
|
312
|
+
['apt-get', 'update', '-y'],
|
|
313
|
+
['apt-get', 'install', '-y', 'git'],
|
|
314
|
+
],
|
|
315
|
+
soft: [0],
|
|
316
|
+
},
|
|
317
|
+
{ bin: 'dnf', steps: [['dnf', 'install', '-y', 'git']] },
|
|
318
|
+
{ bin: 'yum', steps: [['yum', 'install', '-y', 'git']] },
|
|
319
|
+
{
|
|
320
|
+
bin: 'zypper',
|
|
321
|
+
steps: [['zypper', '--non-interactive', 'install', 'git']],
|
|
322
|
+
},
|
|
323
|
+
{ bin: 'pacman', steps: [['pacman', '-S', '--noconfirm', 'git']] },
|
|
324
|
+
{ bin: 'apk', steps: [['apk', 'add', '--no-cache', 'git']] },
|
|
325
|
+
];
|
|
326
|
+
const pm = managers.find((m) => binExists(m.bin));
|
|
327
|
+
if (!pm) {
|
|
328
|
+
throw new Error(
|
|
329
|
+
'git is missing and no supported package manager ' +
|
|
330
|
+
'(apt-get/dnf/yum/zypper/pacman/apk) was found.\n' +
|
|
331
|
+
' Install git manually and re-run.'
|
|
332
|
+
);
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
log(`Installing git via ${pm.bin}${sudo.length ? ' (sudo)' : ''}...`);
|
|
336
|
+
const env = { ...process.env, DEBIAN_FRONTEND: 'noninteractive' };
|
|
337
|
+
pm.steps.forEach((step, i) => {
|
|
338
|
+
const argv = [...sudo, ...step];
|
|
339
|
+
const r = spawnSync(argv[0], argv.slice(1), { stdio: 'inherit', env });
|
|
340
|
+
const ok = !r.error && r.status === 0;
|
|
341
|
+
if (!ok && !(pm.soft && pm.soft.includes(i))) {
|
|
342
|
+
const why = r.error ? r.error.code : `exit ${r.status}`;
|
|
343
|
+
throw new Error(`git install failed: \`${argv.join(' ')}\` (${why}).`);
|
|
344
|
+
}
|
|
345
|
+
});
|
|
346
|
+
|
|
347
|
+
if (!commandWorks('git', ['--version'])) {
|
|
348
|
+
throw new Error('git still not available after the install attempt.');
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
// --- Run bb --------------------------------------------------------------
|
|
353
|
+
|
|
354
|
+
function runBb(bbPath, args, javaHome) {
|
|
355
|
+
const env = { ...process.env };
|
|
356
|
+
if (javaHome) {
|
|
357
|
+
env.JAVA_HOME = javaHome;
|
|
358
|
+
env.PATH =
|
|
359
|
+
path.join(javaHome, 'bin') +
|
|
360
|
+
path.delimiter +
|
|
361
|
+
(process.env.PATH || '');
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
const child = spawn(bbPath, args, {
|
|
365
|
+
stdio: 'inherit',
|
|
366
|
+
cwd: process.cwd(),
|
|
367
|
+
env,
|
|
368
|
+
});
|
|
369
|
+
|
|
370
|
+
const forward = (sig) => {
|
|
371
|
+
try {
|
|
372
|
+
child.kill(sig);
|
|
373
|
+
} catch {
|
|
374
|
+
/* child already gone */
|
|
375
|
+
}
|
|
376
|
+
};
|
|
377
|
+
process.on('SIGINT', forward);
|
|
378
|
+
process.on('SIGTERM', forward);
|
|
379
|
+
|
|
380
|
+
return new Promise((resolve) => {
|
|
381
|
+
child.on('error', (err) => {
|
|
382
|
+
log(`failed to start bb: ${err.message}`);
|
|
383
|
+
resolve(127);
|
|
384
|
+
});
|
|
385
|
+
child.on('exit', (code, signal) => {
|
|
386
|
+
resolve(signal ? 1 : code == null ? 1 : code);
|
|
387
|
+
});
|
|
388
|
+
});
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
async function main(args) {
|
|
392
|
+
const p = resolvePlatform();
|
|
393
|
+
const bbPath = await ensureBabashka(p);
|
|
394
|
+
const javaHome = await ensureJdk(p);
|
|
395
|
+
ensureGit();
|
|
396
|
+
const code = await runBb(bbPath, args, javaHome);
|
|
397
|
+
process.exit(code);
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
if (require.main === module) {
|
|
401
|
+
main(process.argv.slice(2)).catch((err) => {
|
|
402
|
+
log(err && err.message ? err.message : String(err));
|
|
403
|
+
process.exit(1);
|
|
404
|
+
});
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
// Exported for tests / inspection.
|
|
408
|
+
module.exports = { resolvePlatform, cacheRoot, findJavaHome, ensureGit };
|
package/package.json
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@bigconfig/bb",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Bootstrap and run babashka (bb): installs babashka and a Temurin JDK on first use if missing, then forwards all arguments to bb.",
|
|
5
|
+
"bin": {
|
|
6
|
+
"bb": "bin/bb.js"
|
|
7
|
+
},
|
|
8
|
+
"files": [
|
|
9
|
+
"bin/",
|
|
10
|
+
"README.md"
|
|
11
|
+
],
|
|
12
|
+
"engines": {
|
|
13
|
+
"node": ">=18"
|
|
14
|
+
},
|
|
15
|
+
"keywords": [
|
|
16
|
+
"babashka",
|
|
17
|
+
"bb",
|
|
18
|
+
"clojure",
|
|
19
|
+
"jdk",
|
|
20
|
+
"cli"
|
|
21
|
+
],
|
|
22
|
+
"license": "MIT",
|
|
23
|
+
"type": "commonjs"
|
|
24
|
+
}
|