Skip to content

Commit 63d1d85

Browse files
committed
chore: wip
1 parent a2e689d commit 63d1d85

File tree

7 files changed

+163
-73
lines changed

7 files changed

+163
-73
lines changed

packages/launchpad/bin/cli.ts

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1175,16 +1175,25 @@ cli
11751175
dryrun: options?.dryRun || false,
11761176
quiet: options?.quiet || false,
11771177
shellOutput: options?.shell || false,
1178+
skipGlobal: false, // Always enable global support in production CLI
11781179
})
11791180
}
11801181
catch (error) {
11811182
if (!options?.quiet && !options?.shell) {
11821183
console.error('Failed to set up dev environment:', error instanceof Error ? error.message : String(error))
11831184
}
11841185
else if (options?.shell) {
1185-
// For shell mode, output minimal fallback and don't exit with error
1186+
// For shell mode, output robust fallback that ensures basic system tools are available
11861187
// This prevents shell integration from hanging or failing
1187-
console.log('# Environment setup failed, using fallback')
1188+
console.log('# Environment setup failed, using system fallback')
1189+
console.log('# Ensure basic system paths are available')
1190+
console.log('for sys_path in /usr/local/bin /usr/bin /bin /usr/sbin /sbin; do')
1191+
console.log(' if [[ -d "$sys_path" && ":$PATH:" != *":$sys_path:"* ]]; then')
1192+
console.log(' export PATH="$PATH:$sys_path"')
1193+
console.log(' fi')
1194+
console.log('done')
1195+
console.log('# Clear command hash to ensure fresh lookups')
1196+
console.log('hash -r 2>/dev/null || true')
11881197
return
11891198
}
11901199
if (!options?.shell) {

packages/launchpad/src/bun.ts

Lines changed: 36 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -277,7 +277,15 @@ export async function install_bun(installPath: string, version?: string): Promis
277277

278278
// Create a temporary directory for the download/extraction
279279
const tempDir = path.join(installPath, 'temp')
280-
fs.mkdirSync(tempDir, { recursive: true })
280+
try {
281+
fs.mkdirSync(tempDir, { recursive: true })
282+
}
283+
catch (error) {
284+
if (config.verbose) {
285+
console.warn(`Failed to create temp directory ${tempDir}:`, error)
286+
}
287+
throw new Error(`Failed to create temporary directory for bun installation: ${error instanceof Error ? error.message : String(error)}`)
288+
}
281289

282290
let zipPath: string
283291

@@ -293,7 +301,15 @@ export async function install_bun(installPath: string, version?: string): Promis
293301

294302
// Copy cached file to temp directory for extraction
295303
zipPath = path.join(tempDir, filename)
296-
fs.copyFileSync(cachedArchivePath, zipPath)
304+
try {
305+
fs.copyFileSync(cachedArchivePath, zipPath)
306+
}
307+
catch (error) {
308+
if (config.verbose) {
309+
console.warn(`Failed to copy cached bun from ${cachedArchivePath} to ${zipPath}:`, error)
310+
}
311+
throw new Error(`Failed to copy cached bun file: ${error instanceof Error ? error.message : String(error)}`)
312+
}
297313
}
298314
else {
299315
// Download new version
@@ -365,7 +381,15 @@ export async function install_bun(installPath: string, version?: string): Promis
365381
offset += chunk.length
366382
}
367383

368-
fs.writeFileSync(zipPath, buffer)
384+
try {
385+
fs.writeFileSync(zipPath, buffer)
386+
}
387+
catch (error) {
388+
if (config.verbose) {
389+
console.warn(`Failed to write bun archive to ${zipPath}:`, error)
390+
}
391+
throw new Error(`Failed to write bun download: ${error instanceof Error ? error.message : String(error)}`)
392+
}
369393
}
370394
else if (config.verbose) {
371395
// Verbose mode - show size info like CLI upgrade
@@ -384,7 +408,15 @@ export async function install_bun(installPath: string, version?: string): Promis
384408
else {
385409
// Fallback: use arrayBuffer approach for compatibility
386410
const arrayBuffer = await response.arrayBuffer()
387-
fs.writeFileSync(zipPath, new Uint8Array(arrayBuffer))
411+
try {
412+
fs.writeFileSync(zipPath, new Uint8Array(arrayBuffer))
413+
}
414+
catch (error) {
415+
if (config.verbose) {
416+
console.warn(`Failed to write bun archive to ${zipPath}:`, error)
417+
}
418+
throw new Error(`Failed to write bun download: ${error instanceof Error ? error.message : String(error)}`)
419+
}
388420
}
389421

390422
if (config.verbose)

packages/launchpad/src/dev/dump.ts

Lines changed: 46 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -591,8 +591,9 @@ export async function dump(dir: string, options: DumpOptions = {}): Promise<void
591591

592592
const results: string[] = []
593593

594-
// Install global packages first to stable location (only if not skipping global)
595-
if (globalPackages.length > 0 && !globalReady && !skipGlobal) {
594+
// Always ensure global environment is activated first, even if already ready
595+
// This ensures global tools like bash are available for subsequent operations
596+
if (globalPackages.length > 0 && !skipGlobal) {
596597
const originalVerbose = config.verbose
597598
const originalShowShellMessages = config.showShellMessages
598599

@@ -605,15 +606,24 @@ export async function dump(dir: string, options: DumpOptions = {}): Promise<void
605606
}
606607

607608
try {
608-
// Install global packages to the stable global environment
609-
const globalResults = await install(globalPackages, globalEnvDir)
610-
results.push(...globalResults)
609+
// If global environment is already ready, just create/update stubs
610+
if (globalReady) {
611+
await createGlobalStubs(globalEnvDir, globalPackages)
612+
if (shellOutput) {
613+
process.stderr.write(`✅ Global dependencies ready\n`)
614+
}
615+
}
616+
else {
617+
// Install global packages to the stable global environment
618+
const globalResults = await install(globalPackages, globalEnvDir)
619+
results.push(...globalResults)
611620

612-
// Create or update global stubs in system locations (/usr/local/bin)
613-
await createGlobalStubs(globalEnvDir, globalPackages)
621+
// Create or update global stubs in system locations (/usr/local/bin)
622+
await createGlobalStubs(globalEnvDir, globalPackages)
614623

615-
if (shellOutput) {
616-
process.stderr.write(`✅ Global dependencies installed\n`)
624+
if (shellOutput) {
625+
process.stderr.write(`✅ Global dependencies installed\n`)
626+
}
617627
}
618628
}
619629
catch (error) {
@@ -679,7 +689,16 @@ export async function dump(dir: string, options: DumpOptions = {}): Promise<void
679689
}
680690
}
681691
catch (error) {
682-
process.stderr.write(`❌ Failed to install local packages: ${error instanceof Error ? error.message : String(error)}\n`)
692+
const errorMessage = error instanceof Error ? error.message : String(error)
693+
process.stderr.write(`❌ Failed to install local packages: ${errorMessage}\n`)
694+
695+
// Provide helpful guidance for common issues
696+
if (errorMessage.includes('bun')) {
697+
process.stderr.write(`💡 Tip: Install bun manually with: curl -fsSL https://bun.sh/install | bash\n`)
698+
}
699+
if (errorMessage.includes('ENOENT') || errorMessage.includes('permission')) {
700+
process.stderr.write(`💡 Check directory permissions and disk space\n`)
701+
}
683702
}
684703
finally {
685704
// Restore original stdout and console methods
@@ -952,27 +971,35 @@ function outputShellCode(dir: string, envBinPath: string, envSbinPath: string, p
952971
// Build PATH with both project and global environments
953972
const pathComponents = []
954973

955-
// Add project-specific paths first (highest priority)
956-
if (fs.existsSync(envBinPath)) {
957-
pathComponents.push(envBinPath)
958-
}
959-
if (fs.existsSync(envSbinPath)) {
960-
pathComponents.push(envSbinPath)
961-
}
962-
963-
// Add global paths if they exist
974+
// Add global paths first to ensure critical tools like bash are always available
964975
if (globalBinPath && fs.existsSync(globalBinPath)) {
965976
pathComponents.push(globalBinPath)
966977
}
967978
if (globalSbinPath && fs.existsSync(globalSbinPath)) {
968979
pathComponents.push(globalSbinPath)
969980
}
970981

982+
// Add project-specific paths second (can override global if needed)
983+
if (fs.existsSync(envBinPath)) {
984+
pathComponents.push(envBinPath)
985+
}
986+
if (fs.existsSync(envSbinPath)) {
987+
pathComponents.push(envSbinPath)
988+
}
989+
971990
// Add original PATH
972991
pathComponents.push('$LAUNCHPAD_ORIGINAL_PATH')
973992

974993
process.stdout.write(`export PATH="${pathComponents.join(':')}"\n`)
975994

995+
// Ensure system paths are always available (fix for missing bash, etc.)
996+
process.stdout.write(`# Ensure critical system binaries are always available\n`)
997+
process.stdout.write(`for sys_path in /usr/local/bin /usr/bin /bin /usr/sbin /sbin; do\n`)
998+
process.stdout.write(` if [[ -d "$sys_path" && ":$PATH:" != *":$sys_path:"* ]]; then\n`)
999+
process.stdout.write(` export PATH="$PATH:$sys_path"\n`)
1000+
process.stdout.write(` fi\n`)
1001+
process.stdout.write(`done\n`)
1002+
9761003
// Set up dynamic library paths for packages to find their dependencies
9771004
const libraryPathComponents = []
9781005

packages/launchpad/src/dev/shellcode.ts

Lines changed: 30 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -188,7 +188,6 @@ __launchpad_setup_global_deps() {
188188
# Ensure global dependencies are always in PATH
189189
__launchpad_ensure_global_path() {
190190
local global_env_dir="$HOME/.local/share/launchpad/global"
191-
local system_bin_dir="/usr/local/bin"
192191
193192
# Add global environment to PATH if it exists
194193
if [[ -d "$global_env_dir/bin" ]]; then
@@ -198,10 +197,8 @@ __launchpad_ensure_global_path() {
198197
__launchpad_update_path "$global_env_dir/sbin"
199198
fi
200199
201-
# Also ensure system bin directory is in PATH for global stubs
202-
if [[ -d "$system_bin_dir" ]]; then
203-
__launchpad_update_path "$system_bin_dir"
204-
fi
200+
# Always ensure critical system paths are available
201+
__launchpad_ensure_system_path
205202
}
206203
207204
__launchpad_find_deps_file() {
@@ -424,12 +421,36 @@ elif [[ -n "$BASH_VERSION" ]]; then
424421
fi
425422
fi
426423
427-
# Initialize LAUNCHPAD_ORIGINAL_PATH if not set and PATH looks corrupted
428-
if [[ -z "$LAUNCHPAD_ORIGINAL_PATH" && ! "$PATH" =~ "/usr/local/bin" ]]; then
429-
export LAUNCHPAD_ORIGINAL_PATH="/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin"
430-
export PATH="$LAUNCHPAD_ORIGINAL_PATH"
424+
# Initialize LAUNCHPAD_ORIGINAL_PATH if not set and ensure basic system paths are always available
425+
if [[ -z "$LAUNCHPAD_ORIGINAL_PATH" ]]; then
426+
# Store the current PATH as original if it looks valid
427+
if [[ "$PATH" =~ "/usr/bin" && "$PATH" =~ "/bin" ]]; then
428+
export LAUNCHPAD_ORIGINAL_PATH="$PATH"
429+
else
430+
# PATH looks corrupted, use standard system paths
431+
export LAUNCHPAD_ORIGINAL_PATH="/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin"
432+
export PATH="$LAUNCHPAD_ORIGINAL_PATH"
433+
fi
431434
fi
432435
436+
# Ensure critical system paths are always in PATH (for basic commands like bash, grep, etc.)
437+
__launchpad_ensure_system_path() {
438+
local system_paths="/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin"
439+
440+
# Use POSIX-compatible loop instead of bash arrays
441+
local OLD_IFS="$IFS"
442+
IFS=':'
443+
for sys_dir in $system_paths; do
444+
if [[ -d "$sys_dir" && ":$PATH:" != *":$sys_dir:"* ]]; then
445+
export PATH="$PATH:$sys_dir"
446+
fi
447+
done
448+
IFS="$OLD_IFS"
449+
}
450+
451+
# Always ensure system paths are available
452+
__launchpad_ensure_system_path
453+
433454
# Call global setup functions on load
434455
__launchpad_setup_global_deps
435456
__launchpad_ensure_global_path

packages/launchpad/src/install.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2202,8 +2202,8 @@ async function createShims(packageDir: string, installPath: string, domain: stri
22022202
// Add library paths from all installed packages in the environment
22032203
try {
22042204
const domains = fs.readdirSync(installPath, { withFileTypes: true })
2205-
.filter(dirent => dirent.isDirectory() &&
2206-
!['bin', 'sbin', 'lib', 'lib64', 'share', 'include', 'etc', 'pkgs', '.tmp', '.cache'].includes(dirent.name))
2205+
.filter(dirent => dirent.isDirectory()
2206+
&& !['bin', 'sbin', 'lib', 'lib64', 'share', 'include', 'etc', 'pkgs', '.tmp', '.cache'].includes(dirent.name))
22072207

22082208
for (const domainEntry of domains) {
22092209
const domainPath = path.join(installPath, domainEntry.name)

packages/launchpad/test/library-paths-unit.test.ts

Lines changed: 25 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -47,9 +47,10 @@ describe('Library Path Unit Tests', () => {
4747
path.join(tempDir, 'project'),
4848
path.join(envDir, 'bin'),
4949
path.join(envDir, 'sbin'),
50-
'test-hash'
50+
'test-hash',
5151
)
52-
} finally {
52+
}
53+
finally {
5354
process.stdout.write = originalStdout
5455
}
5556

@@ -82,9 +83,10 @@ describe('Library Path Unit Tests', () => {
8283
path.join(tempDir, 'project'),
8384
path.join(envDir, 'bin'),
8485
path.join(envDir, 'sbin'),
85-
'test-hash'
86+
'test-hash',
8687
)
87-
} finally {
88+
}
89+
finally {
8890
process.stdout.write = originalStdout
8991
}
9092

@@ -112,9 +114,10 @@ describe('Library Path Unit Tests', () => {
112114
path.join(tempDir, 'project'),
113115
path.join(envDir, 'bin'),
114116
path.join(envDir, 'sbin'),
115-
'test-hash'
117+
'test-hash',
116118
)
117-
} finally {
119+
}
120+
finally {
118121
process.stdout.write = originalStdout
119122
}
120123

@@ -146,9 +149,10 @@ describe('Library Path Unit Tests', () => {
146149
path.join(tempDir, 'project'),
147150
path.join(envDir, 'bin'),
148151
path.join(envDir, 'sbin'),
149-
'test-hash'
152+
'test-hash',
150153
)
151-
} finally {
154+
}
155+
finally {
152156
process.stdout.write = originalStdout
153157
}
154158

@@ -224,9 +228,10 @@ describe('Library Path Unit Tests', () => {
224228
path.join(tempDir, 'project'),
225229
path.join(emptyEnvDir, 'bin'),
226230
path.join(emptyEnvDir, 'sbin'),
227-
'test-hash'
231+
'test-hash',
228232
)
229-
} finally {
233+
}
234+
finally {
230235
process.stdout.write = originalStdout
231236
}
232237

@@ -263,9 +268,10 @@ describe('Library Path Unit Tests', () => {
263268
path.join(tempDir, 'project'),
264269
path.join(envDir, 'bin'),
265270
path.join(envDir, 'sbin'),
266-
'test-hash'
271+
'test-hash',
267272
)
268-
} finally {
273+
}
274+
finally {
269275
process.stdout.write = originalStdout
270276
}
271277

@@ -303,9 +309,10 @@ describe('Library Path Unit Tests', () => {
303309
path.join(tempDir, 'project'),
304310
path.join(envDir, 'bin'),
305311
path.join(envDir, 'sbin'),
306-
'test-hash'
312+
'test-hash',
307313
)
308-
} finally {
314+
}
315+
finally {
309316
process.stdout.write = originalStdout
310317
}
311318

@@ -342,14 +349,15 @@ describe('Library Path Unit Tests', () => {
342349
path.join(tempDir, 'project'),
343350
path.join(envDir, 'bin'),
344351
path.join(envDir, 'sbin'),
345-
'test-hash'
352+
'test-hash',
346353
)
347-
} finally {
354+
}
355+
finally {
348356
process.stdout.write = originalStdout
349357
}
350358

351359
// Basic syntax validation
352-
expect(shellOutput).toMatch(/^[^]*$/) // Should not contain null bytes
360+
expect(shellOutput).toMatch(/^[\s\S]*$/) // Should not contain null bytes
353361
expect(shellOutput).not.toContain('undefined')
354362
expect(shellOutput).not.toContain('[object Object]')
355363

0 commit comments

Comments
 (0)