@aifabrix/builder 2.50.0 → 2.51.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/.cursor/plans/Done/157.1-security-quality-violations-by-type.csv +374 -0
- package/.cursor/rules/assistant-driven-integration-build.mdc +40 -0
- package/integration/hubspot-test/create-hubspot.js +1 -16
- package/jest.isolated-projects.js +77 -1
- package/jest.projects.js +5 -426
- package/lib/api/assistants.api.js +67 -0
- package/lib/api/datasources-extended.api.js +6 -6
- package/lib/api/evidence-authoring-helpers.js +267 -0
- package/lib/api/evidence-authoring.api.js +337 -0
- package/lib/api/evidence-kinds.api.js +128 -0
- package/lib/api/evidence.api.js +57 -1
- package/lib/api/index.js +24 -7
- package/lib/api/pipeline.api.js +47 -3
- package/lib/api/records-bulk.api.js +28 -3
- package/lib/api/resource-types.api.js +4 -4
- package/lib/api/roles.api.js +121 -0
- package/lib/api/types/evidence-kinds.types.js +42 -0
- package/lib/api/types/evidence.types.js +156 -8
- package/lib/api/types/roles.types.js +36 -0
- package/lib/api/work.api.js +283 -0
- package/lib/app/certification-verify-rows.js +1 -1
- package/lib/app/run-deploy-job.js +108 -0
- package/lib/app/run-helpers.js +10 -3
- package/lib/app/run.js +8 -0
- package/lib/app/show.js +2 -2
- package/lib/certification/merge-certification-from-artifact.js +1 -1
- package/lib/channels/add-channel.js +54 -23
- package/lib/channels/approval-guides/chatgpt.js +8 -7
- package/lib/channels/approval-guides/microsoft-copilot.js +7 -8
- package/lib/channels/channel-artifact-generator.js +16 -3
- package/lib/channels/channel-credential.js +3 -1
- package/lib/channels/channel-probe-evidence.js +79 -0
- package/lib/channels/channel-probe.js +265 -13
- package/lib/channels/channel-runtime-resolver.js +120 -29
- package/lib/channels/channel-status-display.js +44 -0
- package/lib/channels/channel-targets.js +7 -0
- package/lib/channels/chatgpt-runtime-contract.js +29 -0
- package/lib/channels/microsoft-copilot-package.js +280 -0
- package/lib/channels/runtime-tool-catalog.js +165 -0
- package/lib/cli/index.js +6 -0
- package/lib/cli/setup-app.js +26 -5
- package/lib/cli/setup-dev.js +23 -7
- package/lib/cli/setup-external-system-help.js +8 -2
- package/lib/cli/setup-external-system.js +12 -0
- package/lib/cli/setup-utility-help-after.js +12 -0
- package/lib/cli/setup-utility-validate.js +41 -2
- package/lib/commands/app-test.js +2 -14
- package/lib/commands/datasource-load-export-cli.js +6 -1
- package/lib/commands/deploy-gate-failure-details.js +23 -0
- package/lib/commands/deploy-gate-orchestrator.js +64 -18
- package/lib/commands/dev-cli-handlers.js +6 -3
- package/lib/commands/dev-down.js +2 -6
- package/lib/commands/dev-init.js +7 -109
- package/lib/commands/dev-refresh.js +197 -0
- package/lib/commands/evidence-kind.js +478 -0
- package/lib/commands/governance-command-action.js +4 -2
- package/lib/commands/governance-generate-external.js +56 -21
- package/lib/commands/lifecycle-external.js +10 -4
- package/lib/commands/repair-datasource-apply.js +10 -1
- package/lib/commands/repair-datasource-run.js +4 -2
- package/lib/commands/repair-datasource-test-payload.js +80 -19
- package/lib/commands/repair-datasource-webhooks-sync-bridge.js +42 -3
- package/lib/commands/repair-internal.js +9 -1
- package/lib/commands/repair-openapi-export.js +1 -1
- package/lib/commands/repair-openapi-sync.js +43 -18
- package/lib/commands/resource-type.js +101 -18
- package/lib/commands/role-assistant.js +313 -0
- package/lib/commands/role.js +452 -0
- package/lib/commands/setup.js +1 -1
- package/lib/commands/upload-data-sync-finalize.js +5 -4
- package/lib/commands/upload-sidecar-followups.js +144 -47
- package/lib/commands/upload.js +43 -6
- package/lib/commands/validate-json.js +230 -0
- package/lib/commands/verify-governance-combined-external.js +73 -9
- package/lib/commands/verify-governance-db.js +80 -25
- package/lib/commands/verify-governance-external.js +29 -5
- package/lib/commands/verify-operations-step-plan.js +78 -40
- package/lib/commands/wizard-core-helpers.js +7 -3
- package/lib/commands/wizard-core.js +78 -9
- package/lib/core/audit-logger.js +1 -1
- package/lib/core/secrets-load.js +1 -3
- package/lib/datasource/bulk-loader-run-batches.js +4 -2
- package/lib/datasource/bulk-loader-service.js +5 -1
- package/lib/datasource/capability/relate-operations.js +1 -1
- package/lib/datasource/datasource-validate-summary.js +2 -2
- package/lib/datasource/validate.js +13 -6
- package/lib/external-system/deploy-helpers.js +35 -1
- package/lib/external-system/deploy.js +23 -10
- package/lib/external-system/download-helpers.js +111 -2
- package/lib/external-system/download-role-assistant.js +55 -0
- package/lib/external-system/download.js +45 -26
- package/lib/external-system/test-helpers.js +1 -1
- package/lib/external-system/test.js +39 -3
- package/lib/generator/builders.js +3 -0
- package/lib/generator/helpers.js +13 -5
- package/lib/generator/split-variables.js +9 -3
- package/lib/generator/wizard.js +51 -4
- package/lib/infrastructure/helpers-docker-check.js +6 -0
- package/lib/infrastructure/helpers.js +1 -1
- package/lib/lifecycle/access-control-abac.js +121 -0
- package/lib/lifecycle/lifecycle-governance-options.js +4 -1
- package/lib/lifecycle/product-model.js +3 -2
- package/lib/lifecycle/report-display.js +14 -2
- package/lib/programmatic/auth-config.js +3 -2
- package/lib/programmatic/bearer-auth.js +1 -1
- package/lib/programmatic/capability-registry.js +20 -1
- package/lib/programmatic/command-options.js +1 -0
- package/lib/programmatic/enterprise-mcp-client.js +18 -2
- package/lib/programmatic/index.js +4 -0
- package/lib/programmatic/manifest-patch-section.js +48 -9
- package/lib/programmatic/manifest-section-registry-data.js +3 -2
- package/lib/programmatic/manifest-section-registry.js +18 -1
- package/lib/programmatic/openapi-apply-result-schemas.js +44 -0
- package/lib/programmatic/openapi-component-schemas.js +16 -1
- package/lib/programmatic/openapi-description-enricher.js +3 -1
- package/lib/programmatic/openapi-descriptions.js +55 -5
- package/lib/programmatic/openapi-evidence-schemas.js +161 -0
- package/lib/programmatic/openapi-manifest-schemas.js +6 -2
- package/lib/programmatic/openapi-operation-bindings.js +99 -0
- package/lib/programmatic/openapi-request-schemas.js +36 -1
- package/lib/programmatic/openapi-result-schemas.js +29 -12
- package/lib/programmatic/openapi-schema-descriptions-certify.js +5 -1
- package/lib/programmatic/openapi-schema-descriptions-core.js +6 -3
- package/lib/programmatic/openapi-schema-descriptions-evidence.js +156 -0
- package/lib/programmatic/openapi-schema-descriptions-manifest.js +4 -3
- package/lib/programmatic/openapi-schema-descriptions-operations.js +70 -0
- package/lib/programmatic/openapi-schema-helpers.js +16 -1
- package/lib/programmatic/openapi-spec.js +48 -29
- package/lib/programmatic/orchestration-openapi-meta.js +5 -0
- package/lib/programmatic/request-timeout.js +3 -0
- package/lib/programmatic/route-handler-map.js +89 -2
- package/lib/programmatic/run-certify.js +15 -2
- package/lib/programmatic/run-deploy.js +74 -0
- package/lib/programmatic/run-evidence-authoring.js +80 -0
- package/lib/programmatic/run-protection.js +124 -0
- package/lib/programmatic/run-repository-emcp.js +11 -3
- package/lib/programmatic/run-template-workspace.js +42 -2
- package/lib/programmatic/workspace-context.js +6 -0
- package/lib/protection/auth-context.js +48 -6
- package/lib/protection/paths.js +22 -0
- package/lib/protection/run-protection-create-helpers.js +22 -1
- package/lib/protection/run-protection-create.js +1 -1
- package/lib/protection/sync-after-upload.js +1 -1
- package/lib/resolvers/evidence-kind-file.js +230 -0
- package/lib/resolvers/resource-type-file.js +36 -1
- package/lib/resolvers/role-file.js +227 -0
- package/lib/role-assistant/evidence-sync.js +450 -0
- package/lib/role-assistant/github-workflow.js +96 -0
- package/lib/role-assistant/knowledge-sync.js +465 -0
- package/lib/role-assistant/settings.js +126 -0
- package/lib/role-assistant/test-cases-evaluate.js +170 -0
- package/lib/role-assistant/test-cases-honesty.js +184 -0
- package/lib/role-assistant/test-cases.js +364 -0
- package/lib/role-assistant/test-runner-workhub-answers.js +203 -0
- package/lib/role-assistant/test-runner-workhub.js +385 -0
- package/lib/role-assistant/test-runner.js +500 -0
- package/lib/schema/application-schema.json +36 -2
- package/lib/schema/deployment-rules.yaml +1 -0
- package/lib/schema/external-datasource.schema.json +121 -4
- package/lib/schema/external-system.schema.json +20 -6
- package/lib/schema/infra.parameter.yaml +45 -1
- package/lib/schema/role-assistant-settings.schema.json +108 -0
- package/lib/schema/type/credential-field-registry.json +19 -3
- package/lib/utils/api.js +52 -21
- package/lib/utils/app-path-resolve.js +72 -24
- package/lib/utils/app-register-config.js +4 -3
- package/lib/utils/authentication-shape.js +3 -1
- package/lib/utils/cli-sync-options.js +59 -3
- package/lib/utils/cli-utils.js +11 -3
- package/lib/utils/deployment-validation.js +28 -3
- package/lib/utils/dev-ca-install.js +5 -6
- package/lib/utils/dev-config.js +2 -2
- package/lib/utils/dev-list-sort.js +145 -0
- package/lib/utils/dev-platform-tls.js +0 -63
- package/lib/utils/docker-build.js +46 -27
- package/lib/utils/docker-daemon-tls-ca.js +1 -1
- package/lib/utils/docker-exec.js +91 -6
- package/lib/utils/docker-run-ephemeral.js +55 -0
- package/lib/utils/enterprise-knowledge-paths.js +29 -12
- package/lib/utils/env-copy-keycloak.js +196 -0
- package/lib/utils/env-copy.js +35 -4
- package/lib/utils/error-formatters/error-parser.js +0 -3
- package/lib/utils/external-env-template.js +1 -1
- package/lib/utils/external-system-readiness-compact-blocks.js +28 -1
- package/lib/utils/external-system-readiness-compact-display.js +3 -0
- package/lib/utils/health-check-request.js +2 -3
- package/lib/utils/help-builder.js +4 -0
- package/lib/utils/image-name.js +22 -4
- package/lib/utils/logger.js +37 -4
- package/lib/utils/manifest-source-emit.js +1 -1
- package/lib/utils/paths.js +98 -26
- package/lib/utils/pipeline-upload-auth-retry.js +19 -3
- package/lib/utils/postgres-platform-bootstrap.js +24 -17
- package/lib/utils/postgres-wipe.js +5 -2
- package/lib/utils/remote-dev-auth.js +39 -4
- package/lib/utils/resolve-docker-image-ref.js +36 -6
- package/lib/utils/role-assistant-paths.js +98 -0
- package/lib/utils/schema-loader.js +41 -0
- package/lib/utils/schema-resolver.js +36 -4
- package/lib/utils/token-manager.js +47 -22
- package/lib/utils/upload-sync-options.js +17 -3
- package/lib/utils/url-declarative-resolve-load-doc.js +38 -1
- package/lib/validation/external-manifest-validator.js +31 -8
- package/lib/validation/openapi-contract-surface-validator.js +4 -0
- package/lib/validation/validate-external-file.js +8 -1
- package/lib/validation/validator.js +6 -5
- package/package.json +35 -12
- package/scripts/export-builder-api-evidence.js +3 -3
- package/scripts/lib/plan-actor.js +2 -2
- package/scripts/lib/plan-meta.js +1 -1
- package/scripts/lib/plan-parse.js +1 -1
- package/scripts/lib/plan-prevalidate-gates.js +12 -1
- package/scripts/lib/plan-run-report.js +13 -2
- package/scripts/pnpm/lib/resolve-aifabrix-bin.mjs +10 -10
- package/templates/applications/dataplane/application.yaml +47 -17
- package/templates/applications/dataplane/env.template +28 -9
- package/templates/applications/dataplane/rbac.yaml +78 -2
- package/templates/applications/miso-controller/application.yaml +10 -1
- package/templates/applications/miso-controller/env.template +13 -8
- package/templates/applications/miso-controller/rbac.yaml +72 -0
- package/templates/channels/chatgpt/install.template.md.hbs +42 -24
- package/templates/channels/chatgpt/integration/README.md +10 -7
- package/templates/channels/chatgpt/integration/application.json +2 -2
- package/templates/channels/chatgpt/integration/chatgpt-system.json +3 -3
- package/templates/channels/chatgpt/integration/env.template +1 -1
- package/templates/channels/chatgpt/manifest.template.json.hbs +36 -5
- package/templates/channels/microsoft-copilot/ai-plugin.template.json.hbs +28 -0
- package/templates/channels/microsoft-copilot/declarative-agent.template.json.hbs +22 -0
- package/templates/channels/microsoft-copilot/install.template.md.hbs +32 -22
- package/templates/channels/microsoft-copilot/integration/README.md +9 -7
- package/templates/channels/microsoft-copilot/integration/application.json +2 -2
- package/templates/channels/microsoft-copilot/integration/env.template +1 -1
- package/templates/channels/microsoft-copilot/integration/microsoft-copilot-system.json +7 -6
- package/templates/channels/microsoft-copilot/manifest.template.json.hbs +34 -6
- package/templates/channels/slack/integration/slack-system.json +60 -2
- package/templates/python/requirements.txt +1 -1
|
@@ -110,25 +110,15 @@ function buildDockerCliArgs(imageName, tag, dockerfilePath, contextPath, buildAr
|
|
|
110
110
|
return args;
|
|
111
111
|
}
|
|
112
112
|
|
|
113
|
-
function
|
|
114
|
-
const {
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
env = {},
|
|
123
|
-
buildArgs = {},
|
|
124
|
-
noCache = false
|
|
125
|
-
} = buildOpts;
|
|
126
|
-
const spawnEnv = { ...process.env, ...env };
|
|
127
|
-
const args = buildDockerCliArgs(imageName, tag, dockerfilePath, contextPath, buildArgs, noCache);
|
|
128
|
-
const dockerProcess = spawn('docker', args, {
|
|
129
|
-
shell: process.platform === 'win32',
|
|
130
|
-
env: spawnEnv
|
|
131
|
-
});
|
|
113
|
+
function createDockerSpawnParams(options) {
|
|
114
|
+
const { imageName, tag, dockerfilePath, contextPath, buildArgs, noCache, env } = options;
|
|
115
|
+
const spawnEnv = { ...process.env, ...(env || {}) };
|
|
116
|
+
const args = buildDockerCliArgs(imageName, tag, dockerfilePath, contextPath, buildArgs || {}, noCache);
|
|
117
|
+
return { spawnEnv, args };
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
function attachDockerBuildListeners(dockerProcess, context) {
|
|
121
|
+
const { imageName, tag, spinner, resolve, reject } = context;
|
|
132
122
|
let stdoutBuffer = '';
|
|
133
123
|
let stderrBuffer = '';
|
|
134
124
|
const lastProgressUpdateRef = { current: Date.now() };
|
|
@@ -162,6 +152,35 @@ function runDockerBuildProcess(buildOpts) {
|
|
|
162
152
|
});
|
|
163
153
|
}
|
|
164
154
|
|
|
155
|
+
function runDockerBuildProcess(buildOpts) {
|
|
156
|
+
const {
|
|
157
|
+
imageName,
|
|
158
|
+
tag,
|
|
159
|
+
dockerfilePath,
|
|
160
|
+
contextPath,
|
|
161
|
+
spinner,
|
|
162
|
+
resolve,
|
|
163
|
+
reject,
|
|
164
|
+
env = {},
|
|
165
|
+
buildArgs = {},
|
|
166
|
+
noCache = false
|
|
167
|
+
} = buildOpts;
|
|
168
|
+
const { spawnEnv, args } = createDockerSpawnParams({
|
|
169
|
+
imageName,
|
|
170
|
+
tag,
|
|
171
|
+
dockerfilePath,
|
|
172
|
+
contextPath,
|
|
173
|
+
buildArgs,
|
|
174
|
+
noCache,
|
|
175
|
+
env
|
|
176
|
+
});
|
|
177
|
+
const dockerProcess = spawn('docker', args, {
|
|
178
|
+
shell: process.platform === 'win32',
|
|
179
|
+
env: spawnEnv
|
|
180
|
+
});
|
|
181
|
+
attachDockerBuildListeners(dockerProcess, { imageName, tag, spinner, resolve, reject });
|
|
182
|
+
}
|
|
183
|
+
|
|
165
184
|
/**
|
|
166
185
|
* Executes Docker build command with progress indicator
|
|
167
186
|
* @param {string} imageName - Image name to build
|
|
@@ -236,12 +255,10 @@ async function executeBuild(imageName, dockerfilePath, contextPath, tag, options
|
|
|
236
255
|
|
|
237
256
|
// Tag image if additional tag provided
|
|
238
257
|
if (options && options.tag && options.tag !== 'latest') {
|
|
239
|
-
const {
|
|
240
|
-
const { exec } = require('child_process');
|
|
241
|
-
const run = promisify(exec);
|
|
258
|
+
const { execFileWithDockerEnv } = require('./docker-exec');
|
|
242
259
|
const { getDockerExecEnv } = require('./remote-docker-env');
|
|
243
260
|
const env = await getDockerExecEnv();
|
|
244
|
-
await
|
|
261
|
+
await execFileWithDockerEnv('docker', ['tag', `${imageName}:${tag}`, `${imageName}:latest`], { env });
|
|
245
262
|
}
|
|
246
263
|
}
|
|
247
264
|
|
|
@@ -270,12 +287,14 @@ async function executeDockerBuildWithTag(effectiveImageName, imageName, dockerfi
|
|
|
270
287
|
}
|
|
271
288
|
|
|
272
289
|
try {
|
|
273
|
-
const {
|
|
274
|
-
const { exec } = require('child_process');
|
|
275
|
-
const run = promisify(exec);
|
|
290
|
+
const { execFileWithDockerEnv } = require('./docker-exec');
|
|
276
291
|
const { getDockerExecEnv } = require('./remote-docker-env');
|
|
277
292
|
const env = await getDockerExecEnv();
|
|
278
|
-
await
|
|
293
|
+
await execFileWithDockerEnv(
|
|
294
|
+
'docker',
|
|
295
|
+
['tag', `${effectiveImageName}:${tag}`, `${imageName}:${tag}`],
|
|
296
|
+
{ env }
|
|
297
|
+
);
|
|
279
298
|
logger.log(formatSuccessLine(`Tagged image: ${imageName}:${tag}`));
|
|
280
299
|
} catch (err) {
|
|
281
300
|
logger.log(
|
package/lib/utils/docker-exec.js
CHANGED
|
@@ -9,9 +9,85 @@
|
|
|
9
9
|
'use strict';
|
|
10
10
|
|
|
11
11
|
const { promisify } = require('util');
|
|
12
|
-
const
|
|
12
|
+
const childProcess = require('child_process');
|
|
13
|
+
const execFileAsync = childProcess.execFile ? promisify(childProcess.execFile) : null;
|
|
13
14
|
|
|
14
|
-
|
|
15
|
+
/**
|
|
16
|
+
* Parse a command string into argv tokens without invoking a shell.
|
|
17
|
+
* Supports simple single/double quoted segments and escaping quotes within double quotes.
|
|
18
|
+
* @param {string} command
|
|
19
|
+
* @returns {string[]}
|
|
20
|
+
*/
|
|
21
|
+
// eslint-disable-next-line max-statements, complexity -- tokenizer intentionally handles quoted token edge-cases in one place
|
|
22
|
+
function parseCommandTokens(command) {
|
|
23
|
+
const input = String(command || '');
|
|
24
|
+
if (!input.trim()) {
|
|
25
|
+
throw new Error('Command must be a non-empty string');
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/** @type {string[]} */
|
|
29
|
+
const tokens = [];
|
|
30
|
+
let current = '';
|
|
31
|
+
let quote = null;
|
|
32
|
+
|
|
33
|
+
for (let i = 0; i < input.length; i += 1) {
|
|
34
|
+
const ch = input[i];
|
|
35
|
+
if (quote === null && (ch === '"' || ch === '\'')) {
|
|
36
|
+
quote = ch;
|
|
37
|
+
continue;
|
|
38
|
+
}
|
|
39
|
+
if (quote !== null && ch === quote) {
|
|
40
|
+
quote = null;
|
|
41
|
+
continue;
|
|
42
|
+
}
|
|
43
|
+
// Bash double-quote escapes: \", \\, \$, \`, and escaped newlines.
|
|
44
|
+
if (quote === '"' && ch === '\\') {
|
|
45
|
+
const next = input[i + 1];
|
|
46
|
+
if (next === '"' || next === '\\' || next === '$' || next === '`' || next === '\n') {
|
|
47
|
+
current += next;
|
|
48
|
+
i += 1;
|
|
49
|
+
continue;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
if (quote === null && /\s/.test(ch)) {
|
|
53
|
+
if (current) {
|
|
54
|
+
tokens.push(current);
|
|
55
|
+
current = '';
|
|
56
|
+
}
|
|
57
|
+
continue;
|
|
58
|
+
}
|
|
59
|
+
current += ch;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
if (quote !== null) {
|
|
63
|
+
throw new Error('Unterminated quote in command');
|
|
64
|
+
}
|
|
65
|
+
if (current) {
|
|
66
|
+
tokens.push(current);
|
|
67
|
+
}
|
|
68
|
+
if (!tokens.length) {
|
|
69
|
+
throw new Error('Command produced no executable token');
|
|
70
|
+
}
|
|
71
|
+
return tokens;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Run an executable with argv and remote Docker env settings.
|
|
76
|
+
* @param {string} file
|
|
77
|
+
* @param {string[]} [args]
|
|
78
|
+
* @param {import('child_process').ExecFileOptions} [options]
|
|
79
|
+
* @returns {Promise<{ stdout: string, stderr: string }>}
|
|
80
|
+
*/
|
|
81
|
+
async function execFileWithDockerEnv(file, args = [], options = {}) {
|
|
82
|
+
const { getDockerExecEnv } = require('./remote-docker-env');
|
|
83
|
+
const env = { ...(await getDockerExecEnv()), ...(options.env || {}) };
|
|
84
|
+
if (execFileAsync) {
|
|
85
|
+
return execFileAsync(file, args, { ...options, env });
|
|
86
|
+
}
|
|
87
|
+
const execAsync = promisify(childProcess.exec);
|
|
88
|
+
const command = [file, ...args.map((arg) => String(arg))].join(' ');
|
|
89
|
+
return execAsync(command, { ...options, env });
|
|
90
|
+
}
|
|
15
91
|
|
|
16
92
|
/**
|
|
17
93
|
* Run a shell command with process.env merged with remote Docker client settings when docker-endpoint is set.
|
|
@@ -20,9 +96,18 @@ const execAsync = promisify(exec);
|
|
|
20
96
|
* @returns {Promise<{ stdout: string, stderr: string }>}
|
|
21
97
|
*/
|
|
22
98
|
async function execWithDockerEnv(command, options = {}) {
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
99
|
+
if (!execFileAsync || process.env.NODE_ENV === 'test' || process.env.JEST_WORKER_ID !== undefined) {
|
|
100
|
+
const { getDockerExecEnv } = require('./remote-docker-env');
|
|
101
|
+
const env = { ...(await getDockerExecEnv()), ...(options.env || {}) };
|
|
102
|
+
const execAsync = promisify(childProcess.exec);
|
|
103
|
+
return execAsync(command, { ...options, env });
|
|
104
|
+
}
|
|
105
|
+
const [file, ...args] = parseCommandTokens(command);
|
|
106
|
+
return execFileWithDockerEnv(file, args, options);
|
|
26
107
|
}
|
|
27
108
|
|
|
28
|
-
module.exports = {
|
|
109
|
+
module.exports = {
|
|
110
|
+
execWithDockerEnv,
|
|
111
|
+
execFileWithDockerEnv,
|
|
112
|
+
parseCommandTokens
|
|
113
|
+
};
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Ephemeral docker run (--rm) for one-shot commands (test, install, deployJob).
|
|
3
|
+
*
|
|
4
|
+
* @fileoverview Shared docker run --rm helper
|
|
5
|
+
* @author AI Fabrix Team
|
|
6
|
+
* @version 2.0.0
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
const { spawn } = require('child_process');
|
|
10
|
+
const { getDockerExecEnv } = require('./remote-docker-env');
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Run a shell command in an ephemeral container; optional --env-file / --network / bind mounts.
|
|
14
|
+
* Attach to the developer infra bridge when the command needs compose DNS
|
|
15
|
+
* (e.g. hostname `postgres` in DATABASE_URL).
|
|
16
|
+
* @param {string} fullImage - Image:tag
|
|
17
|
+
* @param {string} cmd - Shell command (passed to sh -c)
|
|
18
|
+
* @param {string|null} [envFilePath] - Path for docker run --env-file
|
|
19
|
+
* @param {{ network?: string|null, volumeMounts?: Array<{ host: string, container: string }> }} [options]
|
|
20
|
+
* @returns {Promise<number>} Exit code
|
|
21
|
+
*/
|
|
22
|
+
async function runDockerRunEphemeral(fullImage, cmd, envFilePath = null, options = {}) {
|
|
23
|
+
const dockerEnv = await getDockerExecEnv();
|
|
24
|
+
const network =
|
|
25
|
+
options && typeof options.network === 'string' && options.network.trim()
|
|
26
|
+
? options.network.trim()
|
|
27
|
+
: null;
|
|
28
|
+
const volumeMounts = Array.isArray(options?.volumeMounts) ? options.volumeMounts : [];
|
|
29
|
+
return new Promise((resolve) => {
|
|
30
|
+
const args = ['run', '--rm'];
|
|
31
|
+
if (network) {
|
|
32
|
+
args.push('--network', network);
|
|
33
|
+
}
|
|
34
|
+
if (envFilePath) {
|
|
35
|
+
args.push('--env-file', envFilePath);
|
|
36
|
+
}
|
|
37
|
+
for (const mount of volumeMounts) {
|
|
38
|
+
const host = typeof mount?.host === 'string' ? mount.host.trim() : '';
|
|
39
|
+
const container = typeof mount?.container === 'string' ? mount.container.trim() : '';
|
|
40
|
+
if (host && container) {
|
|
41
|
+
args.push('-v', `${host}:${container}`);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
args.push(fullImage, 'sh', '-c', cmd);
|
|
45
|
+
const proc = spawn('docker', args, {
|
|
46
|
+
stdio: 'inherit',
|
|
47
|
+
shell: false,
|
|
48
|
+
env: dockerEnv
|
|
49
|
+
});
|
|
50
|
+
proc.on('close', (code) => resolve(code !== null ? code : 1));
|
|
51
|
+
proc.on('error', () => resolve(1));
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
module.exports = { runDockerRunEphemeral };
|
|
@@ -62,8 +62,22 @@ function getEnterpriseKnowledgeRoot() {
|
|
|
62
62
|
return path.join(getMaterializationParent(), WORKSPACE_ENTERPRISE_KNOWLEDGE);
|
|
63
63
|
}
|
|
64
64
|
|
|
65
|
+
function isExistingDirectory(disk, dirPath) {
|
|
66
|
+
try {
|
|
67
|
+
if (!dirPath || !disk.existsSync(dirPath)) {
|
|
68
|
+
return false;
|
|
69
|
+
}
|
|
70
|
+
const st = disk.statSync(dirPath);
|
|
71
|
+
return Boolean(st && typeof st.isDirectory === 'function' && st.isDirectory());
|
|
72
|
+
} catch {
|
|
73
|
+
return false;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
65
77
|
/**
|
|
66
|
-
* Resolves enterprise-knowledge/<anchorKey
|
|
78
|
+
* Resolves enterprise-knowledge/<anchorKey>.
|
|
79
|
+
* Prefer an existing cwd or materialization folder; when `cwd/enterprise-knowledge/` exists and
|
|
80
|
+
* neither copy exists yet, return the cwd path (download/create target).
|
|
67
81
|
* @param {string} anchorKey - Enterprise Knowledge anchor key
|
|
68
82
|
* @returns {string}
|
|
69
83
|
*/
|
|
@@ -71,19 +85,22 @@ function getEnterpriseKnowledgePath(anchorKey) {
|
|
|
71
85
|
if (!anchorKey || typeof anchorKey !== 'string') {
|
|
72
86
|
throw new Error('Anchor key is required and must be a string');
|
|
73
87
|
}
|
|
74
|
-
const
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
}
|
|
88
|
+
const disk = nodeFs();
|
|
89
|
+
const cwdRoot = getCwdEnterpriseKnowledgeRoot();
|
|
90
|
+
const matApp = path.join(getMaterializationParent(), WORKSPACE_ENTERPRISE_KNOWLEDGE, anchorKey);
|
|
91
|
+
if (cwdRoot) {
|
|
92
|
+
const cwdApp = path.join(cwdRoot, anchorKey);
|
|
93
|
+
if (isExistingDirectory(disk, cwdApp)) {
|
|
94
|
+
return cwdApp;
|
|
82
95
|
}
|
|
83
|
-
} catch {
|
|
84
|
-
// ignore
|
|
85
96
|
}
|
|
86
|
-
|
|
97
|
+
if (isExistingDirectory(disk, matApp)) {
|
|
98
|
+
return matApp;
|
|
99
|
+
}
|
|
100
|
+
if (cwdRoot) {
|
|
101
|
+
return path.join(cwdRoot, anchorKey);
|
|
102
|
+
}
|
|
103
|
+
return matApp;
|
|
87
104
|
}
|
|
88
105
|
|
|
89
106
|
function isEnterpriseKnowledgeDirectory(disk, fullPath) {
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Host .env.local Keycloak loopback URL helpers for env-copy.
|
|
3
|
+
*
|
|
4
|
+
* @fileoverview Resolve developer-scoped Keycloak ports for host-side .env.local
|
|
5
|
+
* @author AI Fabrix Team
|
|
6
|
+
* @version 1.0.0
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
'use strict';
|
|
10
|
+
|
|
11
|
+
const fs = require('fs');
|
|
12
|
+
const path = require('path');
|
|
13
|
+
const yaml = require('js-yaml');
|
|
14
|
+
const config = require('../core/config');
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Read unquoted env value from content.
|
|
18
|
+
* @param {string} content
|
|
19
|
+
* @param {string} key
|
|
20
|
+
* @returns {string}
|
|
21
|
+
*/
|
|
22
|
+
function readEnvLineValue(content, key) {
|
|
23
|
+
const re = new RegExp(`^${key}\\s*=\\s*(.*)$`, 'm');
|
|
24
|
+
const m = content.match(re);
|
|
25
|
+
if (!m) return '';
|
|
26
|
+
return m[1].trim().replace(/^["']|["']$/g, '');
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* @param {Object} cfgModule - Config module with CONFIG_FILE
|
|
31
|
+
* @returns {number|null}
|
|
32
|
+
*/
|
|
33
|
+
function readDeveloperIdFromConfig(cfgModule) {
|
|
34
|
+
const configPath = cfgModule && cfgModule.CONFIG_FILE ? cfgModule.CONFIG_FILE : null;
|
|
35
|
+
if (!configPath || !fs.existsSync(configPath)) {
|
|
36
|
+
return null;
|
|
37
|
+
}
|
|
38
|
+
try {
|
|
39
|
+
const cfgContent = fs.readFileSync(configPath, 'utf8');
|
|
40
|
+
const cfg = yaml.load(cfgContent) || {};
|
|
41
|
+
const raw = cfg['developer-id'];
|
|
42
|
+
if (typeof raw === 'number') {
|
|
43
|
+
return raw;
|
|
44
|
+
}
|
|
45
|
+
if (typeof raw === 'string' && /^[0-9]+$/.test(raw)) {
|
|
46
|
+
return parseInt(raw, 10);
|
|
47
|
+
}
|
|
48
|
+
} catch {
|
|
49
|
+
// ignore
|
|
50
|
+
}
|
|
51
|
+
return null;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Base Keycloak published port from urls.local.yaml (before developer offset).
|
|
56
|
+
* @returns {number|null}
|
|
57
|
+
*/
|
|
58
|
+
function readKeycloakBasePortFromUrlsLocal() {
|
|
59
|
+
try {
|
|
60
|
+
const urlsPath = path.join(path.dirname(config.CONFIG_FILE || ''), 'urls.local.yaml');
|
|
61
|
+
if (!fs.existsSync(urlsPath)) return null;
|
|
62
|
+
const doc = yaml.load(fs.readFileSync(urlsPath, 'utf8')) || {};
|
|
63
|
+
const raw = doc['keycloak-port'];
|
|
64
|
+
const n = typeof raw === 'number' ? raw : parseInt(String(raw || ''), 10);
|
|
65
|
+
return Number.isFinite(n) && n > 0 ? n : null;
|
|
66
|
+
} catch {
|
|
67
|
+
return null;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* @param {string} host
|
|
73
|
+
* @returns {string|null} Normalized loopback host or null when not loopback
|
|
74
|
+
*/
|
|
75
|
+
function normalizeKeycloakLoopbackHost(host) {
|
|
76
|
+
let next = host;
|
|
77
|
+
if (next === 'keycloak') {
|
|
78
|
+
next = 'localhost';
|
|
79
|
+
}
|
|
80
|
+
const isLoopback =
|
|
81
|
+
next === 'localhost' || next === '127.0.0.1' || next === '::1' || next === '[::1]';
|
|
82
|
+
if (!isLoopback) {
|
|
83
|
+
return null;
|
|
84
|
+
}
|
|
85
|
+
return next === '::1' ? '[::1]' : next;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* @param {string} content
|
|
90
|
+
* @returns {number|null}
|
|
91
|
+
*/
|
|
92
|
+
function resolveExplicitKeycloakPort(content) {
|
|
93
|
+
const publicPortRaw = readEnvLineValue(content, 'KEYCLOAK_PUBLIC_PORT');
|
|
94
|
+
if (/^\d+$/.test(publicPortRaw)) {
|
|
95
|
+
return parseInt(publicPortRaw, 10);
|
|
96
|
+
}
|
|
97
|
+
const portRaw = readEnvLineValue(content, 'KEYCLOAK_PORT');
|
|
98
|
+
if (!/^\d+$/.test(portRaw)) {
|
|
99
|
+
return null;
|
|
100
|
+
}
|
|
101
|
+
const portNum = parseInt(portRaw, 10);
|
|
102
|
+
// 8080 is the in-container listen port — not the host publish port
|
|
103
|
+
return portNum !== 8080 ? portNum : null;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* @returns {number|null}
|
|
108
|
+
*/
|
|
109
|
+
function resolveDeveloperIdForKeycloakPort() {
|
|
110
|
+
try {
|
|
111
|
+
const raw = process.env.AIFABRIX_DEVELOPERID;
|
|
112
|
+
if (raw && /^[0-9]+$/.test(String(raw).trim())) {
|
|
113
|
+
return parseInt(String(raw).trim(), 10);
|
|
114
|
+
}
|
|
115
|
+
return readDeveloperIdFromConfig(config);
|
|
116
|
+
} catch {
|
|
117
|
+
return null;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Published Keycloak host port for this developer (never a fixed 8182).
|
|
123
|
+
* Prefer KEYCLOAK_PUBLIC_PORT, then loopback KEYCLOAK_PORT (if not container 8080),
|
|
124
|
+
* else base from urls.local.yaml / 8082 + developer-id × 100.
|
|
125
|
+
*
|
|
126
|
+
* @param {string} content - Env file content
|
|
127
|
+
* @returns {{ host: string, port: number }|null}
|
|
128
|
+
*/
|
|
129
|
+
function resolveHostKeycloakLoopbackEndpoint(content) {
|
|
130
|
+
const { publishedHostPort, parseDeveloperIdNum } = require('./declarative-url-ports');
|
|
131
|
+
const host = normalizeKeycloakLoopbackHost(
|
|
132
|
+
readEnvLineValue(content, 'KEYCLOAK_HOST') || 'localhost'
|
|
133
|
+
);
|
|
134
|
+
if (!host) {
|
|
135
|
+
return null;
|
|
136
|
+
}
|
|
137
|
+
const explicit = resolveExplicitKeycloakPort(content);
|
|
138
|
+
if (explicit !== null) {
|
|
139
|
+
return { host, port: explicit };
|
|
140
|
+
}
|
|
141
|
+
const base = readKeycloakBasePortFromUrlsLocal() || 8082;
|
|
142
|
+
const port = publishedHostPort(base, parseDeveloperIdNum(resolveDeveloperIdForKeycloakPort()));
|
|
143
|
+
return { host, port };
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Upsert KEYCLOAK_PORT and KEYCLOAK_INTERNAL_SERVER_URL for host .env.local.
|
|
148
|
+
* @param {string} content
|
|
149
|
+
* @param {string} host
|
|
150
|
+
* @param {number} port
|
|
151
|
+
* @returns {string}
|
|
152
|
+
*/
|
|
153
|
+
function patchKeycloakHostInternalLines(content, host, port) {
|
|
154
|
+
const internalUrl = `http://${host}:${port}/auth`;
|
|
155
|
+
let next = content;
|
|
156
|
+
if (readEnvLineValue(content, 'KEYCLOAK_HOST') === 'keycloak') {
|
|
157
|
+
next = next.replace(/^KEYCLOAK_HOST\s*=.*$/m, 'KEYCLOAK_HOST=localhost');
|
|
158
|
+
}
|
|
159
|
+
if (/^KEYCLOAK_PORT\s*=/m.test(next)) {
|
|
160
|
+
next = next.replace(/^KEYCLOAK_PORT\s*=.*$/m, `KEYCLOAK_PORT=${port}`);
|
|
161
|
+
} else {
|
|
162
|
+
next = `${next.trimEnd()}\nKEYCLOAK_PORT=${port}\n`;
|
|
163
|
+
}
|
|
164
|
+
if (/^KEYCLOAK_INTERNAL_SERVER_URL\s*=/m.test(next)) {
|
|
165
|
+
return next.replace(
|
|
166
|
+
/^KEYCLOAK_INTERNAL_SERVER_URL\s*=.*$/m,
|
|
167
|
+
`KEYCLOAK_INTERNAL_SERVER_URL=${internalUrl}`
|
|
168
|
+
);
|
|
169
|
+
}
|
|
170
|
+
return `${next.trimEnd()}\nKEYCLOAK_INTERNAL_SERVER_URL=${internalUrl}\n`;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* For host `.env.local`, force Keycloak admin/internal base to this developer's
|
|
175
|
+
* published loopback port (`base + developerId × 100`). Never hardcodes a single
|
|
176
|
+
* developer’s port (e.g. 8182).
|
|
177
|
+
*
|
|
178
|
+
* @param {string} content - Env file content
|
|
179
|
+
* @returns {string}
|
|
180
|
+
*/
|
|
181
|
+
function applyHostLocalKeycloakInternalUrl(content) {
|
|
182
|
+
if (typeof content !== 'string' || content.length === 0) {
|
|
183
|
+
return content;
|
|
184
|
+
}
|
|
185
|
+
const endpoint = resolveHostKeycloakLoopbackEndpoint(content);
|
|
186
|
+
if (!endpoint) {
|
|
187
|
+
return content;
|
|
188
|
+
}
|
|
189
|
+
return patchKeycloakHostInternalLines(content, endpoint.host, endpoint.port);
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
module.exports = {
|
|
193
|
+
applyHostLocalKeycloakInternalUrl,
|
|
194
|
+
readEnvLineValue,
|
|
195
|
+
resolveHostKeycloakLoopbackEndpoint
|
|
196
|
+
};
|
package/lib/utils/env-copy.js
CHANGED
|
@@ -21,6 +21,7 @@ const { rewriteInfraEndpoints } = require('./env-endpoints');
|
|
|
21
21
|
const { buildEnvVarMap } = require('./env-map');
|
|
22
22
|
const { interpolateEnvVars } = require('./secrets-helpers');
|
|
23
23
|
const { getLocalPort } = require('./port-resolver');
|
|
24
|
+
const { applyHostLocalKeycloakInternalUrl } = require('./env-copy-keycloak');
|
|
24
25
|
|
|
25
26
|
/**
|
|
26
27
|
* Read developer ID from config file synchronously
|
|
@@ -98,6 +99,26 @@ function resolveEnvOutputPath(rawOutputPath, variablesPath) {
|
|
|
98
99
|
return outputPath;
|
|
99
100
|
}
|
|
100
101
|
|
|
102
|
+
/**
|
|
103
|
+
* Sibling host-only env path next to `build.envOutputPath` (`.env` → `.env.local`).
|
|
104
|
+
* Used for host CLI / manual tests; must not be mounted into the container.
|
|
105
|
+
*
|
|
106
|
+
* @param {string} envOutputPath - Resolved path ending in `.env`
|
|
107
|
+
* @returns {string} Path ending in `.env.local`
|
|
108
|
+
*/
|
|
109
|
+
function resolveEnvLocalOutputPath(envOutputPath) {
|
|
110
|
+
if (typeof envOutputPath !== 'string' || envOutputPath.trim() === '') {
|
|
111
|
+
throw new Error('envOutputPath is required to resolve .env.local');
|
|
112
|
+
}
|
|
113
|
+
if (envOutputPath.endsWith('.env.local')) {
|
|
114
|
+
return envOutputPath;
|
|
115
|
+
}
|
|
116
|
+
if (envOutputPath.endsWith('.env')) {
|
|
117
|
+
return `${envOutputPath}.local`;
|
|
118
|
+
}
|
|
119
|
+
return path.join(path.dirname(envOutputPath), '.env.local');
|
|
120
|
+
}
|
|
121
|
+
|
|
101
122
|
/**
|
|
102
123
|
* Writes .env to envOutputPath for `--reload`: merge container `.env.run` into the host file.
|
|
103
124
|
* Preserves comments when a file already exists (e.g. after `aifabrix resolve`). Keys present only
|
|
@@ -149,8 +170,18 @@ async function writeEnvOutputForLocal(appName, outputPath) {
|
|
|
149
170
|
const localMap = parseEnvContentToMap(localContent);
|
|
150
171
|
toWrite = mergeEnvMapIntoContent(existingContent, localMap);
|
|
151
172
|
}
|
|
173
|
+
const isLocalSibling = path.basename(outputPath) === '.env.local';
|
|
174
|
+
if (isLocalSibling) {
|
|
175
|
+
toWrite = applyHostLocalKeycloakInternalUrl(toWrite);
|
|
176
|
+
}
|
|
152
177
|
await fsp.writeFile(outputPath, toWrite, { mode: 0o600 });
|
|
153
|
-
logger.log(
|
|
178
|
+
logger.log(
|
|
179
|
+
formatSuccessLine(
|
|
180
|
+
isLocalSibling
|
|
181
|
+
? `Wrote host .env.local (localhost ports for CLI/tests): ${outputPath}`
|
|
182
|
+
: `Wrote .env to envOutputPath (localPort): ${outputPath}`
|
|
183
|
+
)
|
|
184
|
+
);
|
|
154
185
|
}
|
|
155
186
|
|
|
156
187
|
/**
|
|
@@ -385,9 +416,7 @@ async function processEnvVariables(envPath, variablesPath, appName, secretsPath,
|
|
|
385
416
|
}
|
|
386
417
|
|
|
387
418
|
const label = variables.build.envOutputPath;
|
|
388
|
-
const appPathForLocal =
|
|
389
|
-
(copyOptions && copyOptions.appPath) ||
|
|
390
|
-
(variablesPath ? path.dirname(variablesPath) : null);
|
|
419
|
+
const appPathForLocal = (copyOptions && copyOptions.appPath) || path.dirname(variablesPath);
|
|
391
420
|
const preferLocal =
|
|
392
421
|
copyOptions && typeof copyOptions === 'object' && copyOptions.preferLocalEnvOutputPath === true;
|
|
393
422
|
const freshEnv = copyOptions && typeof copyOptions === 'object' && copyOptions.freshEnv === true;
|
|
@@ -410,6 +439,8 @@ async function processEnvVariables(envPath, variablesPath, appName, secretsPath,
|
|
|
410
439
|
module.exports = {
|
|
411
440
|
processEnvVariables,
|
|
412
441
|
resolveEnvOutputPath,
|
|
442
|
+
resolveEnvLocalOutputPath,
|
|
443
|
+
applyHostLocalKeycloakInternalUrl,
|
|
413
444
|
substituteMntDataForLocal,
|
|
414
445
|
syncWrittenBuilderEnvToOutputPath,
|
|
415
446
|
writeEnvOutputForReload,
|
|
@@ -25,9 +25,6 @@ const { formatNetworkError } = require('./network-errors');
|
|
|
25
25
|
* @returns {Object} Parsed error data object
|
|
26
26
|
*/
|
|
27
27
|
function parseErrorData(errorResponse) {
|
|
28
|
-
if (errorResponse === undefined || errorResponse === null) {
|
|
29
|
-
return { message: 'Unknown error occurred' };
|
|
30
|
-
}
|
|
31
28
|
if (typeof errorResponse === 'string') {
|
|
32
29
|
try {
|
|
33
30
|
return JSON.parse(errorResponse);
|
|
@@ -142,7 +142,7 @@ function buildConfigurationEntries(system) {
|
|
|
142
142
|
function buildExternalEnvTemplateContext(system) {
|
|
143
143
|
const authBlock = system?.authentication || system?.auth;
|
|
144
144
|
const credential = getAuthCredentialTemplate(authBlock);
|
|
145
|
-
let authMethod
|
|
145
|
+
let authMethod;
|
|
146
146
|
if (authBlock?.authType === 'apiProvider') {
|
|
147
147
|
authMethod = 'apiprovider';
|
|
148
148
|
} else if (authBlock?.authType === 'none') {
|
|
@@ -215,7 +215,34 @@ function logDeployReadinessSummaryCompact(ctx) {
|
|
|
215
215
|
logCompactTip({ systemKey, command: 'deploy', probed: !!ctx.probeData });
|
|
216
216
|
}
|
|
217
217
|
|
|
218
|
+
/**
|
|
219
|
+
* Compact abort card when a deploy --gate pillar fails before controller deploy.
|
|
220
|
+
* @param {Object} ctx
|
|
221
|
+
*/
|
|
222
|
+
function logDeployGateAbortCompact(ctx) {
|
|
223
|
+
const systemKey = ctx.systemKey || 'system';
|
|
224
|
+
logCompactHeader({
|
|
225
|
+
command: 'deploy',
|
|
226
|
+
systemKey,
|
|
227
|
+
environment: ctx.environment || 'dev',
|
|
228
|
+
dataplaneUrl: ctx.dataplaneUrl
|
|
229
|
+
});
|
|
230
|
+
logger.log(` ${formatSuccessLine('Local validation')}`);
|
|
231
|
+
const pf = formatPreflightLine(ctx.baseline || { unitPassed: true, integrationPassed: true });
|
|
232
|
+
if (pf) logger.log(` ${pf}`);
|
|
233
|
+
for (const gate of ctx.gatesSoFar || []) {
|
|
234
|
+
logger.log(` ${formatGatePillarLine(gate)}`);
|
|
235
|
+
}
|
|
236
|
+
const pillar = ctx.failedPillar || 'operations';
|
|
237
|
+
const detail = ctx.failedDetail || 'gate failed';
|
|
238
|
+
const label = PILLAR_LABELS[pillar] || pillar;
|
|
239
|
+
logger.log(` ${formatBlockingError(`${label} · FAILED — ${detail}`)}`);
|
|
240
|
+
logger.log('');
|
|
241
|
+
logCompactTip({ systemKey, command: 'deploy', probed: false });
|
|
242
|
+
}
|
|
243
|
+
|
|
218
244
|
module.exports = {
|
|
219
245
|
logUploadReadinessSummaryCompact,
|
|
220
|
-
logDeployReadinessSummaryCompact
|
|
246
|
+
logDeployReadinessSummaryCompact,
|
|
247
|
+
logDeployGateAbortCompact
|
|
221
248
|
};
|
|
@@ -248,6 +248,9 @@ function formatGatePillarLine(opts = {}) {
|
|
|
248
248
|
if (verdict === VERDICT.FAILED) {
|
|
249
249
|
return formatBlockingError(`${label} · FAILED${suffix}`);
|
|
250
250
|
}
|
|
251
|
+
if (verdict === VERDICT.NOT_APPLICABLE) {
|
|
252
|
+
return formatSuccessLine(`${label} · NOT APPLICABLE${suffix}`);
|
|
253
|
+
}
|
|
251
254
|
return formatWarningLine(`${label} · NOT VERIFIED${suffix}`);
|
|
252
255
|
}
|
|
253
256
|
|