Skip to content

Commit ec57a27

Browse files
committed
chore: wip
1 parent afffc8a commit ec57a27

File tree

4 files changed

+112
-81
lines changed

4 files changed

+112
-81
lines changed

packages/launchpad/src/dev/dump.ts

Lines changed: 69 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import fs from 'node:fs'
44
import { homedir } from 'node:os'
55
import path from 'node:path'
66
import process from 'node:process'
7-
import { parse } from 'yaml'
87
import { config } from '../config'
98
import { findDependencyFile } from '../env'
109
import { cleanupSpinner, install } from '../install'
@@ -426,21 +425,21 @@ export async function dump(dir: string, options: DumpOptions = {}): Promise<void
426425
// Fast path for shell output: check if environments exist and have binaries
427426
// Skip fast path in test mode to ensure proper package discovery
428427
if (shellOutput && process.env.NODE_ENV !== 'test') {
429-
const fastProjectHash = generateProjectHash(dir)
430-
const fastEnvDir = path.join(process.env.HOME || '', '.local', 'share', 'launchpad', fastProjectHash)
431-
const fastGlobalEnvDir = path.join(process.env.HOME || '', '.local', 'share', 'launchpad', 'global')
432-
433-
const envBinPath = path.join(fastEnvDir, 'bin')
434-
const envSbinPath = path.join(fastEnvDir, 'sbin')
435-
const globalBinPath = path.join(fastGlobalEnvDir, 'bin')
436-
const globalSbinPath = path.join(fastGlobalEnvDir, 'sbin')
437-
438-
const hasLocalBinaries = fs.existsSync(envBinPath) && fs.readdirSync(envBinPath).length > 0
439-
const hasGlobalBinaries = fs.existsSync(globalBinPath) && fs.readdirSync(globalBinPath).length > 0
440-
441428
// Fast path disabled - always do proper constraint checking to ensure correct versions
442429
// The fast path was causing issues where global binaries would activate environments
443430
// even when local packages weren't properly installed
431+
//
432+
// const fastProjectHash = generateProjectHash(dir)
433+
// const fastEnvDir = path.join(process.env.HOME || '', '.local', 'share', 'launchpad', fastProjectHash)
434+
// const fastGlobalEnvDir = path.join(process.env.HOME || '', '.local', 'share', 'launchpad', 'global')
435+
// const envBinPath = path.join(fastEnvDir, 'bin')
436+
// const envSbinPath = path.join(fastEnvDir, 'sbin')
437+
// const globalBinPath = path.join(fastGlobalEnvDir, 'bin')
438+
// const globalSbinPath = path.join(fastGlobalEnvDir, 'sbin')
439+
//
440+
// const hasLocalBinaries = fs.existsSync(envBinPath) && fs.readdirSync(envBinPath).length > 0
441+
// const hasGlobalBinaries = fs.existsSync(globalBinPath) && fs.readdirSync(globalBinPath).length > 0
442+
//
444443
// if (hasLocalBinaries || hasGlobalBinaries) {
445444
// outputShellCode(dir, envBinPath, envSbinPath, fastProjectHash, minimalSniffResult, globalBinPath, globalSbinPath)
446445
// return
@@ -683,7 +682,7 @@ export async function dump(dir: string, options: DumpOptions = {}): Promise<void
683682
return
684683
}
685684

