Skip to content

feat: add cli offline bundle script#8192

Open
sid-bruno wants to merge 2 commits into
usebruno:mainfrom
sid-bruno:feat/bundle-cli-script
Open

feat: add cli offline bundle script#8192
sid-bruno wants to merge 2 commits into
usebruno:mainfrom
sid-bruno:feat/bundle-cli-script

Conversation

@sid-bruno

@sid-bruno sid-bruno commented Jun 5, 2026

Copy link
Copy Markdown
Collaborator

Description

Add a simple script to create a standalone cli tarball for distributions

Contribution Checklist:

  • I've used AI significantly to create this pull request
  • The pull request only addresses one issue or adds one feature.
  • The pull request does not introduce any breaking changes
  • I have added screenshots or gifs to help explain the change if applicable.
  • I have read the contribution guidelines.
  • Create an issue and link to the pull request.

Note: Keeping the PR small and focused helps make it easier to review and merge. If you have multiple changes you want to make, please consider submitting them as separate pull requests.

Publishing to New Package Managers

Please see here for more information.

Summary by CodeRabbit

  • Chores
    • Added CLI bundling tooling that produces offline-installable, self-contained CLI tarballs containing all workspace dependencies for easy distribution.
    • Includes a wrapper to run the bundler, performs a staged install to assemble bundled dependencies, and emits a final tarball plus suggested install commands. Temporary staging is cleaned up automatically.

Copilot AI review requested due to automatic review settings June 5, 2026 13:02
@coderabbitai

coderabbitai Bot commented Jun 5, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 628e6596-cd75-4e43-a52f-861d46f59184

📥 Commits

Reviewing files that changed from the base of the PR and between 03c9013 and 8f46176.

📒 Files selected for processing (1)
  • scripts/bundle-cli.js
🚧 Files skipped from review as they are similar to previous changes (1)
  • scripts/bundle-cli.js

Walkthrough

Adds scripts to produce an offline-installable tarball for @usebruno/cli: discover workspace packages, npm-pack them, stage the CLI with file: tarball dependencies and overrides, run npm install in the stage, and pack the staged directory into dist/cli-bundle. Includes a Bash wrapper.

Changes

CLI Offline Bundle

Layer / File(s) Summary
Entry point and helpers
scripts/bundle-cli.js
Script header, required Node modules, root/CLI/output path constants, logging and shell-run helpers.
Filesystem copy helper
scripts/bundle-cli.js
Recursive copy function that skips node_modules and preserves permissions for staging.
Discover workspace packages
scripts/bundle-cli.js
Parses root package.json workspaces and loads each workspace package.json to collect package dirs and versions.
Pack workspace tarballs
scripts/bundle-cli.js
Resets dist/cli-bundle, runs npm pack for each workspace, and records a tarballMap of generated .tgz filenames.
Create staging dir and cleanup handlers
scripts/bundle-cli.js
Creates a temp staging directory and registers cleanup handlers for exit, SIGINT, SIGTERM, uncaughtException, and unhandledRejection.
Copy CLI and tarballs into stage
scripts/bundle-cli.js
Copies packages/bruno-cli into staging (excluding node_modules) and copies workspace tarballs into stage/tarballs.
Rewrite staged package.json for offline install
scripts/bundle-cli.js
Rewrites staged package.json dependency entries for packaged workspaces to file:./tarballs/..., merges/sets overrides to those file: tarballs, sets bundledDependencies, and extends files.
Install staged deps and create final bundle
scripts/bundle-cli.js
Runs npm install --ignore-scripts --no-audit --no-fund in staging, packs the staged directory to a final .tgz under dist/cli-bundle, validates output, and prints install suggestions.
Bash wrapper script
scripts/bundle-cli.sh
Portable entrypoint that enables set -euo pipefail, resolves script dir, and runs the Node.js bundle script.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Suggested reviewers

  • helloanoop
  • bijin-bruno
  • lohit-bruno
  • naman-bruno

Poem

📦 A bundler hums in Node and Bash,
Tarballs gathered in a single stash,
Stage rewrites tidy every dep,
Install, pack, and ship—no internet step.
🎉

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the main change: adding a CLI offline bundle script.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a new bundling utility under scripts/ to generate a standalone, offline-installable tarball for the Bruno CLI, intended for distribution scenarios.

Changes:

  • Added a Bash wrapper (bundle-cli.sh) to run the bundling flow consistently from the repo.
  • Added a Node.js script (bundle-cli.js) that packs workspace packages, stages the CLI, installs dependencies, and packs a final distributable .tgz into dist/cli-bundle/.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.

