@aifabrix/builder 2.39.2 → 2.40.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/project-rules.mdc +6 -6
- package/README.md +2 -2
- package/babel.config.js +6 -0
- package/integration/hubspot/README.md +53 -141
- package/integration/hubspot/application.yaml +37 -0
- package/integration/hubspot/env.template +2 -11
- package/integration/hubspot/hubspot-deploy.json +1 -0
- package/integration/hubspot/test.js +5 -5
- package/lib/api/credentials.api.js +5 -5
- package/lib/api/deployments.api.js +2 -2
- package/lib/api/pipeline.api.js +17 -17
- package/lib/api/wizard.api.js +2 -2
- package/lib/app/config.js +11 -6
- package/lib/app/deploy-config.js +13 -16
- package/lib/app/deploy.js +29 -22
- package/lib/app/display.js +1 -1
- package/lib/app/dockerfile.js +11 -12
- package/lib/app/helpers.js +51 -13
- package/lib/app/index.js +14 -2
- package/lib/app/prompts.js +37 -45
- package/lib/app/push.js +8 -11
- package/lib/app/readme.js +16 -12
- package/lib/app/register.js +3 -3
- package/lib/app/run-helpers.js +31 -22
- package/lib/app/run.js +44 -5
- package/lib/app/show-display.js +104 -44
- package/lib/app/show.js +123 -43
- package/lib/build/index.js +11 -18
- package/lib/cli/setup-app.js +38 -28
- package/lib/cli/setup-auth.js +18 -15
- package/lib/cli/setup-credential-deployment.js +3 -1
- package/lib/cli/setup-external-system.js +35 -16
- package/lib/cli/setup-infra.js +45 -23
- package/lib/cli/setup-utility.js +79 -31
- package/lib/commands/app-logs.js +165 -10
- package/lib/commands/app.js +30 -26
- package/lib/commands/convert.js +202 -0
- package/lib/commands/credential-list.js +78 -17
- package/lib/commands/datasource.js +24 -24
- package/lib/commands/deployment-list.js +13 -6
- package/lib/commands/up-common.js +80 -42
- package/lib/commands/up-dataplane.js +15 -14
- package/lib/commands/up-miso.js +15 -14
- package/lib/commands/upload.js +163 -0
- package/lib/commands/wizard-core.js +5 -4
- package/lib/core/diff.js +84 -9
- package/lib/core/key-generator.js +9 -12
- package/lib/core/secrets-docker-env.js +2 -2
- package/lib/core/secrets.js +3 -2
- package/lib/core/templates.js +2 -2
- package/lib/datasource/deploy.js +2 -1
- package/lib/deployment/deployer.js +76 -48
- package/lib/external-system/delete.js +0 -1
- package/lib/external-system/deploy-helpers.js +5 -6
- package/lib/external-system/deploy.js +7 -2
- package/lib/external-system/download-helpers.js +4 -4
- package/lib/external-system/download.js +11 -10
- package/lib/external-system/generator.js +19 -17
- package/lib/external-system/test.js +10 -15
- package/lib/generator/builders.js +1 -1
- package/lib/generator/external-controller-manifest.js +26 -29
- package/lib/generator/external-schema-utils.js +6 -18
- package/lib/generator/external.js +32 -27
- package/lib/generator/github.js +1 -1
- package/lib/generator/helpers.js +12 -19
- package/lib/generator/index.js +15 -15
- package/lib/generator/parse-image.js +35 -0
- package/lib/generator/split-readme.js +105 -0
- package/lib/generator/split-variables.js +149 -0
- package/lib/generator/split.js +86 -246
- package/lib/generator/wizard.js +46 -69
- package/lib/schema/application-schema.json +4 -4
- package/lib/schema/deployment-rules.yaml +0 -4
- package/lib/schema/external-datasource.schema.json +5 -0
- package/lib/schema/external-system.schema.json +10 -0
- package/lib/utils/app-config-resolver.js +52 -0
- package/lib/utils/app-register-api.js +1 -1
- package/lib/utils/app-register-auth.js +1 -1
- package/lib/utils/app-register-config.js +16 -23
- package/lib/utils/app-register-display.js +22 -3
- package/lib/utils/app-register-validator.js +2 -2
- package/lib/utils/cli-utils.js +47 -3
- package/lib/utils/config-format.js +154 -0
- package/lib/utils/config-paths.js +19 -52
- package/lib/utils/config-tokens.js +1 -0
- package/lib/utils/docker-build.js +71 -94
- package/lib/utils/dockerfile-utils.js +1 -1
- package/lib/utils/env-copy.js +4 -4
- package/lib/utils/env-ports.js +2 -2
- package/lib/utils/error-formatter.js +1 -1
- package/lib/utils/error-formatters/validation-errors.js +1 -1
- package/lib/utils/external-readme.js +12 -5
- package/lib/utils/external-system-test-helpers.js +2 -0
- package/lib/utils/health-check.js +55 -66
- package/lib/utils/image-version.js +12 -21
- package/lib/utils/paths.js +39 -66
- package/lib/utils/port-resolver.js +8 -8
- package/lib/utils/schema-loader.js +22 -0
- package/lib/utils/schema-resolver.js +23 -33
- package/lib/utils/secrets-helpers.js +7 -7
- package/lib/utils/secrets-utils.js +10 -12
- package/lib/utils/template-helpers.js +13 -13
- package/lib/utils/token-manager.js +20 -2
- package/lib/utils/variable-transformer.js +2 -2
- package/lib/validation/validate-display.js +3 -4
- package/lib/validation/validate.js +33 -27
- package/lib/validation/validator.js +50 -30
- package/package.json +2 -1
- package/templates/README.md +1 -1
- package/templates/applications/README.md.hbs +3 -3
- package/templates/applications/miso-controller/env.template +3 -1
- package/templates/external-system/README.md.hbs +4 -4
- package/integration/hubspot/variables.yaml +0 -17
- /package/templates/applications/dataplane/{variables.yaml → application.yaml} +0 -0
- /package/templates/applications/keycloak/{variables.yaml → application.yaml} +0 -0
- /package/templates/applications/miso-controller/{variables.yaml → application.yaml} +0 -0
package/lib/generator/split.js
CHANGED
|
@@ -11,6 +11,9 @@
|
|
|
11
11
|
const fs = require('fs').promises;
|
|
12
12
|
const path = require('path');
|
|
13
13
|
const yaml = require('js-yaml');
|
|
14
|
+
const { parseImageReference } = require('./parse-image');
|
|
15
|
+
const { generateReadmeFromDeployJson } = require('./split-readme');
|
|
16
|
+
const { extractVariablesYaml, getExternalDatasourceFileName } = require('./split-variables');
|
|
14
17
|
|
|
15
18
|
/**
|
|
16
19
|
* Converts configuration array back to env.template format
|
|
@@ -43,191 +46,6 @@ function extractEnvTemplate(configuration) {
|
|
|
43
46
|
return lines.join('\n');
|
|
44
47
|
}
|
|
45
48
|
|
|
46
|
-
/**
|
|
47
|
-
* Parses image reference string into components
|
|
48
|
-
* @function parseImageReference
|
|
49
|
-
* @param {string} imageString - Full image string (e.g., "registry/name:tag")
|
|
50
|
-
* @returns {Object} Object with registry, name, and tag
|
|
51
|
-
*/
|
|
52
|
-
function parseImageReference(imageString) {
|
|
53
|
-
if (!imageString || typeof imageString !== 'string') {
|
|
54
|
-
return { registry: null, name: null, tag: 'latest' };
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
// Handle format: registry/name:tag or name:tag or registry/name
|
|
58
|
-
const parts = imageString.split('/');
|
|
59
|
-
let registry = null;
|
|
60
|
-
let nameAndTag = imageString;
|
|
61
|
-
|
|
62
|
-
if (parts.length > 1) {
|
|
63
|
-
// Check if first part looks like a registry (contains .)
|
|
64
|
-
if (parts[0].includes('.')) {
|
|
65
|
-
registry = parts[0];
|
|
66
|
-
nameAndTag = parts.slice(1).join('/');
|
|
67
|
-
} else {
|
|
68
|
-
// No registry, just name:tag
|
|
69
|
-
nameAndTag = imageString;
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
// Split name and tag
|
|
74
|
-
const tagIndex = nameAndTag.lastIndexOf(':');
|
|
75
|
-
let name = nameAndTag;
|
|
76
|
-
let tag = 'latest';
|
|
77
|
-
|
|
78
|
-
if (tagIndex !== -1) {
|
|
79
|
-
name = nameAndTag.substring(0, tagIndex);
|
|
80
|
-
tag = nameAndTag.substring(tagIndex + 1);
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
return { registry, name, tag };
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
/**
|
|
87
|
-
* Extract app section from deployment
|
|
88
|
-
* @param {Object} deployment - Deployment JSON object
|
|
89
|
-
* @returns {Object|undefined} App section or undefined
|
|
90
|
-
*/
|
|
91
|
-
function extractAppSection(deployment) {
|
|
92
|
-
if (!deployment.key && !deployment.displayName && !deployment.description && !deployment.type) {
|
|
93
|
-
return undefined;
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
const app = {};
|
|
97
|
-
if (deployment.key) app.key = deployment.key;
|
|
98
|
-
if (deployment.displayName) app.displayName = deployment.displayName;
|
|
99
|
-
if (deployment.description) app.description = deployment.description;
|
|
100
|
-
if (deployment.type) app.type = deployment.type;
|
|
101
|
-
if (deployment.version) app.version = deployment.version;
|
|
102
|
-
return app;
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
/**
|
|
106
|
-
* Extract image section from deployment
|
|
107
|
-
* @param {Object} deployment - Deployment JSON object
|
|
108
|
-
* @returns {Object|undefined} Image section or undefined
|
|
109
|
-
*/
|
|
110
|
-
function extractImageSection(deployment) {
|
|
111
|
-
if (!deployment.image) {
|
|
112
|
-
return undefined;
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
const imageParts = parseImageReference(deployment.image);
|
|
116
|
-
const image = {};
|
|
117
|
-
if (imageParts.name) image.name = imageParts.name;
|
|
118
|
-
if (imageParts.registry) image.registry = imageParts.registry;
|
|
119
|
-
if (imageParts.tag) image.tag = imageParts.tag;
|
|
120
|
-
if (deployment.registryMode) image.registryMode = deployment.registryMode;
|
|
121
|
-
return image;
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
/**
|
|
125
|
-
* Extract requirements section from deployment
|
|
126
|
-
* @param {Object} deployment - Deployment JSON object
|
|
127
|
-
* @returns {Object|undefined} Requirements section or undefined
|
|
128
|
-
*/
|
|
129
|
-
function extractRequirementsSection(deployment) {
|
|
130
|
-
if (!deployment.requiresDatabase && !deployment.requiresRedis && !deployment.requiresStorage && !deployment.databases) {
|
|
131
|
-
return undefined;
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
const requires = {};
|
|
135
|
-
if (deployment.requiresDatabase !== undefined) requires.database = deployment.requiresDatabase;
|
|
136
|
-
if (deployment.requiresRedis !== undefined) requires.redis = deployment.requiresRedis;
|
|
137
|
-
if (deployment.requiresStorage !== undefined) requires.storage = deployment.requiresStorage;
|
|
138
|
-
if (deployment.databases) requires.databases = deployment.databases;
|
|
139
|
-
return requires;
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
/**
|
|
143
|
-
* Extract optional sections from deployment
|
|
144
|
-
* @param {Object} deployment - Deployment JSON object
|
|
145
|
-
* @returns {Object} Object with optional sections
|
|
146
|
-
*/
|
|
147
|
-
/**
|
|
148
|
-
* Extracts a single optional section if present
|
|
149
|
-
* @function extractOptionalSection
|
|
150
|
-
* @param {Object} deployment - Deployment object
|
|
151
|
-
* @param {string} sectionName - Section name to extract
|
|
152
|
-
* @param {Object} optional - Optional sections object to update
|
|
153
|
-
*/
|
|
154
|
-
function extractOptionalSection(deployment, sectionName, optional) {
|
|
155
|
-
if (deployment[sectionName]) {
|
|
156
|
-
if (sectionName === 'authentication') {
|
|
157
|
-
optional[sectionName] = { ...deployment[sectionName] };
|
|
158
|
-
} else {
|
|
159
|
-
optional[sectionName] = deployment[sectionName];
|
|
160
|
-
}
|
|
161
|
-
}
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
function extractOptionalSections(deployment) {
|
|
165
|
-
const optional = {};
|
|
166
|
-
|
|
167
|
-
const optionalSectionNames = [
|
|
168
|
-
'healthCheck',
|
|
169
|
-
'authentication',
|
|
170
|
-
'build',
|
|
171
|
-
'repository',
|
|
172
|
-
'deployment',
|
|
173
|
-
'startupCommand',
|
|
174
|
-
'runtimeVersion',
|
|
175
|
-
'scaling',
|
|
176
|
-
'frontDoorRouting',
|
|
177
|
-
'roles',
|
|
178
|
-
'permissions'
|
|
179
|
-
];
|
|
180
|
-
|
|
181
|
-
for (const sectionName of optionalSectionNames) {
|
|
182
|
-
extractOptionalSection(deployment, sectionName, optional);
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
return optional;
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
/**
|
|
189
|
-
* Extracts deployment JSON into variables.yaml structure
|
|
190
|
-
* @function extractVariablesYaml
|
|
191
|
-
* @param {Object} deployment - Deployment JSON object
|
|
192
|
-
* @returns {Object} Variables YAML structure
|
|
193
|
-
*/
|
|
194
|
-
function extractVariablesYaml(deployment) {
|
|
195
|
-
if (!deployment || typeof deployment !== 'object') {
|
|
196
|
-
throw new Error('Deployment object is required');
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
const variables = {};
|
|
200
|
-
|
|
201
|
-
// Extract app section
|
|
202
|
-
const appSection = extractAppSection(deployment);
|
|
203
|
-
if (appSection) {
|
|
204
|
-
variables.app = appSection;
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
// Extract image section
|
|
208
|
-
const imageSection = extractImageSection(deployment);
|
|
209
|
-
if (imageSection) {
|
|
210
|
-
variables.image = imageSection;
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
// Extract port
|
|
214
|
-
if (deployment.port !== undefined) {
|
|
215
|
-
variables.port = deployment.port;
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
// Extract requirements section
|
|
219
|
-
const requirementsSection = extractRequirementsSection(deployment);
|
|
220
|
-
if (requirementsSection) {
|
|
221
|
-
variables.requires = requirementsSection;
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
// Extract optional sections
|
|
225
|
-
const optionalSections = extractOptionalSections(deployment);
|
|
226
|
-
Object.assign(variables, optionalSections);
|
|
227
|
-
|
|
228
|
-
return variables;
|
|
229
|
-
}
|
|
230
|
-
|
|
231
49
|
/**
|
|
232
50
|
* Extracts roles and permissions from deployment JSON
|
|
233
51
|
* @function extractRbacYaml
|
|
@@ -257,62 +75,6 @@ function extractRbacYaml(deployment) {
|
|
|
257
75
|
return rbac;
|
|
258
76
|
}
|
|
259
77
|
|
|
260
|
-
/**
|
|
261
|
-
* Generates README.md content from deployment JSON
|
|
262
|
-
* @function generateReadmeFromDeployJson
|
|
263
|
-
* @param {Object} deployment - Deployment JSON object
|
|
264
|
-
* @returns {string} README.md content
|
|
265
|
-
*/
|
|
266
|
-
function generateReadmeFromDeployJson(deployment) {
|
|
267
|
-
if (!deployment || typeof deployment !== 'object') {
|
|
268
|
-
throw new Error('Deployment object is required');
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
const appName = deployment.key || 'application';
|
|
272
|
-
const displayName = deployment.displayName || appName;
|
|
273
|
-
const description = deployment.description || 'Application deployment configuration';
|
|
274
|
-
const port = deployment.port || 3000;
|
|
275
|
-
const image = deployment.image || 'unknown';
|
|
276
|
-
|
|
277
|
-
const lines = [
|
|
278
|
-
`# ${displayName}`,
|
|
279
|
-
'',
|
|
280
|
-
description,
|
|
281
|
-
'',
|
|
282
|
-
'## Quick Start',
|
|
283
|
-
'',
|
|
284
|
-
'This application is configured via deployment JSON and component files.',
|
|
285
|
-
'',
|
|
286
|
-
'## Configuration',
|
|
287
|
-
'',
|
|
288
|
-
`- **Application Key**: \`${appName}\``,
|
|
289
|
-
`- **Port**: \`${port}\``,
|
|
290
|
-
`- **Image**: \`${image}\``,
|
|
291
|
-
'',
|
|
292
|
-
'## Files',
|
|
293
|
-
'',
|
|
294
|
-
'- `variables.yaml` - Application configuration',
|
|
295
|
-
'- `env.template` - Environment variables template',
|
|
296
|
-
'- `rbac.yml` - Roles and permissions (if applicable)',
|
|
297
|
-
'- `README.md` - This file',
|
|
298
|
-
'',
|
|
299
|
-
'## Documentation',
|
|
300
|
-
'',
|
|
301
|
-
'For more information, see the [AI Fabrix Builder documentation](../../docs/README.md).'
|
|
302
|
-
];
|
|
303
|
-
|
|
304
|
-
return lines.join('\n');
|
|
305
|
-
}
|
|
306
|
-
|
|
307
|
-
/**
|
|
308
|
-
* Splits a deployment JSON file into component files
|
|
309
|
-
* @async
|
|
310
|
-
* @function splitDeployJson
|
|
311
|
-
* @param {string} deployJsonPath - Path to deployment JSON file
|
|
312
|
-
* @param {string} [outputDir] - Directory to write component files (defaults to same directory as JSON)
|
|
313
|
-
* @returns {Promise<Object>} Object with paths to generated files
|
|
314
|
-
* @throws {Error} If JSON file not found or invalid
|
|
315
|
-
*/
|
|
316
78
|
/**
|
|
317
79
|
* Validates deployment JSON path
|
|
318
80
|
* @function validateDeployJsonPath
|
|
@@ -396,8 +158,8 @@ async function writeComponentFiles(outputDir, envTemplate, variables, rbac, read
|
|
|
396
158
|
await writeComponentFile(envTemplatePath, envTemplate);
|
|
397
159
|
results.envTemplate = envTemplatePath;
|
|
398
160
|
|
|
399
|
-
// Write
|
|
400
|
-
const variablesPath = path.join(outputDir, '
|
|
161
|
+
// Write application.yaml
|
|
162
|
+
const variablesPath = path.join(outputDir, 'application.yaml');
|
|
401
163
|
const variablesYaml = yaml.dump(variables, { indent: 2, lineWidth: -1 });
|
|
402
164
|
await writeComponentFile(variablesPath, variablesYaml);
|
|
403
165
|
results.variables = variablesPath;
|
|
@@ -418,18 +180,96 @@ async function writeComponentFiles(outputDir, envTemplate, variables, rbac, read
|
|
|
418
180
|
return results;
|
|
419
181
|
}
|
|
420
182
|
|
|
183
|
+
/**
|
|
184
|
+
* Writes external system and datasource YAML files when deployment has system (external format).
|
|
185
|
+
* @async
|
|
186
|
+
* @param {string} outputDir - Output directory
|
|
187
|
+
* @param {Object} deployment - Deployment with system and dataSources
|
|
188
|
+
* @returns {Promise<{ systemFile?: string, datasourceFiles?: string[] }>} Paths to written files
|
|
189
|
+
*/
|
|
190
|
+
async function writeExternalSystemAndDatasourceFiles(outputDir, deployment) {
|
|
191
|
+
if (!deployment || !deployment.system) {
|
|
192
|
+
return {};
|
|
193
|
+
}
|
|
194
|
+
const system = deployment.system;
|
|
195
|
+
const systemKey = system.key || 'external-system';
|
|
196
|
+
const dataSourcesList = deployment.dataSources || deployment.datasources || [];
|
|
197
|
+
const results = {};
|
|
198
|
+
|
|
199
|
+
const systemPath = path.join(outputDir, `${systemKey}-system.yaml`);
|
|
200
|
+
const systemYaml = yaml.dump(system, { indent: 2, lineWidth: -1 });
|
|
201
|
+
await writeComponentFile(systemPath, systemYaml);
|
|
202
|
+
results.systemFile = systemPath;
|
|
203
|
+
|
|
204
|
+
const datasourcePaths = [];
|
|
205
|
+
for (let i = 0; i < dataSourcesList.length; i++) {
|
|
206
|
+
const ds = dataSourcesList[i];
|
|
207
|
+
const fileName = getExternalDatasourceFileName(systemKey, ds, i);
|
|
208
|
+
const dsPath = path.join(outputDir, fileName);
|
|
209
|
+
const dsYaml = yaml.dump(ds, { indent: 2, lineWidth: -1 });
|
|
210
|
+
await writeComponentFile(dsPath, dsYaml);
|
|
211
|
+
datasourcePaths.push(dsPath);
|
|
212
|
+
}
|
|
213
|
+
results.datasourceFiles = datasourcePaths;
|
|
214
|
+
|
|
215
|
+
return results;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
/**
|
|
219
|
+
* Normalizes deployment for split: for external format (deployment.system),
|
|
220
|
+
* lifts configuration and roles/permissions to top level so extractors work.
|
|
221
|
+
* @param {Object} deployment - Raw deployment object
|
|
222
|
+
* @returns {Object} Deployment (mutated) with configuration/roles/permissions at top level when from system
|
|
223
|
+
*/
|
|
224
|
+
function normalizeDeploymentForSplit(deployment) {
|
|
225
|
+
if (!deployment || !deployment.system) {
|
|
226
|
+
return deployment;
|
|
227
|
+
}
|
|
228
|
+
const system = deployment.system;
|
|
229
|
+
if (system.configuration && !deployment.configuration) {
|
|
230
|
+
deployment.configuration = system.configuration;
|
|
231
|
+
}
|
|
232
|
+
if (system.roles && !deployment.roles) {
|
|
233
|
+
deployment.roles = system.roles;
|
|
234
|
+
}
|
|
235
|
+
if (system.permissions && !deployment.permissions) {
|
|
236
|
+
deployment.permissions = system.permissions;
|
|
237
|
+
}
|
|
238
|
+
return deployment;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
/**
|
|
242
|
+
* Splits a deployment JSON file into component files.
|
|
243
|
+
* @async
|
|
244
|
+
* @function splitDeployJson
|
|
245
|
+
* @param {string} deployJsonPath - Path to deployment JSON file
|
|
246
|
+
* @param {string} [outputDir] - Directory to write component files (defaults to same directory as JSON)
|
|
247
|
+
* @returns {Promise<Object>} Object with paths to generated files
|
|
248
|
+
* @throws {Error} If JSON file not found or invalid
|
|
249
|
+
*/
|
|
421
250
|
async function splitDeployJson(deployJsonPath, outputDir = null) {
|
|
422
251
|
validateDeployJsonPath(deployJsonPath);
|
|
423
252
|
const finalOutputDir = await prepareOutputDirectory(deployJsonPath, outputDir);
|
|
424
253
|
const deployment = await loadDeploymentJson(deployJsonPath);
|
|
254
|
+
normalizeDeploymentForSplit(deployment);
|
|
425
255
|
|
|
426
|
-
|
|
427
|
-
const envTemplate = extractEnvTemplate(
|
|
256
|
+
const configArray = deployment.configuration || [];
|
|
257
|
+
const envTemplate = extractEnvTemplate(configArray);
|
|
428
258
|
const variables = extractVariablesYaml(deployment);
|
|
429
259
|
const rbac = extractRbacYaml(deployment);
|
|
430
260
|
const readme = generateReadmeFromDeployJson(deployment);
|
|
431
261
|
|
|
432
|
-
|
|
262
|
+
const result = await writeComponentFiles(finalOutputDir, envTemplate, variables, rbac, readme);
|
|
263
|
+
|
|
264
|
+
if (deployment.system && typeof deployment.system === 'object') {
|
|
265
|
+
const externalFiles = await writeExternalSystemAndDatasourceFiles(finalOutputDir, deployment);
|
|
266
|
+
if (externalFiles.systemFile) result.systemFile = externalFiles.systemFile;
|
|
267
|
+
if (externalFiles.datasourceFiles && externalFiles.datasourceFiles.length > 0) {
|
|
268
|
+
result.datasourceFiles = externalFiles.datasourceFiles;
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
return result;
|
|
433
273
|
}
|
|
434
274
|
|
|
435
275
|
module.exports = {
|
package/lib/generator/wizard.js
CHANGED
|
@@ -6,10 +6,11 @@
|
|
|
6
6
|
|
|
7
7
|
const fs = require('fs').promises;
|
|
8
8
|
const path = require('path');
|
|
9
|
-
const yaml = require('js-yaml');
|
|
10
9
|
const Handlebars = require('handlebars');
|
|
11
10
|
const chalk = require('chalk');
|
|
12
11
|
const logger = require('../utils/logger');
|
|
12
|
+
const { resolveApplicationConfigPath } = require('../utils/app-config-resolver');
|
|
13
|
+
const { loadConfigFile, writeConfigFile } = require('../utils/config-format');
|
|
13
14
|
const { generateExternalReadmeContent } = require('../utils/external-readme');
|
|
14
15
|
|
|
15
16
|
/**
|
|
@@ -39,30 +40,30 @@ function toKeySegment(str) {
|
|
|
39
40
|
/**
|
|
40
41
|
* Writes system JSON file
|
|
41
42
|
* @async
|
|
42
|
-
* @function
|
|
43
|
+
* @function writeSystemYamlFile
|
|
43
44
|
* @param {string} appPath - Application path
|
|
44
45
|
* @param {string} finalSystemKey - Final system key
|
|
45
46
|
* @param {Object} systemConfig - System configuration
|
|
46
47
|
* @returns {Promise<string>} System file path
|
|
47
48
|
*/
|
|
48
|
-
async function
|
|
49
|
-
const systemFileName = `${finalSystemKey}-system.
|
|
49
|
+
async function writeSystemYamlFile(appPath, finalSystemKey, systemConfig) {
|
|
50
|
+
const systemFileName = `${finalSystemKey}-system.yaml`;
|
|
50
51
|
const systemFilePath = path.join(appPath, systemFileName);
|
|
51
|
-
|
|
52
|
+
writeConfigFile(systemFilePath, systemConfig);
|
|
52
53
|
logger.log(chalk.green(`✓ Generated system file: ${systemFileName}`));
|
|
53
54
|
return systemFilePath;
|
|
54
55
|
}
|
|
55
56
|
|
|
56
57
|
/**
|
|
57
|
-
* Writes datasource
|
|
58
|
+
* Writes datasource YAML files
|
|
58
59
|
* @async
|
|
59
|
-
* @function
|
|
60
|
+
* @function writeDatasourceYamlFiles
|
|
60
61
|
* @param {string} appPath - Application path
|
|
61
62
|
* @param {string} finalSystemKey - Final system key
|
|
62
63
|
* @param {Object[]} datasourceConfigs - Array of datasource configurations
|
|
63
64
|
* @returns {Promise<string[]>} Array of datasource file names
|
|
64
65
|
*/
|
|
65
|
-
async function
|
|
66
|
+
async function writeDatasourceYamlFiles(appPath, finalSystemKey, datasourceConfigs) {
|
|
66
67
|
const datasourceFileNames = [];
|
|
67
68
|
for (const datasourceConfig of datasourceConfigs) {
|
|
68
69
|
const entityType = datasourceConfig.entityType || datasourceConfig.entityKey || datasourceConfig.key?.split('-').pop() || 'default';
|
|
@@ -72,9 +73,9 @@ async function writeDatasourceJsonFiles(appPath, finalSystemKey, datasourceConfi
|
|
|
72
73
|
const datasourceKeyOnly = datasourceKey.includes('-') && datasourceKey.startsWith(`${finalSystemKey}-`)
|
|
73
74
|
? datasourceKey.substring(finalSystemKey.length + 1)
|
|
74
75
|
: keySegment;
|
|
75
|
-
const datasourceFileName = `${finalSystemKey}-datasource-${datasourceKeyOnly}.
|
|
76
|
+
const datasourceFileName = `${finalSystemKey}-datasource-${datasourceKeyOnly}.yaml`;
|
|
76
77
|
const datasourceFilePath = path.join(appPath, datasourceFileName);
|
|
77
|
-
|
|
78
|
+
writeConfigFile(datasourceFilePath, datasourceConfig);
|
|
78
79
|
datasourceFileNames.push(datasourceFileName);
|
|
79
80
|
logger.log(chalk.green(`✓ Generated datasource file: ${datasourceFileName}`));
|
|
80
81
|
}
|
|
@@ -99,8 +100,8 @@ async function writeDatasourceJsonFiles(appPath, finalSystemKey, datasourceConfi
|
|
|
99
100
|
async function generateConfigFilesForWizard(params) {
|
|
100
101
|
const { appPath, appName, finalSystemKey, systemFileName, datasourceFileNames, systemConfig, datasourceConfigs, aiGeneratedReadme } = params;
|
|
101
102
|
|
|
102
|
-
// Generate or update
|
|
103
|
-
await generateOrUpdateVariablesYaml({
|
|
103
|
+
// Generate or update application.yaml with externalIntegration block
|
|
104
|
+
const configPath = await generateOrUpdateVariablesYaml({
|
|
104
105
|
appPath,
|
|
105
106
|
appName,
|
|
106
107
|
systemKey: finalSystemKey,
|
|
@@ -126,7 +127,7 @@ async function generateConfigFilesForWizard(params) {
|
|
|
126
127
|
logger.log(chalk.green(`✓ Generated deployment manifest: ${finalSystemKey}-deploy.json`));
|
|
127
128
|
|
|
128
129
|
return {
|
|
129
|
-
variablesPath:
|
|
130
|
+
variablesPath: configPath,
|
|
130
131
|
envTemplatePath: path.join(appPath, 'env.template'),
|
|
131
132
|
readmePath: path.join(appPath, 'README.md'),
|
|
132
133
|
applicationSchemaPath: deployManifestPath,
|
|
@@ -134,48 +135,28 @@ async function generateConfigFilesForWizard(params) {
|
|
|
134
135
|
};
|
|
135
136
|
}
|
|
136
137
|
|
|
138
|
+
async function prepareWizardContext(appName, systemConfig, datasourceConfigs) {
|
|
139
|
+
const appPath = path.join(process.cwd(), 'integration', appName);
|
|
140
|
+
await fs.mkdir(appPath, { recursive: true });
|
|
141
|
+
const finalSystemKey = appName;
|
|
142
|
+
const appDisplayName = appName.replace(/-/g, ' ').replace(/\b\w/g, l => l.toUpperCase());
|
|
143
|
+
const updatedSystemConfig = { ...systemConfig, key: finalSystemKey, displayName: appDisplayName };
|
|
144
|
+
const updatedDatasourceConfigs = datasourceConfigs.map(ds => {
|
|
145
|
+
const entityType = ds.entityType || ds.entityKey || ds.key?.split('-').pop() || 'default';
|
|
146
|
+
const keySegment = toKeySegment(entityType);
|
|
147
|
+
const entityDisplayName = entityType.charAt(0).toUpperCase() + entityType.slice(1).replace(/-/g, ' ');
|
|
148
|
+
return { ...ds, key: `${finalSystemKey}-${keySegment}`, systemKey: finalSystemKey, displayName: `${appDisplayName} ${entityDisplayName}` };
|
|
149
|
+
});
|
|
150
|
+
return { appPath, finalSystemKey, updatedSystemConfig, updatedDatasourceConfigs, appDisplayName };
|
|
151
|
+
}
|
|
152
|
+
|
|
137
153
|
async function generateWizardFiles(appName, systemConfig, datasourceConfigs, systemKey, options = {}) {
|
|
138
154
|
try {
|
|
139
155
|
const { aiGeneratedReadme } = options || {};
|
|
140
|
-
|
|
141
|
-
const
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
await fs.mkdir(appPath, { recursive: true });
|
|
145
|
-
|
|
146
|
-
// Use appName as the system key to ensure consistent naming
|
|
147
|
-
// Priority: appName > systemKey parameter > systemConfig.key
|
|
148
|
-
const finalSystemKey = appName;
|
|
149
|
-
|
|
150
|
-
// Generate displayName from appName (e.g., "my-hubspot" -> "My Hubspot")
|
|
151
|
-
const appDisplayName = appName.replace(/-/g, ' ').replace(/\b\w/g, l => l.toUpperCase());
|
|
152
|
-
|
|
153
|
-
// Update system config to use the appName as key and displayName
|
|
154
|
-
const updatedSystemConfig = {
|
|
155
|
-
...systemConfig,
|
|
156
|
-
key: finalSystemKey,
|
|
157
|
-
displayName: appDisplayName
|
|
158
|
-
};
|
|
159
|
-
|
|
160
|
-
// Update datasource configs to use appName-based keys and systemKey (key must match ^[a-z0-9-]+$)
|
|
161
|
-
const updatedDatasourceConfigs = datasourceConfigs.map(ds => {
|
|
162
|
-
const entityType = ds.entityType || ds.entityKey || ds.key?.split('-').pop() || 'default';
|
|
163
|
-
const keySegment = toKeySegment(entityType);
|
|
164
|
-
const entityDisplayName = entityType.charAt(0).toUpperCase() + entityType.slice(1).replace(/-/g, ' ');
|
|
165
|
-
return {
|
|
166
|
-
...ds,
|
|
167
|
-
key: `${finalSystemKey}-${keySegment}`,
|
|
168
|
-
systemKey: finalSystemKey,
|
|
169
|
-
displayName: `${appDisplayName} ${entityDisplayName}`
|
|
170
|
-
};
|
|
171
|
-
});
|
|
172
|
-
|
|
173
|
-
// Write system and datasource JSON files
|
|
174
|
-
const systemFilePath = await writeSystemJsonFile(appPath, finalSystemKey, updatedSystemConfig);
|
|
175
|
-
const datasourceFileNames = await writeDatasourceJsonFiles(appPath, finalSystemKey, updatedDatasourceConfigs);
|
|
176
|
-
|
|
177
|
-
// Generate configuration files
|
|
178
|
-
const systemFileName = `${finalSystemKey}-system.json`;
|
|
156
|
+
const { appPath, finalSystemKey, updatedSystemConfig, updatedDatasourceConfigs } = await prepareWizardContext(appName, systemConfig, datasourceConfigs);
|
|
157
|
+
const systemFilePath = await writeSystemYamlFile(appPath, finalSystemKey, updatedSystemConfig);
|
|
158
|
+
const datasourceFileNames = await writeDatasourceYamlFiles(appPath, finalSystemKey, updatedDatasourceConfigs);
|
|
159
|
+
const systemFileName = `${finalSystemKey}-system.yaml`;
|
|
179
160
|
const configFiles = await generateConfigFilesForWizard({
|
|
180
161
|
appPath,
|
|
181
162
|
appName,
|
|
@@ -186,7 +167,6 @@ async function generateWizardFiles(appName, systemConfig, datasourceConfigs, sys
|
|
|
186
167
|
datasourceConfigs: updatedDatasourceConfigs,
|
|
187
168
|
aiGeneratedReadme
|
|
188
169
|
});
|
|
189
|
-
|
|
190
170
|
return {
|
|
191
171
|
appPath,
|
|
192
172
|
systemFilePath,
|
|
@@ -199,7 +179,7 @@ async function generateWizardFiles(appName, systemConfig, datasourceConfigs, sys
|
|
|
199
179
|
}
|
|
200
180
|
|
|
201
181
|
/**
|
|
202
|
-
* Generate or update
|
|
182
|
+
* Generate or update application.yaml with externalIntegration block
|
|
203
183
|
* @async
|
|
204
184
|
* @function generateOrUpdateVariablesYaml
|
|
205
185
|
* @param {Object} params - Parameters object
|
|
@@ -209,23 +189,19 @@ async function generateWizardFiles(appName, systemConfig, datasourceConfigs, sys
|
|
|
209
189
|
* @param {string} params.systemFileName - System file name
|
|
210
190
|
* @param {string[]} params.datasourceFileNames - Array of datasource file names
|
|
211
191
|
* @param {Object} params.systemConfig - System configuration
|
|
192
|
+
* @returns {Promise<string>} Path to application config file
|
|
212
193
|
* @throws {Error} If generation fails
|
|
213
194
|
*/
|
|
214
195
|
async function generateOrUpdateVariablesYaml(params) {
|
|
215
196
|
const { appPath, appName, systemFileName, datasourceFileNames, systemConfig } = params;
|
|
197
|
+
let configPath;
|
|
198
|
+
let variables = {};
|
|
216
199
|
try {
|
|
217
|
-
const variablesPath = path.join(appPath, 'variables.yaml');
|
|
218
|
-
let variables = {};
|
|
219
|
-
|
|
220
|
-
// Try to read existing variables.yaml
|
|
221
200
|
try {
|
|
222
|
-
|
|
223
|
-
variables =
|
|
224
|
-
} catch
|
|
225
|
-
|
|
226
|
-
if (error.code !== 'ENOENT') {
|
|
227
|
-
throw error;
|
|
228
|
-
}
|
|
201
|
+
configPath = resolveApplicationConfigPath(appPath);
|
|
202
|
+
variables = loadConfigFile(configPath) || {};
|
|
203
|
+
} catch {
|
|
204
|
+
configPath = path.join(appPath, 'application.yaml');
|
|
229
205
|
}
|
|
230
206
|
|
|
231
207
|
// Set basic app info if not present
|
|
@@ -258,10 +234,11 @@ async function generateOrUpdateVariablesYaml(params) {
|
|
|
258
234
|
version: systemConfig.version || '1.0.0'
|
|
259
235
|
};
|
|
260
236
|
|
|
261
|
-
|
|
262
|
-
logger.log(chalk.green('✓ Generated/updated
|
|
237
|
+
writeConfigFile(configPath, variables);
|
|
238
|
+
logger.log(chalk.green('✓ Generated/updated application.yaml'));
|
|
239
|
+
return configPath;
|
|
263
240
|
} catch (error) {
|
|
264
|
-
throw new Error(`Failed to generate
|
|
241
|
+
throw new Error(`Failed to generate application config: ${error.message}`);
|
|
265
242
|
}
|
|
266
243
|
}
|
|
267
244
|
|
|
@@ -445,7 +422,7 @@ async function generateReadme(appPath, appName, systemKey, systemConfig, datasou
|
|
|
445
422
|
return {
|
|
446
423
|
entityType,
|
|
447
424
|
displayName: ds.displayName || ds.name || ds.key || `Datasource ${index + 1}`,
|
|
448
|
-
fileName: `${systemKey}-datasource-${datasourceKeyOnly}.
|
|
425
|
+
fileName: `${systemKey}-datasource-${datasourceKeyOnly}.yaml`
|
|
449
426
|
};
|
|
450
427
|
});
|
|
451
428
|
|
|
@@ -799,19 +799,19 @@
|
|
|
799
799
|
},
|
|
800
800
|
"systems": {
|
|
801
801
|
"type": "array",
|
|
802
|
-
"description": "List of external-system
|
|
802
|
+
"description": "List of external-system files to deploy via pipeline (.json, .yaml, or .yml).",
|
|
803
803
|
"items": {
|
|
804
804
|
"type": "string",
|
|
805
|
-
"pattern": "^[^ ].+\\.json$"
|
|
805
|
+
"pattern": "^[^ ].+\\.(json|yaml|yml)$"
|
|
806
806
|
},
|
|
807
807
|
"uniqueItems": true
|
|
808
808
|
},
|
|
809
809
|
"dataSources": {
|
|
810
810
|
"type": "array",
|
|
811
|
-
"description": "List of external-datasource
|
|
811
|
+
"description": "List of external-datasource files belonging to this app (.json, .yaml, or .yml).",
|
|
812
812
|
"items": {
|
|
813
813
|
"type": "string",
|
|
814
|
-
"pattern": "^[^ ].+\\.json$"
|
|
814
|
+
"pattern": "^[^ ].+\\.(json|yaml|yml)$"
|
|
815
815
|
},
|
|
816
816
|
"uniqueItems": true
|
|
817
817
|
},
|
|
@@ -49,7 +49,6 @@ application:
|
|
|
49
49
|
overridablePaths:
|
|
50
50
|
- configuration.items.value
|
|
51
51
|
- authentication.endpoints
|
|
52
|
-
- deployment.controllerUrl
|
|
53
52
|
- healthCheck.interval
|
|
54
53
|
- healthCheck.probeIntervalInSeconds
|
|
55
54
|
|
|
@@ -60,7 +59,6 @@ externalSystem:
|
|
|
60
59
|
- description
|
|
61
60
|
- type
|
|
62
61
|
- enabled
|
|
63
|
-
- environment
|
|
64
62
|
- authentication
|
|
65
63
|
- openapi
|
|
66
64
|
- mcp
|
|
@@ -75,8 +73,6 @@ externalSystem:
|
|
|
75
73
|
- generateMcpContract
|
|
76
74
|
- generateOpenApiContract
|
|
77
75
|
overridablePaths:
|
|
78
|
-
- environment.baseUrl
|
|
79
|
-
- environment.region
|
|
80
76
|
- authentication.oauth2
|
|
81
77
|
- authentication.apikey
|
|
82
78
|
- authentication.basic
|
|
@@ -1587,6 +1587,11 @@
|
|
|
1587
1587
|
}
|
|
1588
1588
|
},
|
|
1589
1589
|
"additionalProperties":false
|
|
1590
|
+
},
|
|
1591
|
+
"triggerPathsHash":{
|
|
1592
|
+
"type":"string",
|
|
1593
|
+
"description":"SHA256 hash of triggerPaths payload (64-char hex). Used to detect structural changes. Optional; Dataplane computes when absent.",
|
|
1594
|
+
"pattern":"^[a-f0-9]{64}$"
|
|
1590
1595
|
}
|
|
1591
1596
|
},
|
|
1592
1597
|
"additionalProperties":false
|
|
@@ -102,6 +102,11 @@
|
|
|
102
102
|
"type": "boolean",
|
|
103
103
|
"default": true
|
|
104
104
|
},
|
|
105
|
+
"internal": {
|
|
106
|
+
"type": "boolean",
|
|
107
|
+
"description": "When true, this integration is deployed on dataplane startup (internal integration). When false or absent, deployed via pipeline only.",
|
|
108
|
+
"default": false
|
|
109
|
+
},
|
|
105
110
|
"authentication": {
|
|
106
111
|
"type": "object",
|
|
107
112
|
"description": "Authentication configuration for external system",
|
|
@@ -414,6 +419,11 @@
|
|
|
414
419
|
"type": "boolean",
|
|
415
420
|
"description": "Reserved: whether to generate or expose OpenAPI contract on publish. Not yet implemented.",
|
|
416
421
|
"default": true
|
|
422
|
+
},
|
|
423
|
+
"triggerPathsHash": {
|
|
424
|
+
"type": "string",
|
|
425
|
+
"description": "SHA256 hash of triggerPaths payload (64-char hex). Used to detect structural changes. Optional; Dataplane computes when absent.",
|
|
426
|
+
"pattern": "^[a-f0-9]{64}$"
|
|
417
427
|
}
|
|
418
428
|
},
|
|
419
429
|
"additionalProperties": false
|