Skip to content

Commit 0a44bd6

Browse files
committed
chore: wip
1 parent bea3c67 commit 0a44bd6

File tree

4 files changed

+176
-89
lines changed

4 files changed

+176
-89
lines changed

deps.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
11
dependencies:
2-
bun.sh: 1.2.19
2+
bun.sh: 1.2.17

packages/launchpad/bin/cli.ts

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
#!/usr/bin/env bun
2+
import type { Buffer } from 'node:buffer'
23
// Set CLI mode to prevent fake binaries in normal usage
34
import fs from 'node:fs'
45
import { homedir } from 'node:os'
@@ -1977,6 +1978,34 @@ cli
19771978
}
19781979
})
19791980

1981+
cli
1982+
.command('dev:md5 <file>', 'Compute MD5 hash of a file (first 8 characters)')
1983+
.action((file: string) => {
1984+
try {
1985+
let content: Buffer
1986+
1987+
if (file === '/dev/stdin') {
1988+
// Read from stdin
1989+
content = fs.readFileSync(0) // 0 is stdin file descriptor
1990+
}
1991+
else {
1992+
// Read from file
1993+
content = fs.readFileSync(file)
1994+
}
1995+
1996+
const hasher = new Bun.CryptoHasher('md5')
1997+
hasher.update(content)
1998+
const hash = hasher.digest('hex')
1999+
console.log(hash.slice(0, 8)) // Return first 8 characters
2000+
process.exit(0)
2001+
}
2002+
catch {
2003+
// If file doesn't exist or can't be read, return empty string
2004+
console.log('')
2005+
process.exit(0)
2006+
}
2007+
})
2008+
19802009
cli
19812010
.command('dev [dir]', 'Set up development environment for project dependencies')
19822011
.option('--dry-run', 'Show packages that would be installed without installing them')
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
#!/usr/bin/env bun
2+
import fs from 'node:fs'
3+
import process from 'node:process'
4+
5+
/**
6+
* Simple MD5 utility for shell integration
7+
* Usage: bun md5-util.ts <file-path>
8+
* Returns: MD5 hash (first 8 characters) of the file content
9+
*/
10+
11+
function computeFileMD5(filePath: string): string {
12+
try {
13+
const content = fs.readFileSync(filePath)
14+
const hasher = new Bun.CryptoHasher('md5')
15+
hasher.update(content)
16+
const hash = hasher.digest('hex')
17+
return hash.slice(0, 8) // Return first 8 characters like the shell code expects
18+
}
19+
catch {
20+
// If file doesn't exist or can't be read, return empty string
21+
return ''
22+
}
23+
}
24+
25+
// Get file path from command line arguments
26+
const filePath = process.argv[2]
27+
28+
if (!filePath) {
29+
console.error('Usage: bun md5-util.ts <file-path>')
30+
process.exit(1)
31+
}
32+
33+
// Output just the hash (no extra output for shell parsing)
34+
// eslint-disable-next-line no-console
35+
console.log(computeFileMD5(filePath))

packages/launchpad/src/dev/shellcode.ts