File Description
scripts/bundle-cli.sh Thin shell entrypoint to run the CLI bundle script from a stable path.
scripts/bundle-cli.js Implements the offline bundle creation flow (workspace packing, staging, install, final pack).

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread scripts/bundle-cli.js Outdated
Comment thread scripts/bundle-cli.js
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🧹 Nitpick comments (2)
scripts/bundle-cli.js (2)

66-66: 💤 Low value

Inconsistent error logging.

This line uses console.error(err) directly instead of the log helper defined at line 19, creating inconsistent output formatting.

For consistency, either use the log helper or use console.error everywhere.

♻️ Suggested fix for consistency
  } catch (err) {
-    console.error(err);
-    log(`  skip ${name} (${err.message.split('\n')[0]})`);
+    log(`  skip ${name} (${err.message.split('\n')[0]})`);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@scripts/bundle-cli.js` at line 66, The console.error(err) call is
inconsistent with the project’s logging helper; replace that direct console call
with the defined log helper (use the same helper used at line 19, e.g.,
log.error or the appropriate log(...) method) so error output formatting matches
the rest of the script; update the call where console.error(err) appears and
ensure the log invocation includes the error object or message for full context.

80-81: ⚡ Quick win

Uncaught errors are silently suppressed.

The uncaughtException and unhandledRejection handlers cleanup and exit without logging the actual error, making debugging failures difficult.

Log the error before cleanup to aid troubleshooting.

📝 Proposed fix to log errors
-process.on('uncaughtException', () => { cleanup(); process.exit(1); });
-process.on('unhandledRejection', () => { cleanup(); process.exit(1); });
+process.on('uncaughtException', (err) => { console.error('Uncaught exception:', err); cleanup(); process.exit(1); });
+process.on('unhandledRejection', (err) => { console.error('Unhandled rejection:', err); cleanup(); process.exit(1); });
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@scripts/bundle-cli.js` around lines 80 - 81, The current process.on handlers
for 'uncaughtException' and 'unhandledRejection' call cleanup() and
process.exit(1) without logging the error; update the handlers for
process.on('uncaughtException', ...) and process.on('unhandledRejection', ...)
to accept the error/reason parameter, call processLogger.error (or
console.error) with a descriptive message and the error/reason object, then call
cleanup() and exit; ensure both handlers include the error details to aid
debugging.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@scripts/bundle-cli.js`:
- Line 20: The helper run currently builds a shell string and calls execSync,
which risks command injection; change run to use a non-shell child API (e.g.,
child_process.spawnSync or child_process.execFile) and pass the command and its
arguments as an array instead of a single interpolated string, preserving cwd,
stdio/encoding behavior and returning the trimmed stdout; update every call site
that uses run to pass the executable and args separately (references: run and
its internal use of execSync) and ensure errors are propagated when the child
exits non-zero.
- Line 32: The call to fs.chmodSync(d, fs.statSync(s).mode) is not Windows-safe;
update the bundle logic in scripts/bundle-cli.js to skip or gracefully handle
chmod on Windows by checking process.platform !== 'win32' (or wrapping the
fs.chmodSync call in a try/catch that ignores ENOSYS/EPERM on Windows), using
the same fs.statSync(s).mode when applicable; reference the existing
fs.chmodSync and fs.statSync calls and variables d and s when making the change.

In `@scripts/bundle-cli.sh`:
- Around line 1-6: The Bash-only wrapper scripts/bundle-cli.sh won't run on
native Windows; fix by providing cross-platform ways to invoke bundle-cli.js:
add a package.json script entry (e.g., "bundle-cli": "node
scripts/bundle-cli.js") and update docs/README to instruct users to run node
scripts/bundle-cli.js or npm run bundle-cli as the supported entrypoint, and
optionally add small Windows wrappers (bundle-cli.cmd that runs node
"%~dp0\bundle-cli.js" and bundle-cli.ps1 that runs node
"$PSScriptRoot\bundle-cli.js") so users who call the script directly on Windows
get a working wrapper. Ensure references to scripts/bundle-cli.sh are replaced
or annotated to point to the cross-platform invocation.

---

Nitpick comments:
In `@scripts/bundle-cli.js`:
- Line 66: The console.error(err) call is inconsistent with the project’s
logging helper; replace that direct console call with the defined log helper
(use the same helper used at line 19, e.g., log.error or the appropriate
log(...) method) so error output formatting matches the rest of the script;
update the call where console.error(err) appears and ensure the log invocation
includes the error object or message for full context.
- Around line 80-81: The current process.on handlers for 'uncaughtException' and
'unhandledRejection' call cleanup() and process.exit(1) without logging the
error; update the handlers for process.on('uncaughtException', ...) and
process.on('unhandledRejection', ...) to accept the error/reason parameter, call
processLogger.error (or console.error) with a descriptive message and the
error/reason object, then call cleanup() and exit; ensure both handlers include
the error details to aid debugging.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: fd86da94-90aa-47f0-95d1-13e3ca9a4af8

📥 Commits

Reviewing files that changed from the base of the PR and between f629c3d and 03c9013.

📒 Files selected for processing (2)
  • scripts/bundle-cli.js
  • scripts/bundle-cli.sh

Comment thread scripts/bundle-cli.js
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();

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Command injection risk with string-based execSync.

The run helper passes shell command strings directly to execSync, which can be vulnerable if paths contain special characters (quotes, backticks, semicolons, etc.). Although the paths here are derived from __dirname and constants rather than direct user input, repository paths with unusual characters could still cause issues or security risks.

Use child_process.spawn or child_process.execFile with argument arrays instead of string interpolation.

🔒 Proposed fix using spawn with argument array
-const run = (cmd, cwd) => execSync(cmd, { cwd, stdio: 'pipe', encoding: 'utf8' }).trim();
+const { spawnSync } = require('child_process');
+const run = (cmd, args, cwd) => {
+  const result = spawnSync(cmd, args, { cwd, stdio: 'pipe', encoding: 'utf8' });
+  if (result.error) throw result.error;
+  if (result.status !== 0) throw new Error(`Command failed: ${cmd} ${args.join(' ')}`);
+  return result.stdout.trim();
+};

Then update call sites:

-    run(`npm pack --pack-destination "${TARBALLS}"`, dir);
+    run('npm', ['pack', '--pack-destination', TARBALLS], dir);
-run(`npm pack --pack-destination "${OUT_DIR}"`, stage);
+run('npm', ['pack', '--pack-destination', OUT_DIR], stage);
🧰 Tools
🪛 OpenGrep (1.22.0)

[ERROR] 20-20: Dynamic command passed to child_process.exec/execSync. Use child_process.execFile or spawn with an argument array instead.

(coderabbit.command-injection.exec-js)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@scripts/bundle-cli.js` at line 20, The helper run currently builds a shell
string and calls execSync, which risks command injection; change run to use a
non-shell child API (e.g., child_process.spawnSync or child_process.execFile)
and pass the command and its arguments as an array instead of a single
interpolated string, preserving cwd, stdio/encoding behavior and returning the
trimmed stdout; update every call site that uses run to pass the executable and
args separately (references: run and its internal use of execSync) and ensure
errors are propagated when the child exits non-zero.

Comment thread scripts/bundle-cli.js
copyDir(s, d);
} else {
fs.copyFileSync(s, d);
fs.chmodSync(d, fs.statSync(s).mode);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

File permission handling incompatible with Windows.

fs.chmodSync uses Unix-style permission bits that Windows doesn't support. Bruno is a cross-platform Electron app, and the coding guidelines require accounting for Windows not supporting Unix-style permission bits.

Wrap the chmodSync call in a platform check or handle permission errors gracefully.

🛠️ Proposed fix with platform check
-      fs.copyFileSync(s, d);
-      fs.chmodSync(d, fs.statSync(s).mode);
+      fs.copyFileSync(s, d);
+      if (process.platform !== 'win32') {
+        try {
+          fs.chmodSync(d, fs.statSync(s).mode);
+        } catch (err) {
+          // Ignore permission errors on platforms that don't support them
+        }
+      }

As per coding guidelines: "File permissions (e.g., fs.chmod, fs.access) should account for Windows not supporting Unix-style permission bits."

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@scripts/bundle-cli.js` at line 32, The call to fs.chmodSync(d,
fs.statSync(s).mode) is not Windows-safe; update the bundle logic in
scripts/bundle-cli.js to skip or gracefully handle chmod on Windows by checking
process.platform !== 'win32' (or wrapping the fs.chmodSync call in a try/catch
that ignores ENOSYS/EPERM on Windows), using the same fs.statSync(s).mode when
applicable; reference the existing fs.chmodSync and fs.statSync calls and
variables d and s when making the change.

Comment thread scripts/bundle-cli.sh
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants