mirror of
https://github.com/usebruno/bruno.git
synced 2026-06-24 05:05:39 +00:00
feat: add cli offline bundle script (#8192)
This commit is contained in:
125
scripts/bundle-cli.js
Executable file
125
scripts/bundle-cli.js
Executable file
@@ -0,0 +1,125 @@
|
||||
#!/usr/bin/env node
|
||||
'use strict';
|
||||
|
||||
// pack a self-contained tarball of @usebruno/cli that installs offline
|
||||
// - npm pack all workspace packages into tarballs/
|
||||
// - stage cli without node_modules, patch package.json to use local tarballs
|
||||
// - npm install in stage, then npm pack the whole thing
|
||||
|
||||
const { execSync } = require('child_process');
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const os = require('os');
|
||||
|
||||
const ROOT = path.resolve(__dirname, '..');
|
||||
const CLI_DIR = path.join(ROOT, 'packages', 'bruno-cli');
|
||||
const OUT_DIR = path.join(ROOT, 'dist', 'cli-bundle');
|
||||
const TARBALLS = path.join(OUT_DIR, 'tarballs');
|
||||
|
||||
const log = msg => process.stdout.write(`> ${msg}\n`);
|
||||
const run = (cmd, cwd) => execSync(cmd, { cwd, stdio: 'pipe', encoding: 'utf8' }).trim();
|
||||
|
||||
function copyDir(src, dst) {
|
||||
fs.mkdirSync(dst, { recursive: true });
|
||||
for (const entry of fs.readdirSync(src, { withFileTypes: true })) {
|
||||
if (entry.name === 'node_modules') continue;
|
||||
const s = path.join(src, entry.name);
|
||||
const d = path.join(dst, entry.name);
|
||||
if (entry.isDirectory()) {
|
||||
copyDir(s, d);
|
||||
} else {
|
||||
fs.copyFileSync(s, d);
|
||||
fs.chmodSync(d, fs.statSync(s).mode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const rootPkg = JSON.parse(fs.readFileSync(path.join(ROOT, 'package.json'), 'utf8'));
|
||||
|
||||
const workspaces = {};
|
||||
for (const wsPath of rootPkg.workspaces) {
|
||||
try {
|
||||
const dir = path.join(ROOT, wsPath);
|
||||
const pkgFile = path.join(dir, 'package.json');
|
||||
if (!fs.existsSync(pkgFile)) continue;
|
||||
const pkg = JSON.parse(fs.readFileSync(pkgFile, 'utf8'));
|
||||
workspaces[pkg.name] = { dir, version: pkg.version };
|
||||
} catch (err) {
|
||||
log(` skip ${wsPath} (${err.message.split('\n')[0]})`);
|
||||
}
|
||||
}
|
||||
|
||||
fs.rmSync(OUT_DIR, { recursive: true, force: true });
|
||||
fs.mkdirSync(TARBALLS, { recursive: true });
|
||||
|
||||
log('packing workspace packages...');
|
||||
const tarballMap = {};
|
||||
|
||||
for (const [name, { dir }] of Object.entries(workspaces)) {
|
||||
try {
|
||||
const { version } = workspaces[name];
|
||||
const filename = `${name.replace(/^@/, '').replace('/', '-')}-${version}.tgz`;
|
||||
run(`npm pack --pack-destination "${TARBALLS}"`, dir);
|
||||
tarballMap[name] = filename;
|
||||
log(` ${name} -> ${filename}`);
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
log(` skip ${name} (${err.message.split('\n')[0]})`);
|
||||
}
|
||||
}
|
||||
|
||||
log('staging cli...');
|
||||
const stage = fs.mkdtempSync(path.join(os.tmpdir(), 'bruno-cli-bundle-'));
|
||||
|
||||
const cleanup = () => {
|
||||
try { fs.rmSync(stage, { recursive: true, force: true }); } catch {}
|
||||
};
|
||||
process.on('exit', cleanup);
|
||||
process.on('SIGINT', () => { cleanup(); process.exit(1); });
|
||||
process.on('SIGTERM', () => { cleanup(); process.exit(1); });
|
||||
process.on('uncaughtException', () => { cleanup(); process.exit(1); });
|
||||
process.on('unhandledRejection', () => { cleanup(); process.exit(1); });
|
||||
|
||||
copyDir(CLI_DIR, stage);
|
||||
|
||||
const stageTarballs = path.join(stage, 'tarballs');
|
||||
fs.mkdirSync(stageTarballs, { recursive: true });
|
||||
for (const tarball of Object.values(tarballMap)) {
|
||||
fs.copyFileSync(path.join(TARBALLS, tarball), path.join(stageTarballs, tarball));
|
||||
}
|
||||
|
||||
const pkg = JSON.parse(fs.readFileSync(path.join(stage, 'package.json'), 'utf8'));
|
||||
|
||||
for (const dep of Object.keys(pkg.dependencies || {})) {
|
||||
if (tarballMap[dep]) {
|
||||
pkg.dependencies[dep] = `file:./tarballs/${tarballMap[dep]}`;
|
||||
}
|
||||
}
|
||||
|
||||
const tarballOverrides = {};
|
||||
for (const [name, tarball] of Object.entries(tarballMap)) {
|
||||
tarballOverrides[name] = `file:./tarballs/${tarball}`;
|
||||
}
|
||||
pkg.overrides = { ...(rootPkg.overrides || {}), ...(pkg.overrides || {}), ...tarballOverrides };
|
||||
|
||||
pkg.bundledDependencies = Object.keys(pkg.dependencies || {});
|
||||
pkg.files = [...(pkg.files || []), 'tarballs', 'node_modules'];
|
||||
|
||||
fs.writeFileSync(path.join(stage, 'package.json'), JSON.stringify(pkg, null, 2) + '\n');
|
||||
|
||||
log('installing...');
|
||||
execSync('npm install --ignore-scripts --no-audit --no-fund', {
|
||||
cwd: stage,
|
||||
stdio: 'inherit',
|
||||
});
|
||||
|
||||
log('packing bundle...');
|
||||
run(`npm pack --pack-destination "${OUT_DIR}"`, stage);
|
||||
const packed = fs.readdirSync(OUT_DIR).find(f => f.endsWith('.tgz'));
|
||||
if (!packed) throw new Error('no .tgz found after npm pack');
|
||||
const finalPath = path.join(OUT_DIR, packed);
|
||||
|
||||
log('');
|
||||
log(`done: ${finalPath}`);
|
||||
log(` npm install -g "${finalPath}"`);
|
||||
log(` npm install -g --prefer-offline "${finalPath}"`);
|
||||
6
scripts/bundle-cli.sh
Executable file
6
scripts/bundle-cli.sh
Executable file
@@ -0,0 +1,6 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null && pwd )"
|
||||
|
||||
node "${SCRIPT_DIR}/bundle-cli.js"
|
||||
Reference in New Issue
Block a user