Lines changed: 111 additions & 88 deletions
Original file line numberDiff line numberDiff line change
@@ -540,24 +540,102 @@ __launchpad_chpwd() {
540540
printf "⏱️ [0s] Shell integration started for PWD=%s\n" "$PWD" >&2
541541
fi
542542
543+
# Simple dependency change detection: check if deps file is newer than our cache
544+
if [[ -n "$LAUNCHPAD_CURRENT_PROJECT" && "$PWD" == "$LAUNCHPAD_CURRENT_PROJECT"* ]]; then
545+
local deps_file="$(__launchpad_find_dependency_file_path "$LAUNCHPAD_CURRENT_PROJECT" 2>/dev/null)"
546+
if [[ -n "$deps_file" && -f "$deps_file" ]]; then
547+
local cache_marker="$HOME/.cache/launchpad/shell_cache/project_\${LAUNCHPAD_CURRENT_PROJECT//\//_}"
548+
549+
# If deps file is newer than our cache marker, dependencies may have changed
550+
if [[ ! -f "$cache_marker" || "$deps_file" -nt "$cache_marker" ]]; then
551+
if [[ "$LAUNCHPAD_VERBOSE" == "true" ]]; then
552+
printf "⏱️ [0s] Dependency file changed, clearing cache\n" >&2
553+
fi
554+
# Clear the current project to force a fresh setup
555+
unset LAUNCHPAD_CURRENT_PROJECT
556+
# Update cache marker
557+
mkdir -p "$(dirname "$cache_marker")" 2>/dev/null
558+
touch "$cache_marker" 2>/dev/null
559+
fi
560+
fi
561+
fi
562+
543563
local project_dir
544564
545565
# Super-fast path: if we're already in the same project, skip file search entirely
546566
if [[ -n "$LAUNCHPAD_CURRENT_PROJECT" && "$PWD" == "$LAUNCHPAD_CURRENT_PROJECT"* ]]; then
547567
project_dir="$LAUNCHPAD_CURRENT_PROJECT"
568+
548569
if [[ "$LAUNCHPAD_VERBOSE" == "true" ]]; then
549570
__lp_current_time=$(__lp_get_time_s)
550571
printf "⏱️ [%ss] Using cached project dir (same project): %s\n" "$(__lp_elapsed_s "$__lp_start_time" "$__lp_current_time")" "$project_dir" >&2
551572
fi
552573
else
553574
project_dir=$(__launchpad_find_deps_file "$PWD")
575+
554576
if [[ "$LAUNCHPAD_VERBOSE" == "true" ]]; then
555577
__lp_current_time=$(__lp_get_time_s)
556578
printf "⏱️ [%ss] Dependency file search completed: %s\n" $(__lp_elapsed_s "$__lp_start_time" "$__lp_current_time") "\${project_dir:-none}" >&2
557579
fi
558580
fi
559581
560582
if [[ -n "$project_dir" ]]; then
583+
# Always compute dependency hash (even for same project, dependencies can change)
584+
local real_project_dir
585+
real_project_dir=$(cd "$project_dir" 2>/dev/null && pwd -P || echo "$project_dir")
586+
587+
local dep_file_path dep_short
588+
dep_file_path="$(__launchpad_find_dependency_file_path "$real_project_dir" 2>/dev/null)"
589+
590+
591+
if [[ "$LAUNCHPAD_VERBOSE" == "true" ]]; then
592+
__lp_current_time=$(__lp_get_time_s)
593+
printf "⏱️ [%ss] Dependency file path found: %s\n" $(__lp_elapsed_s "$__lp_start_time" "$__lp_current_time") "\${dep_file_path:-none}" >&2
594+
fi
595+
596+
if [[ -n "$dep_file_path" && -f "$dep_file_path" ]]; then
597+
# Use launchpad's built-in MD5 computation for reliability
598+
dep_short=$(${launchpadBinary} dev:md5 "$dep_file_path" 2>/dev/null || echo "")
599+
fi
600+
601+
# Compute environment directory with dependency hash
602+
local project_basename
603+
project_basename=$(basename "$real_project_dir")
604+
local md5hash
605+
# Use launchpad's built-in MD5 computation for project path
606+
md5hash=$(printf "%s" "$real_project_dir" | ${launchpadBinary} dev:md5 /dev/stdin 2>/dev/null || echo "00000000")
607+
local project_hash="${project_basename}_$(echo "$md5hash" | cut -c1-8)"
608+
local env_dir="$HOME/.local/share/launchpad/envs/$project_hash"
609+
610+
# Add dependency hash suffix if we have dependencies
611+
if [[ -n "$dep_short" ]]; then
612+
env_dir="${env_dir}-d${dep_short}"
613+
fi
614+
615+
if [[ "$LAUNCHPAD_VERBOSE" == "true" ]]; then
616+
__lp_current_time=$(__lp_get_time_s)
617+
printf "⏱️ [%ss] Project hash computed: %s\n" $(__lp_elapsed_s "$__lp_start_time" "$__lp_current_time") "$project_hash" >&2
618+
printf "🔍 Env target: env_dir=%s dep_file=%s dep_hash=%s\n" "$env_dir" "\${dep_file_path:-none}" "\${dep_short:-none}" >&2
619+
fi
620+
621+
# Check if dependencies changed in the same project (force new setup)
622+
if [[ "$LAUNCHPAD_CURRENT_PROJECT" == "$project_dir" && -n "$LAUNCHPAD_ENV_BIN_PATH" ]]; then
623+
# Extract current environment directory from LAUNCHPAD_ENV_BIN_PATH
624+
local current_env_dir="\${LAUNCHPAD_ENV_BIN_PATH%/bin}"
625+
626+
# If current environment doesn't match expected environment, dependencies changed
627+
if [[ "$current_env_dir" != "$env_dir" ]]; then
628+
if [[ "$LAUNCHPAD_VERBOSE" == "true" ]]; then
629+
__lp_current_time=$(__lp_get_time_s)
630+
printf "⏱️ [%ss] Dependencies changed, forcing new setup\n" $(__lp_elapsed_s "$__lp_start_time" "$__lp_current_time") >&2
631+
printf "🔍 Current: %s\n" "$current_env_dir" >&2
632+
printf "🔍 Expected: %s\n" "$env_dir" >&2
633+
fi
634+
# Force new project setup by unsetting the current project
635+
unset LAUNCHPAD_CURRENT_PROJECT
636+
fi
637+
fi
638+
561639
# Check if we're entering a new project or if this is the first time
562640
if [[ "$LAUNCHPAD_CURRENT_PROJECT" != "$project_dir" ]]; then
563641
export LAUNCHPAD_CURRENT_PROJECT="$project_dir"
@@ -569,7 +647,7 @@ __launchpad_chpwd() {
569647
570648
# Check if setup is already in progress to avoid duplicate work
571649
if [[ "$__launchpad_setup_in_progress" == "$project_dir" ]]; then
572-
if [[ "$LAUNCHPAD_VERBOSE" == "true" ]]; then
650+
if [[ "$LAUNCHPAD_VERBOSE" == "true" ]]; then
573651
__lp_current_time=$(__lp_get_time_s)
574652
printf "⏱️ [%ss] Setup already in progress, skipping\n" $(__lp_elapsed_s "$__lp_start_time" "$__lp_current_time") >&2
575653
fi
@@ -581,70 +659,17 @@ __launchpad_chpwd() {
581659
setopt localoptions nonomatch
582660
fi
583661
584-
# Ultra-fast activation: compute environment path and check if ready
585-
# Resolve to physical path and compute MD5 (matches generateProjectHash in dump.ts)
586-
local real_project_dir
587-
real_project_dir=$(cd "$project_dir" 2>/dev/null && pwd -P || echo "$project_dir")
588-
# Enable verbose logs for this shell when project config sets verbose: true
662+
# Ultra-fast activation: enable verbose logs for this shell when project config sets verbose: true
589663
if [[ -f "$real_project_dir/launchpad.config.ts" ]] && /usr/bin/grep -Eq "verbose:\\s*true" "$real_project_dir/launchpad.config.ts" 2>/dev/null; then
590664
export LAUNCHPAD_VERBOSE=true
591665
fi
592-
local project_basename
593-
project_basename=$(basename "$real_project_dir")
594-
local md5hash
595-
if command -v md5 >/dev/null 2>&1; then
596-
md5hash=$(md5 -q -s "$real_project_dir" 2>/dev/null || md5 -q "$real_project_dir" 2>/dev/null)
597-
fi
598-
if [[ -z "$md5hash" ]] && command -v md5sum >/dev/null 2>&1; then
599-
md5hash=$(printf "%s" "$real_project_dir" | md5sum 2>/dev/null | awk '{print $1}')
600-
fi
601-
if [[ -z "$md5hash" ]] && command -v openssl >/dev/null 2>&1; then
602-
md5hash=$(printf "%s" "$real_project_dir" | openssl md5 2>/dev/null | awk '{print $2}')
603-
fi
604-
if [[ -z "$md5hash" ]]; then
605-
md5hash="00000000"
606-
fi
607-
local project_hash="\${project_basename}_$(echo "$md5hash" | cut -c1-8)"
608-
local env_dir="$HOME/.local/share/launchpad/envs/$project_hash"
609-
610-
if [[ "$LAUNCHPAD_VERBOSE" == "true" ]]; then
611-
__lp_current_time=$(__lp_get_time_s)
612-
printf "⏱️ [%ss] Project hash computed: %s\n" $(__lp_elapsed_s "$__lp_start_time" "$__lp_current_time") "$project_hash" >&2
613-
fi
614-
615-
# Optionally suffix environment with dependency hash so version changes map to distinct envs
616-
local dep_file_path dep_md5 dep_short
617-
dep_file_path="$(__launchpad_find_dependency_file_path "$real_project_dir" 2>/dev/null)"
618-
619-
if [[ "$LAUNCHPAD_VERBOSE" == "true" ]]; then
620-
__lp_current_time=$(__lp_get_time_s)
621-
printf "⏱️ [%ss] Dependency file path found: %s\n" $(__lp_elapsed_s "$__lp_start_time" "$__lp_current_time") "\${dep_file_path:-none}" >&2
622-
fi
623-
624-
if [[ -n "$dep_file_path" && -f "$dep_file_path" ]]; then
625-
if command -v md5 >/dev/null 2>&1; then
626-
dep_md5=$(md5 -q "$dep_file_path" 2>/dev/null || echo "")
627-
elif command -v md5sum >/dev/null 2>&1; then
628-
dep_md5=$(md5sum "$dep_file_path" 2>/dev/null | awk '{print $1}')
629-
else
630-
dep_md5=""
631-
fi
632-
if [[ -n "$dep_md5" ]]; then
633-
dep_short=$(printf "%s" "$dep_md5" | cut -c1-8)
634-
env_dir="\${env_dir}-d\${dep_short}"
635-
fi
636-
fi
637-
638-
if [[ "$LAUNCHPAD_VERBOSE" == "true" ]]; then
639-
printf "🔍 Env target: env_dir=%s dep_file=%s dep_hash=%s\n" "$env_dir" "\${dep_file_path:-none}" "\${dep_short:-none}" >&2
640-
fi
641666
642667
# Skip expensive fallback in shell integration - if env doesn't exist, let full setup handle it
643668
if [[ ! -d "$env_dir/bin" && -z "$LAUNCHPAD_SHELL_INTEGRATION" ]]; then
644-
if [[ "$LAUNCHPAD_VERBOSE" == "true" ]]; then
669+
if [[ "$LAUNCHPAD_VERBOSE" == "true" ]]; then
645670
__lp_current_time=$(__lp_get_time_s)
646671
printf "⏱️ [%ss] Starting expensive environment fallback search\n" $(__lp_elapsed_s "$__lp_start_time" "$__lp_current_time") >&2
647-
fi
672+
fi
648673
local envs_root="$HOME/.local/share/launchpad/envs"
649674
if [[ -d "$envs_root" ]]; then
650675
local best_env=""
@@ -722,21 +747,17 @@ __launchpad_chpwd() {
722747
local mtime size
723748
mtime=$(stat -c %Y "$dep_file" 2>/dev/null || stat -f %m "$dep_file" 2>/dev/null || echo 0)
724749
size=$(stat -c %s "$dep_file" 2>/dev/null || stat -f %z "$dep_file" 2>/dev/null || echo 0)
725-
if command -v md5 >/dev/null 2>&1; then
726-
local sum
727-
sum=$(md5 -q "$dep_file" 2>/dev/null || md5 -q -s "$dep_file" 2>/dev/null)
728-
echo "file=$dep_file m=$mtime s=$size md5=$sum"
729-
elif command -v md5sum >/dev/null 2>&1; then
730750
local sum
731-
sum=$(md5sum "$dep_file" 2>/dev/null | awk '{print $1}')
751+
sum=$(${launchpadBinary} dev:md5 "$dep_file" 2>/dev/null || echo "")
752+
if [[ -n "$sum" ]]; then
732753
echo "file=$dep_file m=$mtime s=$size md5=$sum"
733754
else
734755
echo "file=$dep_file m=$mtime s=$size"
735756
fi
736757
}
737758
738759
# Persistent cache: check before slow operations (but invalidate when deps changed)
739-
local cache_file="$__launchpad_persistent_cache_dir/env_cache_$(printf "%s" "$env_dir" | md5sum 2>/dev/null | awk '{print $1}' | cut -c1-16)"
760+
local cache_file="$__launchpad_persistent_cache_dir/env_cache_$(printf "%s" "$env_dir" | ${launchpadBinary} dev:md5 /dev/stdin 2>/dev/null | cut -c1-16)"
740761
local current_time=$(date +%s)
741762
local cache_duration=1800 # 30 minutes for shell integration
742763
# Use longer cache durations for test/development environments
@@ -826,14 +847,10 @@ __launchpad_chpwd() {
826847
if [[ -n "$dep_file" && -f "$dep_file" ]]; then
827848
# Check if the current environment directory matches the expected one for current deps
828849
local expected_dep_md5 expected_dep_short expected_env_dir
829-
if command -v md5 >/dev/null 2>&1; then
830-
expected_dep_md5=$(md5 -q "$dep_file" 2>/dev/null || echo "")
831-
elif command -v md5sum >/dev/null 2>&1; then
832-
expected_dep_md5=$(md5sum "$dep_file" 2>/dev/null | awk '{print $1}')
833-
fi
850+
expected_dep_md5=$(${launchpadBinary} dev:md5 "$dep_file" 2>/dev/null || echo "")
834851
835852
if [[ -n "$expected_dep_md5" ]]; then
836-
expected_dep_short=$(printf "%s" "$expected_dep_md5" | cut -c1-8)
853+
expected_dep_short="$expected_dep_md5" # Already truncated to 8 chars
837854
expected_env_dir="$HOME/.local/share/launchpad/envs/\\${project_basename}_$(echo "$md5hash" | cut -c1-8)-d\\${expected_dep_short}"
838855
839856
# If current environment doesn't match expected environment, dependencies changed
@@ -933,19 +950,19 @@ __launchpad_chpwd() {
933950
934951
# All file operations in background
935952
(
936-
mkdir -p "$(dirname "$cache_file")" 2>/dev/null || true
937-
touch "$cache_file" 2>/dev/null || true
938-
if [[ -z "$dep_file" ]]; then
939-
dep_file="$(__launchpad_find_dependency_file_path "$real_project_dir" 2>/dev/null)"
940-
fi
941-
if [[ -n "$dep_file" && -f "$dep_file" ]]; then
942-
touch -mr "$dep_file" "$cache_file" 2>/dev/null || true
943-
fi
944-
local fp_current
945-
fp_current="$(__launchpad_compute_deps_fingerprint "$real_project_dir")"
946-
if [[ -n "$fp_current" ]]; then
947-
echo "$fp_current" > "$env_dir/.deps_fingerprint" 2>/dev/null || true
948-
fi
953+
mkdir -p "$(dirname "$cache_file")" 2>/dev/null || true
954+
touch "$cache_file" 2>/dev/null || true
955+
if [[ -z "$dep_file" ]]; then
956+
dep_file="$(__launchpad_find_dependency_file_path "$real_project_dir" 2>/dev/null)"
957+
fi
958+
if [[ -n "$dep_file" && -f "$dep_file" ]]; then
959+
touch -mr "$dep_file" "$cache_file" 2>/dev/null || true
960+
fi
961+
local fp_current
962+
fp_current="$(__launchpad_compute_deps_fingerprint "$real_project_dir")"
963+
if [[ -n "$fp_current" ]]; then
964+
echo "$fp_current" > "$env_dir/.deps_fingerprint" 2>/dev/null || true
965+
fi
949966
) >/dev/null 2>&1 &
950967
disown 2>/dev/null || true
951968
@@ -1004,7 +1021,7 @@ export LAUNCHPAD_PROJECT_DIR=\"$project_dir\"
10041021
10051022
# All maintenance in background
10061023
{
1007-
__launchpad_setup_global_deps
1024+
__launchpad_setup_global_deps
10081025
touch "$HOME/.cache/launchpad/shell_cache/global_refresh_needed" 2>/dev/null || true
10091026
mkdir -p "$env_dir" 2>/dev/null || true
10101027
touch "$env_dir/.launchpad_ready" 2>/dev/null || true
@@ -1075,10 +1092,10 @@ export LAUNCHPAD_PROJECT_DIR=\"$project_dir\"
10751092
10761093
# Mark environment ready for instant future activation (both cache and marker) - async
10771094
(
1078-
mkdir -p "$env_dir" 2>/dev/null || true
1079-
touch "$env_dir/.launchpad_ready" 2>/dev/null || true
1080-
mkdir -p "$(dirname "$cache_file")" 2>/dev/null || true
1081-
touch "$cache_file" 2>/dev/null || true
1095+
mkdir -p "$env_dir" 2>/dev/null || true
1096+
touch "$env_dir/.launchpad_ready" 2>/dev/null || true
1097+
mkdir -p "$(dirname "$cache_file")" 2>/dev/null || true
1098+
touch "$cache_file" 2>/dev/null || true
10821099
) >/dev/null 2>&1 &
10831100
disown 2>/dev/null || true
10841101
@@ -1103,6 +1120,12 @@ export LAUNCHPAD_PROJECT_DIR=\"$project_dir\"
11031120
fi
11041121
fi
11051122
fi
1123+
1124+
# Always run environment setup logic (ultra-fast path will handle optimization)
1125+
# Enable verbose logs for this shell when project config sets verbose: true
1126+
if [[ -f "$real_project_dir/launchpad.config.ts" ]] && /usr/bin/grep -Eq "verbose:\\s*true" "$real_project_dir/launchpad.config.ts" 2>/dev/null; then
1127+
export LAUNCHPAD_VERBOSE=true
1128+
fi
11061129
else
11071130
# No deps file found, deactivate if we were in a project
11081131
if [[ -n "$LAUNCHPAD_CURRENT_PROJECT" ]]; then

0 commit comments

Comments
 (0)