@aifabrix/builder 2.44.5 → 2.45.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/.cursor/rules/cli-layout.mdc +8 -4
- package/.cursor/rules/project-rules.mdc +1 -1
- package/README.md +15 -23
- package/integration/hubspot-test/README.md +2 -0
- package/integration/hubspot-test/test.js +5 -3
- package/jest.projects.js +104 -2
- package/lib/api/controller-health.api.js +49 -0
- package/lib/api/dimension-values.api.js +82 -0
- package/lib/api/dimensions.api.js +114 -0
- package/lib/api/external-systems.api.js +1 -0
- package/lib/api/integration-clients.api.js +168 -0
- package/lib/api/types/dimension-values.types.js +28 -0
- package/lib/api/types/dimensions.types.js +31 -0
- package/lib/api/types/integration-clients.types.js +45 -0
- package/lib/api/validation-runner.js +46 -25
- package/lib/app/deploy-config.js +11 -1
- package/lib/app/deploy-status-display.js +3 -3
- package/lib/app/deploy.js +36 -14
- package/lib/app/display.js +15 -11
- package/lib/app/helpers.js +3 -3
- package/lib/app/index.js +3 -3
- package/lib/app/push.js +46 -23
- package/lib/app/register.js +7 -6
- package/lib/app/restart-display.js +126 -0
- package/lib/app/rotate-secret.js +7 -6
- package/lib/app/run-container-start.js +12 -6
- package/lib/app/run-env-compose.js +30 -1
- package/lib/app/run-helpers.js +58 -19
- package/lib/app/run-reload-sync.js +148 -0
- package/lib/app/run-resolve-image.js +51 -1
- package/lib/app/run.js +148 -74
- package/lib/app/show-display.js +7 -0
- package/lib/app/show.js +87 -5
- package/lib/build/index.js +83 -49
- package/lib/cli/doctor-check.js +117 -0
- package/lib/cli/index.js +8 -2
- package/lib/cli/infra-guided.js +460 -0
- package/lib/cli/installation-log-command.js +73 -0
- package/lib/cli/setup-app.js +31 -3
- package/lib/cli/setup-auth.js +98 -27
- package/lib/cli/setup-dev-path-commands.js +50 -3
- package/lib/cli/setup-infra-up-dataplane-action.js +111 -0
- package/lib/cli/setup-infra-up-platform-action.js +131 -0
- package/lib/cli/setup-infra.js +132 -118
- package/lib/cli/setup-integration-client.js +182 -0
- package/lib/cli/setup-parameters.js +21 -2
- package/lib/cli/setup-platform.js +102 -0
- package/lib/cli/setup-secrets.js +18 -6
- package/lib/cli/setup-utility-resolve.js +132 -0
- package/lib/cli/setup-utility.js +143 -84
- package/lib/commands/app-logs.js +81 -33
- package/lib/commands/auth-config.js +116 -18
- package/lib/commands/datasource-capability-dimension-cli.js +128 -0
- package/lib/commands/datasource-capability-output.js +29 -0
- package/lib/commands/datasource-capability-relate-cli.js +140 -0
- package/lib/commands/datasource-capability.js +411 -0
- package/lib/commands/datasource-unified-test-cli.options.js +1 -1
- package/lib/commands/datasource.js +53 -13
- package/lib/commands/dev-down.js +3 -3
- package/lib/commands/dev-infra-gate.js +32 -0
- package/lib/commands/dev-init.js +13 -7
- package/lib/commands/dimension-value.js +179 -0
- package/lib/commands/dimension.js +330 -0
- package/lib/commands/integration-client.js +430 -0
- package/lib/commands/login-device.js +65 -30
- package/lib/commands/login.js +21 -10
- package/lib/commands/parameters-validate.js +78 -13
- package/lib/commands/repair-datasource-auto-rbac.js +166 -0
- package/lib/commands/repair-datasource-keys.js +10 -5
- package/lib/commands/repair-datasource.js +19 -7
- package/lib/commands/repair-env-template.js +4 -1
- package/lib/commands/repair-openapi-sync.js +172 -0
- package/lib/commands/repair-persist.js +102 -0
- package/lib/commands/repair-rbac-extract.js +27 -0
- package/lib/commands/repair-rbac-migrate.js +186 -0
- package/lib/commands/repair-rbac.js +214 -31
- package/lib/commands/repair-system-alignment.js +246 -0
- package/lib/commands/repair-system-permissions.js +168 -0
- package/lib/commands/repair.js +120 -338
- package/lib/commands/secure.js +1 -1
- package/lib/commands/setup-modes.js +468 -0
- package/lib/commands/setup-prompts.js +421 -0
- package/lib/commands/setup.js +254 -0
- package/lib/commands/teardown.js +277 -0
- package/lib/commands/up-common.js +113 -19
- package/lib/commands/up-dataplane.js +44 -19
- package/lib/commands/up-miso.js +18 -18
- package/lib/commands/upload.js +111 -23
- package/lib/commands/wizard-core-helpers.js +14 -11
- package/lib/commands/wizard-core.js +6 -5
- package/lib/commands/wizard-dataplane.js +2 -2
- package/lib/commands/wizard-entity-selection.js +4 -3
- package/lib/commands/wizard-headless.js +2 -1
- package/lib/commands/wizard.js +2 -1
- package/lib/constants/infra-compose-service-names.js +40 -0
- package/lib/core/audit-logger.js +1 -34
- package/lib/core/config-admin-email.js +56 -0
- package/lib/core/config-normalize.js +60 -0
- package/lib/core/config-registered-controller-urls.js +54 -0
- package/lib/core/config.js +33 -50
- package/lib/core/env-reader.js +16 -3
- package/lib/core/secrets-admin-env.js +101 -0
- package/lib/core/secrets-ensure-infra.js +34 -1
- package/lib/core/secrets-ensure.js +88 -66
- package/lib/core/secrets-env-content.js +428 -0
- package/lib/core/secrets-env-declarative-expand.js +170 -0
- package/lib/core/secrets-env-write.js +29 -1
- package/lib/core/secrets-load.js +252 -0
- package/lib/core/secrets-names.js +32 -0
- package/lib/core/secrets.js +17 -757
- package/lib/datasource/capability/basic-exposure.js +76 -0
- package/lib/datasource/capability/capability-diff-slice.js +41 -0
- package/lib/datasource/capability/capability-key.js +34 -0
- package/lib/datasource/capability/capability-resolve.js +172 -0
- package/lib/datasource/capability/capability-storage-keys.js +22 -0
- package/lib/datasource/capability/copy-operations.js +348 -0
- package/lib/datasource/capability/copy-test-payload.js +139 -0
- package/lib/datasource/capability/create-operations.js +235 -0
- package/lib/datasource/capability/dimension-operations.js +151 -0
- package/lib/datasource/capability/dimension-validate.js +219 -0
- package/lib/datasource/capability/json-pointer.js +31 -0
- package/lib/datasource/capability/reference-rewrite.js +51 -0
- package/lib/datasource/capability/relate-operations.js +325 -0
- package/lib/datasource/capability/relate-validate.js +219 -0
- package/lib/datasource/capability/remove-operations.js +275 -0
- package/lib/datasource/capability/run-capability-copy.js +152 -0
- package/lib/datasource/capability/run-capability-diff.js +135 -0
- package/lib/datasource/capability/run-capability-dimension.js +291 -0
- package/lib/datasource/capability/run-capability-edit.js +377 -0
- package/lib/datasource/capability/run-capability-relate.js +193 -0
- package/lib/datasource/capability/run-capability-remove.js +105 -0
- package/lib/datasource/capability/templates/minimal-fetch.json +18 -0
- package/lib/datasource/capability/validate-capability-slice.js +35 -0
- package/lib/datasource/list.js +136 -23
- package/lib/datasource/log-viewer.js +2 -4
- package/lib/datasource/unified-validation-run.js +51 -16
- package/lib/datasource/validate.js +53 -1
- package/lib/deployment/deploy-poll-ui.js +60 -0
- package/lib/deployment/deployer-status.js +29 -3
- package/lib/deployment/deployer.js +48 -30
- package/lib/deployment/environment.js +7 -2
- package/lib/deployment/poll-interval.js +72 -0
- package/lib/deployment/push.js +11 -9
- package/lib/external-system/deploy.js +9 -2
- package/lib/external-system/download.js +61 -32
- package/lib/external-system/sync-deploy-manifest.js +33 -0
- package/lib/infrastructure/index.js +49 -19
- package/lib/infrastructure/orphan-infra-docker-teardown.js +177 -0
- package/lib/internal/node-fs.js +2 -0
- package/lib/parameters/infra-kv-discovery.js +29 -4
- package/lib/parameters/infra-parameter-catalog.js +6 -3
- package/lib/parameters/infra-parameter-validate.js +67 -19
- package/lib/resolvers/datasource-resolver.js +53 -0
- package/lib/resolvers/dimension-file.js +52 -0
- package/lib/resolvers/manifest-resolver.js +133 -0
- package/lib/schema/application-schema.json +4 -0
- package/lib/schema/external-datasource.schema.json +183 -53
- package/lib/schema/external-system.schema.json +23 -10
- package/lib/schema/infra.parameter.yaml +26 -1
- package/lib/schema/wizard-config.schema.json +1 -1
- package/lib/utils/aifabrix-config-dir-walk.js +40 -0
- package/lib/utils/aifabrix-runtime-config-dir.js +26 -3
- package/lib/utils/app-config-resolver.js +24 -1
- package/lib/utils/app-run-containers.js +2 -2
- package/lib/utils/applications-config-defaults.js +206 -0
- package/lib/utils/auth-config-validator.js +2 -12
- package/lib/utils/bash-secret-env.js +59 -0
- package/lib/utils/cli-secrets-error-format.js +78 -0
- package/lib/utils/cli-test-layout-chalk.js +31 -9
- package/lib/utils/cli-utils.js +4 -36
- package/lib/utils/compose-generate-docker-compose.js +111 -6
- package/lib/utils/compose-generator.js +17 -8
- package/lib/utils/controller-url.js +50 -7
- package/lib/utils/datasource-test-run-display.js +8 -0
- package/lib/utils/dev-hosts-helper.js +3 -2
- package/lib/utils/dev-init-ssh-merge.js +2 -1
- package/lib/utils/docker-build.js +17 -9
- package/lib/utils/docker-reload-mount.js +127 -0
- package/lib/utils/env-copy.js +99 -14
- package/lib/utils/env-template.js +5 -1
- package/lib/utils/external-readme.js +71 -2
- package/lib/utils/external-system-local-test-tty.js +3 -2
- package/lib/utils/external-system-readiness-core.js +45 -12
- package/lib/utils/external-system-readiness-deploy-display.js +3 -3
- package/lib/utils/external-system-readiness-display-internals.js +33 -3
- package/lib/utils/external-system-readiness-display.js +10 -1
- package/lib/utils/file-upload.js +40 -3
- package/lib/utils/health-check-db-init.js +107 -0
- package/lib/utils/health-check-public-warn.js +69 -0
- package/lib/utils/health-check-url.js +28 -10
- package/lib/utils/health-check.js +139 -107
- package/lib/utils/help-builder.js +5 -1
- package/lib/utils/image-name.js +34 -7
- package/lib/utils/infra-optional-service-flags.js +69 -0
- package/lib/utils/installation-log-core.js +282 -0
- package/lib/utils/installation-log-record.js +237 -0
- package/lib/utils/installation-log.js +123 -0
- package/lib/utils/integration-file-backup.js +74 -0
- package/lib/utils/log-redaction.js +105 -0
- package/lib/utils/manifest-location.js +164 -0
- package/lib/utils/manifest-source-emit.js +162 -0
- package/lib/utils/mutagen-install.js +30 -3
- package/lib/utils/paths.js +308 -76
- package/lib/utils/postgres-wipe.js +212 -0
- package/lib/utils/register-aifabrix-shell-env.js +15 -0
- package/lib/utils/remote-dev-auth.js +21 -5
- package/lib/utils/remote-docker-env.js +9 -1
- package/lib/utils/remote-secrets-loader.js +49 -4
- package/lib/utils/resolve-docker-image-ref.js +9 -3
- package/lib/utils/run-cli-flags.js +29 -0
- package/lib/utils/secrets-ancestor-paths.js +47 -0
- package/lib/utils/secrets-canonical.js +10 -3
- package/lib/utils/secrets-helpers.js +17 -10
- package/lib/utils/secrets-kv-refs.js +42 -0
- package/lib/utils/secrets-kv-scope.js +19 -2
- package/lib/utils/secrets-materialize-local.js +134 -0
- package/lib/utils/secrets-path.js +26 -13
- package/lib/utils/secrets-utils.js +20 -10
- package/lib/utils/system-builder-root.js +42 -0
- package/lib/utils/url-declarative-public-base.js +80 -12
- package/lib/utils/url-declarative-resolve-build-urls.js +238 -0
- package/lib/utils/url-declarative-resolve-build.js +24 -388
- package/lib/utils/url-declarative-resolve-expand-token.js +189 -0
- package/lib/utils/url-declarative-resolve-load-doc.js +12 -3
- package/lib/utils/url-declarative-resolve-surface-state.js +102 -0
- package/lib/utils/url-declarative-resolve.js +47 -7
- package/lib/utils/url-declarative-runtime-base-path.js +52 -0
- package/lib/utils/url-declarative-vdir-inactive-env.js +2 -1
- package/lib/utils/urls-local-registry-scan.js +103 -0
- package/lib/utils/urls-local-registry.js +158 -76
- package/lib/utils/validation-poll-ui.js +81 -0
- package/lib/utils/validation-run-poll.js +29 -5
- package/lib/utils/with-muted-logger.js +53 -0
- package/package.json +3 -1
- package/templates/applications/dataplane/application.yaml +5 -1
- package/templates/applications/dataplane/rbac.yaml +10 -10
- package/templates/applications/keycloak/env.template +8 -6
- package/templates/applications/miso-controller/application.yaml +9 -0
- package/templates/applications/miso-controller/env.template +27 -29
- package/templates/applications/miso-controller/rbac.yaml +9 -9
- package/templates/external-system/README.md.hbs +83 -123
- package/.npmrc.token +0 -1
- package/.nyc_output/55e9d034-ddab-4579-a706-e02a91d75c91.json +0 -1
- package/.nyc_output/processinfo/55e9d034-ddab-4579-a706-e02a91d75c91.json +0 -1
- package/.nyc_output/processinfo/index.json +0 -1
- package/lib/api/service-users.api.js +0 -150
- package/lib/api/types/service-users.types.js +0 -65
- package/lib/cli/setup-service-user.js +0 -187
- package/lib/commands/service-user.js +0 -429
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
const { formatSuccessLine } = require('../utils/cli-test-layout-chalk');
|
|
1
|
+
const { formatSuccessLine, formatProgress, metadata } = require('../utils/cli-test-layout-chalk');
|
|
2
2
|
/**
|
|
3
3
|
* AI Fabrix Builder Deployment Module
|
|
4
4
|
*
|
|
@@ -22,6 +22,8 @@ const {
|
|
|
22
22
|
convertToPipelineAuthConfig,
|
|
23
23
|
processDeploymentStatusResponse
|
|
24
24
|
} = require('./deployer-status');
|
|
25
|
+
const { resolvePollIntervalFromController } = require('./poll-interval');
|
|
26
|
+
const { createDeployPollHandlers } = require('./deploy-poll-ui');
|
|
25
27
|
|
|
26
28
|
/**
|
|
27
29
|
* For external systems, send full manifest (application + inline system + full dataSources).
|
|
@@ -47,7 +49,10 @@ function transformExternalManifestForPipeline(manifest) {
|
|
|
47
49
|
* @returns {Promise<Object>} Object with validationData, pipelineAuthConfig, and useBearerOnly
|
|
48
50
|
*/
|
|
49
51
|
async function buildValidationData(manifest, validatedEnvKey, authConfig, options) {
|
|
50
|
-
const repositoryUrl =
|
|
52
|
+
const repositoryUrl =
|
|
53
|
+
options.repositoryUrl ||
|
|
54
|
+
manifest?.repository?.repositoryUrl ||
|
|
55
|
+
`https://github.com/aifabrix/${manifest.key}`;
|
|
51
56
|
|
|
52
57
|
if (authConfig.type === 'bearer' && authConfig.token && !authConfig.clientId) {
|
|
53
58
|
const pipelineAuthConfig = { type: 'bearer', token: authConfig.token };
|
|
@@ -295,10 +300,19 @@ async function pollDeploymentStatus(deploymentId, controllerUrl, envKey, authCon
|
|
|
295
300
|
const validatedEnvKey = validateEnvironmentKey(envKey);
|
|
296
301
|
const pipelineAuthConfig = convertToPipelineAuthConfig(authConfig);
|
|
297
302
|
|
|
303
|
+
const onPollProgress = typeof options.onPollProgress === 'function' ? options.onPollProgress : null;
|
|
304
|
+
|
|
298
305
|
for (let attempt = 0; attempt < maxAttempts; attempt++) {
|
|
299
306
|
try {
|
|
300
307
|
const response = await getPipelineDeployment(controllerUrl, validatedEnvKey, deploymentId, pipelineAuthConfig);
|
|
301
|
-
const deploymentData = await processDeploymentStatusResponse(
|
|
308
|
+
const deploymentData = await processDeploymentStatusResponse(
|
|
309
|
+
response,
|
|
310
|
+
attempt,
|
|
311
|
+
maxAttempts,
|
|
312
|
+
interval,
|
|
313
|
+
deploymentId,
|
|
314
|
+
onPollProgress
|
|
315
|
+
);
|
|
302
316
|
if (deploymentData) {
|
|
303
317
|
return deploymentData;
|
|
304
318
|
}
|
|
@@ -354,7 +368,7 @@ async function sendDeployment(url, validatedEnvKey, manifest, authConfig, option
|
|
|
354
368
|
await ensureBearerTokenValid(url, authConfig);
|
|
355
369
|
|
|
356
370
|
// Step 1: Validate deployment
|
|
357
|
-
logger.log(
|
|
371
|
+
logger.log(formatProgress('Validating deployment configuration...'));
|
|
358
372
|
const validateResult = await validateDeployment(url, validatedEnvKey, manifest, authConfig, {
|
|
359
373
|
repositoryUrl: options.repositoryUrl,
|
|
360
374
|
controllerId: options.controllerId,
|
|
@@ -368,11 +382,13 @@ async function sendDeployment(url, validatedEnvKey, manifest, authConfig, option
|
|
|
368
382
|
|
|
369
383
|
logger.log(formatSuccessLine('Validation successful'));
|
|
370
384
|
if (validateResult.draftDeploymentId) {
|
|
371
|
-
logger.log(
|
|
385
|
+
logger.log(metadata(`Draft Deployment ID: ${validateResult.draftDeploymentId}`));
|
|
372
386
|
}
|
|
373
387
|
|
|
374
|
-
// Step 2: Deploy using validateToken
|
|
375
|
-
|
|
388
|
+
// Step 2: Deploy using validateToken (when not polling, show a clear line; otherwise poll phase uses ora)
|
|
389
|
+
if (options.poll === false) {
|
|
390
|
+
logger.log(formatProgress('Deploying application...'));
|
|
391
|
+
}
|
|
376
392
|
const result = await sendDeploymentRequest(url, validatedEnvKey, validateResult.validateToken, authConfig, {
|
|
377
393
|
imageTag: options.imageTag || 'latest',
|
|
378
394
|
timeout: options.timeout || 30000,
|
|
@@ -386,35 +402,37 @@ async function sendDeployment(url, validatedEnvKey, manifest, authConfig, option
|
|
|
386
402
|
return result;
|
|
387
403
|
}
|
|
388
404
|
|
|
389
|
-
/**
|
|
390
|
-
* Polls deployment status if enabled
|
|
391
|
-
* @async
|
|
392
|
-
* @param {Object} result - Deployment result
|
|
393
|
-
* @param {string} url - Controller URL
|
|
394
|
-
* @param {string} validatedEnvKey - Validated environment key
|
|
395
|
-
* @param {Object} authConfig - Authentication configuration
|
|
396
|
-
* @param {Object} options - Deployment options
|
|
397
|
-
* @returns {Promise<Object>} Deployment result with status
|
|
398
|
-
*/
|
|
405
|
+
/** Poll pipeline deployment to terminal status (TTY: ora spinner). */
|
|
399
406
|
async function pollDeployment(result, url, validatedEnvKey, authConfig, options) {
|
|
400
407
|
if (!options.poll || !result.deploymentId) {
|
|
401
408
|
return result;
|
|
402
409
|
}
|
|
403
410
|
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
result.deploymentId,
|
|
407
|
-
url,
|
|
408
|
-
validatedEnvKey,
|
|
409
|
-
authConfig,
|
|
410
|
-
{
|
|
411
|
-
interval: options.pollInterval || 5000,
|
|
412
|
-
maxAttempts: options.pollMaxAttempts || 60
|
|
413
|
-
}
|
|
414
|
-
);
|
|
411
|
+
// Separate validation/draft block from deploy polling (matches guided CLI spacing)
|
|
412
|
+
logger.log('');
|
|
415
413
|
|
|
416
|
-
|
|
417
|
-
|
|
414
|
+
const resolvedInterval = await resolvePollIntervalFromController(url, options.pollInterval);
|
|
415
|
+
const maxAttempts = options.pollMaxAttempts || 60;
|
|
416
|
+
const pollUi = createDeployPollHandlers(maxAttempts, { silent: options.silentPoll === true });
|
|
417
|
+
|
|
418
|
+
try {
|
|
419
|
+
const status = await pollDeploymentStatus(
|
|
420
|
+
result.deploymentId,
|
|
421
|
+
url,
|
|
422
|
+
validatedEnvKey,
|
|
423
|
+
authConfig,
|
|
424
|
+
{
|
|
425
|
+
interval: resolvedInterval,
|
|
426
|
+
maxAttempts,
|
|
427
|
+
onPollProgress: pollUi.onPollProgress
|
|
428
|
+
}
|
|
429
|
+
);
|
|
430
|
+
|
|
431
|
+
result.status = status;
|
|
432
|
+
return result;
|
|
433
|
+
} finally {
|
|
434
|
+
pollUi.finish();
|
|
435
|
+
}
|
|
418
436
|
}
|
|
419
437
|
|
|
420
438
|
/**
|
|
@@ -378,14 +378,19 @@ async function executeEnvironmentDeployment(validatedControllerUrl, envKey, auth
|
|
|
378
378
|
async function pollDeploymentStatusIfEnabled(result, validatedControllerUrl, envKey, authConfig, options) {
|
|
379
379
|
const shouldPoll = options.poll !== false && !options.noPoll;
|
|
380
380
|
if (shouldPoll && result.deploymentId) {
|
|
381
|
+
const { resolvePollIntervalFromController } = require('./poll-interval');
|
|
382
|
+
const pollInterval = await resolvePollIntervalFromController(
|
|
383
|
+
validatedControllerUrl,
|
|
384
|
+
options.pollInterval
|
|
385
|
+
);
|
|
381
386
|
const pollResult = await pollEnvironmentStatus(
|
|
382
387
|
result.deploymentId,
|
|
383
388
|
validatedControllerUrl,
|
|
384
389
|
envKey,
|
|
385
390
|
authConfig,
|
|
386
391
|
{
|
|
387
|
-
pollInterval
|
|
388
|
-
maxAttempts: 60
|
|
392
|
+
pollInterval,
|
|
393
|
+
maxAttempts: options.pollMaxAttempts || 60
|
|
389
394
|
}
|
|
390
395
|
);
|
|
391
396
|
result.status = pollResult.status;
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pipeline deployment polling intervals based on controller DEPLOYMENT mode.
|
|
3
|
+
*
|
|
4
|
+
* Miso-controller exposes `deploymentType` on GET /api/v1/health (azure | azure-mock | local | database).
|
|
5
|
+
* Local/database installs finish quickly; use a shorter poll interval unless the user overrides.
|
|
6
|
+
*
|
|
7
|
+
* @fileoverview Resolve poll interval from controller deployment mode
|
|
8
|
+
* @author AI Fabrix Team
|
|
9
|
+
* @version 2.0.0
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
const FAST_POLL_MS = 1000;
|
|
13
|
+
const STANDARD_POLL_MS = 5000;
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* @param {string} [deploymentType] - From controller health: deploymentType
|
|
17
|
+
* @returns {boolean}
|
|
18
|
+
*/
|
|
19
|
+
function isFastPollingDeploymentType(deploymentType) {
|
|
20
|
+
if (!deploymentType || typeof deploymentType !== 'string') {
|
|
21
|
+
return false;
|
|
22
|
+
}
|
|
23
|
+
const t = deploymentType.trim().toLowerCase();
|
|
24
|
+
return t === 'local' || t === 'database';
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* @param {string} [deploymentType] - Controller deploymentType
|
|
29
|
+
* @param {number|string|undefined|null} explicitPollInterval - User/config override (ms)
|
|
30
|
+
* @returns {number}
|
|
31
|
+
*/
|
|
32
|
+
function resolvePollIntervalMs(deploymentType, explicitPollInterval) {
|
|
33
|
+
const n =
|
|
34
|
+
explicitPollInterval !== undefined && explicitPollInterval !== null
|
|
35
|
+
? Number(explicitPollInterval)
|
|
36
|
+
: NaN;
|
|
37
|
+
if (Number.isFinite(n) && n > 0) {
|
|
38
|
+
return n;
|
|
39
|
+
}
|
|
40
|
+
return isFastPollingDeploymentType(deploymentType) ? FAST_POLL_MS : STANDARD_POLL_MS;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Resolve poll interval: honor explicit ms when valid; else probe controller health for deploymentType.
|
|
45
|
+
* @param {string} controllerUrl - Miso controller base URL
|
|
46
|
+
* @param {number|string|undefined|null} explicitPollInterval - Optional CLI/config override
|
|
47
|
+
* @returns {Promise<number>}
|
|
48
|
+
*/
|
|
49
|
+
async function resolvePollIntervalFromController(controllerUrl, explicitPollInterval) {
|
|
50
|
+
const n =
|
|
51
|
+
explicitPollInterval !== undefined && explicitPollInterval !== null
|
|
52
|
+
? Number(explicitPollInterval)
|
|
53
|
+
: NaN;
|
|
54
|
+
if (Number.isFinite(n) && n > 0) {
|
|
55
|
+
return n;
|
|
56
|
+
}
|
|
57
|
+
try {
|
|
58
|
+
const { getControllerDeploymentType } = require('../api/controller-health.api');
|
|
59
|
+
const deploymentType = await getControllerDeploymentType(controllerUrl);
|
|
60
|
+
return resolvePollIntervalMs(deploymentType, undefined);
|
|
61
|
+
} catch {
|
|
62
|
+
return STANDARD_POLL_MS;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
module.exports = {
|
|
67
|
+
FAST_POLL_MS,
|
|
68
|
+
STANDARD_POLL_MS,
|
|
69
|
+
isFastPollingDeploymentType,
|
|
70
|
+
resolvePollIntervalMs,
|
|
71
|
+
resolvePollIntervalFromController
|
|
72
|
+
};
|
package/lib/deployment/push.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
const { formatSuccessLine } = require('../utils/cli-test-layout-chalk');
|
|
1
|
+
const { formatSuccessLine, formatProgress } = require('../utils/cli-test-layout-chalk');
|
|
2
2
|
/**
|
|
3
3
|
* AI Fabrix Builder Push Utilities
|
|
4
4
|
*
|
|
@@ -150,8 +150,9 @@ function validateRegistryURL(registryUrl) {
|
|
|
150
150
|
async function checkACRAuthentication(registry) {
|
|
151
151
|
try {
|
|
152
152
|
const registryName = extractRegistryName(registry);
|
|
153
|
-
|
|
154
|
-
const
|
|
153
|
+
const { getDockerExecEnv } = require('../utils/remote-docker-env');
|
|
154
|
+
const env = await getDockerExecEnv();
|
|
155
|
+
const options = process.platform === 'win32' ? { shell: true, env } : { env };
|
|
155
156
|
await execAsync(`az acr show --name ${registryName}`, { ...options, timeout: AZ_ACR_SHOW_TIMEOUT_MS });
|
|
156
157
|
return true;
|
|
157
158
|
} catch (error) {
|
|
@@ -167,9 +168,10 @@ async function checkACRAuthentication(registry) {
|
|
|
167
168
|
async function authenticateACR(registry) {
|
|
168
169
|
try {
|
|
169
170
|
const registryName = extractRegistryName(registry);
|
|
170
|
-
logger.log(
|
|
171
|
-
|
|
172
|
-
const
|
|
171
|
+
logger.log(formatProgress(`Authenticating with ${registry}…`));
|
|
172
|
+
const { getDockerExecEnv } = require('../utils/remote-docker-env');
|
|
173
|
+
const env = await getDockerExecEnv();
|
|
174
|
+
const options = process.platform === 'win32' ? { shell: true, env } : { env };
|
|
173
175
|
await execAsync(`az acr login --name ${registryName}`, { ...options, timeout: AZ_ACR_LOGIN_TIMEOUT_MS });
|
|
174
176
|
logger.log(formatSuccessLine(`Authenticated with ${registry}`));
|
|
175
177
|
} catch (error) {
|
|
@@ -192,7 +194,7 @@ async function authenticateACR(registry) {
|
|
|
192
194
|
*/
|
|
193
195
|
async function authenticateExternalRegistry(registry, username, password) {
|
|
194
196
|
try {
|
|
195
|
-
logger.log(
|
|
197
|
+
logger.log(formatProgress(`Authenticating with ${registry}…`));
|
|
196
198
|
|
|
197
199
|
// Use cross-platform approach: write password to stdin directly
|
|
198
200
|
// This works on Windows, Linux, and macOS
|
|
@@ -262,7 +264,7 @@ async function tagImage(sourceImage, targetImage) {
|
|
|
262
264
|
try {
|
|
263
265
|
const { getDockerExecEnv } = require('../utils/remote-docker-env');
|
|
264
266
|
const env = await getDockerExecEnv();
|
|
265
|
-
logger.log(
|
|
267
|
+
logger.log(formatProgress(`Tagging ${sourceImage} → ${targetImage}…`));
|
|
266
268
|
await execAsync(`docker tag ${sourceImage} ${targetImage}`, { env });
|
|
267
269
|
logger.log(formatSuccessLine(`Tagged: ${targetImage}`));
|
|
268
270
|
} catch (error) {
|
|
@@ -280,7 +282,7 @@ async function pushImage(imageWithTag, registry = null) {
|
|
|
280
282
|
try {
|
|
281
283
|
const { getDockerExecEnv } = require('../utils/remote-docker-env');
|
|
282
284
|
const env = await getDockerExecEnv();
|
|
283
|
-
logger.log(
|
|
285
|
+
logger.log(formatProgress(`Pushing ${imageWithTag}…`));
|
|
284
286
|
await execAsync(`docker push ${imageWithTag}`, { env });
|
|
285
287
|
logger.log(formatSuccessLine(`Pushed: ${imageWithTag}`));
|
|
286
288
|
} catch (error) {
|
|
@@ -29,6 +29,7 @@ const { validateExternalSystemComplete } = require('../validation/validate');
|
|
|
29
29
|
const { displayValidationResults } = require('../validation/validate-display');
|
|
30
30
|
const { maybeSyncSystemCertificationFromDataplane } = require('../certification/sync-system-certification');
|
|
31
31
|
const { cliOptsSkipCertSync } = require('../certification/cli-cert-sync-skip');
|
|
32
|
+
const { syncDeployJsonFromSources } = require('./sync-deploy-manifest');
|
|
32
33
|
|
|
33
34
|
/**
|
|
34
35
|
* Lists datasources for a system and loads system record for docs URLs.
|
|
@@ -152,7 +153,7 @@ function logImmediateControllerDeploymentOutcome(deploymentOutcome) {
|
|
|
152
153
|
logger.log(formatSuccessParagraph('Controller deployment OK'));
|
|
153
154
|
return;
|
|
154
155
|
}
|
|
155
|
-
logger.log(chalk.red('
|
|
156
|
+
logger.log(chalk.red('✖ Controller deployment did not complete successfully'));
|
|
156
157
|
const parts = [deploymentOutcome.error, deploymentOutcome.message].filter(Boolean);
|
|
157
158
|
if (parts.length > 0) {
|
|
158
159
|
for (const line of parts) {
|
|
@@ -168,7 +169,7 @@ function logImmediateControllerDeploymentOutcome(deploymentOutcome) {
|
|
|
168
169
|
);
|
|
169
170
|
return;
|
|
170
171
|
}
|
|
171
|
-
logger.log(chalk.gray('
|
|
172
|
+
logger.log(chalk.gray(' Check the controller deployment job or logs for details.'));
|
|
172
173
|
}
|
|
173
174
|
|
|
174
175
|
async function executeDeployAndDisplay(manifest, controllerUrl, environment, authConfig, options) {
|
|
@@ -191,6 +192,10 @@ async function executeDeployAndDisplay(manifest, controllerUrl, environment, aut
|
|
|
191
192
|
const deploymentOutcome = parseControllerDeploymentOutcome(result);
|
|
192
193
|
logImmediateControllerDeploymentOutcome(deploymentOutcome);
|
|
193
194
|
|
|
195
|
+
if (!deploymentOutcome.ok) {
|
|
196
|
+
return result;
|
|
197
|
+
}
|
|
198
|
+
|
|
194
199
|
const ctx = await fetchDataplaneDeployReadiness(
|
|
195
200
|
controllerUrl,
|
|
196
201
|
environment,
|
|
@@ -265,6 +270,8 @@ async function deployExternalSystem(appName, options = {}) {
|
|
|
265
270
|
|
|
266
271
|
logger.log(formatSuccessLine('Local validation passed, proceeding with deployment...'));
|
|
267
272
|
|
|
273
|
+
await syncDeployJsonFromSources(appName);
|
|
274
|
+
|
|
268
275
|
const manifest = await generateControllerManifest(appName, options);
|
|
269
276
|
|
|
270
277
|
const { environment, controllerUrl, authConfig } = await prepareDeploymentConfig(appName, options);
|
|
@@ -1,4 +1,12 @@
|
|
|
1
|
-
const {
|
|
1
|
+
const {
|
|
2
|
+
formatSuccessLine,
|
|
3
|
+
formatSuccessParagraph,
|
|
4
|
+
sectionTitle,
|
|
5
|
+
headerKeyValue,
|
|
6
|
+
metadata,
|
|
7
|
+
formatProgress,
|
|
8
|
+
formatWarningLine
|
|
9
|
+
} = require('../utils/cli-test-layout-chalk');
|
|
2
10
|
/**
|
|
3
11
|
* External System Download Module
|
|
4
12
|
*
|
|
@@ -24,7 +32,6 @@ const fsSync = require('fs');
|
|
|
24
32
|
const path = require('path');
|
|
25
33
|
const readline = require('readline');
|
|
26
34
|
const yaml = require('js-yaml');
|
|
27
|
-
const chalk = require('chalk');
|
|
28
35
|
const { getExternalSystemConfig } = require('../api/external-systems.api');
|
|
29
36
|
const { getDeploymentAuth, requireBearerForDataplanePipeline } = require('../utils/token-manager');
|
|
30
37
|
const { getConfig } = require('../core/config');
|
|
@@ -128,7 +135,7 @@ async function setupAuthenticationAndDataplane(systemKey, _options, _config) {
|
|
|
128
135
|
}
|
|
129
136
|
|
|
130
137
|
const { resolveDataplaneUrl } = require('../utils/dataplane-resolver');
|
|
131
|
-
logger.log(
|
|
138
|
+
logger.log(formatProgress('Resolving dataplane URL…'));
|
|
132
139
|
const dataplaneUrl = await resolveDataplaneUrl(controllerUrl, environment, authConfig);
|
|
133
140
|
logger.log(formatSuccessLine(`Dataplane URL: ${dataplaneUrl}`));
|
|
134
141
|
|
|
@@ -148,7 +155,7 @@ async function setupAuthenticationAndDataplane(systemKey, _options, _config) {
|
|
|
148
155
|
* @throws {Error} If download fails
|
|
149
156
|
*/
|
|
150
157
|
async function downloadFullManifest(dataplaneUrl, systemKey, authConfig) {
|
|
151
|
-
logger.log(
|
|
158
|
+
logger.log(formatProgress(`Downloading manifest for ${systemKey}…`));
|
|
152
159
|
const response = await getExternalSystemConfig(dataplaneUrl, systemKey, authConfig);
|
|
153
160
|
|
|
154
161
|
if (!response.success || !response.data) {
|
|
@@ -268,14 +275,18 @@ function validateSystemKeyFormat(systemKey) {
|
|
|
268
275
|
* @param {string} dataplaneUrl - Dataplane URL
|
|
269
276
|
*/
|
|
270
277
|
function handleDryRun(systemKey, dataplaneUrl) {
|
|
271
|
-
logger.log(
|
|
272
|
-
logger.log(
|
|
273
|
-
logger.log(
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
278
|
+
logger.log(formatWarningLine('Dry run mode: no files written.'));
|
|
279
|
+
logger.log(metadata(`Would fetch: ${dataplaneUrl}/api/v1/external/systems/${systemKey}/config`));
|
|
280
|
+
logger.log(metadata('Would create:'));
|
|
281
|
+
[
|
|
282
|
+
`integration/${systemKey}/${systemKey}-deploy.json`,
|
|
283
|
+
`integration/${systemKey}/application.yaml`,
|
|
284
|
+
`integration/${systemKey}/${systemKey}-system.yaml`,
|
|
285
|
+
`integration/${systemKey}/env.template`,
|
|
286
|
+
`integration/${systemKey}/README.md`
|
|
287
|
+
].forEach(rel => {
|
|
288
|
+
logger.log(metadata(` - ${rel}`));
|
|
289
|
+
});
|
|
279
290
|
}
|
|
280
291
|
|
|
281
292
|
/**
|
|
@@ -285,7 +296,7 @@ function handleDryRun(systemKey, dataplaneUrl) {
|
|
|
285
296
|
* @returns {string} System type
|
|
286
297
|
*/
|
|
287
298
|
function validateAndLogDownloadedData(application, dataSources) {
|
|
288
|
-
logger.log(
|
|
299
|
+
logger.log(formatProgress('Validating downloaded data…'));
|
|
289
300
|
validateDownloadedData(application, dataSources);
|
|
290
301
|
const systemType = validateSystemType(application);
|
|
291
302
|
logger.log(formatSuccessLine(`System type: ${systemType}`));
|
|
@@ -300,7 +311,7 @@ function validateAndLogDownloadedData(application, dataSources) {
|
|
|
300
311
|
function promptReplaceReadme() {
|
|
301
312
|
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
302
313
|
return new Promise(resolve => {
|
|
303
|
-
rl.question(
|
|
314
|
+
rl.question(`${metadata('README.md already exists. Replace it?')} (yes/no) `, answer => {
|
|
304
315
|
rl.close();
|
|
305
316
|
const normalized = (answer || '').trim().toLowerCase();
|
|
306
317
|
resolve(normalized === 'yes' || normalized === 'y');
|
|
@@ -326,7 +337,7 @@ async function resolveDownloadSplitOptions(finalPath, options = {}) {
|
|
|
326
337
|
opts.overwriteReadme = true;
|
|
327
338
|
} else {
|
|
328
339
|
opts.overwriteReadme = await promptReplaceReadme();
|
|
329
|
-
if (!opts.overwriteReadme) logger.log(
|
|
340
|
+
if (!opts.overwriteReadme) logger.log(metadata('Keeping existing README.md'));
|
|
330
341
|
}
|
|
331
342
|
}
|
|
332
343
|
return opts;
|
|
@@ -362,7 +373,8 @@ async function processDownloadedSystem(systemKey, manifest, splitOptions = {}) {
|
|
|
362
373
|
const { application, dataSources, version } = manifest;
|
|
363
374
|
const finalPath = getIntegrationPath(systemKey);
|
|
364
375
|
|
|
365
|
-
logger.log(
|
|
376
|
+
logger.log(formatProgress('Creating integration directory…'));
|
|
377
|
+
logger.log(metadata(finalPath));
|
|
366
378
|
await fs.mkdir(finalPath, { recursive: true });
|
|
367
379
|
|
|
368
380
|
const deployJson = buildDeployJsonFromManifest(application, dataSources, version);
|
|
@@ -370,7 +382,7 @@ async function processDownloadedSystem(systemKey, manifest, splitOptions = {}) {
|
|
|
370
382
|
await fs.writeFile(deployJsonPath, JSON.stringify(deployJson, null, 2), 'utf8');
|
|
371
383
|
logger.log(formatSuccessLine(`Created: ${path.relative(process.cwd(), deployJsonPath)}`));
|
|
372
384
|
|
|
373
|
-
logger.log(
|
|
385
|
+
logger.log(formatProgress('Splitting deploy JSON into component files…'));
|
|
374
386
|
const splitResult = await generator.splitDeployJson(deployJsonPath, finalPath, splitOptions);
|
|
375
387
|
await applyRetemplateToSystemFile(systemKey, splitResult.systemFile);
|
|
376
388
|
|
|
@@ -391,10 +403,37 @@ async function processDownloadedSystem(systemKey, manifest, splitOptions = {}) {
|
|
|
391
403
|
* @param {number} datasourceCount - Number of datasources
|
|
392
404
|
*/
|
|
393
405
|
function displayDownloadSuccess(systemKey, finalPath, datasourceCount) {
|
|
394
|
-
logger.log(
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
406
|
+
logger.log(
|
|
407
|
+
formatSuccessParagraph(
|
|
408
|
+
`Downloaded ${systemKey} (${datasourceCount} datasource${datasourceCount === 1 ? '' : 's'})`
|
|
409
|
+
)
|
|
410
|
+
);
|
|
411
|
+
logger.log(headerKeyValue('Location:', finalPath));
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
/**
|
|
415
|
+
* After YAML split, convert integration files to JSON when --format json.
|
|
416
|
+
* @async
|
|
417
|
+
* @param {string} systemKey
|
|
418
|
+
* @returns {Promise<void>}
|
|
419
|
+
*/
|
|
420
|
+
async function runConvertToJsonIfRequested(systemKey) {
|
|
421
|
+
const { runConvert } = require('../commands/convert');
|
|
422
|
+
try {
|
|
423
|
+
await runConvert(systemKey, { format: 'json', force: true });
|
|
424
|
+
logger.log(formatSuccessLine('Converted component files to JSON'));
|
|
425
|
+
} catch (convertErr) {
|
|
426
|
+
throw new Error(`Download succeeded but convert to JSON failed: ${convertErr.message}`);
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
/**
|
|
431
|
+
* @param {string} systemKey
|
|
432
|
+
*/
|
|
433
|
+
function logDownloadCommandHeader(systemKey) {
|
|
434
|
+
logger.log('');
|
|
435
|
+
logger.log(sectionTitle('Download'));
|
|
436
|
+
logger.log(headerKeyValue('System:', systemKey));
|
|
398
437
|
}
|
|
399
438
|
|
|
400
439
|
/**
|
|
@@ -411,23 +450,13 @@ function displayDownloadSuccess(systemKey, finalPath, datasourceCount) {
|
|
|
411
450
|
* @returns {Promise<void>} Resolves when download completes
|
|
412
451
|
* @throws {Error} If download fails
|
|
413
452
|
*/
|
|
414
|
-
async function runConvertToJsonIfRequested(systemKey) {
|
|
415
|
-
const { runConvert } = require('../commands/convert');
|
|
416
|
-
try {
|
|
417
|
-
await runConvert(systemKey, { format: 'json', force: true });
|
|
418
|
-
logger.log(formatSuccessLine('Converted component files to JSON'));
|
|
419
|
-
} catch (convertErr) {
|
|
420
|
-
throw new Error(`Download succeeded but convert to JSON failed: ${convertErr.message}`);
|
|
421
|
-
}
|
|
422
|
-
}
|
|
423
|
-
|
|
424
453
|
async function downloadExternalSystem(systemKey, options = {}) {
|
|
425
454
|
validateSystemKeyFormat(systemKey);
|
|
426
455
|
|
|
427
456
|
const format = (options.format || 'yaml').toLowerCase();
|
|
428
457
|
|
|
429
458
|
try {
|
|
430
|
-
|
|
459
|
+
logDownloadCommandHeader(systemKey);
|
|
431
460
|
|
|
432
461
|
const config = await getConfig();
|
|
433
462
|
const { authConfig, dataplaneUrl } = await setupAuthenticationAndDataplane(systemKey, options, config);
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Regenerate *-deploy.json on disk for an integration (same as `aifabrix json <systemKey>`).
|
|
3
|
+
*
|
|
4
|
+
* @fileoverview Sync deployment manifest file before upload/deploy
|
|
5
|
+
* @author AI Fabrix Team
|
|
6
|
+
* @version 1.0.0
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
'use strict';
|
|
10
|
+
|
|
11
|
+
const { formatSuccessLine } = require('../utils/cli-test-layout-chalk');
|
|
12
|
+
const generator = require('../generator');
|
|
13
|
+
const logger = require('../utils/logger');
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Writes integration/<systemKey>/<systemKey>-deploy.json from current application sources.
|
|
17
|
+
* Equivalent to running {@link generator.generateDeployJson} / `aifabrix json <systemKey>` for externals.
|
|
18
|
+
*
|
|
19
|
+
* @param {string} systemKey - External system key (integration folder name)
|
|
20
|
+
* @param {{ quiet?: boolean }} [opts] - quiet: omit success log line
|
|
21
|
+
* @returns {Promise<string>} Absolute path to the written deploy JSON file
|
|
22
|
+
*/
|
|
23
|
+
async function syncDeployJsonFromSources(systemKey, opts = {}) {
|
|
24
|
+
const deployPath = await generator.generateDeployJson(systemKey, {});
|
|
25
|
+
if (!opts.quiet) {
|
|
26
|
+
logger.log(formatSuccessLine(`Updated deployment manifest: ${deployPath}`));
|
|
27
|
+
}
|
|
28
|
+
return deployPath;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
module.exports = {
|
|
32
|
+
syncDeployJsonFromSources
|
|
33
|
+
};
|
|
@@ -26,6 +26,7 @@ const {
|
|
|
26
26
|
registerHandlebarsHelper
|
|
27
27
|
} = require('./helpers');
|
|
28
28
|
const secretsEnsure = require('../core/secrets-ensure');
|
|
29
|
+
const { getRestartableInfraServiceNames } = require('../constants/infra-compose-service-names');
|
|
29
30
|
const {
|
|
30
31
|
buildTraefikConfig,
|
|
31
32
|
validateTraefikConfig,
|
|
@@ -36,6 +37,7 @@ const {
|
|
|
36
37
|
startDockerServicesAndConfigure,
|
|
37
38
|
checkInfraHealth
|
|
38
39
|
} = require('./services');
|
|
40
|
+
const { tryComposeProjectDown, stopInfraDockerStackOrphaned } = require('./orphan-infra-docker-teardown');
|
|
39
41
|
const adminSecrets = require('../core/admin-secrets');
|
|
40
42
|
// Lazy require to avoid circular dependency: infra -> app/down -> run-helpers -> infra
|
|
41
43
|
|
|
@@ -220,9 +222,18 @@ async function stopInfra() {
|
|
|
220
222
|
const devId = await config.getDeveloperId();
|
|
221
223
|
const { infraDir, adminSecretsPath } = resolveInfraStatePaths(devId);
|
|
222
224
|
const composePath = path.join(infraDir, 'compose.yaml');
|
|
225
|
+
const hasCompose = fs.existsSync(composePath);
|
|
226
|
+
const hasAdminSecrets = fs.existsSync(adminSecretsPath);
|
|
223
227
|
|
|
224
|
-
if (!
|
|
225
|
-
logger.log('
|
|
228
|
+
if (!hasCompose || !hasAdminSecrets) {
|
|
229
|
+
logger.log('Infra compose or admin-secrets missing; stopping Docker stack (no volume delete)...');
|
|
230
|
+
await stopAllAppContainers(devId);
|
|
231
|
+
try {
|
|
232
|
+
await tryComposeProjectDown(devId, false);
|
|
233
|
+
} catch (err) {
|
|
234
|
+
logger.log(`Compose project down failed (${err.message}); applying orphan cleanup...`);
|
|
235
|
+
await stopInfraDockerStackOrphaned(devId, { removeVolumes: false });
|
|
236
|
+
}
|
|
226
237
|
return;
|
|
227
238
|
}
|
|
228
239
|
|
|
@@ -281,21 +292,34 @@ async function stopInfraWithVolumes() {
|
|
|
281
292
|
const devId = await config.getDeveloperId();
|
|
282
293
|
const { infraDir, adminSecretsPath } = resolveInfraStatePaths(devId);
|
|
283
294
|
const composePath = path.join(infraDir, 'compose.yaml');
|
|
295
|
+
const hasCompose = fs.existsSync(composePath);
|
|
296
|
+
const hasAdminSecrets = fs.existsSync(adminSecretsPath);
|
|
284
297
|
|
|
285
|
-
|
|
286
|
-
|
|
298
|
+
await stopAllAppContainersAndVolumes(devId);
|
|
299
|
+
|
|
300
|
+
if (hasCompose && hasAdminSecrets) {
|
|
301
|
+
await withRunEnv(infraDir, adminSecretsPath, async(runEnvPath) => {
|
|
302
|
+
logger.log('Stopping infrastructure services and removing all data...');
|
|
303
|
+
const projectName = getInfraProjectName(devId);
|
|
304
|
+
const composeCmd = await dockerUtils.getComposeCommand();
|
|
305
|
+
try {
|
|
306
|
+
await execAsyncWithCwd(`${composeCmd} -f "${composePath}" -p ${projectName} --env-file "${runEnvPath}" down -v`, { cwd: infraDir });
|
|
307
|
+
} catch (err) {
|
|
308
|
+
logger.log(`Compose down failed (${err.message}); applying orphan cleanup...`);
|
|
309
|
+
await stopInfraDockerStackOrphaned(devId, { removeVolumes: true });
|
|
310
|
+
}
|
|
311
|
+
logger.log('Infrastructure services stopped and all data removed');
|
|
312
|
+
});
|
|
287
313
|
return;
|
|
288
314
|
}
|
|
289
315
|
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
await
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
logger.log('Infrastructure services stopped and all data removed');
|
|
298
|
-
});
|
|
316
|
+
logger.log('Infra compose or admin-secrets missing; tearing down stuck Docker stack (project/volumes/network)...');
|
|
317
|
+
try {
|
|
318
|
+
await tryComposeProjectDown(devId, true);
|
|
319
|
+
} catch (err) {
|
|
320
|
+
logger.log(`Compose project down failed (${err.message}); applying orphan cleanup...`);
|
|
321
|
+
await stopInfraDockerStackOrphaned(devId, { removeVolumes: true });
|
|
322
|
+
}
|
|
299
323
|
}
|
|
300
324
|
|
|
301
325
|
/**
|
|
@@ -305,18 +329,19 @@ async function stopInfraWithVolumes() {
|
|
|
305
329
|
* @async
|
|
306
330
|
* @function restartService
|
|
307
331
|
* @param {string} serviceName - Name of service to restart
|
|
332
|
+
* @param {{ suppressProgressLog?: boolean }} [options] - When `suppressProgressLog: true`, skip internal logger lines (CLI prints layout).
|
|
308
333
|
* @returns {Promise<void>} Resolves when service is restarted
|
|
309
334
|
* @throws {Error} If service doesn't exist or restart fails
|
|
310
335
|
*
|
|
311
336
|
* @example
|
|
312
|
-
* await restartService('
|
|
313
|
-
* //
|
|
337
|
+
* await restartService('postgres');
|
|
338
|
+
* // Postgres service is restarted
|
|
314
339
|
*/
|
|
315
|
-
async function restartService(serviceName) {
|
|
340
|
+
async function restartService(serviceName, options = {}) {
|
|
316
341
|
if (!serviceName || typeof serviceName !== 'string') {
|
|
317
342
|
throw new Error('Service name is required and must be a string');
|
|
318
343
|
}
|
|
319
|
-
const validServices =
|
|
344
|
+
const validServices = getRestartableInfraServiceNames();
|
|
320
345
|
if (!validServices.includes(serviceName)) {
|
|
321
346
|
throw new Error(`Invalid service name. Must be one of: ${validServices.join(', ')}`);
|
|
322
347
|
}
|
|
@@ -329,12 +354,17 @@ async function restartService(serviceName) {
|
|
|
329
354
|
throw new Error('Infrastructure not properly configured');
|
|
330
355
|
}
|
|
331
356
|
|
|
357
|
+
const suppressLog = Boolean(options && options.suppressProgressLog);
|
|
332
358
|
await withRunEnv(infraDir, adminSecretsPath, async(runEnvPath) => {
|
|
333
|
-
|
|
359
|
+
if (!suppressLog) {
|
|
360
|
+
logger.log(`Restarting ${serviceName} service...`);
|
|
361
|
+
}
|
|
334
362
|
const projectName = getInfraProjectName(devId);
|
|
335
363
|
const composeCmd = await dockerUtils.getComposeCommand();
|
|
336
364
|
await execAsyncWithCwd(`${composeCmd} -f "${composePath}" -p ${projectName} --env-file "${runEnvPath}" restart ${serviceName}`, { cwd: infraDir });
|
|
337
|
-
|
|
365
|
+
if (!suppressLog) {
|
|
366
|
+
logger.log(`${serviceName} service restarted successfully`);
|
|
367
|
+
}
|
|
338
368
|
});
|
|
339
369
|
}
|
|
340
370
|
|