Skip to content

Commit 0b3778a

Browse files
committed
chore: wip
1 parent d61d44a commit 0b3778a

18 files changed

+912
-147
lines changed

packages/launchpad/bin/cli.ts

Lines changed: 48 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
#!/usr/bin/env bun
2+
import { Glob } from 'bun'
23
import fs from 'node:fs'
34
import { homedir } from 'node:os'
45
import path from 'node:path'
56
import process from 'node:process'
67
import { CAC } from 'cac'
7-
import { Glob } from 'bun'
88
import { install, install_prefix, list, uninstall } from '../src'
99
import { config } from '../src/config'
1010
import { dump, integrate, shellcode } from '../src/dev'
@@ -461,7 +461,7 @@ async function installGlobalDependencies(options: {
461461
for await (const file of glob.scan({
462462
cwd: location,
463463
onlyFiles: true,
464-
followSymlinks: false
464+
followSymlinks: false,
465465
})) {
466466
const fullPath = path.resolve(location, file)
467467
foundFiles.push(fullPath)
@@ -1351,6 +1351,52 @@ cli
13511351
console.log(shellcode(testMode))
13521352
})
13531353

1354+
cli
1355+
.command('dev [dir]', 'Set up development environment for project dependencies')
1356+
.option('--dry-run', 'Show packages that would be installed without installing them')
1357+
.option('--quiet', 'Suppress non-error output')
1358+
.option('--shell', 'Output shell code for evaluation (use with eval)')
1359+
.action(async (dir?: string, options?: { dryRun?: boolean, quiet?: boolean, shell?: boolean }) => {
1360+
try {
1361+
const targetDir = dir ? path.resolve(dir) : process.cwd()
1362+
1363+
// For shell integration, force quiet mode and set environment variable
1364+
const isShellIntegration = options?.shell || false
1365+
if (isShellIntegration) {
1366+
process.env.LAUNCHPAD_SHELL_INTEGRATION = '1'
1367+
}
1368+
1369+
await dump(targetDir, {
1370+
dryrun: options?.dryRun || false,
1371+
quiet: options?.quiet || isShellIntegration, // Force quiet for shell integration
1372+
shellOutput: isShellIntegration,
1373+
skipGlobal: process.env.NODE_ENV === 'test' || process.env.LAUNCHPAD_SKIP_GLOBAL_AUTO_SCAN === 'true', // Skip global packages only in test mode or when explicitly disabled
1374+
})
1375+
}
1376+
catch (error) {
1377+
if (!options?.quiet && !options?.shell) {
1378+
console.error('Failed to set up dev environment:', error instanceof Error ? error.message : String(error))
1379+
}
1380+
else if (options?.shell) {
1381+
// For shell mode, output robust fallback that ensures basic system tools are available
1382+
// This prevents shell integration from hanging or failing
1383+
console.log('# Environment setup failed, using system fallback')
1384+
console.log('# Ensure basic system paths are available')
1385+
console.log('for sys_path in /usr/local/bin /usr/bin /bin /usr/sbin /sbin; do')
1386+
console.log(' if [[ -d "$sys_path" && ":$PATH:" != *":$sys_path:"* ]]; then')
1387+
console.log(' export PATH="$PATH:$sys_path"')
1388+
console.log(' fi')
1389+
console.log('done')
1390+
console.log('# Clear command hash to ensure fresh lookups')
1391+
console.log('hash -r 2>/dev/null || true')
1392+
return
1393+
}
1394+
if (!options?.shell) {
1395+
process.exit(1)
1396+
}
1397+
}
1398+
})
1399+
13541400
cli
13551401
.command('dev:integrate', 'Install shell integration hooks')
13561402
.option('--uninstall', 'Remove shell integration hooks')

packages/launchpad/src/dev-setup.ts

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,8 @@ export function parseEnvFile(envPath: string): Record<string, string> {
2424
if (key && valueParts.length > 0) {
2525
let value = valueParts.join('=')
2626
// Remove quotes if present
27-
if ((value.startsWith('"') && value.endsWith('"')) ||
28-
(value.startsWith("'") && value.endsWith("'"))) {
27+
if ((value.startsWith('"') && value.endsWith('"'))
28+
|| (value.startsWith('\'') && value.endsWith('\''))) {
2929
value = value.slice(1, -1)
3030
}
3131
env[key] = value
@@ -34,7 +34,8 @@ export function parseEnvFile(envPath: string): Record<string, string> {
3434
}
3535

3636
return env
37-
} catch (error) {
37+
}
38+
catch (error) {
3839
console.warn(`Warning: Could not parse .env file at ${envPath}:`, error)
3940
return {}
4041
}
@@ -57,8 +58,8 @@ export function getLaravelDatabaseConfig(projectDir: string = process.cwd()): {
5758
return {
5859
driver: env.DB_CONNECTION || 'sqlite',
5960
host: env.DB_HOST || 'localhost',
60-
port: parseInt(env.DB_PORT || '5432', 10),
61-
database: env.DB_DATABASE || path.basename(projectDir).replace(/[^a-zA-Z0-9_]/g, '_'),
61+
port: Number.parseInt(env.DB_PORT || '5432', 10),
62+
database: env.DB_DATABASE || path.basename(projectDir).replace(/\W/g, '_'),
6263
username: env.DB_USERNAME || 'root',
6364
password: env.DB_PASSWORD || 'password',
6465
}

packages/launchpad/src/dev/dump.ts

Lines changed: 16 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -412,7 +412,8 @@ export async function dump(dir: string, options: DumpOptions = {}): Promise<void
412412
// Auto-start services even when no packages need installation
413413
try {
414414
await setupProjectServices(projectDir, sniffResult, !effectiveQuiet)
415-
} catch (error) {
415+
}
416+
catch (error) {
416417
console.error(`⚠️ Service auto-start failed: ${error instanceof Error ? error.message : String(error)}`)
417418
}
418419

@@ -447,7 +448,8 @@ export async function dump(dir: string, options: DumpOptions = {}): Promise<void
447448
// Auto-start services for shell integration too
448449
try {
449450
await setupProjectServices(projectDir, sniffResult, !effectiveQuiet)
450-
} catch (error) {
451+
}
452+
catch (error) {
451453
console.error(`⚠️ Service auto-start failed: ${error instanceof Error ? error.message : String(error)}`)
452454
}
453455

@@ -890,8 +892,6 @@ function outputShellCode(dir: string, envBinPath: string, envSbinPath: string, p
890892
process.stdout.write(`}\n`)
891893
}
892894

893-
894-
895895
/**
896896
* Auto-setup services for any project based on deps.yaml services configuration
897897
*/
@@ -909,10 +909,10 @@ async function setupProjectServices(projectDir: string, sniffResult: any, showMe
909909

910910
// Check deps.yaml to see what services are defined in dependencies
911911
const hasPostgresInDeps = sniffResult?.pkgs?.some((pkg: any) =>
912-
pkg.project.includes('postgres') || pkg.project.includes('postgresql')
912+
pkg.project.includes('postgres') || pkg.project.includes('postgresql'),
913913
)
914914
const hasRedisInDeps = sniffResult?.pkgs?.some((pkg: any) =>
915-
pkg.project.includes('redis')
915+
pkg.project.includes('redis'),
916916
)
917917

918918
// Import service manager
@@ -935,36 +935,39 @@ async function setupProjectServices(projectDir: string, sniffResult: any, showMe
935935

936936
// Special handling for PostgreSQL: create project database
937937
if ((serviceName === 'postgres' || serviceName === 'postgresql') && hasPostgresInDeps) {
938-
const projectName = path.basename(projectDir).replace(/[^a-zA-Z0-9_]/g, '_')
938+
const projectName = path.basename(projectDir).replace(/\W/g, '_')
939939
try {
940940
await createProjectDatabase(projectName, {
941941
type: 'postgres',
942942
host: '127.0.0.1',
943943
port: 5432,
944944
username: 'postgres',
945-
password: ''
945+
password: '',
946946
})
947947

948948
if (showMessages) {
949949
console.log(`✅ PostgreSQL database '${projectName}' created`)
950950
}
951-
} catch (dbError) {
951+
}
952+
catch (dbError) {
952953
if (showMessages) {
953954
console.warn(`⚠️ Database creation warning: ${dbError instanceof Error ? dbError.message : String(dbError)}`)
954955
}
955956
}
956957
}
957-
} else if (showMessages) {
958+
}
959+
else if (showMessages) {
958960
console.warn(`⚠️ Failed to start ${serviceName} service`)
959961
}
960-
} catch (error) {
962+
}
963+
catch (error) {
961964
if (showMessages) {
962965
console.warn(`⚠️ Error starting ${serviceName}: ${error instanceof Error ? error.message : String(error)}`)
963966
}
964967
}
965968
}
966-
967-
} catch (error) {
969+
}
970+
catch (error) {
968971
if (showMessages) {
969972
console.warn(`⚠️ Service setup warning: ${error instanceof Error ? error.message : String(error)}`)
970973
}

packages/launchpad/src/dev/sniff.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -836,7 +836,7 @@ export default async function sniff(dir: SimplePath | { string: string }): Promi
836836
}
837837

838838
pkgs.push(...yaml.deps)
839-
839+
840840
// Collect services configuration (last one wins)
841841
if (yaml.services) {
842842
services = yaml.services
@@ -892,16 +892,16 @@ function extract_well_formatted_entries(
892892
const topLevelGlobal = yaml.global === true || yaml.global === 'true'
893893
const deps = parse_deps(yaml.dependencies, topLevelGlobal)
894894
const env = isPlainObject(yaml.env) ? yaml.env : {}
895-
895+
896896
// Extract services configuration
897897
let services: { enabled?: boolean, autoStart?: string[] } | undefined
898898
if (isPlainObject(yaml.services)) {
899899
services = {
900900
enabled: yaml.services.enabled === true,
901-
autoStart: Array.isArray(yaml.services.autoStart) ? yaml.services.autoStart : undefined
901+
autoStart: Array.isArray(yaml.services.autoStart) ? yaml.services.autoStart : undefined,
902902
}
903903
}
904-
904+
905905
return { deps, env, services }
906906
}
907907

0 commit comments

Comments
 (0)