spindb 0.4.0 → 0.5.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +77 -100
- package/cli/commands/clone.ts +3 -1
- package/cli/commands/connect.ts +50 -24
- package/cli/commands/create.ts +265 -112
- package/cli/commands/delete.ts +3 -1
- package/cli/commands/list.ts +14 -3
- package/cli/commands/menu.ts +250 -84
- package/cli/commands/restore.ts +142 -38
- package/cli/commands/start.ts +30 -4
- package/cli/commands/stop.ts +3 -1
- package/cli/ui/prompts.ts +95 -32
- package/config/defaults.ts +40 -15
- package/config/engine-defaults.ts +84 -0
- package/config/os-dependencies.ts +68 -19
- package/config/paths.ts +116 -23
- package/core/binary-manager.ts +30 -5
- package/core/container-manager.ts +124 -60
- package/core/dependency-manager.ts +44 -22
- package/core/port-manager.ts +42 -31
- package/core/postgres-binary-manager.ts +10 -9
- package/core/process-manager.ts +14 -6
- package/engines/index.ts +7 -2
- package/engines/mysql/binary-detection.ts +248 -0
- package/engines/mysql/index.ts +699 -0
- package/engines/postgresql/index.ts +13 -6
- package/package.json +4 -2
- package/types/index.ts +29 -5
package/cli/commands/restore.ts
CHANGED
|
@@ -15,6 +15,7 @@ import { success, error, warning } from '../ui/theme'
|
|
|
15
15
|
import { platform, tmpdir } from 'os'
|
|
16
16
|
import { spawn } from 'child_process'
|
|
17
17
|
import { join } from 'path'
|
|
18
|
+
import { getMissingDependencies } from '../../core/dependency-manager'
|
|
18
19
|
|
|
19
20
|
export const restoreCommand = new Command('restore')
|
|
20
21
|
.description('Restore a backup to a container')
|
|
@@ -75,8 +76,12 @@ export const restoreCommand = new Command('restore')
|
|
|
75
76
|
process.exit(1)
|
|
76
77
|
}
|
|
77
78
|
|
|
79
|
+
const { engine: engineName } = config
|
|
80
|
+
|
|
78
81
|
// Check if running
|
|
79
|
-
const running = await processManager.isRunning(containerName
|
|
82
|
+
const running = await processManager.isRunning(containerName, {
|
|
83
|
+
engine: engineName,
|
|
84
|
+
})
|
|
80
85
|
if (!running) {
|
|
81
86
|
console.error(
|
|
82
87
|
error(
|
|
@@ -87,18 +92,75 @@ export const restoreCommand = new Command('restore')
|
|
|
87
92
|
}
|
|
88
93
|
|
|
89
94
|
// Get engine
|
|
90
|
-
const engine = getEngine(
|
|
95
|
+
const engine = getEngine(engineName)
|
|
96
|
+
|
|
97
|
+
// Check for required client tools BEFORE doing anything
|
|
98
|
+
const depsSpinner = createSpinner('Checking required tools...')
|
|
99
|
+
depsSpinner.start()
|
|
100
|
+
|
|
101
|
+
let missingDeps = await getMissingDependencies(config.engine)
|
|
102
|
+
if (missingDeps.length > 0) {
|
|
103
|
+
depsSpinner.warn(
|
|
104
|
+
`Missing tools: ${missingDeps.map((d) => d.name).join(', ')}`,
|
|
105
|
+
)
|
|
106
|
+
|
|
107
|
+
// Offer to install
|
|
108
|
+
const installed = await promptInstallDependencies(
|
|
109
|
+
missingDeps[0].binary,
|
|
110
|
+
config.engine,
|
|
111
|
+
)
|
|
112
|
+
|
|
113
|
+
if (!installed) {
|
|
114
|
+
process.exit(1)
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// Verify installation worked
|
|
118
|
+
missingDeps = await getMissingDependencies(config.engine)
|
|
119
|
+
if (missingDeps.length > 0) {
|
|
120
|
+
console.error(
|
|
121
|
+
error(
|
|
122
|
+
`Still missing tools: ${missingDeps.map((d) => d.name).join(', ')}`,
|
|
123
|
+
),
|
|
124
|
+
)
|
|
125
|
+
process.exit(1)
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
console.log(chalk.green(' ✓ All required tools are now available'))
|
|
129
|
+
console.log()
|
|
130
|
+
} else {
|
|
131
|
+
depsSpinner.succeed('Required tools available')
|
|
132
|
+
}
|
|
91
133
|
|
|
92
134
|
// Handle --from-url option
|
|
93
135
|
if (options.fromUrl) {
|
|
94
|
-
// Validate connection string
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
)
|
|
136
|
+
// Validate connection string matches container's engine
|
|
137
|
+
const isPgUrl =
|
|
138
|
+
options.fromUrl.startsWith('postgresql://') ||
|
|
139
|
+
options.fromUrl.startsWith('postgres://')
|
|
140
|
+
const isMysqlUrl = options.fromUrl.startsWith('mysql://')
|
|
141
|
+
|
|
142
|
+
if (engineName === 'postgresql' && !isPgUrl) {
|
|
143
|
+
console.error(
|
|
144
|
+
error(
|
|
145
|
+
'Connection string must start with postgresql:// or postgres:// for PostgreSQL containers',
|
|
146
|
+
),
|
|
147
|
+
)
|
|
148
|
+
process.exit(1)
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
if (engineName === 'mysql' && !isMysqlUrl) {
|
|
99
152
|
console.error(
|
|
100
153
|
error(
|
|
101
|
-
'Connection string must start with
|
|
154
|
+
'Connection string must start with mysql:// for MySQL containers',
|
|
155
|
+
),
|
|
156
|
+
)
|
|
157
|
+
process.exit(1)
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
if (!isPgUrl && !isMysqlUrl) {
|
|
161
|
+
console.error(
|
|
162
|
+
error(
|
|
163
|
+
'Connection string must start with postgresql://, postgres://, or mysql://',
|
|
102
164
|
),
|
|
103
165
|
)
|
|
104
166
|
process.exit(1)
|
|
@@ -108,31 +170,53 @@ export const restoreCommand = new Command('restore')
|
|
|
108
170
|
const timestamp = Date.now()
|
|
109
171
|
tempDumpPath = join(tmpdir(), `spindb-dump-${timestamp}.dump`)
|
|
110
172
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
dumpSpinner.start()
|
|
173
|
+
let dumpSuccess = false
|
|
174
|
+
let attempts = 0
|
|
175
|
+
const maxAttempts = 2 // Allow one retry after installing deps
|
|
115
176
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
dumpSpinner
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
) {
|
|
129
|
-
|
|
177
|
+
while (!dumpSuccess && attempts < maxAttempts) {
|
|
178
|
+
attempts++
|
|
179
|
+
const dumpSpinner = createSpinner(
|
|
180
|
+
'Creating dump from remote database...',
|
|
181
|
+
)
|
|
182
|
+
dumpSpinner.start()
|
|
183
|
+
|
|
184
|
+
try {
|
|
185
|
+
await engine.dumpFromConnectionString(options.fromUrl, tempDumpPath)
|
|
186
|
+
dumpSpinner.succeed('Dump created from remote database')
|
|
187
|
+
backupPath = tempDumpPath
|
|
188
|
+
dumpSuccess = true
|
|
189
|
+
} catch (err) {
|
|
190
|
+
const e = err as Error
|
|
191
|
+
dumpSpinner.fail('Failed to create dump')
|
|
192
|
+
|
|
193
|
+
// Check if this is a missing tool error
|
|
194
|
+
const dumpTool = engineName === 'mysql' ? 'mysqldump' : 'pg_dump'
|
|
195
|
+
if (
|
|
196
|
+
e.message.includes(`${dumpTool} not found`) ||
|
|
197
|
+
e.message.includes('ENOENT')
|
|
198
|
+
) {
|
|
199
|
+
const installed = await promptInstallDependencies(
|
|
200
|
+
dumpTool,
|
|
201
|
+
engineName,
|
|
202
|
+
)
|
|
203
|
+
if (!installed) {
|
|
204
|
+
process.exit(1)
|
|
205
|
+
}
|
|
206
|
+
// Loop will retry
|
|
207
|
+
continue
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
console.log()
|
|
211
|
+
console.error(error(`${dumpTool} error:`))
|
|
212
|
+
console.log(chalk.gray(` ${e.message}`))
|
|
130
213
|
process.exit(1)
|
|
131
214
|
}
|
|
215
|
+
}
|
|
132
216
|
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
console.
|
|
217
|
+
// Safety check - should never reach here without backupPath set
|
|
218
|
+
if (!dumpSuccess) {
|
|
219
|
+
console.error(error('Failed to create dump after retries'))
|
|
136
220
|
process.exit(1)
|
|
137
221
|
}
|
|
138
222
|
} else {
|
|
@@ -162,6 +246,12 @@ export const restoreCommand = new Command('restore')
|
|
|
162
246
|
databaseName = await promptDatabaseName(containerName)
|
|
163
247
|
}
|
|
164
248
|
|
|
249
|
+
// At this point backupPath is guaranteed to be set
|
|
250
|
+
if (!backupPath) {
|
|
251
|
+
console.error(error('No backup path specified'))
|
|
252
|
+
process.exit(1)
|
|
253
|
+
}
|
|
254
|
+
|
|
165
255
|
// Detect backup format
|
|
166
256
|
const detectSpinner = createSpinner('Detecting backup format...')
|
|
167
257
|
detectSpinner.start()
|
|
@@ -251,15 +341,29 @@ export const restoreCommand = new Command('restore')
|
|
|
251
341
|
} catch (err) {
|
|
252
342
|
const e = err as Error
|
|
253
343
|
|
|
254
|
-
// Check if this is a missing tool error
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
344
|
+
// Check if this is a missing tool error (PostgreSQL or MySQL)
|
|
345
|
+
const missingToolPatterns = [
|
|
346
|
+
// PostgreSQL
|
|
347
|
+
'pg_restore not found',
|
|
348
|
+
'psql not found',
|
|
349
|
+
'pg_dump not found',
|
|
350
|
+
// MySQL
|
|
351
|
+
'mysql not found',
|
|
352
|
+
'mysqldump not found',
|
|
353
|
+
]
|
|
354
|
+
|
|
355
|
+
const matchingPattern = missingToolPatterns.find((p) =>
|
|
356
|
+
e.message.includes(p),
|
|
357
|
+
)
|
|
358
|
+
|
|
359
|
+
if (matchingPattern) {
|
|
360
|
+
const missingTool = matchingPattern.replace(' not found', '')
|
|
361
|
+
const installed = await promptInstallDependencies(missingTool)
|
|
362
|
+
if (installed) {
|
|
363
|
+
console.log(
|
|
364
|
+
chalk.yellow(' Please re-run your command to continue.'),
|
|
365
|
+
)
|
|
366
|
+
}
|
|
263
367
|
process.exit(1)
|
|
264
368
|
}
|
|
265
369
|
|
package/cli/commands/start.ts
CHANGED
|
@@ -4,6 +4,7 @@ import { containerManager } from '../../core/container-manager'
|
|
|
4
4
|
import { portManager } from '../../core/port-manager'
|
|
5
5
|
import { processManager } from '../../core/process-manager'
|
|
6
6
|
import { getEngine } from '../../engines'
|
|
7
|
+
import { getEngineDefaults } from '../../config/defaults'
|
|
7
8
|
import { promptContainerSelect } from '../ui/prompts'
|
|
8
9
|
import { createSpinner } from '../ui/spinner'
|
|
9
10
|
import { error, warning } from '../ui/theme'
|
|
@@ -46,18 +47,27 @@ export const startCommand = new Command('start')
|
|
|
46
47
|
process.exit(1)
|
|
47
48
|
}
|
|
48
49
|
|
|
50
|
+
const { engine: engineName } = config
|
|
51
|
+
|
|
49
52
|
// Check if already running
|
|
50
|
-
const running = await processManager.isRunning(containerName
|
|
53
|
+
const running = await processManager.isRunning(containerName, {
|
|
54
|
+
engine: engineName,
|
|
55
|
+
})
|
|
51
56
|
if (running) {
|
|
52
57
|
console.log(warning(`Container "${containerName}" is already running`))
|
|
53
58
|
return
|
|
54
59
|
}
|
|
55
60
|
|
|
61
|
+
// Get engine defaults for port range and database name
|
|
62
|
+
const engineDefaults = getEngineDefaults(engineName)
|
|
63
|
+
|
|
56
64
|
// Check port availability
|
|
57
65
|
const portAvailable = await portManager.isPortAvailable(config.port)
|
|
58
66
|
if (!portAvailable) {
|
|
59
|
-
// Try to find a new port
|
|
60
|
-
const { port: newPort } = await portManager.findAvailablePort(
|
|
67
|
+
// Try to find a new port (using engine-specific port range)
|
|
68
|
+
const { port: newPort } = await portManager.findAvailablePort({
|
|
69
|
+
portRange: engineDefaults.portRange,
|
|
70
|
+
})
|
|
61
71
|
console.log(
|
|
62
72
|
warning(
|
|
63
73
|
`Port ${config.port} is in use, switching to port ${newPort}`,
|
|
@@ -68,7 +78,7 @@ export const startCommand = new Command('start')
|
|
|
68
78
|
}
|
|
69
79
|
|
|
70
80
|
// Get engine and start
|
|
71
|
-
const engine = getEngine(
|
|
81
|
+
const engine = getEngine(engineName)
|
|
72
82
|
|
|
73
83
|
const spinner = createSpinner(`Starting ${containerName}...`)
|
|
74
84
|
spinner.start()
|
|
@@ -78,6 +88,22 @@ export const startCommand = new Command('start')
|
|
|
78
88
|
|
|
79
89
|
spinner.succeed(`Container "${containerName}" started`)
|
|
80
90
|
|
|
91
|
+
// Ensure the user's database exists (if different from default)
|
|
92
|
+
const defaultDb = engineDefaults.superuser // postgres or root
|
|
93
|
+
if (config.database && config.database !== defaultDb) {
|
|
94
|
+
const dbSpinner = createSpinner(
|
|
95
|
+
`Ensuring database "${config.database}" exists...`,
|
|
96
|
+
)
|
|
97
|
+
dbSpinner.start()
|
|
98
|
+
try {
|
|
99
|
+
await engine.createDatabase(config, config.database)
|
|
100
|
+
dbSpinner.succeed(`Database "${config.database}" ready`)
|
|
101
|
+
} catch {
|
|
102
|
+
// Database might already exist, which is fine
|
|
103
|
+
dbSpinner.succeed(`Database "${config.database}" ready`)
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
81
107
|
// Show connection info
|
|
82
108
|
const connectionString = engine.getConnectionString(config)
|
|
83
109
|
console.log()
|
package/cli/commands/stop.ts
CHANGED
|
@@ -67,7 +67,9 @@ export const stopCommand = new Command('stop')
|
|
|
67
67
|
}
|
|
68
68
|
|
|
69
69
|
// Check if running
|
|
70
|
-
const running = await processManager.isRunning(containerName
|
|
70
|
+
const running = await processManager.isRunning(containerName, {
|
|
71
|
+
engine: config.engine,
|
|
72
|
+
})
|
|
71
73
|
if (!running) {
|
|
72
74
|
console.log(warning(`Container "${containerName}" is not running`))
|
|
73
75
|
return
|
package/cli/ui/prompts.ts
CHANGED
|
@@ -2,12 +2,13 @@ import inquirer from 'inquirer'
|
|
|
2
2
|
import chalk from 'chalk'
|
|
3
3
|
import ora from 'ora'
|
|
4
4
|
import { listEngines, getEngine } from '../../engines'
|
|
5
|
-
import { defaults } from '../../config/defaults'
|
|
5
|
+
import { defaults, getEngineDefaults } from '../../config/defaults'
|
|
6
6
|
import { installPostgresBinaries } from '../../core/postgres-binary-manager'
|
|
7
7
|
import {
|
|
8
8
|
detectPackageManager,
|
|
9
9
|
getManualInstallInstructions,
|
|
10
10
|
getCurrentPlatform,
|
|
11
|
+
installEngineDependencies,
|
|
11
12
|
} from '../../core/dependency-manager'
|
|
12
13
|
import { getEngineDependencies } from '../../config/os-dependencies'
|
|
13
14
|
import type { ContainerConfig } from '../../types'
|
|
@@ -36,25 +37,26 @@ export async function promptContainerName(
|
|
|
36
37
|
return name
|
|
37
38
|
}
|
|
38
39
|
|
|
40
|
+
/**
|
|
41
|
+
* Engine icons for display
|
|
42
|
+
*/
|
|
43
|
+
const engineIcons: Record<string, string> = {
|
|
44
|
+
postgresql: '🐘',
|
|
45
|
+
mysql: '🐬',
|
|
46
|
+
}
|
|
47
|
+
|
|
39
48
|
/**
|
|
40
49
|
* Prompt for database engine selection
|
|
41
50
|
*/
|
|
42
51
|
export async function promptEngine(): Promise<string> {
|
|
43
52
|
const engines = listEngines()
|
|
44
53
|
|
|
45
|
-
// Build choices from available engines
|
|
46
|
-
const choices =
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
})),
|
|
52
|
-
{
|
|
53
|
-
name: chalk.gray('🐬 MySQL (coming soon)'),
|
|
54
|
-
value: 'mysql',
|
|
55
|
-
disabled: 'Coming soon',
|
|
56
|
-
},
|
|
57
|
-
]
|
|
54
|
+
// Build choices from available engines
|
|
55
|
+
const choices = engines.map((e) => ({
|
|
56
|
+
name: `${engineIcons[e.name] || '🗄️'} ${e.displayName} ${chalk.gray(`(versions: ${e.supportedVersions.join(', ')})`)}`,
|
|
57
|
+
value: e.name,
|
|
58
|
+
short: e.displayName,
|
|
59
|
+
}))
|
|
58
60
|
|
|
59
61
|
const { engine } = await inquirer.prompt<{ engine: string }>([
|
|
60
62
|
{
|
|
@@ -69,8 +71,8 @@ export async function promptEngine(): Promise<string> {
|
|
|
69
71
|
}
|
|
70
72
|
|
|
71
73
|
/**
|
|
72
|
-
* Prompt for
|
|
73
|
-
* Two-step selection: first major version, then specific minor version
|
|
74
|
+
* Prompt for database version
|
|
75
|
+
* Two-step selection: first major version, then specific minor version (if available)
|
|
74
76
|
*/
|
|
75
77
|
export async function promptVersion(engineName: string): Promise<string> {
|
|
76
78
|
const engine = getEngine(engineName)
|
|
@@ -112,13 +114,13 @@ export async function promptVersion(engineName: string): Promise<string> {
|
|
|
112
114
|
const countLabel =
|
|
113
115
|
versionCount > 0 ? chalk.gray(`(${versionCount} versions)`) : ''
|
|
114
116
|
const label = isLatestMajor
|
|
115
|
-
?
|
|
116
|
-
:
|
|
117
|
+
? `${engine.displayName} ${major} ${countLabel} ${chalk.green('← latest')}`
|
|
118
|
+
: `${engine.displayName} ${major} ${countLabel}`
|
|
117
119
|
|
|
118
120
|
majorChoices.push({
|
|
119
121
|
name: label,
|
|
120
122
|
value: major,
|
|
121
|
-
short:
|
|
123
|
+
short: `${engine.displayName} ${major}`,
|
|
122
124
|
})
|
|
123
125
|
}
|
|
124
126
|
|
|
@@ -150,7 +152,7 @@ export async function promptVersion(engineName: string): Promise<string> {
|
|
|
150
152
|
{
|
|
151
153
|
type: 'list',
|
|
152
154
|
name: 'version',
|
|
153
|
-
message: `Select
|
|
155
|
+
message: `Select ${engine.displayName} ${majorVersion} version:`,
|
|
154
156
|
choices: minorChoices,
|
|
155
157
|
default: minorVersions[0], // Default to latest
|
|
156
158
|
},
|
|
@@ -225,7 +227,7 @@ export async function promptContainerSelect(
|
|
|
225
227
|
name: 'container',
|
|
226
228
|
message,
|
|
227
229
|
choices: containers.map((c) => ({
|
|
228
|
-
name: `${c.name} ${chalk.gray(`(${c.engine} ${c.version}, port ${c.port})`)} ${
|
|
230
|
+
name: `${c.name} ${chalk.gray(`(${engineIcons[c.engine] || '🗄️'} ${c.engine} ${c.version}, port ${c.port})`)} ${
|
|
229
231
|
c.status === 'running'
|
|
230
232
|
? chalk.green('● running')
|
|
231
233
|
: chalk.gray('○ stopped')
|
|
@@ -279,16 +281,17 @@ export type CreateOptions = {
|
|
|
279
281
|
/**
|
|
280
282
|
* Full interactive create flow
|
|
281
283
|
*/
|
|
282
|
-
export async function promptCreateOptions(
|
|
283
|
-
defaultPort: number = defaults.port,
|
|
284
|
-
): Promise<CreateOptions> {
|
|
284
|
+
export async function promptCreateOptions(): Promise<CreateOptions> {
|
|
285
285
|
console.log(chalk.cyan('\n 🗄️ Create New Database Container\n'))
|
|
286
286
|
|
|
287
287
|
const engine = await promptEngine()
|
|
288
288
|
const version = await promptVersion(engine)
|
|
289
289
|
const name = await promptContainerName()
|
|
290
290
|
const database = await promptDatabaseName(name) // Default to container name
|
|
291
|
-
|
|
291
|
+
|
|
292
|
+
// Get engine-specific default port
|
|
293
|
+
const engineDefaults = getEngineDefaults(engine)
|
|
294
|
+
const port = await promptPort(engineDefaults.defaultPort)
|
|
292
295
|
|
|
293
296
|
return { name, engine, version, port, database }
|
|
294
297
|
}
|
|
@@ -393,8 +396,7 @@ export async function promptInstallDependencies(
|
|
|
393
396
|
|
|
394
397
|
console.log()
|
|
395
398
|
|
|
396
|
-
//
|
|
397
|
-
// Future engines will need their own install functions
|
|
399
|
+
// PostgreSQL has its own install function with extra logic
|
|
398
400
|
if (engine === 'postgresql') {
|
|
399
401
|
const success = await installPostgresBinaries()
|
|
400
402
|
|
|
@@ -403,18 +405,79 @@ export async function promptInstallDependencies(
|
|
|
403
405
|
console.log(
|
|
404
406
|
chalk.green(` ${engineName} client tools installed successfully!`),
|
|
405
407
|
)
|
|
406
|
-
console.log(chalk.gray('
|
|
408
|
+
console.log(chalk.gray(' Continuing with your operation...'))
|
|
407
409
|
console.log()
|
|
408
410
|
}
|
|
409
411
|
|
|
410
412
|
return success
|
|
411
413
|
}
|
|
412
414
|
|
|
413
|
-
// For other engines,
|
|
415
|
+
// For other engines (MySQL, etc.), use the generic installer
|
|
414
416
|
console.log(
|
|
415
|
-
chalk.
|
|
417
|
+
chalk.cyan(` Installing ${engineName} with ${packageManager.name}...`),
|
|
416
418
|
)
|
|
417
|
-
console.log(chalk.gray('
|
|
419
|
+
console.log(chalk.gray(' You may be prompted for your password.'))
|
|
418
420
|
console.log()
|
|
419
|
-
|
|
421
|
+
|
|
422
|
+
try {
|
|
423
|
+
const results = await installEngineDependencies(engine, packageManager)
|
|
424
|
+
const allSuccess = results.every((r) => r.success)
|
|
425
|
+
|
|
426
|
+
if (allSuccess) {
|
|
427
|
+
console.log()
|
|
428
|
+
console.log(
|
|
429
|
+
chalk.green(` ${engineName} tools installed successfully!`),
|
|
430
|
+
)
|
|
431
|
+
console.log(chalk.gray(' Continuing with your operation...'))
|
|
432
|
+
console.log()
|
|
433
|
+
return true
|
|
434
|
+
} else {
|
|
435
|
+
const failed = results.filter((r) => !r.success)
|
|
436
|
+
console.log()
|
|
437
|
+
console.log(chalk.red(' Some installations failed:'))
|
|
438
|
+
for (const f of failed) {
|
|
439
|
+
console.log(chalk.red(` ${f.dependency.name}: ${f.error}`))
|
|
440
|
+
}
|
|
441
|
+
console.log()
|
|
442
|
+
|
|
443
|
+
// Show manual install instructions
|
|
444
|
+
if (engineDeps) {
|
|
445
|
+
const instructions = getManualInstallInstructions(
|
|
446
|
+
engineDeps.dependencies[0],
|
|
447
|
+
platform,
|
|
448
|
+
)
|
|
449
|
+
if (instructions.length > 0) {
|
|
450
|
+
console.log(chalk.gray(' To install manually:'))
|
|
451
|
+
for (const instruction of instructions) {
|
|
452
|
+
console.log(chalk.gray(` ${instruction}`))
|
|
453
|
+
}
|
|
454
|
+
console.log()
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
return false
|
|
459
|
+
}
|
|
460
|
+
} catch (err) {
|
|
461
|
+
const e = err as Error
|
|
462
|
+
console.log()
|
|
463
|
+
console.log(chalk.red(` Installation failed: ${e.message}`))
|
|
464
|
+
console.log()
|
|
465
|
+
|
|
466
|
+
// Show manual install instructions on error
|
|
467
|
+
if (engineDeps) {
|
|
468
|
+
const instructions = getManualInstallInstructions(
|
|
469
|
+
engineDeps.dependencies[0],
|
|
470
|
+
platform,
|
|
471
|
+
)
|
|
472
|
+
if (instructions.length > 0) {
|
|
473
|
+
console.log(chalk.gray(' To install manually:'))
|
|
474
|
+
for (const instruction of instructions) {
|
|
475
|
+
console.log(chalk.gray(` ${instruction}`))
|
|
476
|
+
}
|
|
477
|
+
console.log()
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
return false
|
|
482
|
+
}
|
|
420
483
|
}
|
package/config/defaults.ts
CHANGED
|
@@ -1,3 +1,20 @@
|
|
|
1
|
+
import {
|
|
2
|
+
engineDefaults,
|
|
3
|
+
getEngineDefaults,
|
|
4
|
+
isEngineSupported,
|
|
5
|
+
getSupportedEngines,
|
|
6
|
+
type EngineDefaults,
|
|
7
|
+
} from './engine-defaults'
|
|
8
|
+
|
|
9
|
+
// Re-export engine-related functions and types
|
|
10
|
+
export {
|
|
11
|
+
engineDefaults,
|
|
12
|
+
getEngineDefaults,
|
|
13
|
+
isEngineSupported,
|
|
14
|
+
getSupportedEngines,
|
|
15
|
+
type EngineDefaults,
|
|
16
|
+
}
|
|
17
|
+
|
|
1
18
|
export type PlatformMappings = {
|
|
2
19
|
[key: string]: string
|
|
3
20
|
}
|
|
@@ -7,42 +24,50 @@ export type PortRange = {
|
|
|
7
24
|
end: number
|
|
8
25
|
}
|
|
9
26
|
|
|
27
|
+
/**
|
|
28
|
+
* Legacy Defaults type - kept for backward compatibility
|
|
29
|
+
* New code should use getEngineDefaults(engine) instead
|
|
30
|
+
*/
|
|
10
31
|
export type Defaults = {
|
|
32
|
+
/** @deprecated Use getEngineDefaults(engine).defaultVersion instead */
|
|
11
33
|
postgresVersion: string
|
|
12
34
|
port: number
|
|
13
35
|
portRange: PortRange
|
|
14
36
|
engine: string
|
|
37
|
+
/** @deprecated Use getEngineDefaults(engine).supportedVersions instead */
|
|
15
38
|
supportedPostgresVersions: string[]
|
|
16
39
|
superuser: string
|
|
17
40
|
platformMappings: PlatformMappings
|
|
18
41
|
}
|
|
19
42
|
|
|
20
|
-
//
|
|
21
|
-
|
|
22
|
-
|
|
43
|
+
// Get PostgreSQL defaults from engine-defaults
|
|
44
|
+
const pgDefaults = engineDefaults.postgresql
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Default configuration values
|
|
48
|
+
* For backward compatibility, this defaults to PostgreSQL settings.
|
|
49
|
+
* New code should use getEngineDefaults(engine) for engine-specific defaults.
|
|
50
|
+
*/
|
|
23
51
|
export const defaults: Defaults = {
|
|
24
|
-
// Default PostgreSQL version
|
|
25
|
-
postgresVersion:
|
|
52
|
+
// Default PostgreSQL version (from engine defaults)
|
|
53
|
+
postgresVersion: pgDefaults.defaultVersion,
|
|
26
54
|
|
|
27
55
|
// Default port (standard PostgreSQL port)
|
|
28
|
-
port:
|
|
56
|
+
port: pgDefaults.defaultPort,
|
|
29
57
|
|
|
30
58
|
// Port range to scan if default is busy
|
|
31
|
-
portRange:
|
|
32
|
-
start: 5432,
|
|
33
|
-
end: 5500,
|
|
34
|
-
},
|
|
59
|
+
portRange: pgDefaults.portRange,
|
|
35
60
|
|
|
36
61
|
// Default engine
|
|
37
62
|
engine: 'postgresql',
|
|
38
63
|
|
|
39
|
-
// Supported PostgreSQL versions
|
|
40
|
-
supportedPostgresVersions:
|
|
64
|
+
// Supported PostgreSQL versions (from engine defaults)
|
|
65
|
+
supportedPostgresVersions: pgDefaults.supportedVersions,
|
|
41
66
|
|
|
42
|
-
// Default superuser
|
|
43
|
-
superuser:
|
|
67
|
+
// Default superuser (from engine defaults)
|
|
68
|
+
superuser: pgDefaults.superuser,
|
|
44
69
|
|
|
45
|
-
// Platform mappings for zonky.io binaries
|
|
70
|
+
// Platform mappings for zonky.io binaries (PostgreSQL specific)
|
|
46
71
|
platformMappings: {
|
|
47
72
|
'darwin-arm64': 'darwin-arm64v8',
|
|
48
73
|
'darwin-x64': 'darwin-amd64',
|