686-
// For shell output mode, handle different scenarios
685+
// For shell output mode, handle different scenarios
687686
if (shellOutput) {
688687
const hasLocalPackagesInstalled = localReady || localPackages.length === 0
689688
const hasGlobalPackagesInstalled = globalReady || globalPackages.length === 0
@@ -698,35 +697,42 @@ export async function dump(dir: string, options: DumpOptions = {}): Promise<void
698697
const hasOptionalGlobalMissing = globalReadyResult.missingPackages && globalReadyResult.missingPackages.length > 0
699698
const coreGlobalSatisfied = globalConstraintsSatisfied || (hasOptionalGlobalMissing && globalPackages.length > 0)
700699

700+
// Check if we're being called from shell integration
701+
const isShellIntegration = process.env.LAUNCHPAD_SHELL_INTEGRATION === '1'
702+
701703
if (hasLocalPackagesInstalled && hasGlobalPackagesInstalled) {
702704
// Ideal case: all packages properly installed
703705
outputShellCode(dir, envBinPath, envSbinPath, projectHash, sniffResult, globalBinPath, globalSbinPath)
704706
return
705707
}
706-
else if (coreLocalSatisfied && coreGlobalSatisfied) {
707-
// Fallback case: core constraints satisfied by system binaries, but warn user
708-
if (!hasLocalPackagesInstalled && localPackages.length > 0) {
709-
process.stderr.write(`⚠️ Local packages not installed but constraints satisfied by system binaries\n`)
710-
process.stderr.write(`💡 Run 'launchpad dev .' to install proper versions: ${localPackages.join(', ')}\n`)
711-
}
712-
if (!hasGlobalPackagesInstalled && hasOptionalGlobalMissing) {
713-
const missingGlobalPkgs = globalReadyResult.missingPackages?.map(p => p.project) || []
714-
process.stderr.write(`⚠️ Some global packages not available: ${missingGlobalPkgs.join(', ')}\n`)
715-
process.stderr.write(`💡 Install missing global packages if needed\n`)
716-
}
717-
outputShellCode(dir, envBinPath, envSbinPath, projectHash, sniffResult, globalBinPath, globalSbinPath)
718-
return
719-
}
708+
else if (coreLocalSatisfied && coreGlobalSatisfied) {
709+
// Fallback case: core constraints satisfied by system binaries, but warn user
710+
if (!isShellIntegration) {
711+
if (!hasLocalPackagesInstalled && localPackages.length > 0) {
712+
process.stderr.write(`⚠️ Local packages not installed but constraints satisfied by system binaries\n`)
713+
process.stderr.write(`💡 Run 'launchpad dev .' to install proper versions: ${localPackages.join(', ')}\n`)
714+
}
715+
if (!hasGlobalPackagesInstalled && hasOptionalGlobalMissing) {
716+
const missingGlobalPkgs = globalReadyResult.missingPackages?.map(p => p.project) || []
717+
process.stderr.write(`⚠️ Some global packages not available: ${missingGlobalPkgs.join(', ')}\n`)
718+
process.stderr.write(`💡 Install missing global packages if needed\n`)
719+
}
720+
}
721+
outputShellCode(dir, envBinPath, envSbinPath, projectHash, sniffResult, globalBinPath, globalSbinPath)
722+
return
723+
}
720724
else {
721725
// No fallback available - but still generate shell code for development workflows
722-
process.stderr.write(`❌ Environment not ready: local=${localReady}, global=${globalReady}\n`)
723-
if (!localReady && localPackages.length > 0) {
724-
process.stderr.write(`💡 Local packages need installation: ${localPackages.join(', ')}\n`)
725-
}
726-
if (!globalReady && globalPackages.length > 0) {
727-
process.stderr.write(`💡 Global packages need installation: ${globalPackages.join(', ')}\n`)
726+
if (!isShellIntegration) {
727+
process.stderr.write(`❌ Environment not ready: local=${localReady}, global=${globalReady}\n`)
728+
if (!localReady && localPackages.length > 0) {
729+
process.stderr.write(`💡 Local packages need installation: ${localPackages.join(', ')}\n`)
730+
}
731+
if (!globalReady && globalPackages.length > 0) {
732+
process.stderr.write(`💡 Global packages need installation: ${globalPackages.join(', ')}\n`)
733+
}
734+
process.stderr.write(`⚠️ Generating minimal shell environment for development\n`)
728735
}
729-
process.stderr.write(`⚠️ Generating minimal shell environment for development\n`)
730736

731737
// Generate basic shell code even when packages aren't installed
732738
// This allows development workflows to continue with system binaries
@@ -743,20 +749,21 @@ export async function dump(dir: string, options: DumpOptions = {}): Promise<void
743749
if (globalPackages.length > 0 && !skipGlobal) {
744750
const originalVerbose = config.verbose
745751
const originalShowShellMessages = config.showShellMessages
752+
const isShellIntegration = process.env.LAUNCHPAD_SHELL_INTEGRATION === '1'
746753

747-
if (shellOutput) {
754+
if (shellOutput && !isShellIntegration) {
748755
config.showShellMessages = false
749756
process.stderr.write(`🌍 Installing global dependencies...\n`)
750757
}
751-
else if (!quiet) {
758+
else if (!quiet && !isShellIntegration) {
752759
console.log(`🌍 Installing ${globalPackages.length} global packages...`)
753760
}
754761

755762
try {
756763
// If global environment is already ready, just create/update stubs
757764
if (globalReady) {
758765
await createGlobalStubs(globalEnvDir, globalPackages)
759-
if (shellOutput) {
766+
if (shellOutput && !isShellIntegration) {
760767
process.stderr.write(`✅ Global dependencies ready\n`)
761768
}
762769
}
@@ -768,13 +775,13 @@ export async function dump(dir: string, options: DumpOptions = {}): Promise<void
768775
// Create or update global stubs in system locations (/usr/local/bin)
769776
await createGlobalStubs(globalEnvDir, globalPackages)
770777

771-
if (shellOutput) {
778+
if (shellOutput && !isShellIntegration) {
772779
process.stderr.write(`✅ Global dependencies installed\n`)
773780
}
774781
}
775782
}
776783
catch (error) {
777-
if (shellOutput) {
784+
if (shellOutput) {
778785
process.stderr.write(`❌ Failed to install global packages: ${error instanceof Error ? error.message : String(error)}\n`)
779786

780787
// Don't mislead users about system binary usage
@@ -800,8 +807,9 @@ export async function dump(dir: string, options: DumpOptions = {}): Promise<void
800807
if (localPackages.length > 0 && !localReady) {
801808
const originalVerbose = config.verbose
802809
const originalShowShellMessages = config.showShellMessages
810+
const isShellIntegration = process.env.LAUNCHPAD_SHELL_INTEGRATION === '1'
803811

804-
if (shellOutput) {
812+
if (shellOutput && !isShellIntegration) {
805813
config.showShellMessages = false
806814
const projectName = path.basename(dir)
807815
const startTime = Date.now()
@@ -896,22 +904,28 @@ export async function dump(dir: string, options: DumpOptions = {}): Promise<void
896904
// Determine environment state for better messaging
897905
const hasInstallationFailures = localInstallationFailed || globalInstallationFailed
898906
const hasRequiredPackages = localPackages.length > 0 || globalPackages.length > 0
899-
const systemBinariesSatisfyConstraints = (localReadyResult.missingPackages?.length === 0) &&
900-
(globalReadyResult.missingPackages?.length === 0)
901-
902-
if (!hasInstallationFailures && hasRequiredPackages) {
907+
const systemBinariesSatisfyConstraints = (localReadyResult.missingPackages?.length === 0)
908+
&& (globalReadyResult.missingPackages?.length === 0)
909+
910+
if (hasInstallationFailures) {
911+
// Handle installation failures first
912+
if (systemBinariesSatisfyConstraints) {
913+
// Fallback case: installations failed but system binaries work
914+
process.stderr.write(`⚠️ Environment activated with system binaries (installations failed)\n`)
915+
process.stderr.write(`💡 Some packages may not be the exact requested versions\n`)
916+
}
917+
else {
918+
// Bad case: installations failed and system doesn't satisfy requirements
919+
process.stderr.write(`❌ Environment activation failed - required packages unavailable\n`)
920+
process.stderr.write(`💡 Fix installation issues and try again\n`)
921+
// Still generate basic shell code for development workflows with global dependencies
922+
}
923+
}
924+
else if (hasRequiredPackages) {
903925
// Perfect case: all packages installed successfully
904926
process.stderr.write(`✅ Environment activated for ${path.basename(dir)}\n`)
905-
} else if (hasInstallationFailures && systemBinariesSatisfyConstraints) {
906-
// Fallback case: installations failed but system binaries work
907-
process.stderr.write(`⚠️ Environment activated with system binaries (installations failed)\n`)
908-
process.stderr.write(`💡 Some packages may not be the exact requested versions\n`)
909-
} else if (hasInstallationFailures) {
910-
// Bad case: installations failed and system doesn't satisfy requirements
911-
process.stderr.write(`❌ Environment activation failed - required packages unavailable\n`)
912-
process.stderr.write(`💡 Fix installation issues and try again\n`)
913-
return // Don't generate shell code if critical packages are missing
914-
} else {
927+
}
928+
else {
915929
// No packages needed or already satisfied
916930
process.stderr.write(`✅ Environment ready for ${path.basename(dir)}\n`)
917931
}

packages/launchpad/src/dev/shellcode.ts

Lines changed: 33 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ function getLaunchpadBinary(): string {
6262
export function shellcode(): string {
6363
// Use the same launchpad binary that's currently running
6464
const launchpadBinary = getLaunchpadBinary()
65-
const grepFilter = '/usr/bin/grep -E \'^(export|if|fi|#)\' 2>/dev/null'
65+
const grepFilter = '/usr/bin/grep -v \'^$\' 2>/dev/null'
6666

6767
return `
6868
# Launchpad shell integration with progress indicators
@@ -113,7 +113,8 @@ __launchpad_update_library_paths() {
113113
if [[ -d "$env_dir" ]]; then
114114
for domain_dir in "$env_dir"/*; do
115115
if [[ -d "$domain_dir" && "$(basename "$domain_dir")" != "bin" && "$(basename "$domain_dir")" != "sbin" && "$(basename "$domain_dir")" != "lib" && "$(basename "$domain_dir")" != "lib64" && "$(basename "$domain_dir")" != "share" && "$(basename "$domain_dir")" != "include" && "$(basename "$domain_dir")" != "etc" && "$(basename "$domain_dir")" != "pkgs" && "$(basename "$domain_dir")" != ".tmp" && "$(basename "$domain_dir")" != ".cache" ]]; then
116-
for version_dir in "$domain_dir"/v*; do
116+
# Use find to avoid glob expansion issues
117+
while IFS= read -r -d '' version_dir; do
117118
if [[ -d "$version_dir" ]]; then
118119
for lib_dir in "$version_dir/lib" "$version_dir/lib64"; do
119120
if [[ -d "$lib_dir" ]]; then
@@ -128,7 +129,7 @@ __launchpad_update_library_paths() {
128129
fi
129130
done
130131
fi
131-
done
132+
done < <(find "$domain_dir" -maxdepth 1 -name "v*" -type d -print0 2>/dev/null)
132133
fi
133134
done
134135
fi
@@ -255,6 +256,24 @@ __launchpad_chpwd() {
255256
return 0
256257
fi
257258
259+
# Fast path: Check if environment is already ready
260+
local project_hash
261+
project_hash=$(echo -n "$project_dir" | sha256sum 2>/dev/null | cut -d' ' -f1 | cut -c1-8) || project_hash="default"
262+
local env_dir="$HOME/.local/share/launchpad/launchpad_$project_hash"
263+
264+
# If environment exists and has binaries, activate quickly
265+
if [[ -d "$env_dir/bin" && -n "$(ls -A "$env_dir/bin" 2>/dev/null)" ]]; then
266+
export PATH="$env_dir/bin:$LAUNCHPAD_ORIGINAL_PATH"
267+
__launchpad_update_library_paths "$env_dir"
268+
__launchpad_ensure_global_path
269+
hash -r 2>/dev/null || true
270+
271+
if [[ "\$\{LAUNCHPAD_SHOW_ENV_MESSAGES:-true\}" != "false" ]]; then
272+
printf "✅ Environment activated for \\033[3m$(basename "$project_dir")\\033[0m\\n" >&2
273+
fi
274+
return 0
275+
fi
276+
258277
# Skip setup if we've had too many timeouts recently
259278
if [[ $__launchpad_timeout_count -gt 3 ]]; then
260279
if [[ "\$\{LAUNCHPAD_SHOW_ENV_MESSAGES:-true\}" != "false" ]]; then
@@ -274,14 +293,14 @@ __launchpad_chpwd() {
274293
# Ensure global dependencies are available first
275294
__launchpad_setup_global_deps
276295
277-
# Allow stderr to show progress in real-time while capturing stdout for shell evaluation
296+
# Capture both stdout and stderr for clean output management
278297
{
279-
# Create a temp file for stdout only
298+
# Create temp files for stdout and stderr
280299
local temp_file=$(mktemp)
300+
local temp_stderr=$(mktemp)
281301
282-
# Run setup command: stdout goes to temp file, stderr passes through for progress display
283-
# Only capture stdout, let stderr show progress indicators directly
284-
if LAUNCHPAD_SHELL_INTEGRATION=1 LAUNCHPAD_ORIGINAL_PATH="$LAUNCHPAD_ORIGINAL_PATH" ${launchpadBinary} dev "$project_dir" --shell > "$temp_file"; then
302+
# Run setup command silently, capturing both streams
303+
if LAUNCHPAD_SHELL_INTEGRATION=1 LAUNCHPAD_ORIGINAL_PATH="$LAUNCHPAD_ORIGINAL_PATH" ${launchpadBinary} dev "$project_dir" --shell > "$temp_file" 2> "$temp_stderr"; then
285304
# Extract shell code from stdout for evaluation
286305
if [[ -s "$temp_file" ]]; then
287306
env_output=$(cat "$temp_file" | ${grepFilter})
@@ -293,7 +312,7 @@ __launchpad_chpwd() {
293312
else
294313
setup_exit_code=$?
295314
fi
296-
rm -f "$temp_file" 2>/dev/null || true
315+
rm -f "$temp_file" "$temp_stderr" 2>/dev/null || true
297316
}
298317
299318
# Clear the in-progress flag
@@ -321,9 +340,10 @@ __launchpad_chpwd() {
321340
# Clear command hash table to ensure commands are found in new PATH
322341
hash -r 2>/dev/null || true
323342
324-
# Always show activation message for successful project entry
343+
# Show clean activation message that replaces any previous output
325344
if [[ "\$\{LAUNCHPAD_SHOW_ENV_MESSAGES:-true\}" != "false" ]]; then
326-
LAUNCHPAD_SHELL_INTEGRATION=1 ${launchpadBinary} dev:on "$project_dir" --shell-safe || true
345+
# Use carriage return to replace any previous output
346+
printf "\\r\\033[K✅ Environment activated for \\033[3m$(basename "$project_dir")\\033[0m\\n" >&2
327347
fi
328348
else
329349
# Setup failed but not due to timeout - try to set up basic environment silently
@@ -342,7 +362,7 @@ __launchpad_chpwd() {
342362
343363
# Show activation message only if environment already exists
344364
if [[ "\$\{LAUNCHPAD_SHOW_ENV_MESSAGES:-true\}" != "false" ]]; then
345-
LAUNCHPAD_SHELL_INTEGRATION=1 ${launchpadBinary} dev:on "$project_dir" --shell-safe || true
365+
printf "\\r\\033[K✅ Environment activated for \\033[3m$(basename "$project_dir")\\033[0m\\n" >&2
346366
fi
347367
fi
348368
# If no environment exists, be completely silent
@@ -381,7 +401,7 @@ __launchpad_chpwd() {
381401
382402
# Show deactivation message synchronously (no background jobs)
383403
if [[ "\$\{LAUNCHPAD_SHOW_ENV_MESSAGES:-true\}" != "false" ]]; then
384-
LAUNCHPAD_SHELL_INTEGRATION=1 ${launchpadBinary} dev:off 2>/dev/null || true
404+
printf "\\r\\033[K⚪ Environment deactivated\\n" >&2
385405
fi
386406
387407
unset LAUNCHPAD_CURRENT_PROJECT

0 commit comments

Comments
 (0)