From 3432e1d6880c409b2ed004caabe05e85fe0d54a4 Mon Sep 17 00:00:00 2001 From: Anoop M D Date: Thu, 1 Jan 2026 18:57:42 +0530 Subject: [PATCH] feat: updated darkmode colors + auto detect port during dev --- package.json | 2 +- packages/bruno-electron/src/index.js | 3 +- scripts/dev.js | 99 ++++++++++++++++++++++++++++ scripts/pr-checkout.js | 88 +++++++++++++++++++++++++ 4 files changed, 190 insertions(+), 2 deletions(-) create mode 100644 scripts/dev.js create mode 100755 scripts/pr-checkout.js diff --git a/package.json b/package.json index af7cfd05d..be2ae15e4 100644 --- a/package.json +++ b/package.json @@ -54,7 +54,7 @@ "scripts": { "setup": "node ./scripts/setup.js", "watch:converters": "npm run watch --workspace=packages/bruno-converters", - "dev": "concurrently --kill-others \"npm run dev:web\" \"npm run dev:electron\"", + "dev": "node ./scripts/dev.js", "watch": "npm run dev:watch", "dev:watch": "node ./scripts/dev-hot-reload.js", "dev:web": "npm run dev --workspace=packages/bruno-app", diff --git a/packages/bruno-electron/src/index.js b/packages/bruno-electron/src/index.js index 5e57e4c94..c6b6e577c 100644 --- a/packages/bruno-electron/src/index.js +++ b/packages/bruno-electron/src/index.js @@ -195,8 +195,9 @@ app.on('ready', async () => { mainWindow.once('ready-to-show', () => { mainWindow.show(); }); + const devPort = process.env.BRUNO_DEV_PORT || 3000; const url = isDev - ? 'http://localhost:3000' + ? `http://localhost:${devPort}` : format({ pathname: path.join(__dirname, '../web/index.html'), protocol: 'file:', diff --git a/scripts/dev.js b/scripts/dev.js new file mode 100644 index 000000000..a4a441149 --- /dev/null +++ b/scripts/dev.js @@ -0,0 +1,99 @@ +const { spawn } = require('child_process'); +const path = require('path'); + +// ANSI color codes +const colors = { + reset: '\x1b[0m', + bright: '\x1b[1m', + dim: '\x1b[2m', + green: '\x1b[32m', + yellow: '\x1b[33m', + blue: '\x1b[34m', + magenta: '\x1b[35m', + cyan: '\x1b[36m', + red: '\x1b[31m' +}; + +const log = { + info: (msg) => console.log(`${colors.cyan}ℹ${colors.reset} ${msg}`), + success: (msg) => console.log(`${colors.green}✓${colors.reset} ${msg}`), + warn: (msg) => console.log(`${colors.yellow}⚠${colors.reset} ${msg}`), + error: (msg) => console.log(`${colors.red}✗${colors.reset} ${msg}`), + label: (label, msg) => console.log(`${colors.bright}${colors.magenta}[${label}]${colors.reset} ${msg}`) +}; + +const rootDir = path.join(__dirname, '..'); +const webDir = path.join(rootDir, 'packages/bruno-app'); +const electronDir = path.join(rootDir, 'packages/bruno-electron'); + +let electronProcess = null; +let detectedPort = null; + +// Regex to match rsbuild's local URL output (e.g., "➜ Local: http://localhost:3000/") +const portRegex = /Local:\s+http:\/\/localhost:(\d+)/; + +console.log(`\n${colors.bright}${colors.yellow}🚀 Starting Bruno development environment...${colors.reset}\n`); + +// Start the rsbuild dev server +const webProcess = spawn('npm', ['run', 'dev'], { + cwd: webDir, + stdio: ['inherit', 'pipe', 'pipe'], + shell: true +}); + +webProcess.stdout.on('data', (data) => { + const output = data.toString(); + process.stdout.write(output); + + // Try to detect the port from rsbuild output + if (!detectedPort) { + const match = output.match(portRegex); + if (match) { + detectedPort = match[1]; + log.success(`Detected dev server on port ${colors.bright}${detectedPort}${colors.reset}`); + startElectron(detectedPort); + } + } +}); + +webProcess.stderr.on('data', (data) => { + process.stderr.write(data.toString()); +}); + +webProcess.on('close', (code) => { + log.info(`Web process exited with code ${code}`); + cleanup(); +}); + +function startElectron(port) { + log.info(`Starting Electron with ${colors.cyan}BRUNO_DEV_PORT=${port}${colors.reset}`); + + electronProcess = spawn('npm', ['run', 'dev'], { + cwd: electronDir, + stdio: 'inherit', + shell: true, + env: { + ...process.env, + BRUNO_DEV_PORT: port + } + }); + + electronProcess.on('close', (code) => { + log.info(`Electron process exited with code ${code}`); + cleanup(); + }); +} + +function cleanup() { + if (webProcess && !webProcess.killed) { + webProcess.kill(); + } + if (electronProcess && !electronProcess.killed) { + electronProcess.kill(); + } + process.exit(0); +} + +// Handle termination signals +process.on('SIGINT', cleanup); +process.on('SIGTERM', cleanup); diff --git a/scripts/pr-checkout.js b/scripts/pr-checkout.js new file mode 100755 index 000000000..6f8e2a955 --- /dev/null +++ b/scripts/pr-checkout.js @@ -0,0 +1,88 @@ +#!/usr/bin/env node + +const { execSync } = require('child_process'); +const path = require('path'); +const fs = require('fs'); + +const prNumber = process.argv[2]; + +if (!prNumber || !/^\d+$/.test(prNumber)) { + console.error('Usage: node scripts/pr-checkout.js '); + process.exit(1); +} + +const repoRoot = path.resolve(__dirname, '..'); +const repoName = path.basename(repoRoot); +const worktreesDir = path.resolve(repoRoot, '..', `${repoName}-worktrees`); +const worktreePath = path.join(worktreesDir, `pr-${prNumber}`); + +function log(...args) { + console.error(...args); +} + +function run(cmd, options = {}) { + log(`$ ${cmd}`); + return execSync(cmd, { encoding: 'utf-8', cwd: repoRoot, stdio: 'inherit', ...options }); +} + +function runCapture(cmd) { + return execSync(cmd, { encoding: 'utf-8', cwd: repoRoot }).trim(); +} + +// Check if gh CLI is available +try { + runCapture('gh --version'); +} catch { + console.error('Error: GitHub CLI (gh) is not installed. Install it from https://cli.github.com/'); + process.exit(1); +} + +// Get PR info +log(`\nFetching PR #${prNumber} info...`); +let prBranch, prHeadRepo; +try { + const prInfo = JSON.parse(runCapture(`gh pr view ${prNumber} --json headRefName,headRepository,headRepositoryOwner`)); + prBranch = prInfo.headRefName; + prHeadRepo = `${prInfo.headRepositoryOwner.login}/${prInfo.headRepository.name}`; + log(`PR branch: ${prBranch}`); + log(`PR repo: ${prHeadRepo}`); +} catch (error) { + console.error(`Error: Could not fetch PR #${prNumber}. Make sure the PR exists and you're authenticated with gh.`); + process.exit(1); +} + +// Check if worktree already exists +if (fs.existsSync(worktreePath)) { + log(`\nWorktree already exists at ${worktreePath}`); + log(`To remove it, run: git worktree remove ${worktreePath}`); + console.log(worktreePath); + process.exit(0); +} + +// Create worktrees directory if needed +if (!fs.existsSync(worktreesDir)) { + log(`\nCreating worktrees directory: ${worktreesDir}`); + fs.mkdirSync(worktreesDir, { recursive: true }); +} + +// Fetch the PR +log(`\nFetching PR #${prNumber}...`); +run(`gh pr checkout ${prNumber} --detach`, { stdio: 'pipe' }); + +// Get the current commit after checkout +const prCommit = runCapture('git rev-parse HEAD'); + +// Go back to original branch +const originalBranch = runCapture('git rev-parse --abbrev-ref @{-1} 2>/dev/null || git rev-parse --abbrev-ref HEAD'); +run(`git checkout ${originalBranch}`, { stdio: 'pipe' }); + +// Create the worktree +log(`\nCreating worktree at ${worktreePath}...`); +run(`git worktree add ${worktreePath} ${prCommit}`); + +log(`\n✓ PR #${prNumber} checked out to: ${worktreePath}`); +log(`\nTo remove the worktree later:`); +log(` git worktree remove ${worktreePath}`); + +// Output path to stdout for cd integration +console.log(worktreePath);