@aifabrix/builder 2.40.0 → 2.41.0
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 +7 -5
- package/integration/hubspot/test.js +1 -1
- package/jest.config.manual.js +29 -0
- package/lib/api/credential.api.js +40 -0
- package/lib/api/dev.api.js +423 -0
- package/lib/api/types/credential.types.js +23 -0
- package/lib/api/types/dev.types.js +140 -0
- package/lib/app/config.js +21 -0
- package/lib/app/down.js +2 -1
- package/lib/app/index.js +9 -0
- package/lib/app/push.js +36 -12
- package/lib/app/readme.js +1 -3
- package/lib/app/run-env-compose.js +201 -0
- package/lib/app/run-helpers.js +121 -118
- package/lib/app/run.js +148 -28
- package/lib/app/show.js +5 -2
- package/lib/build/index.js +11 -3
- package/lib/cli/setup-app.js +140 -14
- package/lib/cli/setup-auth.js +1 -0
- package/lib/cli/setup-dev.js +180 -17
- package/lib/cli/setup-environment.js +4 -2
- package/lib/cli/setup-external-system.js +71 -21
- package/lib/cli/setup-infra.js +29 -2
- package/lib/cli/setup-secrets.js +52 -5
- package/lib/cli/setup-utility.js +19 -4
- package/lib/commands/app-install.js +172 -0
- package/lib/commands/app-shell.js +75 -0
- package/lib/commands/app-test.js +282 -0
- package/lib/commands/app.js +1 -1
- package/lib/commands/auth-status.js +36 -3
- package/lib/commands/dev-cli-handlers.js +141 -0
- package/lib/commands/dev-down.js +114 -0
- package/lib/commands/dev-init.js +309 -0
- package/lib/commands/secrets-list.js +118 -0
- package/lib/commands/secrets-remove.js +97 -0
- package/lib/commands/secrets-set.js +30 -17
- package/lib/commands/secrets-validate.js +50 -0
- package/lib/commands/up-dataplane.js +2 -2
- package/lib/commands/up-miso.js +0 -25
- package/lib/commands/upload.js +26 -1
- package/lib/core/admin-secrets.js +96 -0
- package/lib/core/secrets-ensure.js +378 -0
- package/lib/core/secrets-env-write.js +157 -0
- package/lib/core/secrets.js +147 -81
- package/lib/datasource/field-reference-validator.js +91 -0
- package/lib/datasource/validate.js +21 -3
- package/lib/deployment/environment-config.js +137 -0
- package/lib/deployment/environment.js +21 -98
- package/lib/deployment/push.js +32 -2
- package/lib/external-system/download.js +7 -0
- package/lib/external-system/test-auth.js +7 -3
- package/lib/external-system/test.js +5 -1
- package/lib/generator/index.js +174 -25
- package/lib/generator/wizard.js +13 -1
- package/lib/infrastructure/helpers.js +103 -20
- package/lib/infrastructure/index.js +88 -10
- package/lib/infrastructure/services.js +70 -15
- package/lib/schema/application-schema.json +24 -3
- package/lib/schema/external-system.schema.json +435 -413
- package/lib/utils/api.js +3 -3
- package/lib/utils/app-register-auth.js +25 -3
- package/lib/utils/cli-utils.js +20 -0
- package/lib/utils/compose-generator.js +76 -75
- package/lib/utils/compose-handlebars-helpers.js +43 -0
- package/lib/utils/compose-vector-helper.js +18 -0
- package/lib/utils/config-paths.js +127 -2
- package/lib/utils/credential-secrets-env.js +267 -0
- package/lib/utils/dev-cert-helper.js +122 -0
- package/lib/utils/device-code-helpers.js +224 -0
- package/lib/utils/device-code.js +37 -336
- package/lib/utils/docker-build.js +40 -8
- package/lib/utils/env-copy.js +83 -13
- package/lib/utils/env-map.js +35 -5
- package/lib/utils/env-template.js +6 -5
- package/lib/utils/error-formatters/http-status-errors.js +20 -1
- package/lib/utils/help-builder.js +15 -2
- package/lib/utils/infra-status.js +30 -1
- package/lib/utils/local-secrets.js +7 -52
- package/lib/utils/mutagen-install.js +195 -0
- package/lib/utils/mutagen.js +146 -0
- package/lib/utils/paths.js +49 -33
- package/lib/utils/port-resolver.js +28 -16
- package/lib/utils/remote-dev-auth.js +38 -0
- package/lib/utils/remote-docker-env.js +43 -0
- package/lib/utils/remote-secrets-loader.js +60 -0
- package/lib/utils/secrets-generator.js +94 -6
- package/lib/utils/secrets-helpers.js +33 -25
- package/lib/utils/secrets-path.js +2 -2
- package/lib/utils/secrets-utils.js +52 -1
- package/lib/utils/secrets-validation.js +84 -0
- package/lib/utils/ssh-key-helper.js +116 -0
- package/lib/utils/token-manager-messages.js +90 -0
- package/lib/utils/token-manager.js +5 -4
- package/lib/utils/variable-transformer.js +3 -3
- package/lib/validation/validate.js +1 -1
- package/lib/validation/validator.js +65 -0
- package/package.json +4 -2
- package/scripts/install-local.js +34 -15
- package/templates/README.md +0 -1
- package/templates/applications/README.md.hbs +4 -4
- package/templates/applications/dataplane/application.yaml +5 -4
- package/templates/applications/dataplane/env.template +12 -7
- package/templates/applications/keycloak/env.template +2 -0
- package/templates/applications/miso-controller/application.yaml +1 -0
- package/templates/applications/miso-controller/env.template +11 -9
- package/templates/external-system/external-system.json.hbs +1 -16
- package/templates/python/docker-compose.hbs +49 -23
- package/templates/typescript/docker-compose.hbs +48 -22
package/lib/cli/setup-app.js
CHANGED
|
@@ -154,6 +154,31 @@ See integration/hubspot/wizard-hubspot-e2e.yaml for an example.`;
|
|
|
154
154
|
});
|
|
155
155
|
}
|
|
156
156
|
|
|
157
|
+
function registerRunCommand(program) {
|
|
158
|
+
const runHelp = `
|
|
159
|
+
In dev: use --reload for sync and mount (requires remote server with Mutagen, or local Docker).
|
|
160
|
+
Examples:
|
|
161
|
+
$ aifabrix run myapp
|
|
162
|
+
$ aifabrix run myapp --env tst
|
|
163
|
+
$ aifabrix run myapp --reload`;
|
|
164
|
+
program.command('run <app>')
|
|
165
|
+
.description('Run application locally (or remotely on your Docker host)')
|
|
166
|
+
.option('-p, --port <port>', 'Override local port')
|
|
167
|
+
.option('-d, --debug', 'Enable debug output with detailed container information')
|
|
168
|
+
.option('-t, --tag <tag>', 'Image tag to run (e.g. v1.0.0); overrides application.yaml image.tag')
|
|
169
|
+
.option('-e, --env <env>', 'Environment: dev (default), tst, or pro', 'dev')
|
|
170
|
+
.option('--reload', 'In dev: use sync and mount (requires remote server; Mutagen or local Docker)')
|
|
171
|
+
.addHelpText('after', runHelp)
|
|
172
|
+
.action(async(appName, options) => {
|
|
173
|
+
try {
|
|
174
|
+
await app.runApp(appName, options);
|
|
175
|
+
} catch (error) {
|
|
176
|
+
handleCommandError(error, 'run');
|
|
177
|
+
process.exit(1);
|
|
178
|
+
}
|
|
179
|
+
});
|
|
180
|
+
}
|
|
181
|
+
|
|
157
182
|
function setupBuildRunLogsDownCommands(program) {
|
|
158
183
|
program.command('build <app>')
|
|
159
184
|
.description('Build container image (auto-detects runtime)')
|
|
@@ -170,19 +195,7 @@ function setupBuildRunLogsDownCommands(program) {
|
|
|
170
195
|
}
|
|
171
196
|
});
|
|
172
197
|
|
|
173
|
-
program
|
|
174
|
-
.description('Run application locally')
|
|
175
|
-
.option('-p, --port <port>', 'Override local port')
|
|
176
|
-
.option('-d, --debug', 'Enable debug output with detailed container information')
|
|
177
|
-
.option('-t, --tag <tag>', 'Image tag to run (e.g. v1.0.0); overrides application.yaml image.tag')
|
|
178
|
-
.action(async(appName, options) => {
|
|
179
|
-
try {
|
|
180
|
-
await app.runApp(appName, options);
|
|
181
|
-
} catch (error) {
|
|
182
|
-
handleCommandError(error, 'run');
|
|
183
|
-
process.exit(1);
|
|
184
|
-
}
|
|
185
|
-
});
|
|
198
|
+
registerRunCommand(program);
|
|
186
199
|
|
|
187
200
|
program.command('logs <app>')
|
|
188
201
|
.description('Show application container logs (and optional env summary with secrets masked)')
|
|
@@ -215,6 +228,117 @@ function setupBuildRunLogsDownCommands(program) {
|
|
|
215
228
|
});
|
|
216
229
|
}
|
|
217
230
|
|
|
231
|
+
function setupShellTestStopCommands(program) {
|
|
232
|
+
program.command('stop <app>')
|
|
233
|
+
.description('Stop and remove application container (alias for down-app)')
|
|
234
|
+
.option('--volumes', 'Remove application Docker volume')
|
|
235
|
+
.action(async(appName, options) => {
|
|
236
|
+
try {
|
|
237
|
+
const { runDownAppWithImageRemoval } = require('../commands/app-down');
|
|
238
|
+
await runDownAppWithImageRemoval(appName, { volumes: options.volumes });
|
|
239
|
+
} catch (error) {
|
|
240
|
+
handleCommandError(error, 'stop');
|
|
241
|
+
process.exit(1);
|
|
242
|
+
}
|
|
243
|
+
});
|
|
244
|
+
|
|
245
|
+
program.command('shell <app>')
|
|
246
|
+
.description('Open interactive shell in the application container')
|
|
247
|
+
.option('--env <env>', 'Environment (dev|tst); dev uses running container', 'dev')
|
|
248
|
+
.action(async(appName, options) => {
|
|
249
|
+
try {
|
|
250
|
+
const { runAppShell } = require('../commands/app-shell');
|
|
251
|
+
await runAppShell(appName, { env: options.env });
|
|
252
|
+
} catch (error) {
|
|
253
|
+
handleCommandError(error, 'shell');
|
|
254
|
+
process.exit(1);
|
|
255
|
+
}
|
|
256
|
+
});
|
|
257
|
+
|
|
258
|
+
program.command('test <app>')
|
|
259
|
+
.description('Run tests (builder app: in container; external system: local validation)')
|
|
260
|
+
.option('--env <env>', 'For builder app: dev (running container) or tst (ephemeral)', 'dev')
|
|
261
|
+
.option('-d, --datasource <key>', 'For external system: test specific datasource only')
|
|
262
|
+
.option('-v, --verbose', 'Verbose output')
|
|
263
|
+
.action(async(appName, options) => {
|
|
264
|
+
try {
|
|
265
|
+
const pathsUtil = require('../utils/paths');
|
|
266
|
+
const appType = await pathsUtil.detectAppType(appName).catch(() => null);
|
|
267
|
+
if (appType && appType.baseDir === 'integration') {
|
|
268
|
+
const test = require('../external-system/test');
|
|
269
|
+
const results = await test.testExternalSystem(appName, options);
|
|
270
|
+
test.displayTestResults(results, options.verbose);
|
|
271
|
+
if (!results.valid) process.exit(1);
|
|
272
|
+
} else {
|
|
273
|
+
const { runAppTest } = require('../commands/app-test');
|
|
274
|
+
await runAppTest(appName, { env: options.env });
|
|
275
|
+
}
|
|
276
|
+
} catch (error) {
|
|
277
|
+
handleCommandError(error, 'test');
|
|
278
|
+
process.exit(1);
|
|
279
|
+
}
|
|
280
|
+
});
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
function setupInstallTestE2eLintCommands(program) {
|
|
284
|
+
program.command('install <app>')
|
|
285
|
+
.description('Install dependencies in container (builder apps only)')
|
|
286
|
+
.option('--env <env>', 'dev (running container) or tst (ephemeral with .env)', 'dev')
|
|
287
|
+
.action(async(appName, options) => {
|
|
288
|
+
try {
|
|
289
|
+
const pathsUtil = require('../utils/paths');
|
|
290
|
+
const appType = await pathsUtil.detectAppType(appName).catch(() => null);
|
|
291
|
+
if (appType && appType.baseDir === 'integration') {
|
|
292
|
+
logger.log(chalk.gray('Install is for builder applications only. Use aifabrix shell <app> to run commands in external setups.'));
|
|
293
|
+
return;
|
|
294
|
+
}
|
|
295
|
+
const { runAppInstall } = require('../commands/app-install');
|
|
296
|
+
await runAppInstall(appName, { env: options.env });
|
|
297
|
+
} catch (error) {
|
|
298
|
+
handleCommandError(error, 'install');
|
|
299
|
+
process.exit(1);
|
|
300
|
+
}
|
|
301
|
+
});
|
|
302
|
+
|
|
303
|
+
program.command('test-e2e <app>')
|
|
304
|
+
.description('Run e2e tests in container (builder apps only)')
|
|
305
|
+
.option('--env <env>', 'dev (running container) or tst (ephemeral with .env)', 'dev')
|
|
306
|
+
.action(async(appName, options) => {
|
|
307
|
+
try {
|
|
308
|
+
const pathsUtil = require('../utils/paths');
|
|
309
|
+
const appType = await pathsUtil.detectAppType(appName).catch(() => null);
|
|
310
|
+
if (appType && appType.baseDir === 'integration') {
|
|
311
|
+
logger.log(chalk.gray('test-e2e is for builder applications only. Use aifabrix shell <app> then make test:e2e or pnpm test:e2e.'));
|
|
312
|
+
return;
|
|
313
|
+
}
|
|
314
|
+
const { runAppTestE2e } = require('../commands/app-test');
|
|
315
|
+
await runAppTestE2e(appName, { env: options.env });
|
|
316
|
+
} catch (error) {
|
|
317
|
+
handleCommandError(error, 'test-e2e');
|
|
318
|
+
process.exit(1);
|
|
319
|
+
}
|
|
320
|
+
});
|
|
321
|
+
|
|
322
|
+
program.command('lint <app>')
|
|
323
|
+
.description('Run lint in container (builder apps only)')
|
|
324
|
+
.option('--env <env>', 'dev (running container) or tst (ephemeral with .env)', 'dev')
|
|
325
|
+
.action(async(appName, options) => {
|
|
326
|
+
try {
|
|
327
|
+
const pathsUtil = require('../utils/paths');
|
|
328
|
+
const appType = await pathsUtil.detectAppType(appName).catch(() => null);
|
|
329
|
+
if (appType && appType.baseDir === 'integration') {
|
|
330
|
+
logger.log(chalk.gray('lint is for builder applications only. Use aifabrix shell <app> then make lint or pnpm lint.'));
|
|
331
|
+
return;
|
|
332
|
+
}
|
|
333
|
+
const { runAppLint } = require('../commands/app-test');
|
|
334
|
+
await runAppLint(appName, { env: options.env });
|
|
335
|
+
} catch (error) {
|
|
336
|
+
handleCommandError(error, 'lint');
|
|
337
|
+
process.exit(1);
|
|
338
|
+
}
|
|
339
|
+
});
|
|
340
|
+
}
|
|
341
|
+
|
|
218
342
|
function setupPushDeployDockerfileCommands(program) {
|
|
219
343
|
program.command('push <app>')
|
|
220
344
|
.description('Push image to Azure Container Registry')
|
|
@@ -230,7 +354,7 @@ function setupPushDeployDockerfileCommands(program) {
|
|
|
230
354
|
});
|
|
231
355
|
|
|
232
356
|
program.command('deploy <app>')
|
|
233
|
-
.description('Deploy to Azure via Miso Controller')
|
|
357
|
+
.description('Deploy to Azure or locally via Miso Controller')
|
|
234
358
|
.option('--local', 'Send manifest to controller then run app locally (app: same as aifabrix run <app>; external: restart dataplane)')
|
|
235
359
|
.option('--client-id <id>', 'Client ID (overrides config)')
|
|
236
360
|
.option('--client-secret <secret>', 'Client Secret (overrides config)')
|
|
@@ -274,6 +398,8 @@ function setupAppCommands(program) {
|
|
|
274
398
|
setupCreateCommand(program);
|
|
275
399
|
setupWizardCommand(program);
|
|
276
400
|
setupBuildRunLogsDownCommands(program);
|
|
401
|
+
setupShellTestStopCommands(program);
|
|
402
|
+
setupInstallTestE2eLintCommands(program);
|
|
277
403
|
setupPushDeployDockerfileCommands(program);
|
|
278
404
|
}
|
|
279
405
|
|
package/lib/cli/setup-auth.js
CHANGED
|
@@ -63,6 +63,7 @@ function setupAuthSubcommands(program) {
|
|
|
63
63
|
const auth = program.command('auth').description('Authentication commands');
|
|
64
64
|
auth.command('status')
|
|
65
65
|
.description('Display authentication status for current controller and environment')
|
|
66
|
+
.option('--validate', 'Exit with code 1 when not authenticated (for scripted use, e.g. manual test setup)')
|
|
66
67
|
.action(authStatusHandler);
|
|
67
68
|
auth.command('config')
|
|
68
69
|
.description('Configure authentication settings (controller, environment)')
|
package/lib/cli/setup-dev.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* CLI developer configuration command setup (dev config, dev set-id).
|
|
2
|
+
* CLI developer configuration command setup (dev config, dev set-id, dev init, dev add/update/pin/delete/list).
|
|
3
3
|
*
|
|
4
4
|
* @fileoverview Developer command definitions for AI Fabrix Builder CLI
|
|
5
5
|
* @author AI Fabrix Team
|
|
@@ -11,20 +11,37 @@ const config = require('../core/config');
|
|
|
11
11
|
const devConfig = require('../utils/dev-config');
|
|
12
12
|
const logger = require('../utils/logger');
|
|
13
13
|
const { handleCommandError } = require('../utils/cli-utils');
|
|
14
|
+
const { runDevInit, runDevRefresh } = require('../commands/dev-init');
|
|
15
|
+
const {
|
|
16
|
+
handleDevList,
|
|
17
|
+
handleDevAdd,
|
|
18
|
+
handleDevUpdate,
|
|
19
|
+
handleDevPin,
|
|
20
|
+
handleDevDelete
|
|
21
|
+
} = require('../commands/dev-cli-handlers');
|
|
14
22
|
|
|
15
23
|
/**
|
|
16
|
-
* Display developer configuration
|
|
24
|
+
* Display developer configuration (local ports and config; remote/settings keys always shown).
|
|
25
|
+
* Always shows environment, controller, and remote keys (value or "(not set)").
|
|
17
26
|
* @param {string} devId - Developer ID
|
|
18
27
|
* @returns {Promise<void>}
|
|
19
28
|
*/
|
|
20
29
|
async function displayDevConfig(devId) {
|
|
21
30
|
const devIdNum = parseInt(devId, 10);
|
|
22
31
|
const ports = devConfig.getDevPorts(devIdNum);
|
|
23
|
-
const
|
|
32
|
+
const environment = await config.getCurrentEnvironment();
|
|
33
|
+
const controller = await config.getControllerUrl();
|
|
34
|
+
|
|
35
|
+
const optionalConfigVars = [
|
|
24
36
|
{ key: 'aifabrix-home', value: await config.getAifabrixHomeOverride() },
|
|
25
37
|
{ key: 'aifabrix-secrets', value: await config.getAifabrixSecretsPath() },
|
|
26
|
-
{ key: 'aifabrix-env-config', value: await config.getAifabrixEnvConfigPath() }
|
|
27
|
-
|
|
38
|
+
{ key: 'aifabrix-env-config', value: await config.getAifabrixEnvConfigPath() },
|
|
39
|
+
{ key: 'remote-server', value: await config.getRemoteServer() },
|
|
40
|
+
{ key: 'docker-endpoint', value: await config.getDockerEndpoint() },
|
|
41
|
+
{ key: 'user-mutagen-folder', value: await config.getUserMutagenFolder() },
|
|
42
|
+
{ key: 'sync-ssh-user', value: await config.getSyncSshUser() },
|
|
43
|
+
{ key: 'sync-ssh-host', value: await config.getSyncSshHost() }
|
|
44
|
+
];
|
|
28
45
|
|
|
29
46
|
logger.log('\n🔧 Developer Configuration\n');
|
|
30
47
|
logger.log(`Developer ID: ${devId}`);
|
|
@@ -35,22 +52,18 @@ async function displayDevConfig(devId) {
|
|
|
35
52
|
logger.log(` pgAdmin: ${ports.pgadmin}`);
|
|
36
53
|
logger.log(` Redis Commander: ${ports.redisCommander}`);
|
|
37
54
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
}
|
|
55
|
+
logger.log('\nConfiguration:');
|
|
56
|
+
logger.log(` environment: '${environment}'`);
|
|
57
|
+
logger.log(controller ? ` controller: '${controller}'` : ' controller: (not set)');
|
|
58
|
+
optionalConfigVars.forEach(v => logger.log(` ${v.key}: ${v.value || '(not set)'}`));
|
|
42
59
|
logger.log('');
|
|
43
60
|
}
|
|
44
61
|
|
|
45
62
|
/**
|
|
46
|
-
*
|
|
47
|
-
* @param {Command}
|
|
63
|
+
* Register dev config and set-id commands.
|
|
64
|
+
* @param {Command} dev - dev subcommand group
|
|
48
65
|
*/
|
|
49
|
-
function
|
|
50
|
-
const dev = program
|
|
51
|
-
.command('dev')
|
|
52
|
-
.description('Developer configuration and isolation');
|
|
53
|
-
|
|
66
|
+
function setupDevConfigCommands(dev) {
|
|
54
67
|
dev
|
|
55
68
|
.command('config')
|
|
56
69
|
.description('Show or set developer configuration')
|
|
@@ -69,7 +82,6 @@ function setupDevCommands(program) {
|
|
|
69
82
|
await displayDevConfig(setIdValue);
|
|
70
83
|
return;
|
|
71
84
|
}
|
|
72
|
-
|
|
73
85
|
const devId = await config.getDeveloperId();
|
|
74
86
|
await displayDevConfig(devId);
|
|
75
87
|
} catch (error) {
|
|
@@ -98,4 +110,155 @@ function setupDevCommands(program) {
|
|
|
98
110
|
});
|
|
99
111
|
}
|
|
100
112
|
|
|
113
|
+
/**
|
|
114
|
+
* Register dev init and refresh commands.
|
|
115
|
+
* @param {Command} dev - dev subcommand group
|
|
116
|
+
*/
|
|
117
|
+
function setupDevInitCommand(dev) {
|
|
118
|
+
dev
|
|
119
|
+
.command('init')
|
|
120
|
+
.description('Onboard with Builder Server (issue certificate, fetch settings, register SSH key for Mutagen)')
|
|
121
|
+
.requiredOption('--developer-id <id>', 'Developer ID (same as dev add; e.g. 01)')
|
|
122
|
+
.requiredOption('--server <url>', 'Builder Server base URL (e.g. https://dev.aifabrix.dev)')
|
|
123
|
+
.requiredOption('--pin <pin>', 'One-time PIN from your admin')
|
|
124
|
+
.action(async(options) => {
|
|
125
|
+
try {
|
|
126
|
+
await runDevInit(options);
|
|
127
|
+
} catch (error) {
|
|
128
|
+
handleCommandError(error, 'dev init');
|
|
129
|
+
process.exit(1);
|
|
130
|
+
}
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
dev
|
|
134
|
+
.command('refresh')
|
|
135
|
+
.description('Fetch settings from Builder Server and update config; refresh certificate if expiring within 14 days or --cert')
|
|
136
|
+
.option('--cert', 'Force certificate refresh (create PIN + issue-cert) even when cert is still valid')
|
|
137
|
+
.action(async(options) => {
|
|
138
|
+
try {
|
|
139
|
+
await runDevRefresh(options);
|
|
140
|
+
} catch (error) {
|
|
141
|
+
handleCommandError(error, 'dev refresh');
|
|
142
|
+
process.exit(1);
|
|
143
|
+
}
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* Register dev list and add commands (remote only).
|
|
149
|
+
* @param {Command} dev - dev subcommand group
|
|
150
|
+
*/
|
|
151
|
+
function setupDevListAddCommands(dev) {
|
|
152
|
+
dev
|
|
153
|
+
.command('list')
|
|
154
|
+
.description('List developer users (remote Builder Server only)')
|
|
155
|
+
.action(async() => {
|
|
156
|
+
try {
|
|
157
|
+
await handleDevList();
|
|
158
|
+
} catch (error) {
|
|
159
|
+
handleCommandError(error, 'dev list');
|
|
160
|
+
process.exit(1);
|
|
161
|
+
}
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
dev
|
|
165
|
+
.command('add')
|
|
166
|
+
.description('Register a new developer (remote Builder Server only; admin)')
|
|
167
|
+
.requiredOption('--developer-id <id>', 'Developer ID (unique, e.g. 01)')
|
|
168
|
+
.requiredOption('--name <name>', 'Display name')
|
|
169
|
+
.requiredOption('--email <email>', 'Email address')
|
|
170
|
+
.option('--groups <items>', 'Comma-separated groups (admin, secret-manager, developer)', 'developer')
|
|
171
|
+
.action(async(options) => {
|
|
172
|
+
try {
|
|
173
|
+
await handleDevAdd(options);
|
|
174
|
+
} catch (error) {
|
|
175
|
+
handleCommandError(error, 'dev add');
|
|
176
|
+
process.exit(1);
|
|
177
|
+
}
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* Register dev update/pin/delete commands (remote only).
|
|
183
|
+
* @param {Command} dev - dev subcommand group
|
|
184
|
+
*/
|
|
185
|
+
function setupDevUpdatePinDeleteCommands(dev) {
|
|
186
|
+
dev
|
|
187
|
+
.command('update [developerId]')
|
|
188
|
+
.description('Update a developer (name, email, groups); use --developer-id like dev add')
|
|
189
|
+
.option('--developer-id <id>', 'Developer ID (same as dev add)')
|
|
190
|
+
.option('--name <name>', 'Display name')
|
|
191
|
+
.option('--email <email>', 'Email address')
|
|
192
|
+
.option('--groups <items>', 'Comma-separated groups (admin, secret-manager, developer)')
|
|
193
|
+
.action(async(developerId, options) => {
|
|
194
|
+
try {
|
|
195
|
+
await handleDevUpdate(developerId, options);
|
|
196
|
+
} catch (error) {
|
|
197
|
+
handleCommandError(error, 'dev update');
|
|
198
|
+
process.exit(1);
|
|
199
|
+
}
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
dev
|
|
203
|
+
.command('pin [developerId]')
|
|
204
|
+
.description('Create or regenerate one-time PIN for onboarding (admin; show once to developer)')
|
|
205
|
+
.action(async(developerId) => {
|
|
206
|
+
try {
|
|
207
|
+
await handleDevPin(developerId);
|
|
208
|
+
} catch (error) {
|
|
209
|
+
handleCommandError(error, 'dev pin');
|
|
210
|
+
process.exit(1);
|
|
211
|
+
}
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
dev
|
|
215
|
+
.command('delete <developerId>')
|
|
216
|
+
.description('Remove a developer (remote Builder Server only; admin)')
|
|
217
|
+
.action(async(developerId) => {
|
|
218
|
+
try {
|
|
219
|
+
await handleDevDelete(developerId);
|
|
220
|
+
} catch (error) {
|
|
221
|
+
handleCommandError(error, 'dev delete');
|
|
222
|
+
process.exit(1);
|
|
223
|
+
}
|
|
224
|
+
});
|
|
225
|
+
|
|
226
|
+
dev
|
|
227
|
+
.command('down')
|
|
228
|
+
.description('Stop Mutagen sync sessions for this developer (and optionally app containers)')
|
|
229
|
+
.option('--apps', 'Also stop running app containers for this developer')
|
|
230
|
+
.action(async(options) => {
|
|
231
|
+
try {
|
|
232
|
+
const { handleDevDown } = require('../commands/dev-down');
|
|
233
|
+
await handleDevDown(options);
|
|
234
|
+
} catch (error) {
|
|
235
|
+
handleCommandError(error, 'dev down');
|
|
236
|
+
process.exit(1);
|
|
237
|
+
}
|
|
238
|
+
});
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
/**
|
|
242
|
+
* Register dev list/add/update/pin/delete commands (remote only).
|
|
243
|
+
* @param {Command} dev - dev subcommand group
|
|
244
|
+
*/
|
|
245
|
+
function setupDevUserCommands(dev) {
|
|
246
|
+
setupDevListAddCommands(dev);
|
|
247
|
+
setupDevUpdatePinDeleteCommands(dev);
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
/**
|
|
251
|
+
* Sets up developer configuration commands
|
|
252
|
+
* @param {Command} program - Commander program instance
|
|
253
|
+
*/
|
|
254
|
+
function setupDevCommands(program) {
|
|
255
|
+
const dev = program
|
|
256
|
+
.command('dev')
|
|
257
|
+
.description('Developer configuration and isolation');
|
|
258
|
+
|
|
259
|
+
setupDevConfigCommands(dev);
|
|
260
|
+
setupDevInitCommand(dev);
|
|
261
|
+
setupDevUserCommands(dev);
|
|
262
|
+
}
|
|
263
|
+
|
|
101
264
|
module.exports = { setupDevCommands };
|
|
@@ -30,7 +30,8 @@ function setupEnvironmentCommands(program) {
|
|
|
30
30
|
environment
|
|
31
31
|
.command('deploy <env>')
|
|
32
32
|
.description('Deploy/setup environment in Miso Controller')
|
|
33
|
-
.option('--config <file>', 'Environment configuration file')
|
|
33
|
+
.option('--config <file>', 'Environment configuration file (optional if --preset is used)')
|
|
34
|
+
.option('--preset <size>', 'Environment size preset: s, m, l, xl (default: s)', 's')
|
|
34
35
|
.option('--skip-validation', 'Skip environment validation')
|
|
35
36
|
.option('--poll', 'Poll for deployment status', true)
|
|
36
37
|
.option('--no-poll', 'Do not poll for status')
|
|
@@ -43,7 +44,8 @@ function setupEnvironmentCommands(program) {
|
|
|
43
44
|
env
|
|
44
45
|
.command('deploy <env>')
|
|
45
46
|
.description('Deploy/setup environment in Miso Controller')
|
|
46
|
-
.option('--config <file>', 'Environment configuration file')
|
|
47
|
+
.option('--config <file>', 'Environment configuration file (optional if --preset is used)')
|
|
48
|
+
.option('--preset <size>', 'Environment size preset: s, m, l, xl (default: s)', 's')
|
|
47
49
|
.option('--skip-validation', 'Skip environment validation')
|
|
48
50
|
.option('--poll', 'Poll for deployment status', true)
|
|
49
51
|
.option('--no-poll', 'Do not poll for status')
|
|
@@ -56,35 +56,85 @@ function setupDeleteCommand(program) {
|
|
|
56
56
|
});
|
|
57
57
|
}
|
|
58
58
|
|
|
59
|
+
/**
|
|
60
|
+
* Try to run builder-style test integration when app is not in builder or has no externalIntegration.
|
|
61
|
+
* @param {string} appName - App or system key
|
|
62
|
+
* @param {Object} options - CLI options
|
|
63
|
+
* @returns {Promise<boolean>} True if builder test was run, false otherwise
|
|
64
|
+
*/
|
|
65
|
+
async function tryBuilderTestIntegration(appName, options) {
|
|
66
|
+
const fsSync = require('fs');
|
|
67
|
+
const pathsUtil = require('../utils/paths');
|
|
68
|
+
const { getIntegrationPath, getBuilderPath } = pathsUtil;
|
|
69
|
+
const { resolveApplicationConfigPath } = require('../utils/app-config-resolver');
|
|
70
|
+
const { loadConfigFile } = require('../utils/config-format');
|
|
71
|
+
const integrationPath = getIntegrationPath(appName);
|
|
72
|
+
let hasExternalIntegration = false;
|
|
73
|
+
try {
|
|
74
|
+
const integrationConfig = loadConfigFile(resolveApplicationConfigPath(integrationPath));
|
|
75
|
+
hasExternalIntegration = !!(integrationConfig && integrationConfig.externalIntegration);
|
|
76
|
+
} catch {
|
|
77
|
+
// integration path or config missing
|
|
78
|
+
}
|
|
79
|
+
if (!hasExternalIntegration) {
|
|
80
|
+
const builderPath = getBuilderPath(appName);
|
|
81
|
+
const builderConfigPath = resolveApplicationConfigPath(builderPath);
|
|
82
|
+
if (fsSync.existsSync(builderPath) && fsSync.existsSync(builderConfigPath)) {
|
|
83
|
+
const { runAppTestIntegration } = require('../commands/app-test');
|
|
84
|
+
const opts = { env: options.env || options.environment || 'dev' };
|
|
85
|
+
await runAppTestIntegration(appName, opts);
|
|
86
|
+
return true;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
return false;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Run external system integration test via dataplane and exit on failure.
|
|
94
|
+
* @param {string} appName - App or system key
|
|
95
|
+
* @param {Object} options - CLI options
|
|
96
|
+
* @returns {Promise<void>}
|
|
97
|
+
*/
|
|
98
|
+
async function runExternalSystemTestIntegration(appName, options) {
|
|
99
|
+
const test = require('../external-system/test');
|
|
100
|
+
const opts = { ...options, environment: options.env || options.environment };
|
|
101
|
+
const results = await test.testExternalSystemIntegration(appName, opts);
|
|
102
|
+
test.displayIntegrationTestResults(results, options.verbose);
|
|
103
|
+
if (!results.success) process.exit(1);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Run test-integration command: builder app in container or external system via dataplane.
|
|
108
|
+
* @param {string} appName - App or system key
|
|
109
|
+
* @param {Object} options - CLI options (datasource, payload, env, verbose, timeout)
|
|
110
|
+
* @returns {Promise<void>}
|
|
111
|
+
*/
|
|
112
|
+
async function runTestIntegrationCommand(appName, options) {
|
|
113
|
+
const pathsUtil = require('../utils/paths');
|
|
114
|
+
const appType = await pathsUtil.detectAppType(appName).catch(() => null);
|
|
115
|
+
if (appType && appType.baseDir === 'builder') {
|
|
116
|
+
const { runAppTestIntegration } = require('../commands/app-test');
|
|
117
|
+
const opts = { env: options.env || options.environment || 'dev' };
|
|
118
|
+
await runAppTestIntegration(appName, opts);
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
const ranBuilder = await tryBuilderTestIntegration(appName, options);
|
|
122
|
+
if (ranBuilder) return;
|
|
123
|
+
await runExternalSystemTestIntegration(appName, options);
|
|
124
|
+
}
|
|
125
|
+
|
|
59
126
|
function setupExternalSystemTestCommands(program) {
|
|
60
|
-
|
|
61
|
-
.description('Run unit tests for external system (local validation, no API calls)')
|
|
62
|
-
.option('-d, --datasource <key>', 'Test specific datasource only')
|
|
63
|
-
.option('-v, --verbose', 'Show detailed validation output')
|
|
64
|
-
.action(async(appName, options) => {
|
|
65
|
-
try {
|
|
66
|
-
const test = require('../external-system/test');
|
|
67
|
-
const results = await test.testExternalSystem(appName, options);
|
|
68
|
-
test.displayTestResults(results, options.verbose);
|
|
69
|
-
if (!results.valid) process.exit(1);
|
|
70
|
-
} catch (error) {
|
|
71
|
-
handleCommandError(error, 'test');
|
|
72
|
-
process.exit(1);
|
|
73
|
-
}
|
|
74
|
-
});
|
|
127
|
+
// 'test <app>' is registered in setup-app.js and dispatches by app type (builder vs external)
|
|
75
128
|
program.command('test-integration <app>')
|
|
76
|
-
.description('Run integration tests via dataplane pipeline API')
|
|
129
|
+
.description('Run integration tests (builder/docker app: in container; external system: via dataplane pipeline API)')
|
|
77
130
|
.option('-d, --datasource <key>', 'Test specific datasource only')
|
|
78
131
|
.option('-p, --payload <file>', 'Path to custom test payload file')
|
|
79
|
-
.option('--
|
|
132
|
+
.option('-e, --env <env>', 'Environment: dev, tst, or pro (default: from aifabrix auth config)')
|
|
80
133
|
.option('-v, --verbose', 'Show detailed test output')
|
|
81
134
|
.option('--timeout <ms>', 'Request timeout in milliseconds', '30000')
|
|
82
135
|
.action(async(appName, options) => {
|
|
83
136
|
try {
|
|
84
|
-
|
|
85
|
-
const results = await test.testExternalSystemIntegration(appName, options);
|
|
86
|
-
test.displayIntegrationTestResults(results, options.verbose);
|
|
87
|
-
if (!results.success) process.exit(1);
|
|
137
|
+
await runTestIntegrationCommand(appName, options);
|
|
88
138
|
} catch (error) {
|
|
89
139
|
handleCommandError(error, 'test-integration');
|
|
90
140
|
process.exit(1);
|
package/lib/cli/setup-infra.js
CHANGED
|
@@ -12,7 +12,9 @@ const appLib = require('../app');
|
|
|
12
12
|
const validator = require('../validation/validator');
|
|
13
13
|
const config = require('../core/config');
|
|
14
14
|
const logger = require('../utils/logger');
|
|
15
|
-
const { handleCommandError } = require('../utils/cli-utils');
|
|
15
|
+
const { handleCommandError, isAuthenticationError } = require('../utils/cli-utils');
|
|
16
|
+
const { resolveControllerUrl } = require('../utils/controller-url');
|
|
17
|
+
const { handleLogin } = require('../commands/login');
|
|
16
18
|
const { handleUpMiso } = require('../commands/up-miso');
|
|
17
19
|
const { handleUpDataplane } = require('../commands/up-dataplane');
|
|
18
20
|
|
|
@@ -45,13 +47,14 @@ async function runUpInfraCommand(options) {
|
|
|
45
47
|
logger.log(chalk.green('✓ Traefik disabled and saved to config'));
|
|
46
48
|
}
|
|
47
49
|
const useTraefik = options.traefik === true ? true : (options.traefik === false ? false : !!(cfg.traefik));
|
|
48
|
-
await infra.startInfra(developerId, { traefik: useTraefik });
|
|
50
|
+
await infra.startInfra(developerId, { traefik: useTraefik, adminPwd: options.adminPwd });
|
|
49
51
|
}
|
|
50
52
|
|
|
51
53
|
function setupUpInfraCommand(program) {
|
|
52
54
|
program.command('up-infra')
|
|
53
55
|
.description('Start local infrastructure: Postgres, Redis, optional Traefik')
|
|
54
56
|
.option('-d, --developer <id>', 'Set developer ID and start infrastructure')
|
|
57
|
+
.option('--adminPwd <password>', 'Override default admin password for new install (Postgres, pgAdmin, Redis Commander)')
|
|
55
58
|
.option('--traefik', 'Include Traefik reverse proxy and save to config')
|
|
56
59
|
.option('--no-traefik', 'Exclude Traefik and save to config')
|
|
57
60
|
.action(async(options) => {
|
|
@@ -75,6 +78,18 @@ function setupUpPlatformCommand(program) {
|
|
|
75
78
|
await handleUpMiso(options);
|
|
76
79
|
await handleUpDataplane(options);
|
|
77
80
|
} catch (error) {
|
|
81
|
+
if (isAuthenticationError(error)) {
|
|
82
|
+
const controllerUrl = error.controllerUrl || await resolveControllerUrl();
|
|
83
|
+
logger.log(chalk.blue('\nAuthentication required. Running aifabrix login...\n'));
|
|
84
|
+
try {
|
|
85
|
+
await handleLogin({ method: 'device', controller: controllerUrl });
|
|
86
|
+
await handleUpDataplane(options);
|
|
87
|
+
return;
|
|
88
|
+
} catch (loginOrRetryError) {
|
|
89
|
+
handleCommandError(loginOrRetryError, 'up-platform');
|
|
90
|
+
process.exit(1);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
78
93
|
handleCommandError(error, 'up-platform');
|
|
79
94
|
process.exit(1);
|
|
80
95
|
}
|
|
@@ -107,6 +122,18 @@ function setupUpDataplaneCommand(program) {
|
|
|
107
122
|
try {
|
|
108
123
|
await handleUpDataplane(options);
|
|
109
124
|
} catch (error) {
|
|
125
|
+
if (isAuthenticationError(error)) {
|
|
126
|
+
const controllerUrl = error.controllerUrl || await resolveControllerUrl();
|
|
127
|
+
logger.log(chalk.blue('\nAuthentication required. Running aifabrix login...\n'));
|
|
128
|
+
try {
|
|
129
|
+
await handleLogin({ method: 'device', controller: controllerUrl });
|
|
130
|
+
await handleUpDataplane(options);
|
|
131
|
+
return;
|
|
132
|
+
} catch (loginOrRetryError) {
|
|
133
|
+
handleCommandError(loginOrRetryError, 'up-dataplane');
|
|
134
|
+
process.exit(1);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
110
137
|
handleCommandError(error, 'up-dataplane');
|
|
111
138
|
process.exit(1);
|
|
112
139
|
}
|