@aifabrix/builder 2.51.1 → 2.52.1
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/jest.isolated-projects.js +25 -2
- package/jest.projects.js +4 -1
- package/lib/api/datasources-extended.api.js +8 -8
- package/lib/api/keycloak.api.js +92 -0
- package/lib/api/token-providers.api.js +77 -0
- package/lib/api/types/keycloak.types.js +23 -0
- package/lib/api/types/token-providers.types.js +65 -0
- package/lib/app/down.js +16 -1
- package/lib/app/run-container-start.js +4 -0
- package/lib/app/run-deploy-job.js +135 -27
- package/lib/app/run-env-compose.js +32 -0
- package/lib/app/run-env-recovery.js +35 -0
- package/lib/app/run-helpers.js +5 -6
- package/lib/app/run.js +11 -3
- package/lib/channels/add-channel.js +126 -19
- package/lib/channels/approval-guides/chatgpt.js +12 -9
- package/lib/channels/approval-guides/microsoft-copilot.js +16 -12
- package/lib/channels/channel-add-ensure.js +128 -0
- package/lib/channels/channel-artifact-generator.js +11 -1
- package/lib/channels/channel-branding.js +94 -0
- package/lib/channels/channel-credential.js +8 -0
- package/lib/channels/channel-discovery.js +330 -0
- package/lib/channels/channel-external-system.js +1 -1
- package/lib/channels/channel-runtime-resolver.js +103 -14
- package/lib/channels/channel-status-display.js +7 -1
- package/lib/channels/channel-targets.js +18 -1
- package/lib/channels/chatgpt-runtime-contract.js +1 -1
- package/lib/channels/microsoft-copilot-channel-add.js +138 -0
- package/lib/channels/microsoft-copilot-entra-sso-form.js +180 -0
- package/lib/channels/microsoft-copilot-entra-sso.js +264 -0
- package/lib/channels/microsoft-copilot-icons.js +74 -0
- package/lib/channels/microsoft-copilot-limits.js +84 -0
- package/lib/channels/microsoft-copilot-package.js +130 -49
- package/lib/channels/microsoft-copilot-validate.js +197 -0
- package/lib/cli/index.js +2 -0
- package/lib/cli/infra-guided-footers.js +1 -1
- package/lib/cli/infra-guided-public-url-warn.js +55 -0
- package/lib/cli/infra-guided.js +78 -62
- package/lib/cli/setup-app.help.js +4 -0
- package/lib/cli/setup-app.test-commands.js +59 -28
- package/lib/cli/setup-external-system.js +50 -12
- package/lib/cli/setup-infra.js +31 -4
- package/lib/commands/channel-add-setup.js +35 -10
- package/lib/commands/channel.js +27 -12
- package/lib/commands/deploy-gate-orchestrator.js +3 -2
- package/lib/commands/governance-verify-external.js +2 -5
- package/lib/commands/integration-definition.js +158 -0
- package/lib/commands/lifecycle-external.js +14 -7
- package/lib/commands/repair-auth-apply.js +19 -5
- package/lib/commands/repair-datasource-sync-operations.js +4 -1
- package/lib/commands/role-assistant-test-output.js +200 -0
- package/lib/commands/role-assistant.js +131 -114
- package/lib/commands/test-governance-external.js +2 -5
- package/lib/commands/up-miso.js +61 -24
- package/lib/commands/upload-data-sync-finalize.js +20 -3
- package/lib/commands/verify-governance-external.js +2 -5
- package/lib/commands/verify-operations-external.js +2 -5
- package/lib/commands/verify-operations-step-plan.js +31 -0
- package/lib/commands/verify-trust-command-action.js +2 -5
- package/lib/datasource/capability/capability-resolve.js +15 -5
- package/lib/datasource/capability/copy-operations.js +14 -4
- package/lib/datasource/datasource-identifier.js +4 -3
- package/lib/datasource/integration-context.js +3 -3
- package/lib/datasource/resolve-app.js +90 -8
- package/lib/datasource/test-e2e.js +2 -2
- package/lib/datasource/unified-validation-run-body.js +15 -1
- package/lib/datasource/unified-validation-run-resolve.js +2 -2
- package/lib/external-system/deploy-helpers.js +9 -5
- package/lib/external-system/deploy.js +2 -1
- package/lib/external-system/test-system-level.js +21 -3
- package/lib/external-system/test.js +4 -4
- package/lib/generator/builders.js +9 -15
- package/lib/infrastructure/index.js +19 -4
- package/lib/integration-definition/apply.js +478 -0
- package/lib/integration-definition/canonical-json.js +51 -0
- package/lib/integration-definition/emit-suite-bid.js +94 -0
- package/lib/integration-definition/enrich-crm-field-mappings.js +240 -0
- package/lib/integration-definition/enrich-openapi-operations.js +434 -0
- package/lib/integration-definition/from-discovery.js +339 -0
- package/lib/integration-definition/hash.js +37 -0
- package/lib/integration-definition/index.js +37 -0
- package/lib/integration-definition/openapi-enrichment/hubspot-crm-companies.json +205 -0
- package/lib/integration-definition/resource-type-catalog.js +59 -0
- package/lib/integration-definition/system-kind-enum.js +40 -0
- package/lib/integration-definition/validate.js +148 -0
- package/lib/programmatic/builder-api-request.js +5 -1
- package/lib/programmatic/builder-help-governance.js +3 -2
- package/lib/programmatic/capability-registry.js +19 -0
- package/lib/programmatic/openapi-component-schemas.js +6 -1
- package/lib/programmatic/openapi-descriptions.js +40 -0
- package/lib/programmatic/openapi-envelope-examples.js +269 -0
- package/lib/programmatic/openapi-operation-bindings.js +45 -0
- package/lib/programmatic/openapi-request-schemas.js +14 -0
- package/lib/programmatic/openapi-result-schemas-overflow.js +101 -0
- package/lib/programmatic/openapi-result-schemas.js +46 -99
- package/lib/programmatic/openapi-schema-descriptions-core.js +7 -4
- package/lib/programmatic/openapi-schema-descriptions-help.js +1 -1
- package/lib/programmatic/openapi-schema-descriptions-operations.js +24 -0
- package/lib/programmatic/openapi-schema-helpers.js +9 -9
- package/lib/programmatic/openapi-spec.js +11 -1
- package/lib/programmatic/orchestration-openapi-meta.js +10 -0
- package/lib/programmatic/route-handler-map.js +29 -1
- package/lib/programmatic/run-channel-package.js +236 -0
- package/lib/programmatic/run-integration-definition.js +69 -0
- package/lib/role-assistant/knowledge-markdown-frontmatter.js +75 -0
- package/lib/role-assistant/knowledge-sync-logging.js +58 -0
- package/lib/role-assistant/knowledge-sync.js +104 -89
- package/lib/role-assistant/test-cases-bid.js +155 -0
- package/lib/role-assistant/test-cases-evaluate.js +9 -1
- package/lib/role-assistant/test-cases-honesty.js +24 -10
- package/lib/role-assistant/test-cases.js +28 -0
- package/lib/role-assistant/test-runner-bid-apply.js +122 -0
- package/lib/role-assistant/test-runner-bid-document.js +278 -0
- package/lib/role-assistant/test-runner-workhub-answers.js +289 -46
- package/lib/role-assistant/test-runner-workhub.js +121 -25
- package/lib/role-assistant/test-suite.js +119 -0
- package/lib/schema/application-schema.json +64 -32
- package/lib/schema/external-datasource.schema.json +103 -17
- package/lib/schema/external-system.schema.json +54 -3
- package/lib/schema/infra.parameter.yaml +0 -20
- package/lib/schema/integration-definition.schema.json +195 -0
- package/lib/utils/authentication-shape.js +6 -0
- package/lib/utils/certify-external-workspace.js +40 -0
- package/lib/utils/docker-build.js +3 -3
- package/lib/utils/docker-run-ephemeral.js +77 -17
- package/lib/utils/health-check-public-warn.js +31 -13
- package/lib/utils/health-check.js +11 -1
- package/lib/utils/help-builder.js +1 -0
- package/lib/utils/paths.js +13 -15
- package/lib/utils/promote-dev-docker-image.js +97 -18
- package/lib/utils/role-assistant-paths.js +76 -14
- package/lib/utils/transient-developer-id.js +28 -0
- package/lib/utils/upload-sync-options.js +7 -4
- package/lib/validation/reserved-system-key-prefixes.js +56 -0
- package/lib/validation/validate.js +54 -19
- package/package.json +1 -1
- package/scripts/pnpm/af.mjs +24 -9
- package/scripts/pnpm/help.mjs +1 -1
- package/templates/applications/builder-api/application.yaml +3 -3
- package/templates/applications/dataplane/application.yaml +5 -5
- package/templates/applications/dataplane/env.template +8 -7
- package/templates/applications/dataplane/rbac.yaml +10 -10
- package/templates/applications/keycloak/application.yaml +2 -2
- package/templates/applications/keycloak/env.template +5 -2
- package/templates/applications/miso-controller/application.yaml +25 -11
- package/templates/applications/miso-controller/env.template +24 -5
- package/templates/channels/README.md +11 -1
- package/templates/channels/brand/color-192.png +0 -0
- package/templates/channels/brand/dark/128.png +0 -0
- package/templates/channels/brand/dark/192.png +0 -0
- package/templates/channels/brand/dark/256.png +0 -0
- package/templates/channels/brand/dark/32.png +0 -0
- package/templates/channels/brand/dark/512.png +0 -0
- package/templates/channels/brand/dark/64.png +0 -0
- package/templates/channels/brand/light/128.png +0 -0
- package/templates/channels/brand/light/192.png +0 -0
- package/templates/channels/brand/light/256.png +0 -0
- package/templates/channels/brand/light/32.png +0 -0
- package/templates/channels/brand/light/512.png +0 -0
- package/templates/channels/brand/light/64.png +0 -0
- package/templates/channels/brand/mono-white/128.png +0 -0
- package/templates/channels/brand/mono-white/192.png +0 -0
- package/templates/channels/brand/mono-white/256.png +0 -0
- package/templates/channels/brand/mono-white/32.png +0 -0
- package/templates/channels/brand/mono-white/512.png +0 -0
- package/templates/channels/brand/mono-white/64.png +0 -0
- package/templates/channels/brand/outline-32.png +0 -0
- package/templates/channels/chatgpt/install.template.md.hbs +76 -36
- package/templates/channels/chatgpt/integration/README.md +4 -4
- package/templates/channels/chatgpt/integration/chatgpt-system.json +1 -16
- package/templates/channels/chatgpt/manifest.template.json.hbs +3 -3
- package/templates/channels/microsoft-copilot/ai-plugin.template.json.hbs +10 -16
- package/templates/channels/microsoft-copilot/declarative-agent.template.json.hbs +5 -3
- package/templates/channels/microsoft-copilot/entra-sso-registration.template.md.hbs +27 -0
- package/templates/channels/microsoft-copilot/install.template.md.hbs +39 -20
- package/templates/channels/microsoft-copilot/integration/README.md +2 -3
- package/templates/channels/microsoft-copilot/integration/microsoft-copilot-system.json +1 -22
- package/templates/channels/microsoft-copilot/manifest.template.json.hbs +7 -7
- package/templates/channels/microsoft-copilot/oauth-plugin-vault-required.template.md.hbs +11 -0
- package/templates/channels/slack/install.template.md.hbs +6 -1
- package/templates/channels/slack/integration/README.md +2 -4
- package/templates/channels/teams-bot/install.template.md.hbs +4 -0
- package/templates/channels/teams-bot/integration/README.md +3 -5
- package/templates/channels/web-embed/integration/web-embed-system.json +1 -16
- package/templates/python/docker-compose.hbs +10 -0
- package/templates/typescript/docker-compose.hbs +10 -0
- /package/.cursor/plans/{Done → Archive}/157.1-security-quality-violations-by-type.csv +0 -0
|
@@ -67,6 +67,11 @@ const ISOLATED_SUITE_ENTRIES = [
|
|
|
67
67
|
['role-assistant-knowledge-sync', ['**/tests/lib/role-assistant/knowledge-sync.test.js']],
|
|
68
68
|
['role-assistant-evidence-sync', ['**/tests/lib/role-assistant/evidence-sync.test.js']],
|
|
69
69
|
['role-assistant-test-cases', ['**/tests/lib/role-assistant/test-cases.test.js']],
|
|
70
|
+
['role-assistant-test-suite', ['**/tests/lib/role-assistant/test-suite.test.js']],
|
|
71
|
+
[
|
|
72
|
+
'role-assistant-workhub-answers',
|
|
73
|
+
['**/tests/lib/role-assistant/test-runner-workhub-answers.test.js']
|
|
74
|
+
],
|
|
70
75
|
['governance-fixture-subjects', ['**/tests/lib/commands/governance-fixture-subjects.test.js']],
|
|
71
76
|
['enterprise-knowledge-generate-manifest', ['**/tests/lib/commands/enterprise-knowledge-generate-manifest.test.js']],
|
|
72
77
|
['plan-prevalidate-gates', ['**/tests/lib/plan-prevalidate-gates.test.js']],
|
|
@@ -152,7 +157,19 @@ const ISOLATED_SUITE_ENTRIES = [
|
|
|
152
157
|
[
|
|
153
158
|
'**/tests/lib/channels/channel-integration-generator.test.js',
|
|
154
159
|
'**/tests/lib/channels/channel-artifact-generator.test.js',
|
|
155
|
-
'**/tests/lib/channels/channel-integration-template.test.js'
|
|
160
|
+
'**/tests/lib/channels/channel-integration-template.test.js',
|
|
161
|
+
'**/tests/lib/channels/add-channel.test.js',
|
|
162
|
+
'**/tests/lib/channels/microsoft-copilot-package-quality.test.js',
|
|
163
|
+
'**/tests/lib/channels/channel-branding.test.js'
|
|
164
|
+
]
|
|
165
|
+
],
|
|
166
|
+
[
|
|
167
|
+
'integration-definition-real-fs',
|
|
168
|
+
[
|
|
169
|
+
'**/tests/lib/validation/integration-definition-schema.test.js',
|
|
170
|
+
'**/tests/lib/integration-definition/apply.test.js',
|
|
171
|
+
'**/tests/lib/integration-definition/from-discovery.test.js',
|
|
172
|
+
'**/tests/lib/integration-definition/emit-suite-bid.test.js'
|
|
156
173
|
]
|
|
157
174
|
],
|
|
158
175
|
[
|
|
@@ -175,6 +192,10 @@ const ISOLATED_SUITE_ENTRIES = [
|
|
|
175
192
|
'**/tests/lib/scripts/sync-builder-api-template.test.js',
|
|
176
193
|
'**/tests/lib/evidence/evidence-service.test.js'
|
|
177
194
|
]
|
|
195
|
+
],
|
|
196
|
+
[
|
|
197
|
+
'enrich-openapi-operations',
|
|
198
|
+
['**/tests/lib/integration-definition/enrich-openapi-operations.test.js']
|
|
178
199
|
]
|
|
179
200
|
];
|
|
180
201
|
|
|
@@ -224,7 +245,9 @@ function buildDefaultTestPathIgnorePatterns(includeLocalTests) {
|
|
|
224
245
|
'/tests/integration/',
|
|
225
246
|
'\\\\tests\\\\integration\\\\',
|
|
226
247
|
'/tests/manual/',
|
|
227
|
-
'\\\\tests\\\\manual\\\\'
|
|
248
|
+
'\\\\tests\\\\manual\\\\',
|
|
249
|
+
'/builder/',
|
|
250
|
+
'\\\\builder\\\\'
|
|
228
251
|
];
|
|
229
252
|
const globs = [
|
|
230
253
|
...ISOLATED_SUITE_ENTRIES.flatMap((entry) => entry[1]),
|
package/jest.projects.js
CHANGED
|
@@ -52,7 +52,10 @@ const defaultProject = {
|
|
|
52
52
|
],
|
|
53
53
|
testPathIgnorePatterns: buildDefaultTestPathIgnorePatterns(),
|
|
54
54
|
modulePathIgnorePatterns: [
|
|
55
|
-
'<rootDir>/docker-builder-api-staging/'
|
|
55
|
+
'<rootDir>/docker-builder-api-staging/',
|
|
56
|
+
// Generated app dirs under builder/ (except builder-api) — avoid haste/transform
|
|
57
|
+
// picking up junk written by generator/cli suites (e.g. builder/testapp).
|
|
58
|
+
'<rootDir>/builder/(?!builder-api(?:/|$))'
|
|
56
59
|
],
|
|
57
60
|
setupFiles: ['<rootDir>/tests/capture-real-fs.js'],
|
|
58
61
|
setupFilesAfterEnv: ['<rootDir>/tests/setup.js'],
|
|
@@ -69,28 +69,28 @@ async function detachPolicy(dataplaneUrl, sourceIdOrKey, policyIdOrKey, authConf
|
|
|
69
69
|
const client = createDataplaneApiClient(dataplaneUrl, authConfig);
|
|
70
70
|
return await client.delete(`/api/v1/external/${sourceIdOrKey}/policies/${policyIdOrKey}`);
|
|
71
71
|
}
|
|
72
|
-
/** @requiresPermission {Dataplane} external-
|
|
72
|
+
/** @requiresPermission {Dataplane} external-data-source:read (sync under /data-storage) */
|
|
73
73
|
async function listSyncJobs(dataplaneUrl, sourceIdOrKey, authConfig, options = {}) {
|
|
74
74
|
const client = createDataplaneApiClient(dataplaneUrl, authConfig);
|
|
75
|
-
return await client.get(`/api/v1/
|
|
75
|
+
return await client.get(`/api/v1/data-storage/${sourceIdOrKey}/sync`, { params: options });
|
|
76
76
|
}
|
|
77
77
|
async function createSyncJob(dataplaneUrl, sourceIdOrKey, authConfig, syncData) {
|
|
78
78
|
const client = createDataplaneApiClient(dataplaneUrl, authConfig);
|
|
79
|
-
return await client.post(`/api/v1/
|
|
79
|
+
return await client.post(`/api/v1/data-storage/${sourceIdOrKey}/sync`, { body: syncData });
|
|
80
80
|
}
|
|
81
|
-
/** @requiresPermission {Dataplane} external-
|
|
81
|
+
/** @requiresPermission {Dataplane} external-data-source:read */
|
|
82
82
|
async function getSyncJob(dataplaneUrl, sourceIdOrKey, syncJobId, authConfig) {
|
|
83
83
|
const client = createDataplaneApiClient(dataplaneUrl, authConfig);
|
|
84
|
-
return await client.get(`/api/v1/
|
|
84
|
+
return await client.get(`/api/v1/data-storage/${sourceIdOrKey}/sync/${syncJobId}`);
|
|
85
85
|
}
|
|
86
|
-
/** @requiresPermission {Dataplane} external-
|
|
86
|
+
/** @requiresPermission {Dataplane} external-data-source:sync */
|
|
87
87
|
async function updateSyncJob(dataplaneUrl, sourceIdOrKey, syncJobId, authConfig, updateData) {
|
|
88
88
|
const client = createDataplaneApiClient(dataplaneUrl, authConfig);
|
|
89
|
-
return await client.put(`/api/v1/
|
|
89
|
+
return await client.put(`/api/v1/data-storage/${sourceIdOrKey}/sync/${syncJobId}`, { body: updateData });
|
|
90
90
|
}
|
|
91
91
|
async function executeSyncJob(dataplaneUrl, sourceIdOrKey, syncJobId, authConfig) {
|
|
92
92
|
const client = createDataplaneApiClient(dataplaneUrl, authConfig);
|
|
93
|
-
return await client.post(`/api/v1/
|
|
93
|
+
return await client.post(`/api/v1/data-storage/${sourceIdOrKey}/sync/${syncJobId}/execute`);
|
|
94
94
|
}
|
|
95
95
|
/** @requiresPermission {Dataplane} external-data-source:sync */
|
|
96
96
|
async function validateDocuments(dataplaneUrl, sourceIdOrKey, authConfig, validateData) {
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Keycloak status API (Controller) — Entra IdP ids for packaging
|
|
3
|
+
* @author AI Fabrix Team
|
|
4
|
+
* @version 1.0.0
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
'use strict';
|
|
8
|
+
|
|
9
|
+
const { ApiClient } = require('./index');
|
|
10
|
+
|
|
11
|
+
const KEYCLOAK_STATUS_PATH = '/api/v1/keycloak/status';
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* @param {Object} response
|
|
15
|
+
* @returns {import('./types/keycloak.types').KeycloakStatus|null}
|
|
16
|
+
*/
|
|
17
|
+
function extractKeycloakStatus(response) {
|
|
18
|
+
if (!response || typeof response !== 'object' || response.success === false) {
|
|
19
|
+
return null;
|
|
20
|
+
}
|
|
21
|
+
const body = response.data ?? response;
|
|
22
|
+
const data = body?.data !== undefined ? body.data : body;
|
|
23
|
+
if (!data || typeof data !== 'object' || Array.isArray(data)) {
|
|
24
|
+
return null;
|
|
25
|
+
}
|
|
26
|
+
return data;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* @param {unknown} value
|
|
31
|
+
* @returns {string|null}
|
|
32
|
+
*/
|
|
33
|
+
function trimNonEmptyString(value) {
|
|
34
|
+
if (typeof value !== 'string') {
|
|
35
|
+
return null;
|
|
36
|
+
}
|
|
37
|
+
const trimmed = value.trim();
|
|
38
|
+
return trimmed || null;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* @param {import('./types/keycloak.types').KeycloakStatus|null} status
|
|
43
|
+
* @returns {import('./types/keycloak.types').KeycloakEntraIdentityProvider|null}
|
|
44
|
+
*/
|
|
45
|
+
function extractEntraIdentityProvider(status) {
|
|
46
|
+
const raw = status?.entraIdentityProvider;
|
|
47
|
+
if (!raw || typeof raw !== 'object') {
|
|
48
|
+
return null;
|
|
49
|
+
}
|
|
50
|
+
const enterpriseAppId = trimNonEmptyString(raw.enterpriseAppId);
|
|
51
|
+
if (!enterpriseAppId) {
|
|
52
|
+
return null;
|
|
53
|
+
}
|
|
54
|
+
const tenantId = trimNonEmptyString(raw.tenantId);
|
|
55
|
+
return tenantId ? { enterpriseAppId, tenantId } : { enterpriseAppId };
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* GET /api/v1/keycloak/status
|
|
60
|
+
* @requiresPermission {Controller} admin:keycloak
|
|
61
|
+
* @async
|
|
62
|
+
* @param {string} controllerUrl - Miso Controller base URL
|
|
63
|
+
* @param {Object} authConfig - Authentication configuration (device Bearer)
|
|
64
|
+
* @returns {Promise<import('./types/keycloak.types').KeycloakStatus|null>}
|
|
65
|
+
*/
|
|
66
|
+
async function getKeycloakStatus(controllerUrl, authConfig) {
|
|
67
|
+
const client = new ApiClient(controllerUrl, authConfig);
|
|
68
|
+
const response = await client.get(KEYCLOAK_STATUS_PATH);
|
|
69
|
+
return extractKeycloakStatus(response);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Resolve Entra SSO enterprise app id from Keycloak status, or null when missing.
|
|
74
|
+
* Does not invent ids.
|
|
75
|
+
* @requiresPermission {Controller} admin:keycloak
|
|
76
|
+
* @async
|
|
77
|
+
* @param {string} controllerUrl - Miso Controller base URL
|
|
78
|
+
* @param {Object} authConfig - Authentication configuration
|
|
79
|
+
* @returns {Promise<import('./types/keycloak.types').KeycloakEntraIdentityProvider|null>}
|
|
80
|
+
*/
|
|
81
|
+
async function getKeycloakEntraIdentityProvider(controllerUrl, authConfig) {
|
|
82
|
+
const status = await getKeycloakStatus(controllerUrl, authConfig);
|
|
83
|
+
return extractEntraIdentityProvider(status);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
module.exports = {
|
|
87
|
+
KEYCLOAK_STATUS_PATH,
|
|
88
|
+
getKeycloakStatus,
|
|
89
|
+
getKeycloakEntraIdentityProvider,
|
|
90
|
+
extractKeycloakStatus,
|
|
91
|
+
extractEntraIdentityProvider
|
|
92
|
+
};
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Admin token providers API (Controller Trust sources)
|
|
3
|
+
* @author AI Fabrix Team
|
|
4
|
+
* @version 1.0.0
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
'use strict';
|
|
8
|
+
|
|
9
|
+
const { ApiClient } = require('./index');
|
|
10
|
+
|
|
11
|
+
const ADMIN_BASE = '/api/v1/admin/token-providers';
|
|
12
|
+
const MICROSOFT_ENTRA_ID_KEY = 'microsoft-entra-id';
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* @param {Object} response
|
|
16
|
+
* @returns {import('./types/token-providers.types').TokenProvider[]}
|
|
17
|
+
*/
|
|
18
|
+
function extractTokenProviders(response) {
|
|
19
|
+
const body = response?.data ?? response;
|
|
20
|
+
const data = body?.data ?? body;
|
|
21
|
+
if (Array.isArray(data)) {
|
|
22
|
+
return data;
|
|
23
|
+
}
|
|
24
|
+
if (Array.isArray(data?.items)) {
|
|
25
|
+
return data.items;
|
|
26
|
+
}
|
|
27
|
+
return [];
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* List admin token providers (paginated).
|
|
32
|
+
* GET /api/v1/admin/token-providers
|
|
33
|
+
* @requiresPermission {Controller} token-provider:read
|
|
34
|
+
* @async
|
|
35
|
+
* @param {string} controllerUrl - Miso Controller base URL
|
|
36
|
+
* @param {Object} authConfig - Authentication configuration (device Bearer)
|
|
37
|
+
* @param {import('./types/token-providers.types').ListAdminTokenProvidersOptions} options
|
|
38
|
+
* @returns {Promise<Object>} Controller list response
|
|
39
|
+
* @throws {Error} If direction is missing or request fails
|
|
40
|
+
*/
|
|
41
|
+
async function listAdminTokenProviders(controllerUrl, authConfig, options = {}) {
|
|
42
|
+
const direction = options?.direction;
|
|
43
|
+
if (direction !== 'inbound' && direction !== 'outbound') {
|
|
44
|
+
throw new Error('direction is required and must be "inbound" or "outbound"');
|
|
45
|
+
}
|
|
46
|
+
const client = new ApiClient(controllerUrl, authConfig);
|
|
47
|
+
const params = { ...options, direction };
|
|
48
|
+
return await client.get(ADMIN_BASE, { params });
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Resolve inbound Microsoft Entra ID Trust source, or null when missing.
|
|
53
|
+
* Does not invent issuer, audience, or scopes.
|
|
54
|
+
* @requiresPermission {Controller} token-provider:read
|
|
55
|
+
* @async
|
|
56
|
+
* @param {string} controllerUrl - Miso Controller base URL
|
|
57
|
+
* @param {Object} authConfig - Authentication configuration
|
|
58
|
+
* @returns {Promise<import('./types/token-providers.types').TokenProvider|null>}
|
|
59
|
+
*/
|
|
60
|
+
async function getInboundMicrosoftEntraIdProvider(controllerUrl, authConfig) {
|
|
61
|
+
const response = await listAdminTokenProviders(controllerUrl, authConfig, {
|
|
62
|
+
direction: 'inbound',
|
|
63
|
+
search: MICROSOFT_ENTRA_ID_KEY,
|
|
64
|
+
page: 1,
|
|
65
|
+
pageSize: 50
|
|
66
|
+
});
|
|
67
|
+
const items = extractTokenProviders(response);
|
|
68
|
+
const exact = items.find((item) => item && item.key === MICROSOFT_ENTRA_ID_KEY);
|
|
69
|
+
return exact || null;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
module.exports = {
|
|
73
|
+
listAdminTokenProviders,
|
|
74
|
+
getInboundMicrosoftEntraIdProvider,
|
|
75
|
+
extractTokenProviders,
|
|
76
|
+
MICROSOFT_ENTRA_ID_KEY
|
|
77
|
+
};
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Keycloak status API type definitions (Controller)
|
|
3
|
+
* @author AI Fabrix Team
|
|
4
|
+
* @version 1.0.0
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Entra identity provider fragment from Keycloak status.
|
|
9
|
+
* @typedef {Object} KeycloakEntraIdentityProvider
|
|
10
|
+
* @property {string} enterpriseAppId - Entra SSO application (client) id
|
|
11
|
+
* @property {string} [tenantId] - Entra tenant id when known
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Keycloak configuration status payload (`data` of GET /api/v1/keycloak/status).
|
|
16
|
+
* @typedef {Object} KeycloakStatus
|
|
17
|
+
* @property {string} [realm]
|
|
18
|
+
* @property {string} [status]
|
|
19
|
+
* @property {string} [controllerClientId]
|
|
20
|
+
* @property {KeycloakEntraIdentityProvider} [entraIdentityProvider]
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
module.exports = {};
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Token provider API type definitions (Controller admin Trust sources)
|
|
3
|
+
* @author AI Fabrix Team
|
|
4
|
+
* @version 1.0.0
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Token provider direction
|
|
9
|
+
* @typedef {'inbound'|'outbound'} TokenProviderDirection
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Token provider subject type
|
|
14
|
+
* @typedef {'user'|'service'|'both'} TokenProviderSubjectType
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Allowed operation on a token provider
|
|
19
|
+
* @typedef {'mcp.invoke'|'dataplane.read'|'dataplane.write'} TokenProviderAllowedOperation
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Admin token provider list item (Controller TokenProvider schema)
|
|
24
|
+
* @typedef {Object} TokenProvider
|
|
25
|
+
* @property {string} id - Unique identifier (cuid)
|
|
26
|
+
* @property {string} key - Unique slug (e.g. microsoft-entra-id)
|
|
27
|
+
* @property {string} displayName - Human-readable name
|
|
28
|
+
* @property {string} issuer - JWT issuer URL (iss claim)
|
|
29
|
+
* @property {string} jwksUri - JWKS endpoint for key verification
|
|
30
|
+
* @property {string|null} [audience] - Expected audience claim (may be null)
|
|
31
|
+
* @property {TokenProviderSubjectType} subjectType - Subject type
|
|
32
|
+
* @property {TokenProviderDirection} direction - inbound or outbound
|
|
33
|
+
* @property {string|null} [identityClaim] - Claim used for user identity
|
|
34
|
+
* @property {string|null} [emailClaim] - Claim used for email
|
|
35
|
+
* @property {boolean} [autoProvision] - Whether to auto-create users
|
|
36
|
+
* @property {string|null} [serviceUserKey] - Service user key for service flow
|
|
37
|
+
* @property {TokenProviderAllowedOperation[]} [allowedOperations] - Allowed operations
|
|
38
|
+
* @property {string[]} [allowedScopes] - OAuth scopes (may be empty)
|
|
39
|
+
* @property {boolean} enabled - Whether this provider is enabled
|
|
40
|
+
* @property {string} [createdAt] - Created at
|
|
41
|
+
* @property {string} [updatedAt] - Updated at
|
|
42
|
+
*/
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* List admin token providers query options
|
|
46
|
+
* @typedef {Object} ListAdminTokenProvidersOptions
|
|
47
|
+
* @property {TokenProviderDirection} direction - Required by OpenAPI
|
|
48
|
+
* @property {string} [search] - Search across key, displayName, issuer
|
|
49
|
+
* @property {number} [page] - Page number
|
|
50
|
+
* @property {number} [pageSize] - Page size
|
|
51
|
+
* @property {string} [sort] - Sort expression
|
|
52
|
+
* @property {string} [filter] - Filter expression
|
|
53
|
+
* @property {string} [enabled] - 'true' | 'false'
|
|
54
|
+
* @property {TokenProviderSubjectType} [subjectType] - Subject type filter
|
|
55
|
+
*/
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Paginated list response for admin token providers
|
|
59
|
+
* @typedef {Object} ListAdminTokenProvidersResponse
|
|
60
|
+
* @property {TokenProvider[]} data - Provider rows
|
|
61
|
+
* @property {Object} meta - Pagination meta
|
|
62
|
+
* @property {Object} [links] - Pagination links
|
|
63
|
+
*/
|
|
64
|
+
|
|
65
|
+
module.exports = {};
|
package/lib/app/down.js
CHANGED
|
@@ -111,6 +111,20 @@ async function stopAppContainers(appName, companionKeys, developerId) {
|
|
|
111
111
|
}
|
|
112
112
|
}
|
|
113
113
|
|
|
114
|
+
/**
|
|
115
|
+
* Resolve developer id for downApp (optional override does not change config).
|
|
116
|
+
* @param {Object} options
|
|
117
|
+
* @returns {Promise<string>}
|
|
118
|
+
*/
|
|
119
|
+
async function resolveDownAppDeveloperId(options) {
|
|
120
|
+
const override = options && options.developerId;
|
|
121
|
+
if (override === undefined || override === null || override === '') {
|
|
122
|
+
return config.getDeveloperId();
|
|
123
|
+
}
|
|
124
|
+
const { validateAndNormalizeDeveloperId } = require('../core/config-normalize');
|
|
125
|
+
return validateAndNormalizeDeveloperId(override);
|
|
126
|
+
}
|
|
127
|
+
|
|
114
128
|
/**
|
|
115
129
|
* Stops and removes a specific application's container.
|
|
116
130
|
* If --volumes is passed, attempts to remove the app's named Docker volume.
|
|
@@ -122,6 +136,7 @@ async function stopAppContainers(appName, companionKeys, developerId) {
|
|
|
122
136
|
* @param {string} appName - Application name
|
|
123
137
|
* @param {Object} options - Options
|
|
124
138
|
* @param {boolean} [options.volumes=false] - Remove Docker volume data
|
|
139
|
+
* @param {string|number} [options.developerId] - Target developer for this stop only
|
|
125
140
|
* @returns {Promise<void>} Resolves when operation completes
|
|
126
141
|
* @throws {Error} If validation fails or Docker operations error (other than missing volume)
|
|
127
142
|
*
|
|
@@ -136,7 +151,7 @@ async function downApp(appName, options = {}) {
|
|
|
136
151
|
validateAppName(appName);
|
|
137
152
|
|
|
138
153
|
const volumes = !!options.volumes;
|
|
139
|
-
const developerId = await
|
|
154
|
+
const developerId = await resolveDownAppDeveloperId(options);
|
|
140
155
|
const appConfig = loadBuilderApplicationConfig(appName);
|
|
141
156
|
const companionKeys = getCeleryCompanionAppKeys(appName, appConfig);
|
|
142
157
|
|
|
@@ -141,6 +141,7 @@ async function startContainer(appName, composePath, port, appConfig = null, opts
|
|
|
141
141
|
debug = false,
|
|
142
142
|
runEnvPath = null,
|
|
143
143
|
runEnvAdminPath = null,
|
|
144
|
+
ensureRunEnvFiles = null,
|
|
144
145
|
runOptions = {},
|
|
145
146
|
devMountPath = null,
|
|
146
147
|
misoEnvironment = 'dev'
|
|
@@ -166,6 +167,9 @@ async function startContainer(appName, composePath, port, appConfig = null, opts
|
|
|
166
167
|
}
|
|
167
168
|
|
|
168
169
|
if (!usedDockerRunFallback) {
|
|
170
|
+
if (typeof ensureRunEnvFiles === 'function') {
|
|
171
|
+
await ensureRunEnvFiles();
|
|
172
|
+
}
|
|
169
173
|
const env = await prepareContainerEnv(debug);
|
|
170
174
|
await executeComposeUp(composeCmdBase, composePath, env, debug);
|
|
171
175
|
}
|
|
@@ -11,11 +11,16 @@ const logger = require('../utils/logger');
|
|
|
11
11
|
const {
|
|
12
12
|
infoLine,
|
|
13
13
|
formatSuccessLine,
|
|
14
|
-
formatBlockingError
|
|
14
|
+
formatBlockingError,
|
|
15
|
+
metadata
|
|
15
16
|
} = require('../utils/cli-test-layout-chalk');
|
|
16
17
|
const { runDockerRunEphemeral } = require('../utils/docker-run-ephemeral');
|
|
17
18
|
const { getInfraBridgeNetworkName } = require('../infrastructure/orphan-infra-docker-teardown');
|
|
18
19
|
|
|
20
|
+
/** Migrate status lines: `[system] head=… applied=3 pending=[]` */
|
|
21
|
+
const DEPLOY_JOB_STATUS_RE =
|
|
22
|
+
/^\[([^\]]+)\]\s+head=\S+\s+applied=(\d+)\s+pending=(\[[^\]]*\])/i;
|
|
23
|
+
|
|
19
24
|
/**
|
|
20
25
|
* Resolve trimmed deployJob from app config, or null if unset.
|
|
21
26
|
* @param {Object} appConfig - Application configuration
|
|
@@ -39,6 +44,115 @@ async function resolveDeployJobNetwork() {
|
|
|
39
44
|
return getInfraBridgeNetworkName(developerId);
|
|
40
45
|
}
|
|
41
46
|
|
|
47
|
+
/**
|
|
48
|
+
* Quiet docker stdio by default (hide app bootstrap warnings). Full inherit only with --debug.
|
|
49
|
+
* @param {{ quiet?: boolean, debug?: boolean }} params
|
|
50
|
+
* @returns {boolean}
|
|
51
|
+
*/
|
|
52
|
+
function shouldQuietDeployJob(params = {}) {
|
|
53
|
+
if (params.debug === true) {
|
|
54
|
+
return false;
|
|
55
|
+
}
|
|
56
|
+
return true;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Parse migrate CLI status lines into totals (no per-chain head/pending dump).
|
|
61
|
+
* @param {string} output
|
|
62
|
+
* @returns {{ chains: number, applied: number, pending: number }|null}
|
|
63
|
+
*/
|
|
64
|
+
function summarizeDeployJobMigrateOutput(output) {
|
|
65
|
+
if (typeof output !== 'string' || !output.trim()) {
|
|
66
|
+
return null;
|
|
67
|
+
}
|
|
68
|
+
let chains = 0;
|
|
69
|
+
let applied = 0;
|
|
70
|
+
let pending = 0;
|
|
71
|
+
for (const line of output.split(/\r?\n/)) {
|
|
72
|
+
const match = DEPLOY_JOB_STATUS_RE.exec(line.trim());
|
|
73
|
+
if (!match) {
|
|
74
|
+
continue;
|
|
75
|
+
}
|
|
76
|
+
chains += 1;
|
|
77
|
+
applied += Number.parseInt(match[2], 10) || 0;
|
|
78
|
+
const inner = match[3].slice(1, -1).trim();
|
|
79
|
+
if (inner) {
|
|
80
|
+
pending += inner.split(',').filter((part) => part.trim()).length;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
if (chains === 0) {
|
|
84
|
+
return null;
|
|
85
|
+
}
|
|
86
|
+
return { chains, applied, pending };
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* One-line migrate totals from quiet deployJob output (replaces per-chain dump).
|
|
91
|
+
* @param {string} output
|
|
92
|
+
* @returns {string|null} Summary text for the completed line, or null
|
|
93
|
+
*/
|
|
94
|
+
function formatDeployJobMigrateSummary(output) {
|
|
95
|
+
const summary = summarizeDeployJobMigrateOutput(output);
|
|
96
|
+
if (!summary) {
|
|
97
|
+
return null;
|
|
98
|
+
}
|
|
99
|
+
return `${summary.applied} applied, ${summary.pending} pending (${summary.chains} chains)`;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* @deprecated Prefer formatDeployJobMigrateSummary — kept for tests.
|
|
104
|
+
* @param {string} output
|
|
105
|
+
*/
|
|
106
|
+
function emitDeployJobSummaryLines(output) {
|
|
107
|
+
const text = formatDeployJobMigrateSummary(output);
|
|
108
|
+
if (text) {
|
|
109
|
+
logger.log(metadata(`DB migrate: ${text}`));
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* @param {string|null|undefined} devMountPath
|
|
115
|
+
* @returns {Array<{ host: string, container: string }>}
|
|
116
|
+
*/
|
|
117
|
+
function _deployJobVolumeMounts(devMountPath) {
|
|
118
|
+
if (typeof devMountPath !== 'string' || !devMountPath.trim()) {
|
|
119
|
+
return [];
|
|
120
|
+
}
|
|
121
|
+
return [{ host: devMountPath.trim(), container: '/app' }];
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Always announce the job; debug adds command/network/mount detail.
|
|
126
|
+
* @param {{ fullImage: string, cmd: string, network: string, volumeMounts: Array, debug: boolean }} params
|
|
127
|
+
*/
|
|
128
|
+
function _logDeployJobStart(params) {
|
|
129
|
+
logger.log(infoLine(`Running build.deployJob once before start (${params.fullImage})…`));
|
|
130
|
+
if (!params.debug) {
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
133
|
+
logger.log(infoLine(`deployJob: ${params.cmd}`));
|
|
134
|
+
logger.log(infoLine(`deployJob network: ${params.network}`));
|
|
135
|
+
if (params.volumeMounts.length) {
|
|
136
|
+
logger.log(infoLine(`deployJob mount: ${params.volumeMounts[0].host} -> /app`));
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* @param {{ result: any, quietJob: boolean }} params
|
|
142
|
+
* @throws {Error}
|
|
143
|
+
*/
|
|
144
|
+
function _assertDeployJobExit(params) {
|
|
145
|
+
const code = params.quietJob ? params.result.code : params.result;
|
|
146
|
+
if (code === 0) {
|
|
147
|
+
return;
|
|
148
|
+
}
|
|
149
|
+
if (params.quietJob && params.result.output) {
|
|
150
|
+
logger.log(params.result.output.trimEnd());
|
|
151
|
+
}
|
|
152
|
+
logger.log(formatBlockingError(`build.deployJob failed with exit code ${code}`));
|
|
153
|
+
throw new Error(`build.deployJob failed with exit code ${code}`);
|
|
154
|
+
}
|
|
155
|
+
|
|
42
156
|
/**
|
|
43
157
|
* If build.deployJob is set, run it once in an ephemeral container (same image + env).
|
|
44
158
|
* Fail-closed on non-zero exit. No-op when unset.
|
|
@@ -53,6 +167,7 @@ async function resolveDeployJobNetwork() {
|
|
|
53
167
|
* @param {string|null} [params.runEnvPath] - Ephemeral .env.run path for --env-file
|
|
54
168
|
* @param {string|null} [params.devMountPath] - Host path mounted at /app for --reload
|
|
55
169
|
* @param {boolean} [params.debug]
|
|
170
|
+
* @param {boolean} [params.quiet] - Unused legacy flag; quiet is default unless debug
|
|
56
171
|
* @returns {Promise<void>}
|
|
57
172
|
* @throws {Error} If deployJob exits non-zero
|
|
58
173
|
*/
|
|
@@ -61,7 +176,8 @@ async function runDeployJobIfConfigured({
|
|
|
61
176
|
fullImage,
|
|
62
177
|
runEnvPath = null,
|
|
63
178
|
devMountPath = null,
|
|
64
|
-
debug = false
|
|
179
|
+
debug = false,
|
|
180
|
+
quiet = false
|
|
65
181
|
}) {
|
|
66
182
|
const cmd = resolveDeployJobCommand(appConfig);
|
|
67
183
|
if (!cmd) {
|
|
@@ -72,37 +188,29 @@ async function runDeployJobIfConfigured({
|
|
|
72
188
|
}
|
|
73
189
|
const network = await resolveDeployJobNetwork();
|
|
74
190
|
const volumeMounts = _deployJobVolumeMounts(devMountPath);
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
logger.log(infoLine(`deployJob network: ${network}`));
|
|
79
|
-
if (volumeMounts.length) {
|
|
80
|
-
logger.log(infoLine(`deployJob mount: ${volumeMounts[0].host} -> /app`));
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
const code = await runDockerRunEphemeral(fullImage, cmd, runEnvPath, {
|
|
191
|
+
const quietJob = shouldQuietDeployJob({ quiet, debug });
|
|
192
|
+
_logDeployJobStart({ fullImage, cmd, network, volumeMounts, debug });
|
|
193
|
+
const result = await runDockerRunEphemeral(fullImage, cmd, runEnvPath, {
|
|
84
194
|
network,
|
|
85
|
-
volumeMounts
|
|
195
|
+
volumeMounts,
|
|
196
|
+
quiet: quietJob
|
|
86
197
|
});
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
198
|
+
_assertDeployJobExit({ result, quietJob });
|
|
199
|
+
if (quietJob && result.output) {
|
|
200
|
+
const migrateSummary = formatDeployJobMigrateSummary(result.output);
|
|
201
|
+
if (migrateSummary) {
|
|
202
|
+
logger.log(formatSuccessLine(`build.deployJob completed — ${migrateSummary}`));
|
|
203
|
+
return;
|
|
204
|
+
}
|
|
90
205
|
}
|
|
91
206
|
logger.log(formatSuccessLine('build.deployJob completed'));
|
|
92
207
|
}
|
|
93
208
|
|
|
94
|
-
/**
|
|
95
|
-
* @param {string|null|undefined} devMountPath
|
|
96
|
-
* @returns {Array<{ host: string, container: string }>}
|
|
97
|
-
*/
|
|
98
|
-
function _deployJobVolumeMounts(devMountPath) {
|
|
99
|
-
if (typeof devMountPath !== 'string' || !devMountPath.trim()) {
|
|
100
|
-
return [];
|
|
101
|
-
}
|
|
102
|
-
return [{ host: devMountPath.trim(), container: '/app' }];
|
|
103
|
-
}
|
|
104
|
-
|
|
105
209
|
module.exports = {
|
|
106
210
|
resolveDeployJobCommand,
|
|
107
|
-
runDeployJobIfConfigured
|
|
211
|
+
runDeployJobIfConfigured,
|
|
212
|
+
shouldQuietDeployJob,
|
|
213
|
+
emitDeployJobSummaryLines,
|
|
214
|
+
formatDeployJobMigrateSummary,
|
|
215
|
+
summarizeDeployJobMigrateOutput
|
|
108
216
|
};
|
|
@@ -315,6 +315,37 @@ async function buildMergedRunEnvAndWrite(appName, appConfig, devDir, developerId
|
|
|
315
315
|
return { runEnvPath, runEnvAdminPath };
|
|
316
316
|
}
|
|
317
317
|
|
|
318
|
+
/**
|
|
319
|
+
* Rebuild run env files when another use of the shared applications directory removed them.
|
|
320
|
+
* @async
|
|
321
|
+
* @param {string} appName - Application name
|
|
322
|
+
* @param {Object} appConfig - Application configuration
|
|
323
|
+
* @param {string} runEnvPath - Expected .env.run path
|
|
324
|
+
* @param {string} runEnvAdminPath - Expected .env.run.admin path
|
|
325
|
+
* @param {number|string} developerId - Developer ID
|
|
326
|
+
* @param {Object|null} scopeOpts - Environment-scoped run options
|
|
327
|
+
* @returns {Promise<void>}
|
|
328
|
+
*/
|
|
329
|
+
async function ensureRunEnvFilesPresent(
|
|
330
|
+
appName,
|
|
331
|
+
appConfig,
|
|
332
|
+
runEnvPath,
|
|
333
|
+
runEnvAdminPath,
|
|
334
|
+
developerId,
|
|
335
|
+
scopeOpts = null
|
|
336
|
+
) {
|
|
337
|
+
if (fsSync.existsSync(runEnvPath) && fsSync.existsSync(runEnvAdminPath)) {
|
|
338
|
+
return;
|
|
339
|
+
}
|
|
340
|
+
await buildMergedRunEnvAndWrite(
|
|
341
|
+
appName,
|
|
342
|
+
appConfig,
|
|
343
|
+
path.dirname(runEnvPath),
|
|
344
|
+
developerId,
|
|
345
|
+
scopeOpts
|
|
346
|
+
);
|
|
347
|
+
}
|
|
348
|
+
|
|
318
349
|
/**
|
|
319
350
|
* Assert generated compose does not contain password literals in environment (ISO 27K).
|
|
320
351
|
* @param {string} composeContent - Generated docker-compose content
|
|
@@ -330,6 +361,7 @@ function assertNoPasswordLiteralsInCompose(composeContent) {
|
|
|
330
361
|
module.exports = {
|
|
331
362
|
cleanApplicationsDir,
|
|
332
363
|
buildMergedRunEnvAndWrite,
|
|
364
|
+
ensureRunEnvFilesPresent,
|
|
333
365
|
assertNoPasswordLiteralsInCompose,
|
|
334
366
|
ensureRunSecretsForApp
|
|
335
367
|
};
|