Skip to content

Commit 86f3b10

Browse files
committed
fix: set NEXT_PUBLIC_CODEBUFF_APP_URL in worktree root .env.development.local
- CLI and SDK now hit local web server in worktrees instead of production - Added documentation about SDK E2E tests requiring local server - Added reminder in init-worktree completion message
1 parent d58fafd commit 86f3b10

File tree

2 files changed

+116
-15
lines changed

2 files changed

+116
-15
lines changed

knowledge.md

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -389,6 +389,30 @@ For git worktrees running on different ports:
389389
- Bun loads `.env.development.local` with highest precedence, so it overrides ports from `.env.local`
390390
- The `scripts/init-worktree.ts` script creates this file automatically
391391

392+
#### Worktree App URL Configuration
393+
394+
The `init-worktree.ts` script sets `NEXT_PUBLIC_CODEBUFF_APP_URL=http://localhost:${webPort}` in the root `.env.development.local`. This means:
395+
396+
- **CLI**: Will hit the local web server instead of production
397+
- **SDK**: Will also use the local web server for API calls
398+
- **SDK E2E Tests**: Require the local web server to be running
399+
400+
**Running SDK E2E tests in a worktree:**
401+
402+
```bash
403+
# First, start the web server in one terminal
404+
bun run --cwd web dev
405+
406+
# Then run SDK E2E tests in another terminal
407+
bun test sdk/e2e
408+
```
409+
410+
If you need to run SDK tests against production instead, override the environment variable:
411+
412+
```bash
413+
NEXT_PUBLIC_CODEBUFF_APP_URL=https://codebuff.com bun test sdk/e2e
414+
```
415+
392416
### Bun Wrapper Script (`.bin/bun`)
393417

394418
The wrapper's role is simple: ensure `.env.local` is synced from Infisical before running bun.

scripts/init-worktree.ts

