Skip to content

Commit aa2f1f7

Browse files
committed
chore: wip
1 parent 5c13d45 commit aa2f1f7

File tree

4 files changed

+323
-1278
lines changed

4 files changed

+323
-1278
lines changed

packages/launchpad/bin/cli.ts

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2003,7 +2003,7 @@ cli
20032003
process.exit(1)
20042004
}
20052005
}
2006-
catch (error) {
2006+
catch {
20072007
if (options?.fallbackShell) {
20082008
// If fast detection fails and fallback is requested, exit with error
20092009
process.exit(1)
@@ -2015,6 +2015,34 @@ cli
20152015
}
20162016
})
20172017

2018+
cli
2019+
.command('dev:scan-library-paths <envDir>', 'Fast scan for library paths in environment directory')
2020+
.action(async (envDir: string) => {
2021+
try {
2022+
const { scanLibraryPaths } = await import('../src/dev/path-scanner')
2023+
const paths = await scanLibraryPaths(envDir)
2024+
console.log(paths.join(':'))
2025+
process.exit(0)
2026+
}
2027+
catch (error) {
2028+
process.exit(1)
2029+
}
2030+
})
2031+
2032+
cli
2033+
.command('dev:scan-global-paths <globalDir>', 'Fast scan for global binary paths')
2034+
.action(async (globalDir: string) => {
2035+
try {
2036+
const { scanGlobalPaths } = await import('../src/dev/path-scanner')
2037+
const paths = await scanGlobalPaths(globalDir)
2038+
console.log(paths.join(' '))
2039+
process.exit(0)
2040+
}
2041+
catch (error) {
2042+
process.exit(1)
2043+
}
2044+
})
2045+
20182046
cli
20192047
.command('dev:md5 <file>', 'Compute MD5 hash of a file (first 8 characters)')
20202048
.action((file: string) => {
@@ -3736,7 +3764,7 @@ cli
37363764
const { runFileDetectionBenchmark } = await import('../src/dev/benchmark')
37373765

37383766
const depths = options?.depths && typeof options.depths === 'string'
3739-
? options.depths.split(',').map(d => Number.parseInt(d.trim(), 10)).filter(d => !isNaN(d))
3767+
? options.depths.split(',').map(d => Number.parseInt(d.trim(), 10)).filter(d => !Number.isNaN(d))
37403768
: [3, 7, 15, 25]
37413769

37423770
const iterations = options?.iterations ? Number.parseInt(options.iterations, 10) : undefined
Lines changed: 217 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,217 @@
1+
import { existsSync, readdirSync } from 'node:fs'
2+
import { join } from 'node:path'
3+
4+
/**
5+
* Fast library path scanning using Bun's efficient filesystem operations
6+
* Replaces expensive shell find operations with direct filesystem calls
7+
*/
8+
export async function scanLibraryPaths(envDir: string): Promise<string[]> {
9+
const libPaths: string[] = []
10+
11+
if (!existsSync(envDir)) {
12+
return libPaths
13+
}
14+
15+
try {
16+
// Add direct lib directories first (fast path)
17+
for (const libDir of ['lib', 'lib64']) {
18+
const fullLibDir = join(envDir, libDir)
19+
if (existsSync(fullLibDir)) {
20+
libPaths.push(fullLibDir)
21+
}
22+
}
23+
24+
// Scan for package-specific library directories efficiently
25+
const entries = readdirSync(envDir, { withFileTypes: true })
26+
27+
for (const entry of entries) {
28+
if (!entry.isDirectory()) continue
29+
30+
const domainName = entry.name
31+
32+
// Skip known non-package directories
33+
if (['bin', 'sbin', 'lib', 'lib64', 'share', 'include', 'etc', 'pkgs', '.tmp', '.cache'].includes(domainName)) {
34+
continue
35+
}
36+
37+
const domainDir = join(envDir, domainName)
38+
39+
try {
40+
// Find version directories efficiently
41+
const versionEntries = readdirSync(domainDir, { withFileTypes: true })
42+
43+
for (const versionEntry of versionEntries) {
44+
if (!versionEntry.isDirectory() || !versionEntry.name.startsWith('v')) continue
45+
46+
const versionDir = join(domainDir, versionEntry.name)
47+
48+
// Check for lib directories in this version
49+
for (const libDir of ['lib', 'lib64']) {
50+
const libPath = join(versionDir, libDir)
51+
if (existsSync(libPath)) {
52+
// Validate that this lib directory has actual library files
53+
if (await hasValidLibraries(libPath, domainName, versionDir)) {
54+
libPaths.push(libPath)
55+
}
56+
}
57+
}
58+
}
59+
} catch {
60+
// Skip directories we can't read
61+
continue
62+
}
63+
}
64+
} catch {
65+
// If we can't read the env directory, return what we have
66+
}
67+
68+
return libPaths
69+
}
70+
71+
/**
72+
* Fast global path scanning using Bun's efficient filesystem operations
73+
* Replaces expensive shell find operations with direct filesystem calls
74+
*/
75+
export async function scanGlobalPaths(globalDir: string): Promise<string[]> {
76+
const globalPaths: string[] = []
77+
78+
if (!existsSync(globalDir)) {
79+
return globalPaths
80+
}
81+
82+
try {
83+
// Add standard global binary directories first (fast path)
84+
for (const binDir of ['bin', 'sbin']) {
85+
const fullBinDir = join(globalDir, binDir)
86+
if (existsSync(fullBinDir)) {
87+
globalPaths.push(fullBinDir)
88+
}
89+
}
90+
91+
// Scan for package-specific binary directories efficiently
92+
const entries = readdirSync(globalDir, { withFileTypes: true })
93+
94+
for (const entry of entries) {
95+
if (!entry.isDirectory()) continue
96+
97+
const domainName = entry.name
98+
99+
// Skip known non-package directories
100+
if (['bin', 'sbin', 'lib', 'lib64', 'share', 'include', 'etc', 'pkgs', '.tmp', '.cache'].includes(domainName)) {
101+
continue
102+
}
103+
104+
const domainDir = join(globalDir, domainName)
105+
106+
try {
107+
// Find latest version directory efficiently
108+
const versionEntries = readdirSync(domainDir, { withFileTypes: true })
109+
const versionDirs = versionEntries
110+
.filter(entry => entry.isDirectory() && entry.name.startsWith('v'))
111+
.map(entry => entry.name)
112+
.sort((a, b) => {
113+
// Simple version sort - extract numbers and compare
114+
const aNum = a.slice(1).split('.').map(n => parseInt(n, 10) || 0)
115+
const bNum = b.slice(1).split('.').map(n => parseInt(n, 10) || 0)
116+
117+
for (let i = 0; i < Math.max(aNum.length, bNum.length); i++) {
118+
const aPart = aNum[i] || 0
119+
const bPart = bNum[i] || 0
120+
if (aPart !== bPart) {
121+
return aPart - bPart
122+
}
123+
}
124+
return 0
125+
})
126+
127+
// Use the latest version
128+
const latestVersion = versionDirs[versionDirs.length - 1]
129+
if (latestVersion) {
130+
const versionDir = join(domainDir, latestVersion)
131+
132+
// Check for binary directories in this version
133+
for (const binDir of ['bin', 'sbin']) {
134+
const binPath = join(versionDir, binDir)
135+
if (existsSync(binPath)) {
136+
globalPaths.push(binPath)
137+
}
138+
}
139+
}
140+
} catch {
141+
// Skip directories we can't read
142+
continue
143+
}
144+
}
145+
} catch {
146+
// If we can't read the global directory, return what we have
147+
}
148+
149+
return globalPaths
150+
}
151+
152+
/**
153+
* Validate that a library directory has actual library files
154+
* Uses fast filesystem operations instead of expensive find commands
155+
*/
156+
async function hasValidLibraries(libDir: string, domainName: string, versionDir: string): Promise<boolean> {
157+
try {
158+
const entries = readdirSync(libDir, { withFileTypes: true })
159+
160+
// Check for common library patterns
161+
for (const entry of entries) {
162+
if (!entry.isFile()) continue
163+
164+
const name = entry.name
165+
166+
// Check for common library file extensions with reasonable size
167+
if (name.endsWith('.dylib') || name.endsWith('.so') || name.includes('.so.') || name.endsWith('.a')) {
168+
// Quick size check - library files should be larger than 100 bytes
169+
try {
170+
const stats = await Bun.file(join(libDir, name)).size
171+
if (stats > 100) {
172+
return true
173+
}
174+
} catch {
175+
// If we can't get size, assume it's valid
176+
return true
177+
}
178+
}
179+
}
180+
181+
// Special case: If this is a source-built package (like PHP), always include it
182+
if (domainName === 'php.net' && existsSync(join(versionDir, 'bin', 'php'))) {
183+
return true
184+
}
185+
186+
return false
187+
} catch {
188+
return false
189+
}
190+
}
191+
192+
/**
193+
* Fast environment readiness check
194+
* Replaces expensive shell operations with direct filesystem calls
195+
*/
196+
export async function checkEnvironmentReady(envDir: string): Promise<{
197+
ready: boolean
198+
binExists: boolean
199+
hasLibraries: boolean
200+
}> {
201+
const binDir = join(envDir, 'bin')
202+
const binExists = existsSync(binDir)
203+
204+
let hasLibraries = false
205+
try {
206+
const libPaths = await scanLibraryPaths(envDir)
207+
hasLibraries = libPaths.length > 0
208+
} catch {
209+
hasLibraries = false
210+
}
211+
212+
return {
213+
ready: binExists, // Environment is ready if bin directory exists
214+
binExists,
215+
hasLibraries
216+
}
217+
}

0 commit comments

Comments
 (0)