@aifabrix/builder 2.60.0 → 2.62.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 +14 -11
- package/docs/README.md +80 -0
- package/docs/builder-help/evidence-patterns.json +155 -0
- package/docs/builder-help/golden-examples/crm-company.json +30 -0
- package/docs/builder-help/golden-examples/crm-deal.json +29 -0
- package/docs/builder-help/golden-examples/document-storage-keyed-get.json +85 -0
- package/docs/builder-help/golden-examples/document-storage.json +30 -0
- package/docs/builder-help/golden-examples/meeting-transcript.json +29 -0
- package/docs/builder-help/golden-examples/repository-template.json +29 -0
- package/docs/builder-help/golden-examples/service-ticket.json +29 -0
- package/docs/builder-help/platform-roles.json +98 -0
- package/docs/builder-help/resource-type-catalog.json +402 -0
- package/lib/api/applications.api.js +22 -2
- package/lib/api/bootstrap-snapshot.api.js +115 -0
- package/lib/api/configuration.api.js +131 -0
- package/lib/api/system-secrets.api.js +72 -0
- package/lib/app/deploy.js +14 -5
- package/lib/app/run-docker-fallback.js +6 -1
- package/lib/app/run-env-compose.js +5 -6
- package/lib/app/run-helpers.js +2 -1
- package/lib/app/run-parameter-sync.js +142 -0
- package/lib/app/run.js +6 -4
- package/lib/build/docker-build-args.js +5 -2
- package/lib/build/index.js +3 -2
- package/lib/build/standard-docker-build.js +6 -2
- package/lib/channels/channel-artifact-generator.js +13 -3
- package/lib/cli/infra-guided-footers.js +10 -8
- package/lib/cli/infra-guided.js +30 -10
- package/lib/cli/setup-app.js +5 -0
- package/lib/cli/setup-environment.js +156 -0
- package/lib/cli/setup-infra-up-platform-action.js +14 -1
- package/lib/cli/setup-platform.js +2 -0
- package/lib/commands/auth-config.js +2 -0
- package/lib/commands/datasource-capability-upsert-cli.js +112 -0
- package/lib/commands/datasource-capability.js +4 -2
- package/lib/commands/env-secret-context.js +113 -0
- package/lib/commands/env-secret-list.js +200 -0
- package/lib/commands/env-secret-push-confirm.js +54 -0
- package/lib/commands/env-secret-push-run.js +227 -0
- package/lib/commands/env-secret-push.js +168 -0
- package/lib/commands/login.js +4 -0
- package/lib/commands/repair-datasource-apply.js +5 -1
- package/lib/commands/repair-datasource-keyed-document.js +122 -0
- package/lib/commands/repair-datasource-legacy.js +94 -0
- package/lib/commands/repair-datasource-run.js +1 -0
- package/lib/commands/setup-image-refresh.js +26 -0
- package/lib/commands/setup-modes.js +8 -9
- package/lib/commands/setup-prompts-platform-mode.js +34 -0
- package/lib/commands/setup-prompts.js +2 -180
- package/lib/commands/setup.js +3 -0
- package/lib/commands/up-builder-api.js +24 -8
- package/lib/commands/up-common.js +16 -1
- package/lib/commands/up-dataplane-bootstrap.js +70 -14
- package/lib/commands/up-dataplane-credentials.js +21 -0
- package/lib/commands/up-dataplane.js +37 -18
- package/lib/commands/up-integration-server.js +112 -0
- package/lib/commands/verify-operations-skip-e2e.js +25 -1
- package/lib/commands/verify-operations-steps.js +13 -1
- package/lib/commands/wizard-config-normalizer.js +12 -5
- package/lib/commands/wizard-core.js +5 -157
- package/lib/commands/wizard-file-saving.js +163 -0
- package/lib/core/admin-secrets-env-overlay.js +8 -3
- package/lib/core/env-platform-expand.js +5 -1
- package/lib/core/environment-access-policy.js +121 -0
- package/lib/core/environment-mode-policy.js +177 -0
- package/lib/core/local-env-overrides.js +149 -0
- package/lib/core/secrets-ensure.js +4 -2
- package/lib/core/secrets-env-content.js +17 -10
- package/lib/core/secrets-env-write.js +11 -4
- package/lib/core/secrets-load.js +4 -2
- package/lib/datasource/binary-documents-validator.js +190 -0
- package/lib/datasource/capability/capability-manifest.js +1 -0
- package/lib/datasource/capability/run-capability-upsert.js +202 -0
- package/lib/datasource/capability/upsert-ingredients.js +291 -0
- package/lib/datasource/capability/upsert-operations.js +138 -0
- package/lib/datasource/capability/upsert-test-scaffold.js +189 -0
- package/lib/datasource/validate.js +12 -5
- package/lib/deployment/installation/index.js +14 -1
- package/lib/deployment/installation/infra-catalog.js +3 -0
- package/lib/deployment/installation/local-environment-stage.js +285 -0
- package/lib/deployment/installation/local-installation.js +76 -0
- package/lib/deployment/installation/stage-input.js +5 -1
- package/lib/generator/builders.js +3 -1
- package/lib/generator/index.js +3 -0
- package/lib/generator/split-variables.js +2 -2
- package/lib/lifecycle/product-model.js +4 -3
- package/lib/lifecycle/report-display.js +3 -2
- package/lib/parameters/database-adoption.js +125 -0
- package/lib/parameters/infra-parameter-catalog.js +1 -1
- package/lib/parameters/physical-database-naming.js +136 -0
- package/lib/parameters/physical-secret-name.js +194 -0
- package/lib/parameters/rsa-secret-values.js +165 -0
- package/lib/programmatic/builder-help-enterprise-sync-fabrix.js +1 -1
- package/lib/programmatic/builder-help-governance.js +1 -1
- package/lib/programmatic/builder-help.js +1 -1
- package/lib/programmatic/run-channel-install-artifacts.js +28 -2
- package/lib/programmatic/run-channel-package.js +3 -1
- package/lib/programmatic/workspace-context.js +37 -2
- package/lib/role-assistant/test-runner-workhub-answers.js +4 -1
- package/lib/role-assistant/test-runner-workhub-missing-fields.js +42 -0
- package/lib/role-assistant/test-runner-workhub-wait-stop.js +94 -0
- package/lib/role-assistant/test-runner-workhub.js +28 -19
- package/lib/schema/application-schema.json +14 -2
- package/lib/schema/external-datasource.schema.json +23 -3
- package/lib/schema/infra.parameter.yaml +440 -56
- package/lib/schema/infrastructure-schema.json +213 -120
- package/lib/utils/compose-generate-docker-compose.js +9 -14
- package/lib/utils/compose-generator.js +4 -7
- package/lib/utils/compose-miso-env.js +2 -2
- package/lib/utils/compose-traefik-ingress-base.js +3 -4
- package/lib/utils/datasource-binary-evidence.js +92 -0
- package/lib/utils/datasource-test-run-capability-scope.js +44 -1
- package/lib/utils/datasource-test-run-debug-display.js +2 -0
- package/lib/utils/datasource-test-run-display.js +8 -2
- package/lib/utils/datasource-test-run-issue-guidance.js +176 -0
- package/lib/utils/datasource-test-run-tty-log.js +2 -0
- package/lib/utils/declarative-url-ports.js +3 -0
- package/lib/utils/env-copy.js +11 -10
- package/lib/utils/env-environment-file-paths.js +105 -0
- package/lib/utils/environment-scoped-resources.js +48 -14
- package/lib/utils/external-system-system-test-tty.js +3 -2
- package/lib/utils/image-tags.js +2 -2
- package/lib/utils/paths-system-builder-keys.js +7 -1
- package/lib/utils/paths.js +7 -2
- package/lib/utils/platform-kv-ref.js +1 -1
- package/lib/utils/postgres-platform-bootstrap.js +35 -1
- package/lib/utils/prepare-local-data-mount.js +58 -0
- package/lib/utils/redis-env-scope.js +21 -1
- package/lib/utils/registry-auth-sources.js +154 -0
- package/lib/utils/registry-credentials.js +131 -14
- package/lib/utils/secrets-generator.js +30 -21
- package/lib/utils/secrets-helpers.js +5 -8
- package/lib/utils/secrets-kv-scope.js +127 -28
- package/lib/utils/secrets-materialize-local.js +9 -0
- package/lib/utils/secrets-missing-error.js +35 -1
- package/lib/utils/system-secret-mapping.js +125 -0
- package/lib/utils/test-log-writer.js +2 -1
- package/lib/utils/token-manager.js +14 -8
- package/lib/utils/url-public-path-prefix.js +6 -10
- package/lib/validation/external-manifest-validator.js +5 -0
- package/lib/validation/openapi-contract-surface-validator.js +3 -1
- package/lib/validation/validate-external-file.js +5 -1
- package/package.json +6 -4
- package/templates/agent-kit/agent-kit.yaml +1 -1
- package/templates/agent-kit/skills/aifabrix-connected-system/SKILL.md +2 -1
- package/templates/agent-kit/skills/aifabrix-connected-system/references/delivery-gates.md +15 -0
- package/templates/agent-kit/skills/aifabrix-plan/SKILL.md +3 -2
- package/templates/agent-kit/skills/aifabrix-prove/SKILL.md +6 -2
- package/templates/agent-kit/skills/aifabrix-prove/references/evidence-lifecycle.md +22 -0
- package/templates/agent-kit/skills/aifabrix-role-assistant/SKILL.md +7 -1
- package/templates/agent-kit/skills/aifabrix-role-assistant/references/testing-playbook.md +117 -0
- package/templates/agent-kit/skills/shared/interaction.md +83 -0
- package/templates/agent-kit/skills/shared/status.md +3 -12
- package/templates/applications/builder-api/application.yaml +1 -1
- package/templates/applications/builder-api/env.template +5 -1
- package/templates/applications/dataplane/application.yaml +2 -2
- package/templates/applications/dataplane/env.template +10 -12
- package/templates/applications/integration-server/application.yaml +91 -0
- package/templates/applications/integration-server/env.template +215 -0
- package/templates/applications/keycloak/application.yaml +6 -1
- package/templates/applications/mirrored-templates.json +52 -0
- package/templates/applications/miso-controller/application.yaml +116 -2
- package/templates/applications/miso-controller/env.template +62 -17
- package/templates/applications/miso-controller/rbac.yaml +17 -0
- package/templates/external-system/external-datasource.yaml.hbs +7 -1
- package/templates/marketplace/createUiDefinition.json +52 -4
- package/templates/marketplace/main.json +88 -10
- package/templates/python/docker-compose.hbs +1 -1
- package/templates/agent-kit/skills/aifabrix-plan/references/interaction.md +0 -41
- /package/{lib/programmatic/help-content → docs/builder-help/content}/channel-onboarding.md +0 -0
- /package/{lib/programmatic/help-content → docs/builder-help/content}/cip-overview.md +0 -0
- /package/{lib/programmatic/help-content → docs/builder-help/content}/connected-system-ui.md +0 -0
- /package/{lib/programmatic/help-content → docs/builder-help/content}/dimensions-guide.md +0 -0
- /package/{lib/programmatic/help-content → docs/builder-help/content}/enterprise-sync-fabrix.md +0 -0
- /package/{lib/programmatic/help-content → docs/builder-help/content}/overview.md +0 -0
- /package/{lib/programmatic/help-content → docs/builder-help/content}/subscription-guide.md +0 -0
- /package/{lib/programmatic/help-content → docs/builder-help/content}/workflow.md +0 -0
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* System secrets API (Miso Controller).
|
|
3
|
+
*
|
|
4
|
+
* Deliberately narrow: a value can be written and never read back, and only against a
|
|
5
|
+
* catalog key the installation already defines. There is no create endpoint, so this module
|
|
6
|
+
* must not grow one — a key that does not exist is a catalog change in aifabrix-miso, not
|
|
7
|
+
* something the CLI can fix.
|
|
8
|
+
*
|
|
9
|
+
* The PUT request body marks `value` as writeOnly. Nothing here returns a value, and no
|
|
10
|
+
* caller should construct a response shape that could carry one.
|
|
11
|
+
*
|
|
12
|
+
* @fileoverview System secrets API functions (put-only over catalog keys)
|
|
13
|
+
* @author AI Fabrix Team
|
|
14
|
+
* @version 1.0.0
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
'use strict';
|
|
18
|
+
|
|
19
|
+
const { ApiClient } = require('./index');
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* @param {string} envKey - Environment key
|
|
23
|
+
* @returns {string} Collection endpoint for that environment
|
|
24
|
+
*/
|
|
25
|
+
function systemSecretsEndpoint(envKey) {
|
|
26
|
+
return `/api/v1/environments/${encodeURIComponent(envKey)}/system-secrets`;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Set one system secret value. The value is write-only: it cannot be read back, and this
|
|
31
|
+
* function returns the server response unchanged without echoing what was sent.
|
|
32
|
+
*
|
|
33
|
+
* PUT /api/v1/environments/{envKey}/system-secrets/{catalogKey}
|
|
34
|
+
* @requiresPermission {Miso} controller:admin
|
|
35
|
+
* @async
|
|
36
|
+
* @function putSystemSecret
|
|
37
|
+
* @param {string} controllerUrl - Controller base URL
|
|
38
|
+
* @param {Object} authConfig - Authentication configuration
|
|
39
|
+
* @param {string} envKey - Environment key
|
|
40
|
+
* @param {string} catalogKey - Catalog key the installation already defines
|
|
41
|
+
* @param {string} value - Secret value, read from a file by the caller
|
|
42
|
+
* @param {string} revision - Opaque revision returned by the configuration list
|
|
43
|
+
* @returns {Promise<Object>} Server response; carries no value
|
|
44
|
+
* @throws {Error} If the request fails. The message must never include the value
|
|
45
|
+
*/
|
|
46
|
+
async function putSystemSecret(controllerUrl, authConfig, envKey, catalogKey, value, revision) {
|
|
47
|
+
if (!controllerUrl || typeof controllerUrl !== 'string') {
|
|
48
|
+
throw new Error('controllerUrl is required and must be a string');
|
|
49
|
+
}
|
|
50
|
+
if (!envKey || typeof envKey !== 'string') {
|
|
51
|
+
throw new Error('envKey is required and must be a string');
|
|
52
|
+
}
|
|
53
|
+
if (!catalogKey || typeof catalogKey !== 'string') {
|
|
54
|
+
throw new Error('catalogKey is required and must be a string');
|
|
55
|
+
}
|
|
56
|
+
if (typeof value !== 'string' || value.length === 0) {
|
|
57
|
+
// Names the key, never the value, because this message reaches the terminal.
|
|
58
|
+
throw new Error(`No value supplied for ${catalogKey}`);
|
|
59
|
+
}
|
|
60
|
+
if (!revision || typeof revision !== 'string') {
|
|
61
|
+
throw new Error(`No configuration revision supplied for ${catalogKey}`);
|
|
62
|
+
}
|
|
63
|
+
const client = new ApiClient(controllerUrl, authConfig);
|
|
64
|
+
return await client.put(`${systemSecretsEndpoint(envKey)}/${encodeURIComponent(catalogKey)}`, {
|
|
65
|
+
body: { value },
|
|
66
|
+
headers: { 'If-Match': revision }
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
module.exports = {
|
|
71
|
+
putSystemSecret
|
|
72
|
+
};
|
package/lib/app/deploy.js
CHANGED
|
@@ -191,12 +191,21 @@ async function handleDeploymentError(error, appName, controllerUrl, usedExternal
|
|
|
191
191
|
* @throws {Error} If image is missing or has no registry
|
|
192
192
|
*/
|
|
193
193
|
function validateImageIsPullable(imageRef, appName) {
|
|
194
|
-
if (
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
194
|
+
if (imageRef && imageRef.includes('/')) {
|
|
195
|
+
return;
|
|
196
|
+
}
|
|
197
|
+
// No registry configured means the installation deploys the application.yaml image as it
|
|
198
|
+
// stands, from the local Docker daemon. Demanding a registry component here would make a
|
|
199
|
+
// local install impossible for the one case it is meant to serve. A configured registry
|
|
200
|
+
// still has to appear in the reference, because then the image is expected to be pullable.
|
|
201
|
+
const { readInstallationRegistrySync } = require('../utils/config-registry-preference');
|
|
202
|
+
if (!readInstallationRegistrySync()) {
|
|
203
|
+
return;
|
|
199
204
|
}
|
|
205
|
+
const hint = `Set image.registry and image.tag in builder/${appName}/application.yaml, or pass a full image ref (e.g. --image <registry>/${appName}:<tag>) when deploying`;
|
|
206
|
+
throw new Error(
|
|
207
|
+
`Deployed image must be pullable (include a registry). Current image: "${imageRef || 'none'}". ${hint}`
|
|
208
|
+
);
|
|
200
209
|
}
|
|
201
210
|
|
|
202
211
|
/**
|
|
@@ -8,6 +8,8 @@
|
|
|
8
8
|
'use strict';
|
|
9
9
|
|
|
10
10
|
const chalk = require('chalk');
|
|
11
|
+
const { prepareLocalDataMount } = require('../utils/prepare-local-data-mount');
|
|
12
|
+
const { resolveLocalDataMountPathForCompose } = require('../utils/compose-generate-docker-compose');
|
|
11
13
|
const logger = require('../utils/logger');
|
|
12
14
|
const { execWithDockerEnv } = require('../utils/docker-exec');
|
|
13
15
|
const { getContainerPort } = require('../utils/port-resolver');
|
|
@@ -87,7 +89,10 @@ async function executeDockerRunUp(o) {
|
|
|
87
89
|
];
|
|
88
90
|
|
|
89
91
|
if (needsStorage) {
|
|
90
|
-
|
|
92
|
+
const localPath = resolveLocalDataMountPathForCompose({ appName, appConfig });
|
|
93
|
+
if (localPath) prepareLocalDataMount(localPath, runEnvPath);
|
|
94
|
+
const source = localPath || storageVolumeName(appName, appConfig.developerId);
|
|
95
|
+
parts.push(`-v ${shellSingleQuote(source)}:/mnt/data`);
|
|
91
96
|
}
|
|
92
97
|
if (runEnvPath && typeof runEnvPath === 'string') {
|
|
93
98
|
parts.push(`--env-file ${shellSingleQuote(runEnvPath)}`);
|
|
@@ -13,6 +13,7 @@ const path = require('path');
|
|
|
13
13
|
const fs = require('fs').promises;
|
|
14
14
|
const fsSync = require('fs');
|
|
15
15
|
const pathsUtil = require('../utils/paths');
|
|
16
|
+
const { isScopeActiveForOptions } = require('../utils/environment-scoped-resources');
|
|
16
17
|
const adminSecrets = require('../core/admin-secrets');
|
|
17
18
|
const config = require('../core/config');
|
|
18
19
|
const secretsEnsure = require('../core/secrets-ensure');
|
|
@@ -25,8 +26,8 @@ const { applyAdminSecretsPlatformOverlay } = require('../core/admin-secrets-env-
|
|
|
25
26
|
* @param {Object.<string, string>} merged
|
|
26
27
|
* @param {Object.<string, string>} adminObj
|
|
27
28
|
*/
|
|
28
|
-
function mergeRunEnvWithAdminOverlay(merged, adminObj) {
|
|
29
|
-
applyAdminSecretsPlatformOverlay(merged, adminObj);
|
|
29
|
+
function mergeRunEnvWithAdminOverlay(merged, adminObj, appName) {
|
|
30
|
+
applyAdminSecretsPlatformOverlay(merged, adminObj, appName);
|
|
30
31
|
}
|
|
31
32
|
|
|
32
33
|
async function applyOnboardingPlatformModeIfNeeded(appName, merged) {
|
|
@@ -108,9 +109,7 @@ function injectDatabaseNamesAndUsers(env, appConfig, scopeOpts = null) {
|
|
|
108
109
|
if (!Array.isArray(databases) || databases.length === 0) return;
|
|
109
110
|
const prefix =
|
|
110
111
|
scopeOpts &&
|
|
111
|
-
scopeOpts
|
|
112
|
-
scopeOpts.runEnvKey &&
|
|
113
|
-
(scopeOpts.runEnvKey === 'dev' || scopeOpts.runEnvKey === 'tst')
|
|
112
|
+
isScopeActiveForOptions(scopeOpts)
|
|
114
113
|
? `${String(scopeOpts.runEnvKey).toLowerCase()}-`
|
|
115
114
|
: '';
|
|
116
115
|
for (let i = 0; i < databases.length; i++) {
|
|
@@ -298,7 +297,7 @@ async function buildMergedRunEnvAndWrite(appName, appConfig, devDir, developerId
|
|
|
298
297
|
runEnvKey
|
|
299
298
|
});
|
|
300
299
|
const merged = { ...adminObj, ...appObj };
|
|
301
|
-
mergeRunEnvWithAdminOverlay(merged, adminObj);
|
|
300
|
+
mergeRunEnvWithAdminOverlay(merged, adminObj, appName);
|
|
302
301
|
await applyOnboardingPlatformModeIfNeeded(appName, merged);
|
|
303
302
|
await applyPostgresPasswordFromPgpass(merged, developerId);
|
|
304
303
|
injectDatabaseNamesAndUsers(merged, appConfig, scopeOpts);
|
package/lib/app/run-helpers.js
CHANGED
|
@@ -368,6 +368,8 @@ async function prepareEnvironment(appName, appConfig, options) {
|
|
|
368
368
|
developerId,
|
|
369
369
|
{ runEnvKey, effectiveEnvironmentScopedResources }
|
|
370
370
|
);
|
|
371
|
+
const { hydrateMissingLocalParameters } = require('./run-parameter-sync');
|
|
372
|
+
await hydrateMissingLocalParameters(appName, runEnvPath);
|
|
371
373
|
|
|
372
374
|
const composeOptions = {
|
|
373
375
|
...options,
|
|
@@ -438,4 +440,3 @@ module.exports = {
|
|
|
438
440
|
displayRunStatus,
|
|
439
441
|
cleanApplicationsDir: runEnvCompose.cleanApplicationsDir
|
|
440
442
|
};
|
|
441
|
-
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hydrate missing application-owned parameters before a local governed run.
|
|
3
|
+
*
|
|
4
|
+
* Local/database controllers deliberately leave database generators to Builder. In
|
|
5
|
+
* client-credentials mode the application cannot start until those values are in its
|
|
6
|
+
* bootstrap snapshot, so Builder fills only unresolved declared rows through the
|
|
7
|
+
* application's own client token. Existing Controller values are never overwritten.
|
|
8
|
+
*
|
|
9
|
+
* @fileoverview Local run parameter hydration for governed bootstrap
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
'use strict';
|
|
13
|
+
|
|
14
|
+
const fs = require('fs').promises;
|
|
15
|
+
const path = require('path');
|
|
16
|
+
const { ApiClient } = require('../api');
|
|
17
|
+
const { getToken } = require('../api/auth.api');
|
|
18
|
+
const { getControllerDeploymentType } = require('../api/controller-health.api');
|
|
19
|
+
const { resolveControllerUrl } = require('../utils/controller-url');
|
|
20
|
+
const { getBuilderPath } = require('../utils/paths');
|
|
21
|
+
const { mapEnvToCatalogKeys, parseEnvAssignments } = require('../utils/system-secret-mapping');
|
|
22
|
+
const { getInfraParameterCatalog } = require('../parameters/infra-parameter-catalog');
|
|
23
|
+
|
|
24
|
+
const LOCAL_DEPLOYMENTS = new Set(['local', 'database']);
|
|
25
|
+
const IDENTITY_NAMES = new Set([
|
|
26
|
+
'MISO_CLIENTID',
|
|
27
|
+
'MISO_CLIENT_ID',
|
|
28
|
+
'MISO_CLIENTSECRET',
|
|
29
|
+
'MISO_CLIENT_SECRET'
|
|
30
|
+
]);
|
|
31
|
+
|
|
32
|
+
function statusOf(response) {
|
|
33
|
+
const value = response && (response.status || response.statusCode || response.response?.status);
|
|
34
|
+
return Number.isInteger(value) ? value : 0;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function failed(response) {
|
|
38
|
+
return Boolean(response && (response.success === false || statusOf(response) >= 400));
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function tokenFrom(response) {
|
|
42
|
+
const body = response && response.data !== undefined ? response.data : response;
|
|
43
|
+
const payload = body && body.data !== undefined ? body.data : body;
|
|
44
|
+
const token = payload && payload.token;
|
|
45
|
+
if (!token || typeof token !== 'string') {
|
|
46
|
+
throw new Error('Controller client-token response is invalid');
|
|
47
|
+
}
|
|
48
|
+
return token;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function credentialPair(env) {
|
|
52
|
+
const clientId = env.get('MISO_CLIENTID') || env.get('MISO_CLIENT_ID');
|
|
53
|
+
const clientSecret = env.get('MISO_CLIENTSECRET') || env.get('MISO_CLIENT_SECRET');
|
|
54
|
+
if (!clientId || !clientSecret) {
|
|
55
|
+
throw new Error('MISO client credentials are required for governed local configuration');
|
|
56
|
+
}
|
|
57
|
+
return { clientId, clientSecret };
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function uniqueRows(mapping, catalog = getInfraParameterCatalog()) {
|
|
61
|
+
const rows = new Map();
|
|
62
|
+
for (const row of mapping.pushable) {
|
|
63
|
+
if (IDENTITY_NAMES.has(row.name)) continue;
|
|
64
|
+
const generatorType = catalog.findEntryForKey(row.catalogKey)?.generator?.type;
|
|
65
|
+
if (generatorType !== 'databaseUrl' && generatorType !== 'databasePassword') continue;
|
|
66
|
+
const prior = rows.get(row.catalogKey);
|
|
67
|
+
if (prior && prior.value !== row.value) {
|
|
68
|
+
throw new Error(`Conflicting local values resolve to parameter '${row.catalogKey}'`);
|
|
69
|
+
}
|
|
70
|
+
rows.set(row.catalogKey, row);
|
|
71
|
+
}
|
|
72
|
+
return [...rows.values()];
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
async function isMissing(client, key) {
|
|
76
|
+
const response = await client.get(`/api/v1/parameters/${encodeURIComponent(key)}`);
|
|
77
|
+
if (!failed(response)) return false;
|
|
78
|
+
if (statusOf(response) === 404) return true;
|
|
79
|
+
throw new Error(`Unable to inspect governed parameter '${key}'`);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
async function writeMissing(client, row) {
|
|
83
|
+
const response = await client.put(`/api/v1/parameters/${encodeURIComponent(row.catalogKey)}`, {
|
|
84
|
+
body: { value: row.value }
|
|
85
|
+
});
|
|
86
|
+
if (failed(response)) {
|
|
87
|
+
throw new Error(`Unable to initialize governed parameter '${row.catalogKey}'`);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
async function resolveSyncContext(appName, runEnvPath, deps) {
|
|
92
|
+
const readFile = deps.readFile || fs.readFile;
|
|
93
|
+
const envContent = await readFile(runEnvPath, 'utf8');
|
|
94
|
+
const env = parseEnvAssignments(envContent);
|
|
95
|
+
if (env.get('MISO_AUTH_MODE') !== 'client-credentials') return null;
|
|
96
|
+
const controllerUrl = await (deps.resolveControllerUrl || resolveControllerUrl)();
|
|
97
|
+
if (!controllerUrl) throw new Error('Controller URL is required for governed local configuration');
|
|
98
|
+
const deploymentType = await (deps.getControllerDeploymentType || getControllerDeploymentType)(controllerUrl);
|
|
99
|
+
if (!LOCAL_DEPLOYMENTS.has(String(deploymentType || '').toLowerCase())) return null;
|
|
100
|
+
const templatePath = path.join((deps.getBuilderPath || getBuilderPath)(appName), 'env.template');
|
|
101
|
+
const templateContent = await readFile(templatePath, 'utf8');
|
|
102
|
+
const catalog = (deps.getInfraParameterCatalog || getInfraParameterCatalog)();
|
|
103
|
+
return {
|
|
104
|
+
controllerUrl,
|
|
105
|
+
credentials: credentialPair(env),
|
|
106
|
+
rows: uniqueRows(mapEnvToCatalogKeys(envContent, templateContent), catalog)
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Initialize unresolved local parameter rows needed by a client-credentials snapshot.
|
|
112
|
+
* @param {string} appName Application key
|
|
113
|
+
* @param {string} runEnvPath Resolved app-only run env
|
|
114
|
+
* @param {Object} [deps] Test dependencies
|
|
115
|
+
* @returns {Promise<number>} Number of rows initialized
|
|
116
|
+
*/
|
|
117
|
+
async function hydrateMissingLocalParameters(appName, runEnvPath, deps = {}) {
|
|
118
|
+
const context = await resolveSyncContext(appName, runEnvPath, deps);
|
|
119
|
+
if (!context || context.rows.length === 0) return 0;
|
|
120
|
+
const response = await (deps.getToken || getToken)(
|
|
121
|
+
context.credentials.clientId,
|
|
122
|
+
context.credentials.clientSecret,
|
|
123
|
+
context.controllerUrl
|
|
124
|
+
);
|
|
125
|
+
const client = deps.client || new ApiClient(context.controllerUrl, {
|
|
126
|
+
type: 'client-token', token: tokenFrom(response)
|
|
127
|
+
});
|
|
128
|
+
let initialized = 0;
|
|
129
|
+
for (const row of context.rows) {
|
|
130
|
+
if (!(await isMissing(client, row.catalogKey))) continue;
|
|
131
|
+
await writeMissing(client, row);
|
|
132
|
+
initialized += 1;
|
|
133
|
+
}
|
|
134
|
+
return initialized;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
module.exports = {
|
|
138
|
+
credentialPair,
|
|
139
|
+
hydrateMissingLocalParameters,
|
|
140
|
+
tokenFrom,
|
|
141
|
+
uniqueRows
|
|
142
|
+
};
|
package/lib/app/run.js
CHANGED
|
@@ -258,10 +258,12 @@ function applyTraefikFlagToRunOptions(runOptions, userCfg) {
|
|
|
258
258
|
|
|
259
259
|
/**
|
|
260
260
|
* @param {string} envKey
|
|
261
|
-
* @
|
|
261
|
+
* @param {string} appName
|
|
262
|
+
* @throws {Error} If env is not a supported workload environment or a miso-only platform app
|
|
262
263
|
*/
|
|
263
|
-
function assertValidRunEnvKey(envKey) {
|
|
264
|
-
|
|
264
|
+
function assertValidRunEnvKey(envKey, appName) {
|
|
265
|
+
const misoOnlyPlatformApp = appName === 'builder-api' || appName === 'integration-server';
|
|
266
|
+
if (envKey !== 'dev' && envKey !== 'tst' && envKey !== 'pro' && !(envKey === 'miso' && misoOnlyPlatformApp)) {
|
|
265
267
|
throw new Error('--env must be dev, tst, or pro');
|
|
266
268
|
}
|
|
267
269
|
}
|
|
@@ -345,7 +347,7 @@ function emitRunManifestMetadataIfRequested(appName, options) {
|
|
|
345
347
|
*/
|
|
346
348
|
async function prepareAppRun(appName, options, debug) {
|
|
347
349
|
const envKey = (options.env || 'dev').toLowerCase();
|
|
348
|
-
assertValidRunEnvKey(envKey);
|
|
350
|
+
assertValidRunEnvKey(envKey, appName);
|
|
349
351
|
const shouldContinue = await validateAppForRun(appName, debug);
|
|
350
352
|
if (!shouldContinue) {
|
|
351
353
|
return null;
|
|
@@ -52,10 +52,13 @@ function applyMisoControllerUiBuildArgs(buildArgs, envMap) {
|
|
|
52
52
|
*
|
|
53
53
|
* @param {string} appName
|
|
54
54
|
* @param {string} [contextPath] Docker build context (repo root for miso-controller)
|
|
55
|
+
* @param {Object} [options] - `runEnvKey`: environment key for manifest {NAME} resolution
|
|
56
|
+
* (defaults to the current CLI environment inside generateEnvContent)
|
|
55
57
|
* @returns {Promise<Object.<string, string>>}
|
|
56
58
|
*/
|
|
57
|
-
async function resolveDockerBuildArgsForApp(appName, contextPath) {
|
|
58
|
-
const
|
|
59
|
+
async function resolveDockerBuildArgsForApp(appName, contextPath, options = {}) {
|
|
60
|
+
const runEnvKey = options && options.runEnvKey ? String(options.runEnvKey) : undefined;
|
|
61
|
+
const envMap = await secretsEnvWrite.resolveAndGetEnvMap(appName, { environment: 'docker', runEnvKey });
|
|
59
62
|
const { getBashPrefixedProcessEnvOverlay } = require('../utils/bash-secret-env');
|
|
60
63
|
const bashOverlay = await getBashPrefixedProcessEnvOverlay(null, appName);
|
|
61
64
|
const buildArgs = {};
|
package/lib/build/index.js
CHANGED
|
@@ -191,12 +191,13 @@ async function generateDockerfile(appNameOrPath, language, config, buildConfig =
|
|
|
191
191
|
* @param {Object} options - Build options
|
|
192
192
|
*/
|
|
193
193
|
|
|
194
|
-
async function postBuildTasks(appName, buildConfig) {
|
|
194
|
+
async function postBuildTasks(appName, buildConfig, options = {}) {
|
|
195
195
|
try {
|
|
196
196
|
// Validate that env.template + secrets resolve cleanly, but never write <appPath>/.env or
|
|
197
197
|
// envOutputPath. Build args still flow through resolveAndGetEnvMap (in-memory). Run
|
|
198
198
|
// `aifabrix resolve <app>` to materialize an on-disk .env.
|
|
199
|
-
|
|
199
|
+
const runEnvKey = options && options.env ? String(options.env) : undefined;
|
|
200
|
+
await secrets.generateEnvFile(appName, buildConfig.secrets, 'docker', false, { noWrite: true, runEnvKey });
|
|
200
201
|
logger.log(formatSuccessLine('Env resolution validated (in-memory only; run "aifabrix resolve ' + appName + '" for on-disk .env)'));
|
|
201
202
|
} catch (error) {
|
|
202
203
|
logger.log(formatWarningLine(`Could not resolve env: ${error.message}`));
|
|
@@ -79,7 +79,7 @@ async function executeDockerImageBuild(appName, options, buildConfig) {
|
|
|
79
79
|
buildHelpers
|
|
80
80
|
);
|
|
81
81
|
await ensureRunSecretsForApp(appName);
|
|
82
|
-
const buildArgs = await resolveDockerBuildArgsForApp(appName, contextPath);
|
|
82
|
+
const buildArgs = await resolveDockerBuildArgsForApp(appName, contextPath, { runEnvKey: options.env });
|
|
83
83
|
const { primary, aliases, tags } = options.tagPlan;
|
|
84
84
|
// `build.target` selects a stage of a multi-stage Dockerfile, so one source tree can
|
|
85
85
|
// produce more than one image. Absent, Docker builds the last stage as before.
|
|
@@ -92,7 +92,7 @@ async function executeDockerImageBuild(appName, options, buildConfig) {
|
|
|
92
92
|
primary,
|
|
93
93
|
{ ...options, buildArgs, tagAliases: aliases, target }
|
|
94
94
|
);
|
|
95
|
-
await postBuildTasks(appName, buildConfig);
|
|
95
|
+
await postBuildTasks(appName, buildConfig, options);
|
|
96
96
|
logger.log(formatSuccessParagraph('Build completed successfully!'));
|
|
97
97
|
return tags.map((tag) => `${effectiveImageName}:${tag}`).join(', ');
|
|
98
98
|
}
|
|
@@ -104,6 +104,10 @@ async function executeDockerImageBuild(appName, options, buildConfig) {
|
|
|
104
104
|
* @returns {Promise<string>} Built image ref e.g. name:tag
|
|
105
105
|
*/
|
|
106
106
|
async function runStandardDockerBuild(appName, options) {
|
|
107
|
+
// Before the tag plan, the promote shortcut and any Docker call: every build path reaches
|
|
108
|
+
// here, and a build into tst or pro must not get as far as producing an artifact.
|
|
109
|
+
const { assertBuildEnvironmentForOptions } = require('../core/environment-mode-policy');
|
|
110
|
+
await assertBuildEnvironmentForOptions(options);
|
|
107
111
|
const { resolveBuildTagPlan, readAppVersion } = require('../utils/image-tags');
|
|
108
112
|
const { buildConfig, config: appConfig } = await buildHelpers.loadAndValidateConfig(appName);
|
|
109
113
|
// Resolve and validate every tag before any Docker build, tag, login or promote.
|
|
@@ -92,7 +92,7 @@ function renderTemplateFile(templatePath, context) {
|
|
|
92
92
|
* @param {boolean} dryRun
|
|
93
93
|
* @returns {{ installPath: string, content: string }|null}
|
|
94
94
|
*/
|
|
95
|
-
function renderInstallGuideIfPresent(templatesDir, runtimeValues, outputDir, dryRun) {
|
|
95
|
+
function renderInstallGuideIfPresent(templatesDir, runtimeValues, outputDir, dryRun, durableDir) {
|
|
96
96
|
const installTemplate = path.join(templatesDir, 'install.template.md.hbs');
|
|
97
97
|
if (!fs.existsSync(installTemplate)) {
|
|
98
98
|
return null;
|
|
@@ -100,11 +100,19 @@ function renderInstallGuideIfPresent(templatesDir, runtimeValues, outputDir, dry
|
|
|
100
100
|
const content = renderTemplateFile(installTemplate, runtimeValues);
|
|
101
101
|
assertNoSecretsInArtifact(content);
|
|
102
102
|
const installPath = path.join(outputDir, 'INSTALL.md');
|
|
103
|
+
// `outputDir` is under gitignored dist/, so a copy there alone is lost on any clean and the
|
|
104
|
+
// API that serves this document 404s afterwards. Write the durable copy too when the caller
|
|
105
|
+
// knows where the channel's committed files live.
|
|
106
|
+
const durablePath = durableDir ? path.join(durableDir, 'INSTALL.md') : '';
|
|
103
107
|
if (!dryRun) {
|
|
104
108
|
fs.mkdirSync(outputDir, { recursive: true });
|
|
105
109
|
fs.writeFileSync(installPath, content, 'utf8');
|
|
110
|
+
if (durablePath) {
|
|
111
|
+
fs.mkdirSync(durableDir, { recursive: true });
|
|
112
|
+
fs.writeFileSync(durablePath, content, 'utf8');
|
|
113
|
+
}
|
|
106
114
|
}
|
|
107
|
-
return { installPath, content };
|
|
115
|
+
return { installPath, content, durablePath: durablePath || undefined };
|
|
108
116
|
}
|
|
109
117
|
|
|
110
118
|
/**
|
|
@@ -130,6 +138,7 @@ function generateCopilotChannelArtifact(params, templatesDir, outputPath) {
|
|
|
130
138
|
* @param {string} params.channelTarget
|
|
131
139
|
* @param {Object} params.runtimeValues
|
|
132
140
|
* @param {boolean} [params.dryRun]
|
|
141
|
+
* @param {string} [params.durableDir] - Committed channel directory for the persisted INSTALL.md
|
|
133
142
|
* @returns {{ outputPath: string, artifactHash: string, content: string, installPath?: string }}
|
|
134
143
|
*/
|
|
135
144
|
function generateChannelArtifact(params) {
|
|
@@ -158,7 +167,8 @@ function generateChannelArtifact(params) {
|
|
|
158
167
|
templatesDir,
|
|
159
168
|
runtimeValues,
|
|
160
169
|
outputDir,
|
|
161
|
-
params.dryRun === true
|
|
170
|
+
params.dryRun === true,
|
|
171
|
+
params.durableDir
|
|
162
172
|
);
|
|
163
173
|
|
|
164
174
|
const installReady = runtimeValues?.installReady === true;
|
|
@@ -46,11 +46,12 @@ async function resolvePlatformControllerUrlForDisplay() {
|
|
|
46
46
|
return resolvePlatformControllerUrl();
|
|
47
47
|
}
|
|
48
48
|
|
|
49
|
-
async function logPlatformReadyFooter() {
|
|
50
|
-
const [controllerUrl, dataplaneUrl, keycloakUrl] = await Promise.all([
|
|
49
|
+
async function logPlatformReadyFooter(options = {}) {
|
|
50
|
+
const [controllerUrl, dataplaneUrl, keycloakUrl, integrationServerUrl] = await Promise.all([
|
|
51
51
|
resolvePlatformControllerUrlForDisplay(),
|
|
52
52
|
computeAppBaseUrl('dataplane'),
|
|
53
|
-
computeAppBaseUrl('keycloak')
|
|
53
|
+
computeAppBaseUrl('keycloak'),
|
|
54
|
+
options.integrationServer === true ? computeAppBaseUrl('integration-server') : null
|
|
54
55
|
]);
|
|
55
56
|
const cfg = await config.getConfig();
|
|
56
57
|
const env = cfg && cfg.environment ? cfg.environment : 'dev';
|
|
@@ -60,7 +61,7 @@ async function logPlatformReadyFooter() {
|
|
|
60
61
|
logSection('Miso Controller', controllerUrl);
|
|
61
62
|
logSection('Dataplane (DEV)', dataplaneUrl);
|
|
62
63
|
logSection('Keycloak', keycloakUrl);
|
|
63
|
-
logSection('
|
|
64
|
+
if (integrationServerUrl) logSection('Integration Server', `${integrationServerUrl}/download`);
|
|
64
65
|
logSection('Knowledge Base', 'https://docs.aifabrix.ai');
|
|
65
66
|
logSection('Getting Started', 'https://docs.aifabrix.ai/get-started');
|
|
66
67
|
logFooterEnd();
|
|
@@ -91,20 +92,21 @@ async function logDataplaneReadyFooter() {
|
|
|
91
92
|
/**
|
|
92
93
|
* @param {'platform'|'dataplane'} [scope]
|
|
93
94
|
*/
|
|
94
|
-
function logPlatformSetupDurationHint(scope = 'platform') {
|
|
95
|
+
function logPlatformSetupDurationHint(scope = 'platform', options = {}) {
|
|
96
|
+
const optionalApp = options.integrationServer === true ? ', Builder API, and Integration Server' : ', and Builder API';
|
|
95
97
|
const line =
|
|
96
98
|
scope === 'dataplane'
|
|
97
99
|
? 'Installation may take a few minutes. Authenticating and starting Dataplane — please wait.'
|
|
98
|
-
:
|
|
100
|
+
: `Installation may take a few minutes. Keycloak, Miso Controller, Dataplane${optionalApp} are starting — please wait.`;
|
|
99
101
|
logger.log(chalk.yellow(`⚠ ${line}`));
|
|
100
102
|
}
|
|
101
103
|
|
|
102
|
-
function logGuidedPlatformSetupHeader() {
|
|
104
|
+
function logGuidedPlatformSetupHeader(options = {}) {
|
|
103
105
|
logger.log('');
|
|
104
106
|
logger.log(title('AI Fabrix Platform Setup'));
|
|
105
107
|
logger.log(SEPARATOR);
|
|
106
108
|
logger.log('');
|
|
107
|
-
logPlatformSetupDurationHint('platform');
|
|
109
|
+
logPlatformSetupDurationHint('platform', options);
|
|
108
110
|
logger.log('');
|
|
109
111
|
}
|
|
110
112
|
|
package/lib/cli/infra-guided.js
CHANGED
|
@@ -14,6 +14,10 @@ const { execWithDockerEnv } = require('../utils/docker-exec');
|
|
|
14
14
|
const { getLogLevel, passesLevelFilter } = require('../commands/app-logs');
|
|
15
15
|
const { handleLogin } = require('../commands/login');
|
|
16
16
|
const { handleUpBuilderApi } = require('../commands/up-builder-api');
|
|
17
|
+
const {
|
|
18
|
+
handleUpIntegrationServer,
|
|
19
|
+
isIntegrationServerSelected
|
|
20
|
+
} = require('../commands/up-integration-server');
|
|
17
21
|
const {
|
|
18
22
|
resolveGuidedAuthControllerUrl,
|
|
19
23
|
runGuidedAuthStep,
|
|
@@ -295,8 +299,25 @@ async function runGuidedDataplaneInstallPhase(options, handleUpDataplane, handle
|
|
|
295
299
|
await refreshDataplaneVersionAfterPlatformInstall();
|
|
296
300
|
}
|
|
297
301
|
|
|
302
|
+
async function runGuidedIntegrationServerPhase(options, selected) {
|
|
303
|
+
if (!selected) return;
|
|
304
|
+
const spin = startSpinner('Starting Integration Server...');
|
|
305
|
+
await withMutedLogger(() => handleUpIntegrationServer({ ...options, platformInstall: true }));
|
|
306
|
+
stopSpinnerSuccess(spin, 'Integration Server ready');
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
async function runGuidedKeycloakRealmPhase(options) {
|
|
310
|
+
if (!options || options.requireKeycloakRealm !== true) return;
|
|
311
|
+
const { waitForKeycloakRealmReady } = require('../utils/keycloak-realm-ready');
|
|
312
|
+
const spin = startSpinner('Waiting for Keycloak realm (aifabrix)...');
|
|
313
|
+
await waitForKeycloakRealmReady({ timeoutSeconds: 180 });
|
|
314
|
+
stopSpinnerSuccess(spin, 'Keycloak realm ready');
|
|
315
|
+
logger.log('');
|
|
316
|
+
}
|
|
317
|
+
|
|
298
318
|
async function runGuidedUpPlatform(options, handleUpMiso, handleUpDataplane, handleLogin, forceCleanSummary = null) {
|
|
299
|
-
|
|
319
|
+
const integrationServerSelected = await isIntegrationServerSelected();
|
|
320
|
+
logGuidedPlatformSetupHeader({ integrationServer: integrationServerSelected });
|
|
300
321
|
if (forceCleanSummary && forceCleanSummary.forceSummary) {
|
|
301
322
|
logUpPlatformForceCleanSummary(forceCleanSummary.forceSummary, forceCleanSummary.cleanedApps);
|
|
302
323
|
}
|
|
@@ -308,21 +329,20 @@ async function runGuidedUpPlatform(options, handleUpMiso, handleUpDataplane, han
|
|
|
308
329
|
|
|
309
330
|
await withMutedLogger(() => prepareUrlsLocalRegistryForUpPlatform());
|
|
310
331
|
await runGuidedKeycloakAndMisoControllerPhase(options, handleUpMiso);
|
|
311
|
-
|
|
312
|
-
const { waitForKeycloakRealmReady } = require('../utils/keycloak-realm-ready');
|
|
313
|
-
const realmSpin = startSpinner('Waiting for Keycloak realm (aifabrix)...');
|
|
314
|
-
await waitForKeycloakRealmReady({ timeoutSeconds: 180 });
|
|
315
|
-
stopSpinnerSuccess(realmSpin, 'Keycloak realm ready');
|
|
316
|
-
logger.log('');
|
|
317
|
-
}
|
|
332
|
+
await runGuidedKeycloakRealmPhase(options);
|
|
318
333
|
await runGuidedAuthStep(handleLogin, authOpts, { startSpinner, stopSpinnerSuccess });
|
|
319
334
|
const controllerUrl = await resolveGuidedAuthControllerUrl(authOpts);
|
|
320
335
|
await runGuidedDataplaneInstallPhase(options, handleUpDataplane, handleLogin, controllerUrl);
|
|
321
336
|
const baSpin = startSpinner('Starting Builder API...');
|
|
322
337
|
await withMutedLogger(() => handleUpBuilderApi({ ...options, platformInstall: true }));
|
|
323
338
|
stopSpinnerSuccess(baSpin, 'Builder API ready');
|
|
324
|
-
await
|
|
325
|
-
|
|
339
|
+
await runGuidedIntegrationServerPhase(options, integrationServerSelected);
|
|
340
|
+
const appNames = [
|
|
341
|
+
'miso-controller', 'dataplane', 'builder-api',
|
|
342
|
+
...(integrationServerSelected ? ['integration-server'] : [])
|
|
343
|
+
];
|
|
344
|
+
await validateAppErrorLogs(appNames);
|
|
345
|
+
await logPlatformReadyFooter({ integrationServer: integrationServerSelected });
|
|
326
346
|
}
|
|
327
347
|
|
|
328
348
|
async function runGuidedDownInfra(appName, options, infraMod, appLib) {
|
package/lib/cli/setup-app.js
CHANGED
|
@@ -243,6 +243,10 @@ function registerBuildCommand(program) {
|
|
|
243
243
|
'Image tag or comma-separated tags (default: latest); {version} expands to app.version, e.g. "latest,{version}"'
|
|
244
244
|
)
|
|
245
245
|
.option('-r, --registry <registry>', 'Registry host for the image name (overrides dev set-registry and application.yaml)')
|
|
246
|
+
.option(
|
|
247
|
+
'-e, --env <env>',
|
|
248
|
+
'Environment key used to resolve manifest {NAME} values (dev|tst|pro); defaults to the current CLI environment'
|
|
249
|
+
)
|
|
246
250
|
.option(
|
|
247
251
|
'--base',
|
|
248
252
|
'Build/run the manifest base image (no -devN suffix). Default local build uses a developer-scoped repository when developer-id > 0.'
|
|
@@ -255,6 +259,7 @@ function registerBuildCommand(program) {
|
|
|
255
259
|
Examples:
|
|
256
260
|
$ aifabrix build myapp
|
|
257
261
|
$ aifabrix build myapp --tag v1.0.0 # also tags latest
|
|
262
|
+
$ aifabrix build myapp --env dev # resolve manifest {NAME} values for dev
|
|
258
263
|
$ aifabrix build myapp --base --from-dev --tag 'latest,{version}'
|
|
259
264
|
$ aifabrix build myapp --registry myregistry.azurecr.io`)
|
|
260
265
|
.action(async(appName, options) => {
|