Lines changed: 92 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -210,22 +210,24 @@ function createEnvDevelopmentLocalFile(
210210
worktreePath: string,
211211
args: WorktreeArgs,
212212
): void {
213-
// Root .env.development.local - only port overrides that apply globally
214-
const rootEnvContent = `# Worktree-specific port overrides
213+
// Root .env.development.local - worktree-specific overrides that apply globally (CLI, web, scripts, etc.)
214+
const rootEnvContent = `# Worktree-specific overrides
215215
# Generated by init-worktree.ts for worktree: ${args.name}
216216
# This file has highest precedence in Bun's .env loading order, overriding .env.local
217217
PORT=${args.webPort}
218218
NEXT_PUBLIC_WEB_PORT=${args.webPort}
219+
NEXT_PUBLIC_CODEBUFF_APP_URL=http://localhost:${args.webPort}
219220
`
220221

221222
writeFileSync(join(worktreePath, '.env.development.local'), rootEnvContent)
222-
console.log('Created .env.development.local with port configurations')
223+
console.log('Created .env.development.local with port and app URL configurations')
223224

224-
// Web-specific .env.development.local - URL override only affects web package
225-
// This prevents SDK integration tests from hitting localhost instead of production
225+
// Web-specific .env.development.local - keep an explicit copy for the web package
226+
// The root .env.development.local already ensures the CLI and other packages use the same app URL.
226227
const webEnvContent = `# Web-specific worktree overrides
227228
# Generated by init-worktree.ts for worktree: ${args.name}
228-
# This URL override only affects the web package, not SDK tests
229+
# This URL override is duplicated here for clarity; the root .env.development.local
230+
# already sets NEXT_PUBLIC_CODEBUFF_APP_URL for all packages.
229231
NEXT_PUBLIC_CODEBUFF_APP_URL=http://localhost:${args.webPort}
230232
`
231233

@@ -247,6 +249,82 @@ function copyInfisicalConfig(worktreePath: string): void {
247249
}
248250
}
249251

252+
async function syncInfisicalSecrets(worktreePath: string): Promise<boolean> {
253+
const envLocalPath = join(worktreePath, '.env.local')
254+
const infisicalJsonPath = join(worktreePath, '.infisical.json')
255+
256+
// Check if .infisical.json exists in worktree
257+
if (!existsSync(infisicalJsonPath)) {
258+
console.warn('Skipping Infisical sync: .infisical.json not found in worktree')
259+
return false
260+
}
261+
262+
console.log('Syncing secrets from Infisical to .env.local...')
263+
264+
return new Promise((resolve) => {
265+
const proc = spawn('infisical', ['export'], {
266+
cwd: worktreePath,
267+
stdio: 'pipe',
268+
shell: false,
269+
env: {
270+
...process.env,
271+
INFISICAL_DISABLE_UPDATE_CHECK: 'true',
272+
},
273+
})
274+
275+
let stdout = ''
276+
let stderr = ''
277+
278+
proc.stdout?.on('data', (data) => {
279+
stdout += data.toString()
280+
})
281+
282+
proc.stderr?.on('data', (data) => {
283+
stderr += data.toString()
284+
})
285+
286+
// 10 second timeout
287+
const timeout = setTimeout(() => {
288+
proc.kill()
289+
console.warn('Warning: Infisical sync timed out after 10 seconds')
290+
console.warn('You may need to run `infisical login` and try again')
291+
resolve(false)
292+
}, 10000)
293+
294+
proc.on('close', (code) => {
295+
clearTimeout(timeout)
296+
297+
if (code === 0 && stdout.length > 0) {
298+
writeFileSync(envLocalPath, stdout)
299+
console.log('Synced secrets from Infisical to .env.local')
300+
resolve(true)
301+
} else {
302+
if (stderr.includes('Select the environment') || stderr.includes('not logged in')) {
303+
console.warn('Warning: Infisical session expired or not logged in')
304+
console.warn('Please run `infisical login` in the worktree')
305+
} else if (code !== 0) {
306+
console.warn(`Warning: Infisical sync failed (exit code ${code})`)
307+
if (stderr) {
308+
console.warn(` ${stderr.trim()}`)
309+
}
310+
}
311+
resolve(false)
312+
}
313+
})
314+
315+
proc.on('error', (error) => {
316+
clearTimeout(timeout)
317+
if ((error as NodeJS.ErrnoException).code === 'ENOENT') {
318+
console.warn('Warning: infisical CLI not found, skipping secret sync')
319+
console.warn('Install it with: brew install infisical/get-cli/infisical')
320+
} else {
321+
console.warn(`Warning: Failed to run infisical: ${error.message}`)
322+
}
323+
resolve(false)
324+
})
325+
})
326+
}
327+
250328
// Wrapper script no longer needed - .bin/bun handles .env.worktree loading
251329
// function createWrapperScript(worktreePath: string): void {
252330
// // This function is deprecated - the .bin/bun wrapper now handles .env.worktree loading
@@ -324,7 +402,10 @@ async function main(): Promise<void> {
324402
// Create configuration files
325403
createEnvDevelopmentLocalFile(worktreePath, args)
326404
copyInfisicalConfig(worktreePath)
327-
// Note: Bun loads .env.development.local with highest precedence, overriding .env.local
405+
406+
// Sync secrets from Infisical to .env.local before running bun install
407+
// This ensures the .bin/bun wrapper doesn't complain about missing .env.local
408+
await syncInfisicalSecrets(worktreePath)
328409

329410
// Run direnv allow
330411
await runDirenvAllow(worktreePath)
@@ -333,18 +414,14 @@ async function main(): Promise<void> {
333414
console.log('Installing dependencies with bun...')
334415
await runCommand('bun', ['install'], worktreePath)
335416

336-
// Build web directory
337-
console.log('Building web directory...')
338-
await runCommand('bun', ['run', '--cwd', 'web', 'build'], worktreePath)
339-
340-
// Run typecheck
341-
console.log('Running typecheck...')
342-
await runCommand('bun', ['run', 'typecheck'], worktreePath)
343-
344417
console.log(`✅ Worktree '${args.name}' created and set up successfully!`)
345418
console.log(`📁 Location: ${worktreePath}`)
346419
console.log(`🚀 You can now cd into the worktree and start working:`)
347420
console.log(` cd ${worktreePath}`)
421+
console.log(``)
422+
console.log(`⚠️ Note: NEXT_PUBLIC_CODEBUFF_APP_URL is set to http://localhost:${args.webPort}`)
423+
console.log(` The CLI and SDK will hit your local web server.`)
424+
console.log(` To run SDK E2E tests, start the web server first: bun run --cwd web dev`)
348425
} catch (error) {
349426
if (error instanceof WorktreeError) {
350427
console.error(`Error: ${error.message}`)

0 commit comments

Comments
 (0)