genbox 1.0.3 → 1.0.4
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/dist/commands/create.js +369 -130
- package/dist/commands/db-sync.js +364 -0
- package/dist/commands/init.js +669 -402
- package/dist/commands/profiles.js +333 -0
- package/dist/commands/push.js +140 -47
- package/dist/config-loader.js +529 -0
- package/dist/index.js +5 -1
- package/dist/profile-resolver.js +547 -0
- package/dist/scanner/compose-parser.js +441 -0
- package/dist/scanner/config-generator.js +620 -0
- package/dist/scanner/env-analyzer.js +503 -0
- package/dist/scanner/framework-detector.js +621 -0
- package/dist/scanner/index.js +424 -0
- package/dist/scanner/runtime-detector.js +330 -0
- package/dist/scanner/structure-detector.js +412 -0
- package/dist/scanner/types.js +7 -0
- package/dist/schema-v3.js +12 -0
- package/package.json +2 -1
package/dist/commands/create.js
CHANGED
|
@@ -38,27 +38,27 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
38
38
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
39
|
exports.createCommand = void 0;
|
|
40
40
|
const commander_1 = require("commander");
|
|
41
|
-
const
|
|
41
|
+
const prompts = __importStar(require("@inquirer/prompts"));
|
|
42
42
|
const chalk_1 = __importDefault(require("chalk"));
|
|
43
43
|
const ora_1 = __importDefault(require("ora"));
|
|
44
|
-
const config_1 = require("../config");
|
|
45
44
|
const fs = __importStar(require("fs"));
|
|
46
45
|
const path = __importStar(require("path"));
|
|
47
46
|
const os = __importStar(require("os"));
|
|
47
|
+
const config_loader_1 = require("../config-loader");
|
|
48
|
+
const profile_resolver_1 = require("../profile-resolver");
|
|
48
49
|
const api_1 = require("../api");
|
|
49
50
|
const ssh_config_1 = require("../ssh-config");
|
|
50
|
-
|
|
51
|
-
async function provisionGenbox(options) {
|
|
51
|
+
async function provisionGenbox(payload) {
|
|
52
52
|
return (0, api_1.fetchApi)('/genboxes', {
|
|
53
53
|
method: 'POST',
|
|
54
|
-
body: JSON.stringify(
|
|
54
|
+
body: JSON.stringify(payload),
|
|
55
55
|
});
|
|
56
56
|
}
|
|
57
57
|
function getPublicSshKey() {
|
|
58
58
|
const home = os.homedir();
|
|
59
59
|
const potentialKeys = [
|
|
60
|
-
path.join(home, '.ssh', 'id_rsa.pub'),
|
|
61
60
|
path.join(home, '.ssh', 'id_ed25519.pub'),
|
|
61
|
+
path.join(home, '.ssh', 'id_rsa.pub'),
|
|
62
62
|
];
|
|
63
63
|
for (const keyPath of potentialKeys) {
|
|
64
64
|
if (fs.existsSync(keyPath)) {
|
|
@@ -67,172 +67,411 @@ function getPublicSshKey() {
|
|
|
67
67
|
return content;
|
|
68
68
|
}
|
|
69
69
|
}
|
|
70
|
-
throw new Error('No public SSH key found in ~/.ssh/
|
|
70
|
+
throw new Error('No public SSH key found in ~/.ssh/');
|
|
71
71
|
}
|
|
72
72
|
function getPrivateSshKey() {
|
|
73
73
|
const home = os.homedir();
|
|
74
74
|
const potentialKeys = [
|
|
75
|
-
path.join(home, '.ssh', 'id_rsa'),
|
|
76
75
|
path.join(home, '.ssh', 'id_ed25519'),
|
|
76
|
+
path.join(home, '.ssh', 'id_rsa'),
|
|
77
77
|
];
|
|
78
78
|
for (const keyPath of potentialKeys) {
|
|
79
79
|
if (fs.existsSync(keyPath)) {
|
|
80
|
-
return keyPath;
|
|
80
|
+
return fs.readFileSync(keyPath, 'utf-8');
|
|
81
81
|
}
|
|
82
82
|
}
|
|
83
|
-
|
|
83
|
+
return undefined;
|
|
84
84
|
}
|
|
85
85
|
exports.createCommand = new commander_1.Command('create')
|
|
86
|
-
.description('Create a new Genbox')
|
|
86
|
+
.description('Create a new Genbox environment')
|
|
87
87
|
.argument('<name>', 'Name of the Genbox')
|
|
88
|
-
.option('--
|
|
88
|
+
.option('-p, --profile <profile>', 'Use a predefined profile')
|
|
89
|
+
.option('-a, --apps <apps>', 'Comma-separated list of apps to include')
|
|
90
|
+
.option('--add-apps <apps>', 'Add apps to the profile')
|
|
91
|
+
.option('--api <mode>', 'API mode: local, staging, production')
|
|
92
|
+
.option('--db <mode>', 'Database mode: none, local, copy, remote')
|
|
93
|
+
.option('--db-source <source>', 'Database source: staging, production')
|
|
94
|
+
.option('-s, --size <size>', 'Server size: small, medium, large, xl')
|
|
95
|
+
.option('-b, --branch <branch>', 'Git branch to checkout')
|
|
96
|
+
.option('-y, --yes', 'Skip interactive prompts')
|
|
97
|
+
.option('--dry-run', 'Show what would be created without actually creating')
|
|
89
98
|
.action(async (name, options) => {
|
|
90
99
|
try {
|
|
91
|
-
|
|
92
|
-
const
|
|
100
|
+
// Load configuration
|
|
101
|
+
const configLoader = new config_loader_1.ConfigLoader();
|
|
102
|
+
const loadResult = await configLoader.load();
|
|
103
|
+
if (!loadResult.config || loadResult.config.version !== '3.0') {
|
|
104
|
+
// Fall back to legacy v1/v2 handling
|
|
105
|
+
await createLegacy(name, options);
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
const config = loadResult.config;
|
|
109
|
+
const profileResolver = new profile_resolver_1.ProfileResolver(configLoader);
|
|
110
|
+
// Build create options
|
|
111
|
+
const createOptions = {
|
|
112
|
+
name,
|
|
113
|
+
profile: options.profile,
|
|
114
|
+
apps: options.apps ? options.apps.split(',').map((a) => a.trim()) : undefined,
|
|
115
|
+
addApps: options.addApps ? options.addApps.split(',').map((a) => a.trim()) : undefined,
|
|
116
|
+
api: options.api,
|
|
117
|
+
db: options.db,
|
|
118
|
+
dbSource: options.dbSource,
|
|
119
|
+
size: options.size,
|
|
120
|
+
branch: options.branch,
|
|
121
|
+
yes: options.yes,
|
|
122
|
+
dryRun: options.dryRun,
|
|
123
|
+
};
|
|
124
|
+
// Resolve configuration
|
|
125
|
+
console.log(chalk_1.default.blue('Resolving configuration...'));
|
|
126
|
+
console.log('');
|
|
127
|
+
const resolved = await profileResolver.resolve(config, createOptions);
|
|
128
|
+
// Display resolved configuration
|
|
129
|
+
displayResolvedConfig(resolved);
|
|
130
|
+
// Ask to save as profile (if not using one already)
|
|
131
|
+
if (!options.profile && !options.yes && !options.dryRun) {
|
|
132
|
+
await profileResolver.askSaveProfile(config, resolved);
|
|
133
|
+
}
|
|
134
|
+
// Dry run mode
|
|
135
|
+
if (options.dryRun) {
|
|
136
|
+
console.log(chalk_1.default.yellow('\nDry run mode - no genbox created'));
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
// Confirm creation
|
|
140
|
+
if (!options.yes) {
|
|
141
|
+
const confirm = await prompts.confirm({
|
|
142
|
+
message: `Create genbox '${name}'?`,
|
|
143
|
+
default: true,
|
|
144
|
+
});
|
|
145
|
+
if (!confirm) {
|
|
146
|
+
console.log(chalk_1.default.dim('Cancelled.'));
|
|
147
|
+
return;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
// Get SSH keys
|
|
93
151
|
const publicKey = getPublicSshKey();
|
|
94
|
-
// Check if SSH auth is needed for git
|
|
152
|
+
// Check if SSH auth is needed for git
|
|
95
153
|
let privateKeyContent;
|
|
96
|
-
const
|
|
97
|
-
|
|
98
|
-
if (
|
|
99
|
-
const
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
default: true
|
|
104
|
-
}]);
|
|
154
|
+
const usesSSH = config.git_auth?.method === 'ssh' ||
|
|
155
|
+
Object.values(config.repos || {}).some(r => r.auth === 'ssh');
|
|
156
|
+
if (usesSSH && !options.yes) {
|
|
157
|
+
const injectKey = await prompts.confirm({
|
|
158
|
+
message: 'Inject SSH private key for git cloning?',
|
|
159
|
+
default: true,
|
|
160
|
+
});
|
|
105
161
|
if (injectKey) {
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
privateKeyContent = fs.readFileSync(privKeyPath, 'utf-8');
|
|
110
|
-
console.log(chalk_1.default.dim(` Using private key: ${privKeyPath}`));
|
|
111
|
-
}
|
|
112
|
-
else {
|
|
113
|
-
console.warn(chalk_1.default.yellow('No private key found to inject!'));
|
|
162
|
+
privateKeyContent = getPrivateSshKey();
|
|
163
|
+
if (privateKeyContent) {
|
|
164
|
+
console.log(chalk_1.default.dim(' Using local SSH private key'));
|
|
114
165
|
}
|
|
115
166
|
}
|
|
116
167
|
}
|
|
117
|
-
|
|
168
|
+
// Build payload
|
|
169
|
+
const payload = buildPayload(resolved, config, publicKey, privateKeyContent, configLoader);
|
|
170
|
+
// Create genbox
|
|
171
|
+
const spinner = (0, ora_1.default)(`Creating Genbox '${name}'...`).start();
|
|
118
172
|
try {
|
|
119
|
-
|
|
120
|
-
const filesBundle = [];
|
|
121
|
-
if (config.files) {
|
|
122
|
-
for (const f of config.files) {
|
|
123
|
-
const sourcePath = path.resolve(process.cwd(), f.source);
|
|
124
|
-
if (fs.existsSync(sourcePath)) {
|
|
125
|
-
const content = fs.readFileSync(sourcePath, 'utf-8');
|
|
126
|
-
filesBundle.push({
|
|
127
|
-
path: f.target,
|
|
128
|
-
content: content,
|
|
129
|
-
permissions: f.permissions || '0644'
|
|
130
|
-
});
|
|
131
|
-
}
|
|
132
|
-
else {
|
|
133
|
-
console.warn(chalk_1.default.yellow(`Warning: File ${f.source} not found. Skipping.`));
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
// Scripts handling (simplistic: upload and run)
|
|
138
|
-
const postDetails = [];
|
|
139
|
-
if (config.scripts) {
|
|
140
|
-
for (const s of config.scripts) {
|
|
141
|
-
const scriptName = path.basename(s);
|
|
142
|
-
const targetPath = `/home/dev/${scriptName}`;
|
|
143
|
-
const sourcePath = path.resolve(process.cwd(), s);
|
|
144
|
-
if (fs.existsSync(sourcePath)) {
|
|
145
|
-
const content = fs.readFileSync(sourcePath, 'utf-8');
|
|
146
|
-
filesBundle.push({
|
|
147
|
-
path: targetPath,
|
|
148
|
-
content: content,
|
|
149
|
-
permissions: '0755'
|
|
150
|
-
});
|
|
151
|
-
postDetails.push(`su - dev -c "${targetPath}"`);
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
}
|
|
155
|
-
// project_name from config becomes 'workspace' in the API
|
|
156
|
-
const workspace = config.project_name || 'default';
|
|
157
|
-
// Load environment variables (including GIT_TOKEN for PAT auth)
|
|
158
|
-
const envVars = (0, config_1.loadEnvVars)();
|
|
159
|
-
const gitToken = envVars.GIT_TOKEN;
|
|
160
|
-
const genbox = await provisionGenbox({
|
|
161
|
-
name,
|
|
162
|
-
size,
|
|
163
|
-
publicKey,
|
|
164
|
-
workspace,
|
|
165
|
-
services: config.services,
|
|
166
|
-
files: filesBundle,
|
|
167
|
-
postDetails,
|
|
168
|
-
repos: config.repos,
|
|
169
|
-
privateKey: privateKeyContent,
|
|
170
|
-
gitToken,
|
|
171
|
-
});
|
|
173
|
+
const genbox = await provisionGenbox(payload);
|
|
172
174
|
spinner.succeed(chalk_1.default.green(`Genbox '${name}' created successfully!`));
|
|
173
|
-
// Add SSH config
|
|
175
|
+
// Add SSH config
|
|
174
176
|
if (genbox.ipAddress) {
|
|
175
|
-
const
|
|
177
|
+
const sshAdded = (0, ssh_config_1.addSshConfigEntry)({
|
|
176
178
|
name,
|
|
177
179
|
ipAddress: genbox.ipAddress,
|
|
178
180
|
});
|
|
179
|
-
if (
|
|
181
|
+
if (sshAdded) {
|
|
180
182
|
console.log(chalk_1.default.dim(` SSH config added: ssh ${(0, ssh_config_1.getSshAlias)(name)}`));
|
|
181
183
|
}
|
|
182
184
|
}
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
if (
|
|
189
|
-
console.log(
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
// Display URLs
|
|
193
|
-
if (genbox.urls && Object.keys(genbox.urls).length > 0) {
|
|
194
|
-
console.log('\n' + chalk_1.default.bold('Access URLs:'));
|
|
195
|
-
for (const [service, url] of Object.entries(genbox.urls)) {
|
|
196
|
-
console.log(` ${chalk_1.default.cyan(service.padEnd(10))} ${url}`);
|
|
197
|
-
}
|
|
198
|
-
}
|
|
199
|
-
console.log(chalk_1.default.dim('───────────────────────────────────────────────'));
|
|
200
|
-
console.log(`\n${chalk_1.default.bold('Quick Commands:')}`);
|
|
201
|
-
console.log(` SSH: ${chalk_1.default.cyan(`genbox connect ${name}`)}`);
|
|
202
|
-
console.log(` Status: ${chalk_1.default.cyan(`genbox status ${name}`)}`);
|
|
203
|
-
console.log(` URLs: ${chalk_1.default.cyan(`genbox urls ${name}`)}`);
|
|
204
|
-
console.log(` Destroy: ${chalk_1.default.cyan(`genbox destroy ${name}`)}`);
|
|
205
|
-
}
|
|
206
|
-
catch (innerError) {
|
|
207
|
-
if (innerError instanceof api_1.AuthenticationError) {
|
|
208
|
-
spinner.fail(chalk_1.default.red('Not logged in'));
|
|
209
|
-
console.error('');
|
|
210
|
-
console.error(chalk_1.default.yellow(' Please authenticate first:'));
|
|
211
|
-
console.error(chalk_1.default.cyan(' $ genbox login'));
|
|
212
|
-
console.error('');
|
|
213
|
-
}
|
|
214
|
-
else {
|
|
215
|
-
spinner.fail(chalk_1.default.red(`Failed to create Genbox '${name}': ${innerError.message}`));
|
|
185
|
+
// Display results
|
|
186
|
+
displayGenboxInfo(genbox, resolved);
|
|
187
|
+
}
|
|
188
|
+
catch (error) {
|
|
189
|
+
spinner.fail(chalk_1.default.red(`Failed to create Genbox: ${error.message}`));
|
|
190
|
+
if (error instanceof api_1.AuthenticationError) {
|
|
191
|
+
console.log('');
|
|
192
|
+
console.log(chalk_1.default.yellow(' Please authenticate first:'));
|
|
193
|
+
console.log(chalk_1.default.cyan(' $ genbox login'));
|
|
216
194
|
}
|
|
217
195
|
}
|
|
218
196
|
}
|
|
219
197
|
catch (error) {
|
|
220
|
-
// Handle user cancellation (Ctrl+C) gracefully
|
|
221
198
|
if (error.name === 'ExitPromptError' || error.message?.includes('force closed')) {
|
|
222
199
|
console.log('');
|
|
223
200
|
console.log(chalk_1.default.dim('Cancelled.'));
|
|
224
201
|
return;
|
|
225
202
|
}
|
|
226
|
-
// Handle authentication errors with a helpful message
|
|
227
203
|
if (error instanceof api_1.AuthenticationError) {
|
|
228
|
-
console.
|
|
229
|
-
console.
|
|
230
|
-
console.
|
|
231
|
-
console.
|
|
232
|
-
console.error(chalk_1.default.cyan(' $ genbox login'));
|
|
233
|
-
console.error('');
|
|
204
|
+
console.log(chalk_1.default.red('✖ Not logged in'));
|
|
205
|
+
console.log('');
|
|
206
|
+
console.log(chalk_1.default.yellow(' Please authenticate first:'));
|
|
207
|
+
console.log(chalk_1.default.cyan(' $ genbox login'));
|
|
234
208
|
return;
|
|
235
209
|
}
|
|
236
210
|
console.error(chalk_1.default.red(`Error: ${error.message}`));
|
|
237
211
|
}
|
|
238
212
|
});
|
|
213
|
+
/**
|
|
214
|
+
* Display resolved configuration
|
|
215
|
+
*/
|
|
216
|
+
function displayResolvedConfig(resolved) {
|
|
217
|
+
console.log(chalk_1.default.bold('Configuration:'));
|
|
218
|
+
console.log(chalk_1.default.dim('───────────────────────────────────────────────'));
|
|
219
|
+
console.log(` ${chalk_1.default.bold('Name:')} ${resolved.name}`);
|
|
220
|
+
console.log(` ${chalk_1.default.bold('Project:')} ${resolved.project.name}`);
|
|
221
|
+
console.log(` ${chalk_1.default.bold('Size:')} ${resolved.size}`);
|
|
222
|
+
if (resolved.profile) {
|
|
223
|
+
console.log(` ${chalk_1.default.bold('Profile:')} ${resolved.profile}`);
|
|
224
|
+
}
|
|
225
|
+
console.log('');
|
|
226
|
+
console.log(` ${chalk_1.default.bold('Apps:')}`);
|
|
227
|
+
for (const app of resolved.apps) {
|
|
228
|
+
const deps = Object.entries(app.dependencies)
|
|
229
|
+
.map(([name, d]) => `${name}:${d.mode}`)
|
|
230
|
+
.join(', ');
|
|
231
|
+
console.log(` • ${app.name} (${app.type}${app.framework ? `, ${app.framework}` : ''})`);
|
|
232
|
+
if (deps) {
|
|
233
|
+
console.log(chalk_1.default.dim(` deps: ${deps}`));
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
if (resolved.infrastructure.length > 0) {
|
|
237
|
+
console.log('');
|
|
238
|
+
console.log(` ${chalk_1.default.bold('Infrastructure:')}`);
|
|
239
|
+
for (const infra of resolved.infrastructure) {
|
|
240
|
+
console.log(` • ${infra.name} (${infra.mode})`);
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
console.log('');
|
|
244
|
+
console.log(` ${chalk_1.default.bold('Database:')} ${resolved.database.mode}${resolved.database.source ? ` (from ${resolved.database.source})` : ''}`);
|
|
245
|
+
if (resolved.warnings.length > 0) {
|
|
246
|
+
console.log('');
|
|
247
|
+
console.log(chalk_1.default.yellow(' Warnings:'));
|
|
248
|
+
for (const warning of resolved.warnings) {
|
|
249
|
+
console.log(chalk_1.default.dim(` - ${warning}`));
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
console.log(chalk_1.default.dim('───────────────────────────────────────────────'));
|
|
253
|
+
console.log('');
|
|
254
|
+
}
|
|
255
|
+
/**
|
|
256
|
+
* Build API payload from resolved config
|
|
257
|
+
*/
|
|
258
|
+
function buildPayload(resolved, config, publicKey, privateKey, configLoader) {
|
|
259
|
+
// Load env vars
|
|
260
|
+
const envVars = configLoader.loadEnvVars(process.cwd());
|
|
261
|
+
// Build services map
|
|
262
|
+
const services = {};
|
|
263
|
+
for (const app of resolved.apps) {
|
|
264
|
+
if (app.services) {
|
|
265
|
+
for (const [name, svc] of Object.entries(app.services)) {
|
|
266
|
+
services[name] = { port: svc.port, healthcheck: svc.healthcheck };
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
else if (app.port) {
|
|
270
|
+
services[app.name] = { port: app.port };
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
// Build files bundle
|
|
274
|
+
const files = [];
|
|
275
|
+
// Add setup script if generated
|
|
276
|
+
const setupScript = generateSetupScript(resolved, config);
|
|
277
|
+
if (setupScript) {
|
|
278
|
+
files.push({
|
|
279
|
+
path: '/home/dev/setup-genbox.sh',
|
|
280
|
+
content: setupScript,
|
|
281
|
+
permissions: '0755',
|
|
282
|
+
});
|
|
283
|
+
}
|
|
284
|
+
// Build post details (scripts to run)
|
|
285
|
+
const postDetails = [];
|
|
286
|
+
if (setupScript) {
|
|
287
|
+
postDetails.push('su - dev -c "/home/dev/setup-genbox.sh"');
|
|
288
|
+
}
|
|
289
|
+
// Build repos
|
|
290
|
+
const repos = {};
|
|
291
|
+
for (const repo of resolved.repos) {
|
|
292
|
+
repos[repo.name] = {
|
|
293
|
+
url: repo.url,
|
|
294
|
+
path: repo.path,
|
|
295
|
+
branch: repo.branch,
|
|
296
|
+
};
|
|
297
|
+
}
|
|
298
|
+
return {
|
|
299
|
+
name: resolved.name,
|
|
300
|
+
size: resolved.size,
|
|
301
|
+
publicKey,
|
|
302
|
+
workspace: resolved.project.name,
|
|
303
|
+
services,
|
|
304
|
+
files,
|
|
305
|
+
postDetails,
|
|
306
|
+
repos,
|
|
307
|
+
privateKey,
|
|
308
|
+
gitToken: envVars.GIT_TOKEN,
|
|
309
|
+
envVars: resolved.env,
|
|
310
|
+
apps: resolved.apps.map(a => a.name),
|
|
311
|
+
infrastructure: resolved.infrastructure.map(i => ({
|
|
312
|
+
name: i.name,
|
|
313
|
+
type: i.type,
|
|
314
|
+
mode: i.mode,
|
|
315
|
+
})),
|
|
316
|
+
database: resolved.database,
|
|
317
|
+
};
|
|
318
|
+
}
|
|
319
|
+
/**
|
|
320
|
+
* Generate setup script
|
|
321
|
+
*/
|
|
322
|
+
function generateSetupScript(resolved, config) {
|
|
323
|
+
const lines = [
|
|
324
|
+
'#!/bin/bash',
|
|
325
|
+
'# Generated by genbox create',
|
|
326
|
+
'set -e',
|
|
327
|
+
'',
|
|
328
|
+
];
|
|
329
|
+
// Change to project directory
|
|
330
|
+
if (resolved.repos.length > 0) {
|
|
331
|
+
lines.push(`cd ${resolved.repos[0].path} || exit 1`);
|
|
332
|
+
lines.push('');
|
|
333
|
+
}
|
|
334
|
+
// Install dependencies - detect package manager from lockfiles
|
|
335
|
+
lines.push('# Install dependencies');
|
|
336
|
+
lines.push('if [ -f "pnpm-lock.yaml" ]; then');
|
|
337
|
+
lines.push(' echo "Installing dependencies with pnpm..."');
|
|
338
|
+
lines.push(' pnpm install --frozen-lockfile');
|
|
339
|
+
lines.push('elif [ -f "yarn.lock" ]; then');
|
|
340
|
+
lines.push(' echo "Installing dependencies with yarn..."');
|
|
341
|
+
lines.push(' yarn install --frozen-lockfile');
|
|
342
|
+
lines.push('elif [ -f "bun.lockb" ]; then');
|
|
343
|
+
lines.push(' echo "Installing dependencies with bun..."');
|
|
344
|
+
lines.push(' bun install --frozen-lockfile');
|
|
345
|
+
lines.push('elif [ -f "package-lock.json" ]; then');
|
|
346
|
+
lines.push(' echo "Installing dependencies with npm..."');
|
|
347
|
+
lines.push(' npm ci');
|
|
348
|
+
lines.push('fi');
|
|
349
|
+
// Start Docker if API is included locally
|
|
350
|
+
const hasLocalApi = resolved.apps.some(a => a.name === 'api' || a.type === 'backend');
|
|
351
|
+
if (hasLocalApi) {
|
|
352
|
+
lines.push('');
|
|
353
|
+
lines.push('echo "Starting Docker services..."');
|
|
354
|
+
lines.push('if [ -f "docker-compose.yml" ] || [ -f "compose.yml" ]; then');
|
|
355
|
+
lines.push(' docker compose up -d');
|
|
356
|
+
lines.push('fi');
|
|
357
|
+
}
|
|
358
|
+
lines.push('');
|
|
359
|
+
lines.push('echo "Setup complete!"');
|
|
360
|
+
return lines.join('\n');
|
|
361
|
+
}
|
|
362
|
+
/**
|
|
363
|
+
* Display genbox info after creation
|
|
364
|
+
*/
|
|
365
|
+
function displayGenboxInfo(genbox, resolved) {
|
|
366
|
+
console.log(chalk_1.default.dim('───────────────────────────────────────────────'));
|
|
367
|
+
console.log(` ${chalk_1.default.bold('Environment:')} ${genbox.name || resolved.name}`);
|
|
368
|
+
console.log(` ${chalk_1.default.bold('Workspace:')} ${genbox.workspace || resolved.project.name}`);
|
|
369
|
+
console.log(` ${chalk_1.default.bold('Size:')} ${genbox.size || resolved.size}`);
|
|
370
|
+
console.log(` ${chalk_1.default.bold('Status:')} ${chalk_1.default.yellow(genbox.status || 'creating')}`);
|
|
371
|
+
if (genbox.ipAddress) {
|
|
372
|
+
console.log(` ${chalk_1.default.bold('IP:')} ${genbox.ipAddress}`);
|
|
373
|
+
}
|
|
374
|
+
console.log(chalk_1.default.dim('───────────────────────────────────────────────'));
|
|
375
|
+
// Display URLs
|
|
376
|
+
if (genbox.urls && Object.keys(genbox.urls).length > 0) {
|
|
377
|
+
console.log('');
|
|
378
|
+
console.log(chalk_1.default.bold('Access URLs:'));
|
|
379
|
+
for (const [service, url] of Object.entries(genbox.urls)) {
|
|
380
|
+
console.log(` ${chalk_1.default.cyan(service.padEnd(12))} ${url}`);
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
console.log(chalk_1.default.dim('───────────────────────────────────────────────'));
|
|
384
|
+
console.log('');
|
|
385
|
+
console.log(chalk_1.default.bold('Quick Commands:'));
|
|
386
|
+
console.log(` SSH: ${chalk_1.default.cyan(`genbox connect ${resolved.name}`)}`);
|
|
387
|
+
console.log(` Status: ${chalk_1.default.cyan(`genbox status ${resolved.name}`)}`);
|
|
388
|
+
console.log(` URLs: ${chalk_1.default.cyan(`genbox urls ${resolved.name}`)}`);
|
|
389
|
+
console.log(` Destroy: ${chalk_1.default.cyan(`genbox destroy ${resolved.name}`)}`);
|
|
390
|
+
}
|
|
391
|
+
/**
|
|
392
|
+
* Legacy v1/v2 create handling
|
|
393
|
+
*/
|
|
394
|
+
async function createLegacy(name, options) {
|
|
395
|
+
// Import legacy config handling
|
|
396
|
+
const { loadConfig, loadEnvVars } = await Promise.resolve().then(() => __importStar(require('../config')));
|
|
397
|
+
const config = loadConfig();
|
|
398
|
+
const size = options.size || config.system?.server_size || 'small';
|
|
399
|
+
const publicKey = getPublicSshKey();
|
|
400
|
+
// Check if SSH auth needed
|
|
401
|
+
let privateKeyContent;
|
|
402
|
+
const usesSSH = config.git_auth?.method === 'ssh' ||
|
|
403
|
+
(config.repos && Object.values(config.repos).some((r) => r.auth === 'ssh'));
|
|
404
|
+
if (usesSSH && !options.yes) {
|
|
405
|
+
const injectKey = await prompts.confirm({
|
|
406
|
+
message: 'Inject SSH private key for git cloning?',
|
|
407
|
+
default: true,
|
|
408
|
+
});
|
|
409
|
+
if (injectKey) {
|
|
410
|
+
privateKeyContent = getPrivateSshKey();
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
const spinner = (0, ora_1.default)(`Creating Genbox '${name}'...`).start();
|
|
414
|
+
try {
|
|
415
|
+
// Prepare files
|
|
416
|
+
const filesBundle = [];
|
|
417
|
+
if (config.files) {
|
|
418
|
+
for (const f of config.files) {
|
|
419
|
+
const sourcePath = path.resolve(process.cwd(), f.source);
|
|
420
|
+
if (fs.existsSync(sourcePath)) {
|
|
421
|
+
filesBundle.push({
|
|
422
|
+
path: f.target,
|
|
423
|
+
content: fs.readFileSync(sourcePath, 'utf-8'),
|
|
424
|
+
permissions: f.permissions || '0644',
|
|
425
|
+
});
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
// Prepare scripts
|
|
430
|
+
const postDetails = [];
|
|
431
|
+
if (config.scripts) {
|
|
432
|
+
for (const s of config.scripts) {
|
|
433
|
+
const scriptName = path.basename(s);
|
|
434
|
+
const targetPath = `/home/dev/${scriptName}`;
|
|
435
|
+
const sourcePath = path.resolve(process.cwd(), s);
|
|
436
|
+
if (fs.existsSync(sourcePath)) {
|
|
437
|
+
filesBundle.push({
|
|
438
|
+
path: targetPath,
|
|
439
|
+
content: fs.readFileSync(sourcePath, 'utf-8'),
|
|
440
|
+
permissions: '0755',
|
|
441
|
+
});
|
|
442
|
+
postDetails.push(`su - dev -c "${targetPath}"`);
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
const envVars = loadEnvVars();
|
|
447
|
+
const genbox = await provisionGenbox({
|
|
448
|
+
name,
|
|
449
|
+
size,
|
|
450
|
+
publicKey,
|
|
451
|
+
workspace: config.project_name || 'default',
|
|
452
|
+
services: config.services,
|
|
453
|
+
files: filesBundle,
|
|
454
|
+
postDetails,
|
|
455
|
+
repos: config.repos,
|
|
456
|
+
privateKey: privateKeyContent,
|
|
457
|
+
gitToken: envVars.GIT_TOKEN,
|
|
458
|
+
});
|
|
459
|
+
spinner.succeed(chalk_1.default.green(`Genbox '${name}' created!`));
|
|
460
|
+
if (genbox.ipAddress) {
|
|
461
|
+
(0, ssh_config_1.addSshConfigEntry)({ name, ipAddress: genbox.ipAddress });
|
|
462
|
+
}
|
|
463
|
+
console.log(chalk_1.default.dim('───────────────────────────────────────────────'));
|
|
464
|
+
console.log(` ${chalk_1.default.bold('Environment:')} ${name}`);
|
|
465
|
+
console.log(` ${chalk_1.default.bold('Status:')} ${chalk_1.default.yellow(genbox.status)}`);
|
|
466
|
+
if (genbox.ipAddress) {
|
|
467
|
+
console.log(` ${chalk_1.default.bold('IP:')} ${genbox.ipAddress}`);
|
|
468
|
+
}
|
|
469
|
+
console.log(chalk_1.default.dim('───────────────────────────────────────────────'));
|
|
470
|
+
}
|
|
471
|
+
catch (error) {
|
|
472
|
+
spinner.fail(chalk_1.default.red(`Failed: ${error.message}`));
|
|
473
|
+
if (error instanceof api_1.AuthenticationError) {
|
|
474
|
+
console.log(chalk_1.default.yellow('\n Run: genbox login'));
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
}
|