@bigconfig/bb 0.1.0 → 0.2.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 +28 -4
- package/bin/bb.js +153 -3
- package/package.json +13 -22
package/README.md
CHANGED
|
@@ -37,6 +37,27 @@ and may prompt for a sudo password; in non-interactive environments without
|
|
|
37
37
|
passwordless sudo it will fail with an actionable message — pre-install git to
|
|
38
38
|
avoid this entirely.
|
|
39
39
|
|
|
40
|
+
## bb.edn bootstrap (optional)
|
|
41
|
+
|
|
42
|
+
If the current directory has **no `bb.edn`** and `BB_EDN_REPO=owner/project`
|
|
43
|
+
is set, that repo's `bb.edn` is downloaded (pinned to its default branch's
|
|
44
|
+
latest commit) and the repo itself is added to `:deps` as
|
|
45
|
+
`io.github.<owner>/<project> {:git/sha "<sha>"}`. The edit is done with
|
|
46
|
+
`borkdude/rewrite-edn`, so existing comments and formatting are preserved.
|
|
47
|
+
|
|
48
|
+
Any dependency using `:local/root` (in `:deps` or a task's `:extra-deps`) is
|
|
49
|
+
removed first, since those paths don't exist once the file is downloaded.
|
|
50
|
+
Valid Maven/git deps are kept.
|
|
51
|
+
|
|
52
|
+
- Skipped entirely if `BB_EDN_REPO` is unset or a `bb.edn` already exists.
|
|
53
|
+
- Fatal error if the repo is missing/inaccessible or has no `bb.edn`.
|
|
54
|
+
- Set `GITHUB_TOKEN` for private repos or to avoid GitHub's unauthenticated
|
|
55
|
+
API rate limit.
|
|
56
|
+
|
|
57
|
+
```sh
|
|
58
|
+
BB_EDN_REPO=my-org/shared-tasks npx @bigconfig/bb tasks
|
|
59
|
+
```
|
|
60
|
+
|
|
40
61
|
## Cache location
|
|
41
62
|
|
|
42
63
|
A single shared directory, reused across all projects:
|
|
@@ -50,10 +71,13 @@ Delete that directory to force a clean reinstall.
|
|
|
50
71
|
|
|
51
72
|
## Configuration
|
|
52
73
|
|
|
53
|
-
| Env var
|
|
54
|
-
|
|
|
55
|
-
| `BB_VERSION`
|
|
56
|
-
| `JDK_VERSION`
|
|
74
|
+
| Env var | Default | Effect |
|
|
75
|
+
| --------------------- | ---------- | ----------------------------------------------- |
|
|
76
|
+
| `BB_VERSION` | `1.12.196` | babashka release version to install |
|
|
77
|
+
| `JDK_VERSION` | `21` | Temurin feature version (e.g. `17`, `21`, `25`) |
|
|
78
|
+
| `BB_EDN_REPO` | _(unset)_ | `owner/project` to bootstrap a `bb.edn` from (see below) |
|
|
79
|
+
| `GITHUB_TOKEN` | _(unset)_ | Used for `BB_EDN_REPO` (private repos / higher API rate limit) |
|
|
80
|
+
| `REWRITE_EDN_VERSION` | `0.5.9` | `borkdude/rewrite-edn` version used to edit the `bb.edn` |
|
|
57
81
|
|
|
58
82
|
## Supported platforms
|
|
59
83
|
|
package/bin/bb.js
CHANGED
|
@@ -14,6 +14,7 @@ const { pipeline } = require('stream/promises');
|
|
|
14
14
|
// Pinned, known-good versions. Overridable via env.
|
|
15
15
|
const DEFAULT_BB_VERSION = process.env.BB_VERSION || '1.12.196';
|
|
16
16
|
const DEFAULT_JDK_VERSION = process.env.JDK_VERSION || '21';
|
|
17
|
+
const REWRITE_EDN_VERSION = process.env.REWRITE_EDN_VERSION || '0.5.9';
|
|
17
18
|
|
|
18
19
|
const TAG = '[@bigconfig/bb]';
|
|
19
20
|
|
|
@@ -349,9 +350,10 @@ function ensureGit() {
|
|
|
349
350
|
}
|
|
350
351
|
}
|
|
351
352
|
|
|
352
|
-
// ---
|
|
353
|
+
// --- bb.edn bootstrap ----------------------------------------------------
|
|
353
354
|
|
|
354
|
-
|
|
355
|
+
// Env augmented so the spawned bb finds the cached JDK; nothing system-wide.
|
|
356
|
+
function bbEnv(javaHome) {
|
|
355
357
|
const env = { ...process.env };
|
|
356
358
|
if (javaHome) {
|
|
357
359
|
env.JAVA_HOME = javaHome;
|
|
@@ -360,6 +362,147 @@ function runBb(bbPath, args, javaHome) {
|
|
|
360
362
|
path.delimiter +
|
|
361
363
|
(process.env.PATH || '');
|
|
362
364
|
}
|
|
365
|
+
return env;
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
function ghFetch(url, accept) {
|
|
369
|
+
const headers = {
|
|
370
|
+
'user-agent': '@bigconfig/bb',
|
|
371
|
+
accept: accept || 'application/vnd.github+json',
|
|
372
|
+
'x-github-api-version': '2022-11-28',
|
|
373
|
+
};
|
|
374
|
+
if (process.env.GITHUB_TOKEN) {
|
|
375
|
+
headers.authorization = `Bearer ${process.env.GITHUB_TOKEN}`;
|
|
376
|
+
}
|
|
377
|
+
return fetch(url, { headers, redirect: 'follow' });
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
// Reads/edits the EDN with borkdude/rewrite-edn so comments & formatting of
|
|
381
|
+
// untouched nodes survive. Params are passed via env to avoid quoting issues.
|
|
382
|
+
const REWRITE_SCRIPT = `(require '[babashka.deps :as deps])
|
|
383
|
+
(deps/add-deps {:deps {'borkdude/rewrite-edn {:mvn/version (System/getenv "BBEDN_REWRITE_VERSION")}}})
|
|
384
|
+
(require '[borkdude.rewrite-edn :as r])
|
|
385
|
+
|
|
386
|
+
(defn local-root? [coord]
|
|
387
|
+
(and (map? coord) (contains? coord :local/root)))
|
|
388
|
+
|
|
389
|
+
;; Drop every entry whose effective coord (sexpr respects #_ discard) is a
|
|
390
|
+
;; map containing :local/root. Returns the (possibly unchanged) map node.
|
|
391
|
+
(defn strip-local-root [m-node]
|
|
392
|
+
(if (nil? m-node)
|
|
393
|
+
m-node
|
|
394
|
+
(let [m (r/sexpr m-node)]
|
|
395
|
+
(reduce (fn [acc k]
|
|
396
|
+
(if (local-root? (get m k)) (r/dissoc acc k) acc))
|
|
397
|
+
m-node
|
|
398
|
+
(keys m)))))
|
|
399
|
+
|
|
400
|
+
(let [in (System/getenv "BBEDN_IN")
|
|
401
|
+
out (System/getenv "BBEDN_OUT")
|
|
402
|
+
owner (System/getenv "BBEDN_OWNER")
|
|
403
|
+
proj (System/getenv "BBEDN_PROJECT")
|
|
404
|
+
sha (System/getenv "BBEDN_SHA")
|
|
405
|
+
dep (symbol (str "io.github." owner) proj)
|
|
406
|
+
nodes (r/parse-string (slurp in))
|
|
407
|
+
;; 1. strip :local/root from top-level :deps
|
|
408
|
+
nodes (if (r/get nodes :deps)
|
|
409
|
+
(r/update nodes :deps strip-local-root)
|
|
410
|
+
nodes)
|
|
411
|
+
;; 2. strip :local/root from each task's :extra-deps
|
|
412
|
+
tasks (some-> (r/get nodes :tasks) r/sexpr)
|
|
413
|
+
nodes (reduce (fn [acc tk]
|
|
414
|
+
(let [tv (get tasks tk)]
|
|
415
|
+
(if (and (map? tv) (map? (:extra-deps tv)))
|
|
416
|
+
(r/update-in acc [:tasks tk :extra-deps] strip-local-root)
|
|
417
|
+
acc)))
|
|
418
|
+
nodes
|
|
419
|
+
(keys tasks))
|
|
420
|
+
;; 3. ensure :deps exists, then inject the repo as a git dep
|
|
421
|
+
nodes (if (nil? (r/get nodes :deps)) (r/assoc nodes :deps {}) nodes)
|
|
422
|
+
nodes (r/assoc-in nodes [:deps dep] {:git/sha sha})]
|
|
423
|
+
(spit out (str nodes)))
|
|
424
|
+
`;
|
|
425
|
+
|
|
426
|
+
// When the cwd has no bb.edn and BB_EDN_REPO=owner/project is set, fetch that
|
|
427
|
+
// repo's bb.edn (pinned to its default-branch HEAD) and add the repo itself
|
|
428
|
+
// as an io.github git dep. Disabled (skipped) if the env var is unset.
|
|
429
|
+
async function ensureBbEdn(bbPath, javaHome) {
|
|
430
|
+
const repo = process.env.BB_EDN_REPO;
|
|
431
|
+
if (!repo) return; // step disabled — proceed straight to bb
|
|
432
|
+
const target = path.join(process.cwd(), 'bb.edn');
|
|
433
|
+
if (fs.existsSync(target)) return; // already present — leave it alone
|
|
434
|
+
|
|
435
|
+
const m = repo.trim().match(/^([^/\s@]+)\/([^/\s@]+)$/);
|
|
436
|
+
if (!m) {
|
|
437
|
+
throw new Error(`BB_EDN_REPO must be "owner/project" (got "${repo}")`);
|
|
438
|
+
}
|
|
439
|
+
const [, owner, project] = m;
|
|
440
|
+
const slug = `${owner}/${project}`;
|
|
441
|
+
const api = `https://api.github.com/repos/${owner}/${project}`;
|
|
442
|
+
|
|
443
|
+
const cr = await ghFetch(`${api}/commits?per_page=1`);
|
|
444
|
+
if (cr.status === 404) {
|
|
445
|
+
throw new Error(
|
|
446
|
+
`BB_EDN_REPO: ${slug} not found or not accessible ` +
|
|
447
|
+
`(set GITHUB_TOKEN for private repos)`
|
|
448
|
+
);
|
|
449
|
+
}
|
|
450
|
+
if (!cr.ok) {
|
|
451
|
+
throw new Error(`GitHub API error ${cr.status} resolving ${slug}`);
|
|
452
|
+
}
|
|
453
|
+
const commits = await cr.json();
|
|
454
|
+
const sha = Array.isArray(commits) && commits[0] && commits[0].sha;
|
|
455
|
+
if (!sha) throw new Error(`${slug} has no commits`);
|
|
456
|
+
|
|
457
|
+
const fr = await ghFetch(
|
|
458
|
+
`${api}/contents/bb.edn?ref=${sha}`,
|
|
459
|
+
'application/vnd.github.raw'
|
|
460
|
+
);
|
|
461
|
+
if (fr.status === 404) {
|
|
462
|
+
throw new Error(`${slug} (at ${sha.slice(0, 7)}) has no bb.edn`);
|
|
463
|
+
}
|
|
464
|
+
if (!fr.ok) {
|
|
465
|
+
throw new Error(`GitHub API error ${fr.status} fetching bb.edn from ${slug}`);
|
|
466
|
+
}
|
|
467
|
+
const ednText = await fr.text();
|
|
468
|
+
|
|
469
|
+
log(`Bootstrapping bb.edn from ${slug}@${sha.slice(0, 7)}...`);
|
|
470
|
+
const tmp = fs.mkdtempSync(path.join(os.tmpdir(), 'bb-edn-'));
|
|
471
|
+
try {
|
|
472
|
+
const inFile = path.join(tmp, 'in.edn');
|
|
473
|
+
const script = path.join(tmp, 'rewrite.clj');
|
|
474
|
+
fs.writeFileSync(inFile, ednText);
|
|
475
|
+
fs.writeFileSync(script, REWRITE_SCRIPT);
|
|
476
|
+
const env = bbEnv(javaHome);
|
|
477
|
+
Object.assign(env, {
|
|
478
|
+
BBEDN_IN: inFile,
|
|
479
|
+
BBEDN_OUT: target,
|
|
480
|
+
BBEDN_OWNER: owner,
|
|
481
|
+
BBEDN_PROJECT: project,
|
|
482
|
+
BBEDN_SHA: sha,
|
|
483
|
+
BBEDN_REWRITE_VERSION: REWRITE_EDN_VERSION,
|
|
484
|
+
});
|
|
485
|
+
const r = spawnSync(bbPath, [script], {
|
|
486
|
+
stdio: ['ignore', 'ignore', 'inherit'],
|
|
487
|
+
cwd: process.cwd(),
|
|
488
|
+
env,
|
|
489
|
+
});
|
|
490
|
+
if (r.error || r.status !== 0) {
|
|
491
|
+
const why = r.error ? r.error.message : `exit ${r.status}`;
|
|
492
|
+
throw new Error(`failed to write bb.edn via rewrite-edn (${why})`);
|
|
493
|
+
}
|
|
494
|
+
if (!fs.existsSync(target)) {
|
|
495
|
+
throw new Error('rewrite-edn step did not produce a bb.edn');
|
|
496
|
+
}
|
|
497
|
+
} finally {
|
|
498
|
+
rmrf(tmp);
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
// --- Run bb --------------------------------------------------------------
|
|
503
|
+
|
|
504
|
+
function runBb(bbPath, args, javaHome) {
|
|
505
|
+
const env = bbEnv(javaHome);
|
|
363
506
|
|
|
364
507
|
const child = spawn(bbPath, args, {
|
|
365
508
|
stdio: 'inherit',
|
|
@@ -393,6 +536,7 @@ async function main(args) {
|
|
|
393
536
|
const bbPath = await ensureBabashka(p);
|
|
394
537
|
const javaHome = await ensureJdk(p);
|
|
395
538
|
ensureGit();
|
|
539
|
+
await ensureBbEdn(bbPath, javaHome);
|
|
396
540
|
const code = await runBb(bbPath, args, javaHome);
|
|
397
541
|
process.exit(code);
|
|
398
542
|
}
|
|
@@ -405,4 +549,10 @@ if (require.main === module) {
|
|
|
405
549
|
}
|
|
406
550
|
|
|
407
551
|
// Exported for tests / inspection.
|
|
408
|
-
module.exports = {
|
|
552
|
+
module.exports = {
|
|
553
|
+
resolvePlatform,
|
|
554
|
+
cacheRoot,
|
|
555
|
+
findJavaHome,
|
|
556
|
+
ensureGit,
|
|
557
|
+
ensureBbEdn,
|
|
558
|
+
};
|
package/package.json
CHANGED
|
@@ -1,24 +1,15 @@
|
|
|
1
1
|
{
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
"
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
"
|
|
14
|
-
|
|
15
|
-
"keywords": [
|
|
16
|
-
"babashka",
|
|
17
|
-
"bb",
|
|
18
|
-
"clojure",
|
|
19
|
-
"jdk",
|
|
20
|
-
"cli"
|
|
21
|
-
],
|
|
22
|
-
"license": "MIT",
|
|
23
|
-
"type": "commonjs"
|
|
2
|
+
"name": "@bigconfig/bb",
|
|
3
|
+
"version": "0.2.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": ["bin/", "README.md"],
|
|
9
|
+
"engines": {
|
|
10
|
+
"node": ">=18"
|
|
11
|
+
},
|
|
12
|
+
"keywords": ["babashka", "bb", "clojure", "jdk", "cli"],
|
|
13
|
+
"license": "MIT",
|
|
14
|
+
"type": "commonjs"
|
|
24
15
|
}
|