Skip to content

Commit c3e5587

Browse files
committed
chore: wip
1 parent 3bdb4e4 commit c3e5587

File tree

4 files changed

+76
-45
lines changed

4 files changed

+76
-45
lines changed

packages/launchpad/src/binary-downloader.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -800,7 +800,11 @@ Thanks for helping us make Launchpad better! 🙏
800800

801801
// First, on macOS fix absolute Homebrew dylib references to Launchpad-managed libs
802802
try {
803-
await this.fixMacOSDylibs(packageDir, launchpadLibraryPaths)
803+
// Best-effort only; if this fails due to headerpad limits, skip silently (shims set DYLD paths)
804+
try {
805+
await this.fixMacOSDylibs(packageDir, launchpadLibraryPaths)
806+
}
807+
catch {}
804808
}
805809
catch (err) {
806810
console.warn(`⚠️ Could not fix macOS dylib references: ${err instanceof Error ? err.message : String(err)}`)

packages/launchpad/src/dev/dump.ts

Lines changed: 42 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -285,6 +285,30 @@ async function executepostSetup(projectDir: string, commands: PostSetupCommand[]
285285
}
286286
// Small grace delay after ready
287287
await new Promise(r => setTimeout(r, 250))
288+
289+
// If still not listening, wait a bit longer with additional probes instead of restarting
290+
const readyAfterFirstPass = await new Promise<boolean>((resolve) => {
291+
// eslint-disable-next-line ts/no-require-imports
292+
const { spawn } = require('node:child_process')
293+
const p = spawn(pgIsReady, ['-h', host, '-p', port], { stdio: 'ignore' })
294+
p.on('close', (code: number) => resolve(code === 0))
295+
p.on('error', () => resolve(false))
296+
})
297+
if (!readyAfterFirstPass) {
298+
for (let i = 0; i < 12; i++) {
299+
const ok = await new Promise<boolean>((resolve) => {
300+
// eslint-disable-next-line ts/no-require-imports
301+
const { spawn } = require('node:child_process')
302+
const p = spawn(pgIsReady, ['-h', host, '-p', port], { stdio: 'ignore' })
303+
p.on('close', (code: number) => resolve(code === 0))
304+
p.on('error', () => resolve(false))
305+
})
306+
if (ok)
307+
break
308+
await new Promise(r => setTimeout(r, Math.min(750 + i * 250, 3000)))
309+
}
310+
await new Promise(r => setTimeout(r, 250))
311+
}
288312
}
289313
catch {}
290314
}
@@ -815,10 +839,20 @@ export async function dump(dir: string, options: DumpOptions = {}): Promise<void
815839
// Regular path for non-shell integration calls
816840
if (localPackages.length > 0 || globalPackages.length > 0) {
817841
await installPackagesOptimized(localPackages, globalPackages, envDir, globalEnvDir, dryrun, quiet)
842+
// Visual separator after dependency install list
843+
try { console.log() }
844+
catch {}
818845
}
819846

820847
// Auto-start services for any project that has services configuration
848+
// Suppress interstitial processing messages during service startup phase
849+
const prevProcessing = process.env.LAUNCHPAD_PROCESSING
850+
process.env.LAUNCHPAD_PROCESSING = '0'
821851
await setupProjectServices(projectDir, sniffResult, !effectiveQuiet)
852+
if (prevProcessing === undefined)
853+
delete process.env.LAUNCHPAD_PROCESSING
854+
else
855+
process.env.LAUNCHPAD_PROCESSING = prevProcessing
822856

823857
// Ensure php.ini and Laravel post-setup runs (regular path)
824858
await ensureProjectPhpIni(projectDir, envDir)
@@ -845,9 +879,12 @@ export async function dump(dir: string, options: DumpOptions = {}): Promise<void
845879

