Skip to content

Commit fa66533

Browse files
committed
chore: wip
1 parent 21b341c commit fa66533

File tree

13 files changed

+1733
-1298
lines changed

13 files changed

+1733
-1298
lines changed

.github/workflows/precompile-php.yml

Lines changed: 83 additions & 1190 deletions
Large diffs are not rendered by default.

packages/launchpad/src/binary-downloader.ts

Lines changed: 306 additions & 86 deletions
Large diffs are not rendered by default.

packages/launchpad/src/commands/install.ts

Lines changed: 27 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ async function ensureShellIntegrationInstalled(): Promise<void> {
9898
catch {}
9999
}
100100

101-
async function installPackagesGlobally(packages: string[], options: { verbose?: boolean, quiet?: boolean }) {
101+
async function installPackagesGlobally(packages: string[], options: { verbose?: boolean, quiet?: boolean, noInteractive?: boolean }) {
102102
const { install } = await import('../install-main')
103103
const globalEnvDir = path.join(homedir(), '.local', 'share', 'launchpad', 'global')
104104
if (!options.quiet)
@@ -120,8 +120,10 @@ async function installPackagesGlobally(packages: string[], options: { verbose?:
120120
}
121121

122122
await createGlobalBinarySymlinks(globalEnvDir)
123-
await ensureShellIntegrationInstalled()
124-
triggerShellGlobalRefresh()
123+
if (!options.quiet && !options.noInteractive) {
124+
await ensureShellIntegrationInstalled()
125+
triggerShellGlobalRefresh()
126+
}
125127

126128
if (!options.quiet) {
127129
if (results.length > 0) {
@@ -134,7 +136,7 @@ async function installPackagesGlobally(packages: string[], options: { verbose?:
134136
}
135137
}
136138

137-
async function installGlobalDependencies(options: { dryRun?: boolean, quiet?: boolean, verbose?: boolean }) {
139+
async function installGlobalDependencies(options: { dryRun?: boolean, quiet?: boolean, verbose?: boolean, noInteractive?: boolean }) {
138140
if (!options.quiet)
139141
console.log('🔍 Scanning machine for dependency files...')
140142

@@ -350,17 +352,20 @@ async function installGlobalDependencies(options: { dryRun?: boolean, quiet?: bo
350352
return val.some(v => typeof v === 'string' && pkgNames.includes(v))
351353
return false
352354
}
353-
const globalBuildDeps = (config as any).installBuildDeps
354-
const phpBuildDeps = (config.services?.php as any)?.installBuildDeps
355-
const shouldInstallBuildDeps = (
356-
process.env.LAUNCHPAD_INSTALL_BUILD_DEPS === '1'
357-
|| globalBuildDeps === true
358-
|| isListed(globalBuildDeps)
359-
|| phpBuildDeps === true
360-
|| isListed(phpBuildDeps)
361-
)
362-
363-
if (!shouldInstallBuildDeps) {
355+
// Check for excluded dependencies configuration
356+
const excludedDeps = (config as any).excludeDependencies || []
357+
const globalExcludedDeps = (config as any).excludeGlobalDependencies || []
358+
const phpExcludedDeps = (config.services?.php as any)?.excludeDependencies || []
359+
360+
const allExcludedDeps = new Set([
361+
...excludedDeps,
362+
...globalExcludedDeps,
363+
...phpExcludedDeps
364+
])
365+
366+
// Always install PHP dependencies by default - they are runtime dependencies, not build dependencies
367+
// Only exclude if explicitly configured to do so
368+
if (allExcludedDeps.size > 0) {
364369
try {
365370
const { pantry } = await import('ts-pkgx')
366371
const phpPackage = (pantry as any)?.phpnet
@@ -375,13 +380,13 @@ async function installGlobalDependencies(options: { dryRun?: boolean, quiet?: bo
375380
name = name.replace(/[~^<>=].*$/, '')
376381
return name
377382
}).filter(Boolean)
383+
378384
if (phpDeps.length > 0) {
379-
const phpDepSet = new Set(phpDeps)
380385
const before = filteredPackages.length
381-
filteredPackages = filteredPackages.filter(pkg => !phpDepSet.has(pkg))
386+
filteredPackages = filteredPackages.filter(pkg => !allExcludedDeps.has(pkg))
382387
const removed = before - filteredPackages.length
383388
if (options.verbose && removed > 0)
384-
console.log(`ℹ️ Skipping ${removed} PHP build-time dependencies during global auto-install. Set LAUNCHPAD_INSTALL_BUILD_DEPS=1 to include them.`)
389+
console.log(`ℹ️ Excluded ${removed} dependencies from global install based on configuration.`)
385390
}
386391
}
387392
catch {}
@@ -404,8 +409,10 @@ async function installGlobalDependencies(options: { dryRun?: boolean, quiet?: bo
404409
delete process.env.LAUNCHPAD_SUPPRESS_INSTALL_SUMMARY
405410

406411
await createGlobalBinarySymlinks(globalEnvDir)
407-
await ensureShellIntegrationInstalled()
408-
triggerShellGlobalRefresh()
412+
if (!options.noInteractive) {
413+
await ensureShellIntegrationInstalled()
414+
triggerShellGlobalRefresh()
415+
}
409416

410417
if (!options.quiet) {
411418
if (results.length > 0)

packages/launchpad/src/config.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,8 +50,8 @@ export const defaultConfig: LaunchpadConfig = {
5050
useRegistry: true,
5151
installMethod: 'curl',
5252
installPath: getDefaultInstallPath(),
53-
// By default, do NOT install runtime dependencies unless explicitly enabled
54-
installDependencies: process.env.LAUNCHPAD_INSTALL_DEPS === '1' || process.env.LAUNCHPAD_INSTALL_DEPS === 'true' || false,
53+
// By default, install runtime dependencies - they are needed for packages to work properly
54+
installDependencies: process.env.LAUNCHPAD_INSTALL_DEPS === '0' || process.env.LAUNCHPAD_INSTALL_DEPS === 'false' ? false : true,
5555
// By default, do NOT install build-time dependencies (pantry/build deps)
5656
installBuildDeps: process.env.LAUNCHPAD_INSTALL_BUILD_DEPS === '1' || process.env.LAUNCHPAD_INSTALL_BUILD_DEPS === 'true' || false,
5757
postSetup: {
Lines changed: 229 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,229 @@
1+
import { execSync } from 'node:child_process'
2+
import { existsSync } from 'node:fs'
3+
import { join } from 'node:path'
4+
import { homedir, platform } from 'node:os'
5+
import { logUniqueMessage } from '../logging'
6+
import { install } from '../install'
7+
8+
export interface DependencyPaths {
9+
includeDirs: string[]
10+
libDirs: string[]
11+
pkgConfigDirs: string[]
12+
}
13+
14+
export class BuildDependencyManager {
15+
private launchpadDir: string
16+
private currentPlatform: string
17+
18+
constructor() {
19+
this.currentPlatform = platform()
20+
this.launchpadDir = join(homedir(), '.local')
21+
}
22+
23+
async installBuildDependencies(): Promise<void> {
24+
if (this.currentPlatform === 'darwin') {
25+
await this.installMacOSDependencies()
26+
} else if (this.currentPlatform === 'linux') {
27+
await this.installLinuxDependencies()
28+
}
29+
}
30+
31+
private async installMacOSDependencies(): Promise<void> {
32+
logUniqueMessage('Installing macOS build dependencies via Launchpad...')
33+
34+
const launchpadPackages = [
35+
'gnu.org/autoconf',
36+
'gnu.org/automake',
37+
'gnu.org/libtool',
38+
'gnu.org/bison',
39+
're2c.org',
40+
'freedesktop.org/pkg-config',
41+
'xmlsoft.org/libxml2',
42+
'openssl.org',
43+
'curl.se',
44+
'libzip.org',
45+
'zlib.net',
46+
'github.com/kkos/oniguruma',
47+
'sqlite.org',
48+
'postgresql.org',
49+
'mysql.com'
50+
]
51+
52+
try {
53+
// Install packages via Launchpad
54+
const installDir = this.launchpadDir
55+
56+
for (const pkg of launchpadPackages) {
57+
logUniqueMessage(`Installing ${pkg}...`)
58+
await install([pkg], installDir)
59+
}
60+
61+
logUniqueMessage('✅ macOS dependencies installed successfully')
62+
} catch (error) {
63+
throw new Error(`Failed to install macOS dependencies: ${error}`)
64+
}
65+
}
66+
67+
private async installLinuxDependencies(): Promise<void> {
68+
logUniqueMessage('Installing Linux build dependencies via Launchpad...')
69+
70+
const launchpadPackages = [
71+
'gnu.org/gcc',
72+
'gnu.org/autoconf',
73+
'gnu.org/automake',
74+
'gnu.org/libtool',
75+
'gnu.org/bison',
76+
're2c.org',
77+
'freedesktop.org/pkg-config',
78+
'xmlsoft.org/libxml2',
79+
'openssl.org',
80+
'curl.se',
81+
'libzip.org',
82+
'zlib.net',
83+
'github.com/kkos/oniguruma',
84+
'sqlite.org',
85+
'postgresql.org',
86+
'mysql.com'
87+
]
88+
89+
try {
90+
// Install packages via Launchpad
91+
const installDir = this.launchpadDir
92+
93+
for (const pkg of launchpadPackages) {
94+
logUniqueMessage(`Installing ${pkg}...`)
95+
await install([pkg], installDir)
96+
}
97+
98+
logUniqueMessage('✅ Linux dependencies installed successfully')
99+
} catch (error) {
100+
throw new Error(`Failed to install Linux dependencies: ${error}`)
101+
}
102+
}
103+
104+
getDependencyPaths(): DependencyPaths {
105+
if (this.currentPlatform === 'darwin') {
106+
return this.findLaunchpadPaths()
107+
} else if (this.currentPlatform === 'linux') {
108+
return this.getLinuxDependencyPaths()
109+
}
110+
111+
return { includeDirs: [], libDirs: [], pkgConfigDirs: [] }
112+
}
113+
114+
private findLaunchpadPaths(): DependencyPaths {
115+
const launchpadDir = this.launchpadDir
116+
const includeDirs: string[] = []
117+
const libDirs: string[] = []
118+
const pkgConfigDirs: string[] = []
119+
120+
if (!existsSync(launchpadDir)) {
121+
logUniqueMessage(`⚠️ Launchpad directory not found: ${launchpadDir}`)
122+
return { includeDirs, libDirs, pkgConfigDirs }
123+
}
124+
125+
try {
126+
// Find all package directories in Launchpad
127+
const entries = execSync(`find "${launchpadDir}" -maxdepth 3 -type d -name "v*"`, { encoding: 'utf8' })
128+
.split('\n')
129+
.filter(Boolean)
130+
131+
for (const entry of entries) {
132+
// Add include directories
133+
const includeDir = join(entry, 'include')
134+
if (existsSync(includeDir)) {
135+
includeDirs.push(includeDir)
136+
}
137+
138+
// Add lib directories
139+
const libDir = join(entry, 'lib')
140+
if (existsSync(libDir)) {
141+
libDirs.push(libDir)
142+
}
143+
144+
// Add pkgconfig directories
145+
const pkgConfigDir = join(entry, 'lib', 'pkgconfig')
146+
if (existsSync(pkgConfigDir)) {
147+
pkgConfigDirs.push(pkgConfigDir)
148+
}
149+
}
150+
151+
logUniqueMessage(`Found ${includeDirs.length} include dirs, ${libDirs.length} lib dirs, ${pkgConfigDirs.length} pkgconfig dirs`)
152+
153+
} catch (error) {
154+
logUniqueMessage(`⚠️ Failed to scan Launchpad directories: ${error}`)
155+
}
156+
157+
return { includeDirs, libDirs, pkgConfigDirs }
158+
}
159+
160+
private getLinuxDependencyPaths(): DependencyPaths {
161+
return {
162+
includeDirs: ['/usr/include', '/usr/local/include'],
163+
libDirs: ['/usr/lib', '/usr/local/lib', '/usr/lib/x86_64-linux-gnu'],
164+
pkgConfigDirs: ['/usr/lib/pkgconfig', '/usr/local/lib/pkgconfig', '/usr/lib/x86_64-linux-gnu/pkgconfig']
165+
}
166+
}
167+
168+
findPostgresPrefix(): string | null {
169+
const possiblePaths = [
170+
join(this.launchpadDir, 'postgresql.org'),
171+
'/usr/lib/postgresql/16',
172+
'/usr/lib/postgresql/15',
173+
'/usr/lib/postgresql/14',
174+
'/usr/lib/postgresql/13',
175+
'/usr/local/pgsql'
176+
]
177+
178+
for (const path of possiblePaths) {
179+
if (existsSync(path)) {
180+
// For Launchpad installations, find the latest version
181+
if (path.includes('postgresql.org')) {
182+
try {
183+
const versions = execSync(`find "${path}" -maxdepth 1 -type d -name "v*" | sort -V | tail -1`, { encoding: 'utf8' }).trim()
184+
if (versions && existsSync(versions)) {
185+
return versions
186+
}
187+
} catch (error) {
188+
continue
189+
}
190+
}
191+
return path
192+
}
193+
}
194+
195+
return null
196+
}
197+
198+
findLibraryPrefix(library: string): string | null {
199+
// Check Launchpad first
200+
const launchpadPath = join(this.launchpadDir, library)
201+
if (existsSync(launchpadPath)) {
202+
// Find the latest version directory
203+
try {
204+
const versions = execSync(`find "${launchpadPath}" -maxdepth 1 -type d -name "v*" | sort -V | tail -1`, { encoding: 'utf8' }).trim()
205+
if (versions && existsSync(versions)) {
206+
return versions
207+
}
208+
} catch (error) {
209+
// Continue to system paths
210+
}
211+
}
212+
213+
// Fallback to system paths
214+
const systemPaths = [
215+
`/usr/local/opt/${library}`,
216+
`/usr/lib/${library}`,
217+
`/usr/local/lib/${library}`,
218+
`/opt/${library}`
219+
]
220+
221+
for (const path of systemPaths) {
222+
if (existsSync(path)) {
223+
return path
224+
}
225+
}
226+
227+
return null
228+
}
229+
}

0 commit comments

Comments
 (0)