@aifabrix/builder 2.40.2 ā 2.42.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.cursor/rules/docs-rules.mdc +30 -0
- package/README.md +7 -5
- package/integration/hubspot/README.md +8 -4
- package/integration/hubspot/application.json +54 -0
- package/integration/hubspot/create-hubspot.js +9 -136
- package/integration/hubspot/env.template +3 -4
- package/integration/hubspot/hubspot-datasource-company.json +343 -5
- package/integration/hubspot/hubspot-datasource-contact.json +413 -5
- package/integration/hubspot/hubspot-datasource-deal.json +341 -4
- package/integration/hubspot/hubspot-datasource-users.json +116 -0
- package/integration/hubspot/hubspot-deploy.json +1250 -108
- package/integration/hubspot/hubspot-system.json +15 -32
- package/integration/hubspot/test-dataplane-down-tests.js +17 -16
- package/integration/hubspot/test-dataplane-down.js +2 -2
- package/integration/hubspot/test.js +1 -1
- package/jest.config.manual.js +2 -1
- package/lib/api/credential.api.js +40 -0
- package/lib/api/dev.api.js +423 -0
- package/lib/api/external-test.api.js +111 -0
- package/lib/api/index.js +42 -19
- package/lib/api/pipeline.api.js +66 -120
- package/lib/api/types/credential.types.js +23 -0
- package/lib/api/types/dev.types.js +140 -0
- package/lib/api/types/pipeline.types.js +37 -0
- package/lib/api/wizard-platform.api.js +61 -0
- package/lib/api/wizard.api.js +34 -1
- package/lib/app/config.js +44 -11
- package/lib/app/down.js +2 -1
- package/lib/app/index.js +12 -1
- package/lib/app/prompts.js +44 -29
- package/lib/app/push.js +36 -12
- package/lib/app/readme.js +9 -6
- package/lib/app/run-env-compose.js +264 -0
- package/lib/app/run-helpers.js +121 -118
- package/lib/app/run.js +148 -28
- package/lib/app/show-display.js +1 -1
- package/lib/app/show.js +5 -2
- package/lib/build/index.js +11 -3
- package/lib/cli/setup-app.js +172 -15
- package/lib/cli/setup-credential-deployment.js +31 -6
- package/lib/cli/setup-dev.js +206 -16
- package/lib/cli/setup-environment.js +16 -6
- package/lib/cli/setup-external-system.js +89 -24
- package/lib/cli/setup-infra.js +82 -15
- package/lib/cli/setup-secrets.js +52 -5
- package/lib/cli/setup-utility.js +129 -24
- package/lib/commands/app-install.js +172 -0
- package/lib/commands/app-shell.js +75 -0
- package/lib/commands/app-test.js +282 -0
- package/lib/commands/app.js +1 -1
- package/lib/commands/credential-env.js +162 -0
- package/lib/commands/credential-list.js +17 -22
- package/lib/commands/credential-push.js +96 -0
- package/lib/commands/datasource.js +77 -6
- package/lib/commands/dev-cli-handlers.js +141 -0
- package/lib/commands/dev-down.js +114 -0
- package/lib/commands/dev-init.js +347 -0
- package/lib/commands/repair-auth-config.js +99 -0
- package/lib/commands/repair-datasource-keys.js +208 -0
- package/lib/commands/repair-datasource.js +235 -0
- package/lib/commands/repair-env-template.js +348 -0
- package/lib/commands/repair-internal.js +85 -0
- package/lib/commands/repair-rbac.js +158 -0
- package/lib/commands/repair.js +507 -0
- package/lib/commands/secrets-list.js +118 -0
- package/lib/commands/secrets-remove.js +97 -0
- package/lib/commands/secrets-set.js +30 -17
- package/lib/commands/secrets-validate.js +50 -0
- package/lib/commands/test-e2e-external.js +165 -0
- package/lib/commands/up-dataplane.js +2 -2
- package/lib/commands/up-miso.js +0 -25
- package/lib/commands/upload.js +96 -40
- package/lib/commands/wizard-core-helpers.js +226 -4
- package/lib/commands/wizard-core.js +67 -29
- package/lib/commands/wizard-dataplane.js +1 -1
- package/lib/commands/wizard-entity-selection.js +43 -0
- package/lib/commands/wizard-headless.js +44 -5
- package/lib/commands/wizard-helpers.js +7 -3
- package/lib/commands/wizard.js +86 -64
- package/lib/core/admin-secrets.js +96 -0
- package/lib/core/config.js +7 -1
- package/lib/core/secrets-ensure.js +378 -0
- package/lib/core/secrets-env-write.js +157 -0
- package/lib/core/secrets.js +176 -89
- package/lib/datasource/deploy.js +12 -3
- package/lib/datasource/field-reference-validator.js +91 -0
- package/lib/datasource/test-e2e.js +219 -0
- package/lib/datasource/test-integration.js +154 -0
- package/lib/datasource/validate.js +21 -3
- package/lib/deployment/deployer.js +7 -5
- package/lib/deployment/environment-config.js +137 -0
- package/lib/deployment/environment.js +21 -98
- package/lib/deployment/push.js +32 -2
- package/lib/external-system/download.js +188 -203
- package/lib/external-system/generator.js +204 -56
- package/lib/external-system/test-auth.js +7 -3
- package/lib/external-system/test-execution.js +2 -1
- package/lib/external-system/test-system-level.js +73 -0
- package/lib/external-system/test.js +56 -19
- package/lib/generator/external-controller-manifest.js +29 -2
- package/lib/generator/external-schema-utils.js +1 -1
- package/lib/generator/external.js +10 -3
- package/lib/generator/index.js +177 -25
- package/lib/generator/split-readme.js +1 -0
- package/lib/generator/split-variables.js +7 -1
- package/lib/generator/split.js +194 -54
- package/lib/generator/wizard-prompts-secondary.js +294 -0
- package/lib/generator/wizard-prompts.js +105 -106
- package/lib/generator/wizard-readme.js +88 -0
- package/lib/generator/wizard.js +155 -158
- package/lib/infrastructure/compose.js +11 -1
- package/lib/infrastructure/helpers.js +103 -20
- package/lib/infrastructure/index.js +98 -12
- package/lib/infrastructure/services.js +88 -22
- package/lib/schema/application-schema.json +32 -8
- package/lib/schema/external-datasource.schema.json +49 -26
- package/lib/schema/external-system.schema.json +509 -411
- package/lib/schema/wizard-config.schema.json +16 -0
- package/lib/utils/api.js +41 -13
- package/lib/utils/app-register-auth.js +25 -3
- package/lib/utils/auth-headers.js +8 -7
- package/lib/utils/cli-utils.js +20 -0
- package/lib/utils/compose-generator.js +77 -76
- package/lib/utils/compose-handlebars-helpers.js +54 -0
- package/lib/utils/compose-vector-helper.js +18 -0
- package/lib/utils/config-format-preference.js +51 -0
- package/lib/utils/config-format.js +36 -0
- package/lib/utils/config-paths.js +127 -2
- package/lib/utils/configuration-env-resolver.js +179 -0
- package/lib/utils/credential-display.js +83 -0
- package/lib/utils/credential-secrets-env.js +357 -0
- package/lib/utils/dataplane-pipeline-warning.js +28 -0
- package/lib/utils/deployment-validation-helpers.js +4 -4
- package/lib/utils/dev-ca-install.js +139 -0
- package/lib/utils/dev-cert-helper.js +122 -0
- package/lib/utils/device-code-helpers.js +224 -0
- package/lib/utils/device-code.js +37 -336
- package/lib/utils/docker-build.js +40 -8
- package/lib/utils/env-copy.js +103 -13
- package/lib/utils/env-map.js +35 -5
- package/lib/utils/env-template.js +6 -5
- package/lib/utils/error-formatters/http-status-errors.js +20 -2
- package/lib/utils/error-formatters/permission-errors.js +0 -1
- package/lib/utils/error-formatters/validation-errors.js +0 -1
- package/lib/utils/external-readme.js +56 -29
- package/lib/utils/external-system-display.js +59 -1
- package/lib/utils/external-system-test-helpers.js +21 -8
- package/lib/utils/external-system-validators.js +3 -0
- package/lib/utils/file-upload.js +20 -50
- package/lib/utils/help-builder.js +16 -2
- package/lib/utils/infra-status.js +80 -45
- package/lib/utils/local-secrets.js +7 -52
- package/lib/utils/mutagen-install.js +195 -0
- package/lib/utils/mutagen.js +146 -0
- package/lib/utils/paths.js +128 -37
- package/lib/utils/port-resolver.js +28 -16
- package/lib/utils/remote-dev-auth.js +38 -0
- package/lib/utils/remote-docker-env.js +43 -0
- package/lib/utils/remote-secrets-loader.js +60 -0
- package/lib/utils/secrets-canonical.js +93 -0
- package/lib/utils/secrets-generator.js +114 -6
- package/lib/utils/secrets-helpers.js +108 -114
- package/lib/utils/secrets-path.js +2 -2
- package/lib/utils/secrets-utils.js +52 -1
- package/lib/utils/secrets-validation.js +84 -0
- package/lib/utils/ssh-key-helper.js +116 -0
- package/lib/utils/test-log-writer.js +56 -0
- package/lib/utils/token-manager-messages.js +90 -0
- package/lib/utils/token-manager.js +29 -36
- package/lib/utils/variable-transformer.js +3 -3
- package/lib/validation/env-template-auth.js +157 -0
- package/lib/validation/env-template-kv.js +41 -0
- package/lib/validation/external-manifest-validator.js +25 -0
- package/lib/validation/external-system-auth-rules.js +86 -0
- package/lib/validation/validate-batch.js +149 -0
- package/lib/validation/validate-datasource-keys-api.js +33 -0
- package/lib/validation/validate-display.js +94 -16
- package/lib/validation/validate.js +25 -12
- package/lib/validation/validator.js +72 -9
- package/lib/validation/wizard-datasource-validation.js +50 -0
- package/package.json +8 -3
- package/scripts/install-local.js +34 -15
- package/templates/README.md +0 -1
- package/templates/applications/README.md.hbs +4 -4
- package/templates/applications/dataplane/application.yaml +6 -5
- package/templates/applications/dataplane/env.template +15 -10
- package/templates/applications/dataplane/rbac.yaml +2 -2
- package/templates/applications/keycloak/env.template +2 -0
- package/templates/applications/miso-controller/application.yaml +1 -0
- package/templates/applications/miso-controller/env.template +12 -10
- package/templates/external-system/README.md.hbs +65 -25
- package/templates/external-system/deploy.js.hbs +4 -2
- package/templates/external-system/external-datasource.yaml.hbs +217 -0
- package/templates/external-system/external-system.json.hbs +1 -18
- package/templates/infra/compose.yaml.hbs +6 -0
- package/templates/python/docker-compose.hbs +49 -23
- package/templates/typescript/docker-compose.hbs +48 -22
- package/integration/hubspot/application.yaml +0 -37
|
@@ -95,9 +95,19 @@ function handleDockerClose(code, ctx) {
|
|
|
95
95
|
}
|
|
96
96
|
|
|
97
97
|
function runDockerBuildProcess(buildOpts) {
|
|
98
|
-
const { imageName, tag, dockerfilePath, contextPath, spinner, resolve, reject } = buildOpts;
|
|
99
|
-
const
|
|
100
|
-
|
|
98
|
+
const { imageName, tag, dockerfilePath, contextPath, spinner, resolve, reject, env = {}, buildArgs = {} } = buildOpts;
|
|
99
|
+
const spawnEnv = { ...process.env, ...env };
|
|
100
|
+
const args = ['build', '-t', `${imageName}:${tag}`, '-f', dockerfilePath];
|
|
101
|
+
// Pass NPM_TOKEN/PYPI_TOKEN etc. so private registry auth works during RUN npm install / pip install
|
|
102
|
+
for (const [key, value] of Object.entries(buildArgs)) {
|
|
103
|
+
if (value !== null && value !== undefined && String(value).length > 0) {
|
|
104
|
+
args.push('--build-arg', `${key}=${String(value)}`);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
args.push(contextPath);
|
|
108
|
+
const dockerProcess = spawn('docker', args, {
|
|
109
|
+
shell: process.platform === 'win32',
|
|
110
|
+
env: spawnEnv
|
|
101
111
|
});
|
|
102
112
|
let stdoutBuffer = '';
|
|
103
113
|
let stderrBuffer = '';
|
|
@@ -138,13 +148,15 @@ function runDockerBuildProcess(buildOpts) {
|
|
|
138
148
|
* @param {string} dockerfilePath - Path to Dockerfile
|
|
139
149
|
* @param {string} contextPath - Build context path
|
|
140
150
|
* @param {string} tag - Image tag
|
|
151
|
+
* @param {Object} [buildArgs={}] - Optional build args (e.g. NPM_TOKEN, PYPI_TOKEN) for private registries
|
|
141
152
|
* @returns {Promise<void>} Resolves when build completes
|
|
142
153
|
* @throws {Error} If build fails
|
|
143
154
|
*/
|
|
144
|
-
async function executeDockerBuild(imageName, dockerfilePath, contextPath, tag) {
|
|
155
|
+
async function executeDockerBuild(imageName, dockerfilePath, contextPath, tag, buildArgs = {}) {
|
|
145
156
|
const spinner = ora({ text: 'Starting Docker build...', spinner: 'dots' }).start();
|
|
146
157
|
const fsSync = require('fs');
|
|
147
158
|
const path = require('path');
|
|
159
|
+
const { getRemoteDockerEnv } = require('./remote-docker-env');
|
|
148
160
|
dockerfilePath = path.resolve(dockerfilePath);
|
|
149
161
|
contextPath = path.resolve(contextPath);
|
|
150
162
|
|
|
@@ -162,8 +174,21 @@ async function executeDockerBuild(imageName, dockerfilePath, contextPath, tag) {
|
|
|
162
174
|
}
|
|
163
175
|
}
|
|
164
176
|
|
|
177
|
+
const isTest = process.env.NODE_ENV === 'test' || process.env.JEST_WORKER_ID !== undefined;
|
|
178
|
+
const remoteEnv = isTest ? {} : await getRemoteDockerEnv();
|
|
179
|
+
const resolvedBuildArgs = buildArgs && typeof buildArgs === 'object' ? buildArgs : {};
|
|
165
180
|
return new Promise((resolve, reject) => {
|
|
166
|
-
runDockerBuildProcess({
|
|
181
|
+
runDockerBuildProcess({
|
|
182
|
+
imageName,
|
|
183
|
+
tag,
|
|
184
|
+
dockerfilePath,
|
|
185
|
+
contextPath,
|
|
186
|
+
spinner,
|
|
187
|
+
resolve,
|
|
188
|
+
reject,
|
|
189
|
+
env: remoteEnv,
|
|
190
|
+
buildArgs: resolvedBuildArgs
|
|
191
|
+
});
|
|
167
192
|
});
|
|
168
193
|
}
|
|
169
194
|
|
|
@@ -179,14 +204,18 @@ async function executeDockerBuild(imageName, dockerfilePath, contextPath, tag) {
|
|
|
179
204
|
* @throws {Error} If build fails
|
|
180
205
|
*/
|
|
181
206
|
async function executeBuild(imageName, dockerfilePath, contextPath, tag, options) {
|
|
182
|
-
|
|
207
|
+
const buildArgs = (options && options.buildArgs) || {};
|
|
208
|
+
await executeDockerBuild(imageName, dockerfilePath, contextPath, tag, buildArgs);
|
|
183
209
|
|
|
184
210
|
// Tag image if additional tag provided
|
|
185
211
|
if (options && options.tag && options.tag !== 'latest') {
|
|
186
212
|
const { promisify } = require('util');
|
|
187
213
|
const { exec } = require('child_process');
|
|
188
214
|
const run = promisify(exec);
|
|
189
|
-
|
|
215
|
+
const { getRemoteDockerEnv } = require('./remote-docker-env');
|
|
216
|
+
const remoteEnv = await getRemoteDockerEnv();
|
|
217
|
+
const env = { ...process.env, ...remoteEnv };
|
|
218
|
+
await run(`docker tag ${imageName}:${tag} ${imageName}:latest`, { env });
|
|
190
219
|
}
|
|
191
220
|
}
|
|
192
221
|
|
|
@@ -215,7 +244,10 @@ async function executeDockerBuildWithTag(effectiveImageName, imageName, dockerfi
|
|
|
215
244
|
const { promisify } = require('util');
|
|
216
245
|
const { exec } = require('child_process');
|
|
217
246
|
const run = promisify(exec);
|
|
218
|
-
|
|
247
|
+
const { getRemoteDockerEnv } = require('./remote-docker-env');
|
|
248
|
+
const remoteEnv = await getRemoteDockerEnv();
|
|
249
|
+
const env = { ...process.env, ...remoteEnv };
|
|
250
|
+
await run(`docker tag ${effectiveImageName}:${tag} ${imageName}:${tag}`, { env });
|
|
219
251
|
logger.log(chalk.green(`ā Tagged image: ${imageName}:${tag}`));
|
|
220
252
|
} catch (err) {
|
|
221
253
|
logger.log(chalk.yellow(`ā ļø Warning: Could not create compatibility tag ${imageName}:${tag} - ${err.message}`));
|
package/lib/utils/env-copy.js
CHANGED
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
'use strict';
|
|
10
10
|
|
|
11
11
|
const fs = require('fs');
|
|
12
|
+
const fsp = require('fs').promises;
|
|
12
13
|
const path = require('path');
|
|
13
14
|
const yaml = require('js-yaml');
|
|
14
15
|
const chalk = require('chalk');
|
|
@@ -48,6 +49,22 @@ function readDeveloperIdFromConfig(config) {
|
|
|
48
49
|
return null;
|
|
49
50
|
}
|
|
50
51
|
|
|
52
|
+
/**
|
|
53
|
+
* Substitute /mnt/data with local mount path for local .env and ensure mount dir exists on disk.
|
|
54
|
+
* Creates the mount folder on the local filesystem (next to the .env file) when it does not exist.
|
|
55
|
+
* @param {string} content - Env file content
|
|
56
|
+
* @param {string} outputPath - Resolved path of the .env file being written
|
|
57
|
+
* @returns {string} Content with /mnt/data replaced by path to mount directory
|
|
58
|
+
*/
|
|
59
|
+
function substituteMntDataForLocal(content, outputPath) {
|
|
60
|
+
const outputDir = path.dirname(outputPath);
|
|
61
|
+
const localMountPath = path.resolve(outputDir, 'mount');
|
|
62
|
+
if (!fs.existsSync(localMountPath)) {
|
|
63
|
+
fs.mkdirSync(localMountPath, { recursive: true });
|
|
64
|
+
}
|
|
65
|
+
return content.replace(/\/mnt\/data/g, localMountPath);
|
|
66
|
+
}
|
|
67
|
+
|
|
51
68
|
/**
|
|
52
69
|
* Resolve output path for env file
|
|
53
70
|
* @param {string} rawOutputPath - Raw output path from application config
|
|
@@ -72,6 +89,45 @@ function resolveEnvOutputPath(rawOutputPath, variablesPath) {
|
|
|
72
89
|
return outputPath;
|
|
73
90
|
}
|
|
74
91
|
|
|
92
|
+
/**
|
|
93
|
+
* Writes .env to envOutputPath for reload path: merge run .env into existing file.
|
|
94
|
+
* @async
|
|
95
|
+
* @param {string} outputPath - Resolved output path
|
|
96
|
+
* @param {string} runEnvPath - Path to .env.run
|
|
97
|
+
*/
|
|
98
|
+
async function writeEnvOutputForReload(outputPath, runEnvPath) {
|
|
99
|
+
const { parseEnvContentToMap, mergeEnvMapIntoContent } = require('../core/secrets');
|
|
100
|
+
const runContent = await fsp.readFile(runEnvPath, 'utf8');
|
|
101
|
+
const runMap = parseEnvContentToMap(runContent);
|
|
102
|
+
let toWrite = runContent;
|
|
103
|
+
if (fs.existsSync(outputPath)) {
|
|
104
|
+
const existingContent = await fsp.readFile(outputPath, 'utf8');
|
|
105
|
+
toWrite = mergeEnvMapIntoContent(existingContent, runMap);
|
|
106
|
+
}
|
|
107
|
+
await fsp.writeFile(outputPath, toWrite, { mode: 0o600 });
|
|
108
|
+
logger.log(chalk.green(`ā Wrote .env to envOutputPath (same as container, for --reload): ${outputPath}`));
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Writes local .env to envOutputPath (no reload).
|
|
113
|
+
* @async
|
|
114
|
+
* @param {string} appName - Application name
|
|
115
|
+
* @param {string} outputPath - Resolved output path
|
|
116
|
+
*/
|
|
117
|
+
async function writeEnvOutputForLocal(appName, outputPath) {
|
|
118
|
+
const { generateEnvContent, parseEnvContentToMap, mergeEnvMapIntoContent } = require('../core/secrets');
|
|
119
|
+
let localContent = await generateEnvContent(appName, null, 'local', false);
|
|
120
|
+
localContent = substituteMntDataForLocal(localContent, outputPath);
|
|
121
|
+
let toWrite = localContent;
|
|
122
|
+
if (fs.existsSync(outputPath)) {
|
|
123
|
+
const existingContent = await fsp.readFile(outputPath, 'utf8');
|
|
124
|
+
const localMap = parseEnvContentToMap(localContent);
|
|
125
|
+
toWrite = mergeEnvMapIntoContent(existingContent, localMap);
|
|
126
|
+
}
|
|
127
|
+
await fsp.writeFile(outputPath, toWrite, { mode: 0o600 });
|
|
128
|
+
logger.log(chalk.green(`ā Wrote .env to envOutputPath (localPort): ${outputPath}`));
|
|
129
|
+
}
|
|
130
|
+
|
|
75
131
|
/**
|
|
76
132
|
* Calculate developer-specific app port
|
|
77
133
|
* @param {number} baseAppPort - Base application port
|
|
@@ -180,6 +236,44 @@ async function patchEnvContentForLocal(envContent, variables) {
|
|
|
180
236
|
return envContent;
|
|
181
237
|
}
|
|
182
238
|
|
|
239
|
+
/**
|
|
240
|
+
* Write regenerated local .env to output path (merge with existing if present).
|
|
241
|
+
* @async
|
|
242
|
+
* @param {string} outputPath - Resolved output path
|
|
243
|
+
* @param {string} appName - Application name
|
|
244
|
+
* @param {string} [secretsPath] - Path to secrets file (optional)
|
|
245
|
+
* @param {string} envOutputPathLabel - Label for log message (e.g. variables.build.envOutputPath)
|
|
246
|
+
*/
|
|
247
|
+
async function writeLocalEnvToOutputPath(outputPath, appName, secretsPath, envOutputPathLabel) {
|
|
248
|
+
const { generateEnvContent, parseEnvContentToMap, mergeEnvMapIntoContent } = require('../core/secrets');
|
|
249
|
+
let localEnvContent = await generateEnvContent(appName, secretsPath, 'local', false);
|
|
250
|
+
localEnvContent = substituteMntDataForLocal(localEnvContent, outputPath);
|
|
251
|
+
let toWrite = localEnvContent;
|
|
252
|
+
if (fs.existsSync(outputPath)) {
|
|
253
|
+
const existingContent = fs.readFileSync(outputPath, 'utf8');
|
|
254
|
+
const localMap = parseEnvContentToMap(localEnvContent);
|
|
255
|
+
toWrite = mergeEnvMapIntoContent(existingContent, localMap);
|
|
256
|
+
}
|
|
257
|
+
fs.writeFileSync(outputPath, toWrite, { mode: 0o600 });
|
|
258
|
+
logger.log(chalk.green(`ā Generated local .env at: ${envOutputPathLabel}`));
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
/**
|
|
262
|
+
* Write patched .env to output path (fallback when appName not provided).
|
|
263
|
+
* @async
|
|
264
|
+
* @param {string} envPath - Path to generated .env file
|
|
265
|
+
* @param {string} outputPath - Resolved output path
|
|
266
|
+
* @param {Object} variables - Loaded variables config
|
|
267
|
+
* @param {string} envOutputPathLabel - Label for log message
|
|
268
|
+
*/
|
|
269
|
+
async function writePatchedEnvToOutputPath(envPath, outputPath, variables, envOutputPathLabel) {
|
|
270
|
+
const envContent = fs.readFileSync(envPath, 'utf8');
|
|
271
|
+
let patchedContent = await patchEnvContentForLocal(envContent, variables);
|
|
272
|
+
patchedContent = substituteMntDataForLocal(patchedContent, outputPath);
|
|
273
|
+
fs.writeFileSync(outputPath, patchedContent, { mode: 0o600 });
|
|
274
|
+
logger.log(chalk.green(`ā Copied .env to: ${envOutputPathLabel}`));
|
|
275
|
+
}
|
|
276
|
+
|
|
183
277
|
/**
|
|
184
278
|
* Process and optionally copy env file to envOutputPath if configured
|
|
185
279
|
* Regenerates .env file with env=local for local development (apps/.env)
|
|
@@ -191,7 +285,7 @@ async function patchEnvContentForLocal(envContent, variables) {
|
|
|
191
285
|
* @param {string} [secretsPath] - Path to secrets file (optional, for regenerating)
|
|
192
286
|
*/
|
|
193
287
|
async function processEnvVariables(envPath, variablesPath, appName, secretsPath) {
|
|
194
|
-
if (!fs.existsSync(variablesPath)) {
|
|
288
|
+
if (!variablesPath || !fs.existsSync(variablesPath)) {
|
|
195
289
|
return;
|
|
196
290
|
}
|
|
197
291
|
const variablesContent = fs.readFileSync(variablesPath, 'utf8');
|
|
@@ -200,29 +294,25 @@ async function processEnvVariables(envPath, variablesPath, appName, secretsPath)
|
|
|
200
294
|
return;
|
|
201
295
|
}
|
|
202
296
|
|
|
203
|
-
// Resolve output path
|
|
204
297
|
const outputPath = resolveEnvOutputPath(variables.build.envOutputPath, variablesPath);
|
|
205
298
|
const outputDir = path.dirname(outputPath);
|
|
206
299
|
if (!fs.existsSync(outputDir)) {
|
|
207
300
|
fs.mkdirSync(outputDir, { recursive: true });
|
|
208
301
|
}
|
|
209
302
|
|
|
210
|
-
|
|
303
|
+
const label = variables.build.envOutputPath;
|
|
211
304
|
if (appName) {
|
|
212
|
-
|
|
213
|
-
const localEnvContent = await generateEnvContent(appName, secretsPath, 'local', false);
|
|
214
|
-
fs.writeFileSync(outputPath, localEnvContent, { mode: 0o600 });
|
|
215
|
-
logger.log(chalk.green(`ā Generated local .env at: ${variables.build.envOutputPath}`));
|
|
305
|
+
await writeLocalEnvToOutputPath(outputPath, appName, secretsPath, label);
|
|
216
306
|
} else {
|
|
217
|
-
|
|
218
|
-
const envContent = fs.readFileSync(envPath, 'utf8');
|
|
219
|
-
const patchedContent = await patchEnvContentForLocal(envContent, variables);
|
|
220
|
-
fs.writeFileSync(outputPath, patchedContent, { mode: 0o600 });
|
|
221
|
-
logger.log(chalk.green(`ā Copied .env to: ${variables.build.envOutputPath}`));
|
|
307
|
+
await writePatchedEnvToOutputPath(envPath, outputPath, variables, label);
|
|
222
308
|
}
|
|
223
309
|
}
|
|
224
310
|
|
|
225
311
|
module.exports = {
|
|
226
|
-
processEnvVariables
|
|
312
|
+
processEnvVariables,
|
|
313
|
+
resolveEnvOutputPath,
|
|
314
|
+
substituteMntDataForLocal,
|
|
315
|
+
writeEnvOutputForReload,
|
|
316
|
+
writeEnvOutputForLocal
|
|
227
317
|
};
|
|
228
318
|
|
package/lib/utils/env-map.js
CHANGED
|
@@ -144,6 +144,27 @@ function handlePlainValue(result, key, rawVal, options) {
|
|
|
144
144
|
result[key] = val;
|
|
145
145
|
}
|
|
146
146
|
|
|
147
|
+
/** Placeholder in env-config values; replaced with application or default port */
|
|
148
|
+
const PORT_PLACEHOLDER = '${PORT}';
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Replaces ${PORT} in all string values of env object (in-place).
|
|
152
|
+
* Used so env-config.yaml docker/local values resolve correctly (e.g. PORT: ${PORT} -> application port).
|
|
153
|
+
*
|
|
154
|
+
* @param {Object} envObj - Flat key-value object (e.g. merged env-config environments.docker)
|
|
155
|
+
* @param {number} [portNumber=3000] - Port to substitute when value contains ${PORT}
|
|
156
|
+
*/
|
|
157
|
+
function resolvePortInEnvValues(envObj, portNumber = 3000) {
|
|
158
|
+
if (!envObj || typeof envObj !== 'object') return;
|
|
159
|
+
const portStr = String(portNumber);
|
|
160
|
+
for (const key of Object.keys(envObj)) {
|
|
161
|
+
const val = envObj[key];
|
|
162
|
+
if (typeof val === 'string' && val.includes(PORT_PLACEHOLDER)) {
|
|
163
|
+
envObj[key] = val.split(PORT_PLACEHOLDER).join(portStr);
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
147
168
|
/**
|
|
148
169
|
* Normalize environment variable map by splitting host:port values
|
|
149
170
|
* @function normalizeEnvVars
|
|
@@ -270,30 +291,38 @@ function calculateDockerPublicPorts(result, devIdNum, schemaBaseVars = {}) {
|
|
|
270
291
|
/**
|
|
271
292
|
* Build environment variable map for interpolation based on env-config.yaml
|
|
272
293
|
* - Supports values like "host:port" by splitting into *_HOST (host) and *_PORT (port)
|
|
294
|
+
* - Resolves ${PORT} in env-config values using options.appPort or default 3000 (so docker/local values are correct)
|
|
273
295
|
* - Merges overrides from ~/.aifabrix/config.yaml under environments.{env}
|
|
274
296
|
* - Applies aifabrix-localhost override for local context if configured
|
|
275
297
|
* - Applies developer-id adjustment to port variables for local context
|
|
276
|
-
* - Calculates *_PUBLIC_PORT for docker context (basePort + developer-id * 100)
|
|
298
|
+
* - Calculates *_PUBLIC_PORT for both local and docker context (basePort + developer-id * 100)
|
|
277
299
|
* @async
|
|
278
300
|
* @function buildEnvVarMap
|
|
279
301
|
* @param {'docker'|'local'} context - Environment context
|
|
280
302
|
* @param {Object} [osModule] - Optional os module (for testing). If not provided, requires 'os'
|
|
281
303
|
* @param {number|null} [developerId] - Optional developer ID for port adjustment. If not provided, will be fetched from config for local context.
|
|
304
|
+
* @param {Object} [options] - Optional options
|
|
305
|
+
* @param {number} [options.appPort] - Port to use when resolving ${PORT} in env-config values (e.g. from application.yaml)
|
|
282
306
|
* @returns {Promise<Object>} Map of variables for interpolation
|
|
283
307
|
*/
|
|
284
|
-
async function buildEnvVarMap(context, osModule = null, developerId = null) {
|
|
308
|
+
async function buildEnvVarMap(context, osModule = null, developerId = null, options = null) {
|
|
285
309
|
const baseVars = await loadBaseVars(context);
|
|
286
310
|
const os = osModule || require('os');
|
|
287
311
|
const overrideVars = loadOverrideVars(context, os);
|
|
288
312
|
const localhostOverride = context === 'local' ? getLocalhostOverride(os) : null;
|
|
289
313
|
const merged = { ...baseVars, ...overrideVars };
|
|
314
|
+
const appPort = (options && options.appPort !== undefined && options.appPort !== null && Number.isFinite(Number(options.appPort)))
|
|
315
|
+
? Number(options.appPort) : 3000;
|
|
316
|
+
resolvePortInEnvValues(merged, appPort);
|
|
290
317
|
const result = normalizeEnvVars(merged, context, localhostOverride);
|
|
291
318
|
|
|
319
|
+
const devIdNum = await getDeveloperIdNumber(developerId);
|
|
292
320
|
if (context === 'local') {
|
|
293
|
-
const devIdNum = await getDeveloperIdNumber(developerId);
|
|
294
321
|
applyLocalPortAdjustment(result, devIdNum);
|
|
322
|
+
const schemaCfg = loadSchemaEnvConfig();
|
|
323
|
+
const schemaBaseVars = (schemaCfg && schemaCfg.environments && schemaCfg.environments.local) ? schemaCfg.environments.local : {};
|
|
324
|
+
calculateDockerPublicPorts(result, devIdNum, schemaBaseVars);
|
|
295
325
|
} else if (context === 'docker') {
|
|
296
|
-
const devIdNum = await getDeveloperIdNumber(developerId);
|
|
297
326
|
const schemaCfg = loadSchemaEnvConfig();
|
|
298
327
|
const schemaBaseVars = (schemaCfg && schemaCfg.environments && schemaCfg.environments[context]) ? schemaCfg.environments[context] : {};
|
|
299
328
|
calculateDockerPublicPorts(result, devIdNum, schemaBaseVars);
|
|
@@ -304,6 +333,7 @@ async function buildEnvVarMap(context, osModule = null, developerId = null) {
|
|
|
304
333
|
|
|
305
334
|
module.exports = {
|
|
306
335
|
buildEnvVarMap,
|
|
307
|
-
getDeveloperIdNumber
|
|
336
|
+
getDeveloperIdNumber,
|
|
337
|
+
resolvePortInEnvValues
|
|
308
338
|
};
|
|
309
339
|
|
|
@@ -15,7 +15,8 @@ const chalk = require('chalk');
|
|
|
15
15
|
const logger = require('../utils/logger');
|
|
16
16
|
|
|
17
17
|
/**
|
|
18
|
-
* Updates env.template to add MISO_CLIENTID, MISO_CLIENTSECRET, and MISO_CONTROLLER_URL
|
|
18
|
+
* Updates env.template to add MISO_CLIENTID, MISO_CLIENTSECRET, and optionally MISO_CONTROLLER_URL.
|
|
19
|
+
* When MISO_CONTROLLER_URL already exists, its value is left unchanged (e.g. http://${MISO_HOST}:${MISO_PORT}).
|
|
19
20
|
* @async
|
|
20
21
|
* @param {string} appKey - Application key
|
|
21
22
|
* @param {string} clientIdKey - Secret key for client ID (e.g., 'myapp-client-idKeyVault')
|
|
@@ -39,7 +40,9 @@ function checkMisoEntries(content) {
|
|
|
39
40
|
}
|
|
40
41
|
|
|
41
42
|
/**
|
|
42
|
-
* Updates existing MISO entries
|
|
43
|
+
* Updates existing MISO entries.
|
|
44
|
+
* MISO_CONTROLLER_URL is never overwritten when present so that template form
|
|
45
|
+
* (e.g. http://${MISO_HOST}:${MISO_PORT}) or custom URLs are preserved.
|
|
43
46
|
* @function updateExistingMisoEntries
|
|
44
47
|
* @param {string} content - File content
|
|
45
48
|
* @param {string} clientIdKey - Client ID key
|
|
@@ -54,9 +57,7 @@ function updateExistingMisoEntries(content, clientIdKey, clientSecretKey, entrie
|
|
|
54
57
|
if (entries.hasClientSecret) {
|
|
55
58
|
content = content.replace(/^MISO_CLIENTSECRET\s*=.*$/m, `MISO_CLIENTSECRET=kv://${clientSecretKey}`);
|
|
56
59
|
}
|
|
57
|
-
|
|
58
|
-
content = content.replace(/^MISO_CONTROLLER_URL\s*=.*$/m, 'MISO_CONTROLLER_URL=http://${MISO_HOST}:${MISO_PORT}');
|
|
59
|
-
}
|
|
60
|
+
// Do not change existing MISO_CONTROLLER_URL (preserve template or custom value)
|
|
60
61
|
return content;
|
|
61
62
|
}
|
|
62
63
|
|
|
@@ -79,12 +79,31 @@ function addErrorMessageIfNotGeneric(lines, errorData) {
|
|
|
79
79
|
}
|
|
80
80
|
|
|
81
81
|
/**
|
|
82
|
-
*
|
|
82
|
+
* Returns true when the error is about Builder Server client certificate (not Controller token).
|
|
83
|
+
* @param {Object} errorData - Error data
|
|
84
|
+
* @returns {boolean}
|
|
85
|
+
*/
|
|
86
|
+
function isBuilderServerCertError(errorData) {
|
|
87
|
+
const msg = (errorData.message || errorData.error || errorData.detail || '').toLowerCase();
|
|
88
|
+
return msg.includes('client certificate') || msg.includes('issue-cert') || msg.includes('x-client-cert');
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Adds authentication guidance (Controller token login). Skipped for Builder Server cert errors.
|
|
83
93
|
* @function addAuthenticationGuidance
|
|
84
94
|
* @param {string[]} lines - Error message lines
|
|
85
95
|
* @param {Object} errorData - Error data
|
|
86
96
|
*/
|
|
87
97
|
function addAuthenticationGuidance(lines, errorData) {
|
|
98
|
+
if (isBuilderServerCertError(errorData)) {
|
|
99
|
+
lines.push(chalk.gray('Use a certificate from: aifabrix dev init --developer-id <id> --server <url> --pin <pin>'));
|
|
100
|
+
if (errorData.correlationId) {
|
|
101
|
+
lines.push('');
|
|
102
|
+
lines.push(chalk.gray(`Correlation ID: ${errorData.correlationId}`));
|
|
103
|
+
}
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
|
|
88
107
|
lines.push(chalk.gray('Your authentication token is invalid or has expired.'));
|
|
89
108
|
lines.push('');
|
|
90
109
|
lines.push(chalk.gray('To authenticate, run:'));
|
|
@@ -304,4 +323,3 @@ module.exports = {
|
|
|
304
323
|
formatNotFoundError,
|
|
305
324
|
formatGenericError
|
|
306
325
|
};
|
|
307
|
-
|
|
@@ -30,42 +30,65 @@ function formatDisplayName(key) {
|
|
|
30
30
|
.join(' ');
|
|
31
31
|
}
|
|
32
32
|
|
|
33
|
+
/**
|
|
34
|
+
* Derives suffix from datasource key for filename generation
|
|
35
|
+
* @param {string} key - Datasource key
|
|
36
|
+
* @param {string} systemKey - System key
|
|
37
|
+
* @param {string} entityType - Fallback entity type
|
|
38
|
+
* @returns {string} Suffix segment
|
|
39
|
+
*/
|
|
40
|
+
function getDatasourceKeySuffix(key, systemKey, entityType) {
|
|
41
|
+
if (key.startsWith(`${systemKey}-deploy-`)) {
|
|
42
|
+
return key.slice(`${systemKey}-deploy-`.length);
|
|
43
|
+
}
|
|
44
|
+
if (systemKey && key.startsWith(`${systemKey}-`)) {
|
|
45
|
+
return key.slice(systemKey.length + 1);
|
|
46
|
+
}
|
|
47
|
+
if (key) {
|
|
48
|
+
return key;
|
|
49
|
+
}
|
|
50
|
+
return entityType;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Normalizes a single datasource entry for template use
|
|
55
|
+
* @param {Object} datasource - Datasource object
|
|
56
|
+
* @param {number} index - Index in array
|
|
57
|
+
* @param {string} systemKey - System key for filename generation
|
|
58
|
+
* @param {string} ext - File extension (e.g. '.json', '.yaml')
|
|
59
|
+
* @returns {{entityType: string, displayName: string, fileName: string, datasourceKey: string}} Normalized entry
|
|
60
|
+
*/
|
|
61
|
+
function normalizeOneDatasource(datasource, index, systemKey, ext) {
|
|
62
|
+
const entityType = datasource.entityType ||
|
|
63
|
+
datasource.entityKey ||
|
|
64
|
+
datasource.key?.split('-').pop() ||
|
|
65
|
+
`entity${index + 1}`;
|
|
66
|
+
const displayName = datasource.displayName ||
|
|
67
|
+
datasource.name ||
|
|
68
|
+
`Datasource ${index + 1}`;
|
|
69
|
+
const key = datasource.key || '';
|
|
70
|
+
const suffix = getDatasourceKeySuffix(key, systemKey, entityType);
|
|
71
|
+
const datasourceKey = key || (systemKey ? `${systemKey}-${suffix}` : suffix);
|
|
72
|
+
const fileName = datasource.fileName || datasource.file ||
|
|
73
|
+
(systemKey ? `${systemKey}-datasource-${suffix}${ext}` : `${suffix}${ext}`);
|
|
74
|
+
return { entityType, displayName, fileName, datasourceKey };
|
|
75
|
+
}
|
|
76
|
+
|
|
33
77
|
/**
|
|
34
78
|
* Normalizes datasource entries for template use
|
|
35
79
|
* @param {Array} datasources - Datasource objects
|
|
36
80
|
* @param {string} systemKey - System key for filename generation
|
|
37
|
-
* @
|
|
81
|
+
* @param {string} [fileExt='.json'] - File extension for generated filenames (e.g. '.json', '.yaml')
|
|
82
|
+
* @returns {Array<{entityType: string, displayName: string, fileName: string, datasourceKey: string}>} Normalized entries
|
|
38
83
|
*/
|
|
39
|
-
function normalizeDatasources(datasources, systemKey) {
|
|
84
|
+
function normalizeDatasources(datasources, systemKey, fileExt = '.json') {
|
|
40
85
|
if (!Array.isArray(datasources)) {
|
|
41
86
|
return [];
|
|
42
87
|
}
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
`entity${index + 1}`;
|
|
48
|
-
const displayName = datasource.displayName ||
|
|
49
|
-
datasource.name ||
|
|
50
|
-
`Datasource ${index + 1}`;
|
|
51
|
-
let fileName = datasource.fileName || datasource.file;
|
|
52
|
-
if (!fileName) {
|
|
53
|
-
const key = datasource.key || '';
|
|
54
|
-
// Suffix matches split getExternalDatasourceFileName for consistent README and file names
|
|
55
|
-
let suffix;
|
|
56
|
-
if (key.startsWith(`${systemKey}-deploy-`)) {
|
|
57
|
-
suffix = key.slice(`${systemKey}-deploy-`.length);
|
|
58
|
-
} else if (systemKey && key.startsWith(`${systemKey}-`)) {
|
|
59
|
-
suffix = key.slice(systemKey.length + 1);
|
|
60
|
-
} else if (key) {
|
|
61
|
-
suffix = key;
|
|
62
|
-
} else {
|
|
63
|
-
suffix = entityType;
|
|
64
|
-
}
|
|
65
|
-
fileName = systemKey ? `${systemKey}-datasource-${suffix}.yaml` : `${suffix}.yaml`;
|
|
66
|
-
}
|
|
67
|
-
return { entityType, displayName, fileName };
|
|
68
|
-
});
|
|
88
|
+
const ext = fileExt && fileExt.startsWith('.') ? fileExt : `.${fileExt || 'json'}`;
|
|
89
|
+
return datasources.map((datasource, index) =>
|
|
90
|
+
normalizeOneDatasource(datasource, index, systemKey, ext)
|
|
91
|
+
);
|
|
69
92
|
}
|
|
70
93
|
|
|
71
94
|
/**
|
|
@@ -78,6 +101,7 @@ function normalizeDatasources(datasources, systemKey) {
|
|
|
78
101
|
* @param {string} [params.displayName] - Display name
|
|
79
102
|
* @param {string} [params.description] - Description
|
|
80
103
|
* @param {Array} [params.datasources] - Datasource objects
|
|
104
|
+
* @param {string} [params.fileExt] - File extension for config files (e.g. '.json', '.yaml'); default '.json'
|
|
81
105
|
* @returns {Object} Template context
|
|
82
106
|
*/
|
|
83
107
|
function buildExternalReadmeContext(params = {}) {
|
|
@@ -86,7 +110,8 @@ function buildExternalReadmeContext(params = {}) {
|
|
|
86
110
|
const displayName = params.displayName || formatDisplayName(systemKey);
|
|
87
111
|
const description = params.description || `External system integration for ${systemKey}`;
|
|
88
112
|
const systemType = params.systemType || 'openapi';
|
|
89
|
-
const
|
|
113
|
+
const fileExt = params.fileExt !== undefined ? params.fileExt : '.json';
|
|
114
|
+
const datasources = normalizeDatasources(params.datasources, systemKey, fileExt);
|
|
90
115
|
|
|
91
116
|
return {
|
|
92
117
|
appName,
|
|
@@ -94,7 +119,9 @@ function buildExternalReadmeContext(params = {}) {
|
|
|
94
119
|
displayName,
|
|
95
120
|
description,
|
|
96
121
|
systemType,
|
|
122
|
+
fileExt: fileExt && fileExt.startsWith('.') ? fileExt : `.${fileExt || 'json'}`,
|
|
97
123
|
datasourceCount: datasources.length,
|
|
124
|
+
hasDatasources: datasources.length > 0,
|
|
98
125
|
datasources
|
|
99
126
|
};
|
|
100
127
|
}
|
|
@@ -241,8 +241,66 @@ function displayIntegrationTestResults(results, verbose = false) {
|
|
|
241
241
|
}
|
|
242
242
|
}
|
|
243
243
|
|
|
244
|
+
/**
|
|
245
|
+
* Displays E2E test results (steps: config, credential, sync, data, cip).
|
|
246
|
+
* Supports sync response (data.steps only), final poll (data.steps + data.success), and running poll
|
|
247
|
+
* (data.completedActions, no data.steps yet). When status is present (async flow), shows it.
|
|
248
|
+
*
|
|
249
|
+
* @param {Object} data - E2E response or poll data
|
|
250
|
+
* @param {string} [data.status] - Optional status: 'running' | 'completed' | 'failed' (async flow)
|
|
251
|
+
* @param {Object[]} [data.steps] - Per-step results (final state)
|
|
252
|
+
* @param {Object[]} [data.completedActions] - Steps completed so far (running state when steps absent)
|
|
253
|
+
* @param {boolean} [data.success] - Overall success (final state)
|
|
254
|
+
* @param {string} [data.error] - Error message when failed
|
|
255
|
+
* @param {boolean} [verbose] - Show detailed output
|
|
256
|
+
*/
|
|
257
|
+
/* eslint-disable max-statements,complexity -- Step iteration and status display */
|
|
258
|
+
function displayE2EResults(data, verbose = false) {
|
|
259
|
+
logger.log(chalk.blue('\nš E2E Test Results\n'));
|
|
260
|
+
if (data.status) {
|
|
261
|
+
const statusLabel = data.status === 'running'
|
|
262
|
+
? chalk.yellow('running')
|
|
263
|
+
: data.status === 'completed'
|
|
264
|
+
? chalk.green('completed')
|
|
265
|
+
: data.status === 'failed'
|
|
266
|
+
? chalk.red('failed')
|
|
267
|
+
: data.status;
|
|
268
|
+
logger.log(`Status: ${statusLabel}`);
|
|
269
|
+
}
|
|
270
|
+
const steps = data.steps || data.completedActions || [];
|
|
271
|
+
if (steps.length === 0) {
|
|
272
|
+
if (data.success === false) {
|
|
273
|
+
logger.log(chalk.red('ā E2E test failed'));
|
|
274
|
+
if (data.error) logger.log(chalk.red(` Error: ${data.error}`));
|
|
275
|
+
} else if (data.status === 'running') {
|
|
276
|
+
logger.log(chalk.gray(' No steps completed yet'));
|
|
277
|
+
} else {
|
|
278
|
+
logger.log(chalk.yellow('No step results returned'));
|
|
279
|
+
}
|
|
280
|
+
return;
|
|
281
|
+
}
|
|
282
|
+
const isRunning = data.status === 'running' && !data.steps;
|
|
283
|
+
if (isRunning && verbose) {
|
|
284
|
+
logger.log(chalk.gray(` (${steps.length} step(s) completed so far)`));
|
|
285
|
+
}
|
|
286
|
+
for (const step of steps) {
|
|
287
|
+
const name = step.name || step.step || 'unknown';
|
|
288
|
+
const ok = step.success !== false && !step.error;
|
|
289
|
+
logger.log(` ${ok ? chalk.green('ā') : chalk.red('ā')} ${name}`);
|
|
290
|
+
if (!ok && (step.error || step.message)) logger.log(chalk.red(` ${step.error || step.message}`));
|
|
291
|
+
if (verbose && step.message && ok) logger.log(chalk.gray(` ${step.message}`));
|
|
292
|
+
}
|
|
293
|
+
if (isRunning) {
|
|
294
|
+
return;
|
|
295
|
+
}
|
|
296
|
+
const allPassed = steps.every(s => s.success !== false && !s.error);
|
|
297
|
+
logger.log(allPassed ? chalk.green('\nā
E2E test passed!') : chalk.red('\nā E2E test failed'));
|
|
298
|
+
}
|
|
299
|
+
|
|
244
300
|
module.exports = {
|
|
245
301
|
displayTestResults,
|
|
246
|
-
displayIntegrationTestResults
|
|
302
|
+
displayIntegrationTestResults,
|
|
303
|
+
displayE2EResults,
|
|
304
|
+
displayDatasourceIntegrationResult
|
|
247
305
|
};
|
|
248
306
|
|