846880
outputShellCode(dir, envBinPath, envSbinPath, projectHash, sniffResult, globalBinPath, globalSbinPath)
847881
}
848-
else if (!effectiveQuiet) {
849-
// Final success summary for non-shell runs (used by tests)
850-
console.log('Successfully set up environment')
882+
else {
883+
// Always print a final activation message, even in quiet mode
884+
const { config } = await import('../config')
885+
const activation = (config.shellActivationMessage || '✅ Environment activated for {path}')
886+
.replace('{path}', process.cwd())
887+
console.log(activation)
851888
}
852889
}
853890
catch (error) {
@@ -1384,9 +1421,8 @@ async function setupProjectServices(projectDir: string, sniffResult: any, showMe
13841421

13851422
// Special handling for PostgreSQL: wait for readiness and create project database
13861423
if ((serviceName === 'postgres' || serviceName === 'postgresql') && hasPostgresInDeps) {
1387-
if (showMessages) {
1424+
if (showMessages)
13881425
console.log('⏳ Verifying PostgreSQL readiness...')
1389-
}
13901426
// Ensure postgres is actually accepting connections
13911427
try {
13921428
const { findBinaryInPath } = await import('../utils')
@@ -1407,9 +1443,8 @@ async function setupProjectServices(projectDir: string, sniffResult: any, showMe
14071443
}
14081444
catch {}
14091445

1410-
if (showMessages) {
1446+
if (showMessages)
14111447
console.log('🔧 Creating project PostgreSQL database...')
1412-
}
14131448
const projectName = path.basename(projectDir).replace(/\W/g, '_')
14141449
try {
14151450
await createProjectDatabase(projectName, {

packages/launchpad/src/logging.ts

Lines changed: 0 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -127,35 +127,6 @@ export function logUniqueMessage(message: string, forceLog = false): void {
127127
console.log(message)
128128
}
129129

130-
// Show temporary processing message immediately after package success messages (not the final environment message)
131-
// Use a simple static message instead of animated spinner to avoid conflicts with download progress
132-
// But don't show it for the final package completion or summary messages
133-
if (!config.verbose && message.startsWith('✅')
134-
&& !message.includes('Environment activated')
135-
&& !message.includes('Successfully set up environment')
136-
&& !message.includes('Installed')
137-
&& !message.includes('packages')
138-
&& !message.includes('(v')) { // Don't show processing message after individual package success messages
139-
// Add a small delay to make the success message visible before showing processing message
140-
setTimeout(() => {
141-
// Only show processing message if we haven't completed all packages
142-
if (!hasTemporaryProcessingMessage) {
143-
const processingMsg = `🔄 Processing next dependency...`
144-
145-
if (process.env.LAUNCHPAD_SHELL_INTEGRATION === '1') {
146-
process.stderr.write(`${processingMsg}\n`)
147-
if (process.stderr.isTTY) {
148-
fs.writeSync(process.stderr.fd, '')
149-
}
150-
}
151-
else {
152-
process.stdout.write(`${processingMsg}\n`)
153-
}
154-
155-
hasTemporaryProcessingMessage = true
156-
}
157-
}, 50) // Small delay to ensure success message is visible
158-
}
159130
}
160131

161132
export function clearMessageCache(): void {

packages/launchpad/src/services/manager.ts

Lines changed: 29 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -110,14 +110,27 @@ export async function startService(serviceName: string): Promise<boolean> {
110110
const service = await getOrCreateServiceInstance(serviceName)
111111

112112
if (service.status === 'running') {
113-
console.log(`✅ Service ${serviceName} is already running`)
114-
operation.result = 'success'
115-
operation.duration = 0
116-
manager.operations.push(operation)
117-
return true
113+
// Verify actual health with a couple retries to avoid false negatives
114+
let healthy = await checkServiceHealth(service)
115+
if (!healthy) {
116+
await new Promise(r => setTimeout(r, 500))
117+
healthy = await checkServiceHealth(service)
118+
}
119+
if (healthy) {
120+
logUniqueMessage(`✅ Service ${serviceName} is already running`, true)
121+
operation.result = 'success'
122+
operation.duration = 0
123+
manager.operations.push(operation)
124+
return true
125+
}
126+
else {
127+
logUniqueMessage(`⚠️ ${service.definition?.displayName || serviceName} reported running but appears unhealthy. Attempting gentle restart...`, true)
128+
await stopService(serviceName)
129+
// fall through to start flow
130+
}
118131
}
119132

120-
console.warn(`🚀 Starting ${service.definition?.displayName || serviceName}...`)
133+
logUniqueMessage(`🚀 Starting ${service.definition?.displayName || serviceName}...`, true)
121134

122135
// Update status to starting
123136
service.status = 'starting'
@@ -171,10 +184,18 @@ export async function startService(serviceName: string): Promise<boolean> {
171184
service.startedAt = new Date()
172185
service.pid = await getServicePid(service)
173186

174-
console.log(`✅ ${service.definition?.displayName || serviceName} started successfully`)
187+
logUniqueMessage(`✅ ${service.definition?.displayName || serviceName} started successfully`, true)
175188

176189
// Execute post-start setup commands
177190
await executePostStartCommands(service)
191+
// For databases, perform a final readiness handshake to avoid races with immediate consumers
192+
if (service.definition?.name === 'postgres' && process.platform === 'darwin') {
193+
try {
194+
// quick extra wait to ensure socket/listen transition is complete
195+
await new Promise(r => setTimeout(r, 300))
196+
}
197+
catch {}
198+
}
178199

179200
// Health check after starting
180201
setTimeout(() => {
@@ -1167,7 +1188,7 @@ async function executePostStartCommands(service: ServiceInstance): Promise<void>
11671188
}
11681189

11691190
// Announce post-start setup phase for databases
1170-
logUniqueMessage(`🔧 Running ${definition.displayName} post-start setup...`, true)
1191+
logUniqueMessage(`🔧 ${definition.displayName} post-start setup...`, true)
11711192
}
11721193
else {
11731194
// For other services, use the original wait time

0 commit comments

Comments
 (0)