Skip to content

Commit e6c3b5f

Browse files
author
Sebastian Benjamin
committed
Swap from pkg to npm sea for jbrowse-cli
1 parent 2a00a36 commit e6c3b5f

File tree

4 files changed

+160
-4
lines changed

4 files changed

+160
-4
lines changed

jbrowse/build.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ dependencies {
3030
external "org.apache.lucene:lucene-backward-codecs:${luceneVersion}"
3131
}
3232

33-
def jbPkgTask = project.tasks.named("npm_run_jb-pkg")
33+
def jbPkgTask = project.tasks.named("npm_run_jb-sea")
3434

3535
jbPkgTask.configure {
3636
outputs.cacheIf {false}

jbrowse/package.json

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,16 +10,17 @@
1010
"build-dev": "npm run build-no-pkg",
1111
"build-prod": "npm run clean && cross-env NODE_ENV=production PROD_SOURCE_MAP=source-map webpack --config ./config/prod.config.js --progress --profile",
1212
"clean": "rimraf resources/web/gen && rimraf resources/web/jbrowse/gen && rimraf resources/views/gen",
13-
"prepareCli": "rimraf ./buildCli && rimraf ./resources/external/jb-cli && npm install @jbrowse/cli@1.7.4 --prefix ./buildCli",
14-
"jb-pkg": "npm run prepareCli && npx pkg --outdir=./resources/external/jb-cli ./buildCli/node_modules/@jbrowse/cli && rimraf ./buildCli"
13+
"jb-sea:fetch": "node ./resources/external/fetch-jbrowse-bundle.mjs",
14+
"jb-sea:build": "node ./resources/external/build-sea.mjs ./resources/external/jbrowse.js",
15+
"jb-sea": "npm run jb-sea:fetch && npm run jb-sea:build"
1516
},
1617
"dependencies": {
1718
"@gmod/vcf": "^6.0.9",
1819
"@jbrowse/core": "^3.2.0",
19-
"@jbrowse/product-core": "^3.2.0",
2020
"@jbrowse/plugin-linear-genome-view": "^3.2.0",
2121
"@jbrowse/plugin-svg": "^3.2.0",
2222
"@jbrowse/plugin-variants": "^3.2.0",
23+
"@jbrowse/product-core": "^3.2.0",
2324
"@jbrowse/react-linear-genome-view2": "^3.2.0",
2425
"@labkey/api": "^1.39.0",
2526
"@labkey/components": "^6.32.2",
@@ -54,6 +55,7 @@
5455
"@types/react": "^18.3.0",
5556
"@types/react-dom": "^18.3.0",
5657
"rimraf": "^6.0.1",
58+
"tar": "^6.2.1",
5759
"typescript": "^5.1.6"
5860
}
5961
}
Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
import { execFileSync, execSync, spawnSync } from 'node:child_process';
2+
import { copyFileSync, chmodSync, existsSync, mkdirSync, rmSync, renameSync, writeFileSync } from 'node:fs';
3+
import { basename, join, resolve } from 'node:path';
4+
5+
const input = process.argv[2];
6+
if (!input) {
7+
console.error('Usage: node build-sea.mjs <input.js> [outputBaseName]');
8+
process.exit(1);
9+
}
10+
const inputAbs = resolve(input);
11+
const baseName = process.argv[3] || basename(input, '.js');
12+
13+
const ROOT = resolve('.');
14+
const OUTDIR = join(ROOT, 'resources', 'external', 'jb-cli');
15+
mkdirSync(OUTDIR, { recursive: true });
16+
17+
const platform = process.platform; // 'win32' | 'darwin' | 'linux'
18+
const outName =
19+
platform === 'win32' ? 'cli-win.exe' :
20+
platform === 'darwin' ? 'cli-macos' : 'cli-linux';
21+
22+
const tmpCfg = 'sea-config.json';
23+
const tmpBlob = 'sea-prep.blob';
24+
const tmpOut = `${baseName}${platform === 'win32' ? '.exe' : ''}`;
25+
26+
console.log(`SEA build: ${inputAbs} -> ${join(OUTDIR, outName)} [${platform}]`);
27+
28+
// 1) Create SEA config
29+
const cfg = {
30+
main: inputAbs,
31+
output: tmpBlob,
32+
disableExperimentalSEAWarning: true,
33+
};
34+
writeFileSync(tmpCfg, JSON.stringify(cfg, null, 2));
35+
36+
// 2) Produce blob
37+
execFileSync(process.execPath, ['--experimental-sea-config', tmpCfg], { stdio: 'inherit' });
38+
39+
// 3) Copy current Node runtime as the base executable
40+
copyFileSync(process.execPath, tmpOut);
41+
42+
// 4) Remove signature (it'll be invalid once we postject the Jbrowse CLI into the node runtime executable)
43+
if (platform === 'darwin') {
44+
try { spawnSync('codesign', ['--remove-signature', tmpOut], { stdio: 'inherit' }); } catch {}
45+
}
46+
if (platform === 'win32') {
47+
try { spawnSync('signtool', ['remove', '/s', tmpOut], { stdio: 'inherit' }); } catch {}
48+
}
49+
50+
// Helper: run postject with multiple fallbacks
51+
function runPostject(argsList) {
52+
const isWin = platform === 'win32';
53+
const npxCmd = isWin ? 'npx.cmd' : 'npx';
54+
const npmCmd = isWin ? 'npm.cmd' : 'npm';
55+
const postjectPkg = 'postject@1.0.0-alpha.6';
56+
57+
// Attempt 1: npx (execFileSync)
58+
try {
59+
execFileSync(npxCmd, ['--yes', postjectPkg, ...argsList], { stdio: 'inherit' });
60+
return;
61+
} catch (e1) {
62+
console.warn('[postject] npx (execFileSync) failed, trying npm exec…');
63+
// Attempt 2: npm exec (execFileSync)
64+
try {
65+
execFileSync(npmCmd, ['exec', '-y', postjectPkg, '--', ...argsList], { stdio: 'inherit' });
66+
return;
67+
} catch (e2) {
68+
console.warn('[postject] npm exec (execFileSync) failed, trying shell npx…');
69+
// Attempt 3: npx via shell (execSync)
70+
try {
71+
const cmd = `npx --yes ${postjectPkg} ${argsList.map(a => `"${a}"`).join(' ')}`;
72+
execSync(cmd, { stdio: 'inherit', shell: true });
73+
return;
74+
} catch (e3) {
75+
console.warn('[postject] shell npx failed, trying shell npm exec…');
76+
// Attempt 4: npm exec via shell (execSync)
77+
const cmd2 = `npm exec -y ${postjectPkg} -- ${argsList.map(a => `"${a}"`).join(' ')}`;
78+
execSync(cmd2, { stdio: 'inherit', shell: true });
79+
}
80+
}
81+
}
82+
}
83+
84+
// Postject magic from the docs to do the jbrowse->node appending
85+
const SENTINEL = 'NODE_SEA_FUSE_fce680ab2cc467b6e072b8b5df1996b2';
86+
const postjectArgs = platform === 'darwin'
87+
? [tmpOut, 'NODE_SEA_BLOB', tmpBlob, '--sentinel-fuse', SENTINEL, '--macho-segment-name', 'NODE_SEA']
88+
: [tmpOut, 'NODE_SEA_BLOB', tmpBlob, '--sentinel-fuse', SENTINEL];
89+
90+
// 5) Inject the SEA blob
91+
runPostject(postjectArgs);
92+
93+
// 6) Re-sign for the new combined executable
94+
if (platform === 'darwin') {
95+
try { spawnSync('codesign', ['--sign', '-', tmpOut], { stdio: 'inherit' }); } catch {}
96+
}
97+
if (platform === 'win32') {
98+
try { spawnSync('signtool', ['sign', '/fd', 'SHA256', tmpOut], { stdio: 'inherit' }); } catch {}
99+
}
100+
101+
// 7) POSIX chmod
102+
if (platform !== 'win32') {
103+
chmodSync(tmpOut, 0o755);
104+
}
105+
106+
// 8) Move to final destination
107+
const finalPath = join(OUTDIR, outName);
108+
if (existsSync(finalPath)) rmSync(finalPath, { force: true });
109+
renameSync(tmpOut, finalPath);
110+
111+
// 9) Cleanup
112+
rmSync(tmpCfg, { force: true });
113+
rmSync(tmpBlob, { force: true });
114+
115+
const jbrowseJsPath = join(ROOT, 'resources', 'external', 'jbrowse.js');
116+
if (existsSync(jbrowseJsPath)) {
117+
rmSync(jbrowseJsPath, { force: true });
118+
}
119+
120+
console.log(`Done: ${finalPath}`);
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import { execSync } from 'node:child_process';
2+
import { existsSync, mkdirSync, copyFileSync, rmSync } from 'node:fs';
3+
import { join, resolve } from 'node:path';
4+
import tar from 'tar';
5+
6+
const ROOT = resolve('.');
7+
const BUILD = join(ROOT, 'buildCli');
8+
const OUTDIR = join(ROOT, 'resources', 'external');
9+
const OUTFILE = join(OUTDIR, 'jbrowse.js');
10+
11+
if (existsSync(BUILD)) rmSync(BUILD, { recursive: true, force: true });
12+
mkdirSync(BUILD, { recursive: true });
13+
mkdirSync(OUTDIR, { recursive: true });
14+
15+
console.log('Packing @jbrowse/cli (latest)…');
16+
const out = execSync('npm pack @jbrowse/cli', { cwd: BUILD, stdio: ['ignore', 'pipe', 'inherit'] }).toString().trim();
17+
18+
// npm outputs the filename on stdout, e.g. "jbrowse-cli-3.6.4.tgz"
19+
const tgz = join(BUILD, out);
20+
console.log(`Downloaded: ${tgz}`);
21+
22+
console.log('Extracting tarball…');
23+
await tar.x({ file: tgz, cwd: BUILD });
24+
25+
const bundled = join(BUILD, 'package', 'bundle', 'index.js');
26+
if (!existsSync(bundled)) {
27+
throw new Error(`bundle/index.js not found at ${bundled}`);
28+
}
29+
30+
copyFileSync(bundled, OUTFILE);
31+
32+
rmSync(BUILD, { recursive: true, force: true });
33+
34+
console.log(`Copied bundle to ${OUTFILE}`);

0 commit comments

Comments
 (0)