@aifabrix/builder 2.39.3 → 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 +1 -1
- 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 +36 -29
- 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 +28 -20
- 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/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-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
|
@@ -56,18 +56,6 @@ function getSystemKey(application) {
|
|
|
56
56
|
return systemKey;
|
|
57
57
|
}
|
|
58
58
|
|
|
59
|
-
/**
|
|
60
|
-
* Writes JSON file with formatting
|
|
61
|
-
* @async
|
|
62
|
-
* @function writeJsonFile
|
|
63
|
-
* @param {string} filePath - File path
|
|
64
|
-
* @param {Object} data - JSON data
|
|
65
|
-
* @returns {Promise<void>} Resolves when file is written
|
|
66
|
-
*/
|
|
67
|
-
async function writeJsonFile(filePath, data) {
|
|
68
|
-
await fs.promises.writeFile(filePath, JSON.stringify(data, null, 2), 'utf8');
|
|
69
|
-
}
|
|
70
|
-
|
|
71
59
|
/**
|
|
72
60
|
* Resolves datasource entity type
|
|
73
61
|
* @function getDatasourceEntityType
|
|
@@ -104,7 +92,7 @@ function getDatasourceFileName(systemKey, datasource, index) {
|
|
|
104
92
|
} else {
|
|
105
93
|
datasourceKeyOnly = getDatasourceEntityType(datasource, index);
|
|
106
94
|
}
|
|
107
|
-
return `${systemKey}-datasource-${datasourceKeyOnly}.
|
|
95
|
+
return `${systemKey}-datasource-${datasourceKeyOnly}.yaml`;
|
|
108
96
|
}
|
|
109
97
|
|
|
110
98
|
/**
|
|
@@ -122,14 +110,14 @@ async function writeDatasourceFiles(outputDir, systemKey, dataSources) {
|
|
|
122
110
|
const datasource = dataSources[i];
|
|
123
111
|
const datasourceFileName = getDatasourceFileName(systemKey, datasource, i);
|
|
124
112
|
const datasourceFilePath = path.join(outputDir, datasourceFileName);
|
|
125
|
-
await
|
|
113
|
+
await writeYamlFile(datasourceFilePath, datasource, { indent: 2, lineWidth: 120, noRefs: true });
|
|
126
114
|
datasourceFileNames.push(datasourceFileName);
|
|
127
115
|
}
|
|
128
116
|
return datasourceFileNames;
|
|
129
117
|
}
|
|
130
118
|
|
|
131
119
|
/**
|
|
132
|
-
* Builds
|
|
120
|
+
* Builds application config content for external integrations
|
|
133
121
|
* @function buildExternalVariables
|
|
134
122
|
* @param {string} systemKey - System key
|
|
135
123
|
* @param {Object} application - Application schema
|
|
@@ -187,14 +175,14 @@ async function writeYamlFile(filePath, data, options) {
|
|
|
187
175
|
* @returns {Promise<Object>} Paths to generated files
|
|
188
176
|
*/
|
|
189
177
|
async function writeSplitExternalSchemaFiles({ outputDir, systemKey, application, dataSources, version }) {
|
|
190
|
-
const systemFileName = `${systemKey}-system.
|
|
178
|
+
const systemFileName = `${systemKey}-system.yaml`;
|
|
191
179
|
const systemFilePath = path.join(outputDir, systemFileName);
|
|
192
|
-
await
|
|
180
|
+
await writeYamlFile(systemFilePath, application, { indent: 2, lineWidth: 120, noRefs: true });
|
|
193
181
|
|
|
194
182
|
const datasourceFileNames = await writeDatasourceFiles(outputDir, systemKey, dataSources);
|
|
195
183
|
const variables = buildExternalVariables(systemKey, application, systemFileName, datasourceFileNames, version);
|
|
196
184
|
|
|
197
|
-
const variablesPath = path.join(outputDir, '
|
|
185
|
+
const variablesPath = path.join(outputDir, 'application.yaml');
|
|
198
186
|
await writeYamlFile(variablesPath, variables, { indent: 2, lineWidth: 120, noRefs: true });
|
|
199
187
|
|
|
200
188
|
const envTemplatePath = path.join(outputDir, 'env.template');
|
|
@@ -12,6 +12,8 @@ const fs = require('fs');
|
|
|
12
12
|
const path = require('path');
|
|
13
13
|
const Ajv = require('ajv');
|
|
14
14
|
const { detectAppType, getDeployJsonPath } = require('../utils/paths');
|
|
15
|
+
const { resolveApplicationConfigPath } = require('../utils/app-config-resolver');
|
|
16
|
+
const { loadConfigFile } = require('../utils/config-format');
|
|
15
17
|
const { loadVariables, loadRbac } = require('./helpers');
|
|
16
18
|
const {
|
|
17
19
|
parseApplicationSchema,
|
|
@@ -41,26 +43,29 @@ const {
|
|
|
41
43
|
function resolveSystemFilePath(variables, appPath, appName) {
|
|
42
44
|
const systemFileName = variables.externalIntegration.systems && variables.externalIntegration.systems.length > 0
|
|
43
45
|
? variables.externalIntegration.systems[0]
|
|
44
|
-
: `${appName}-system.
|
|
46
|
+
: `${appName}-system.yaml`;
|
|
45
47
|
|
|
46
|
-
|
|
48
|
+
let schemaBasePath = variables.externalIntegration.schemaBasePath || './';
|
|
49
|
+
const normalizedBase = path.normalize(schemaBasePath).replace(/[/\\]+$/, '');
|
|
50
|
+
if (normalizedBase === path.join('integration', appName)) {
|
|
51
|
+
schemaBasePath = './';
|
|
52
|
+
}
|
|
47
53
|
const systemFilePath = path.isAbsolute(schemaBasePath)
|
|
48
54
|
? path.join(schemaBasePath, systemFileName)
|
|
49
55
|
: path.join(appPath, schemaBasePath, systemFileName);
|
|
50
56
|
|
|
51
|
-
// Support both
|
|
57
|
+
// Support both .yaml and legacy .json for backward compatibility
|
|
52
58
|
if (!fs.existsSync(systemFilePath)) {
|
|
53
|
-
|
|
54
|
-
const
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
return oldSystemFilePath;
|
|
59
|
+
const altFileName = systemFileName.replace(/-system\.yaml$/, '-system.json').replace(/-system\.yml$/, '-system.json');
|
|
60
|
+
const altSystemFilePath = path.isAbsolute(schemaBasePath)
|
|
61
|
+
? path.join(schemaBasePath, altFileName)
|
|
62
|
+
: path.join(appPath, schemaBasePath, altFileName);
|
|
63
|
+
|
|
64
|
+
if (fs.existsSync(altSystemFilePath)) {
|
|
65
|
+
return altSystemFilePath;
|
|
61
66
|
}
|
|
62
67
|
|
|
63
|
-
throw new Error(`External system file not found: ${systemFilePath} (also checked: ${
|
|
68
|
+
throw new Error(`External system file not found: ${systemFilePath} (also checked: ${altSystemFilePath}). Please create it first.`);
|
|
64
69
|
}
|
|
65
70
|
|
|
66
71
|
return systemFilePath;
|
|
@@ -87,15 +92,14 @@ function mergeRbacIntoSystemJson(systemJson, rbac) {
|
|
|
87
92
|
}
|
|
88
93
|
|
|
89
94
|
/**
|
|
90
|
-
* Loads and parses system file
|
|
95
|
+
* Loads and parses system file (YAML or JSON)
|
|
91
96
|
* @async
|
|
92
97
|
* @function loadSystemFileContent
|
|
93
98
|
* @param {string} systemFilePath - System file path
|
|
94
|
-
* @returns {Promise<Object>} Parsed system
|
|
99
|
+
* @returns {Promise<Object>} Parsed system config object
|
|
95
100
|
*/
|
|
96
101
|
async function loadSystemFileContent(systemFilePath) {
|
|
97
|
-
|
|
98
|
-
return JSON.parse(systemContent);
|
|
102
|
+
return loadConfigFile(systemFilePath);
|
|
99
103
|
}
|
|
100
104
|
|
|
101
105
|
async function generateExternalSystemDeployJson(appName, appPath) {
|
|
@@ -103,11 +107,11 @@ async function generateExternalSystemDeployJson(appName, appPath) {
|
|
|
103
107
|
throw new Error('App name is required and must be a string');
|
|
104
108
|
}
|
|
105
109
|
|
|
106
|
-
const
|
|
107
|
-
const { parsed: variables } = loadVariables(
|
|
110
|
+
const configPath = resolveApplicationConfigPath(appPath);
|
|
111
|
+
const { parsed: variables } = loadVariables(configPath);
|
|
108
112
|
|
|
109
113
|
if (!variables.externalIntegration) {
|
|
110
|
-
throw new Error('externalIntegration block not found in
|
|
114
|
+
throw new Error('externalIntegration block not found in application.yaml');
|
|
111
115
|
}
|
|
112
116
|
|
|
113
117
|
const systemFilePath = resolveSystemFilePath(variables, appPath, appName);
|
|
@@ -141,11 +145,13 @@ async function loadSystemFile(appPath, schemaBasePath, systemFileName) {
|
|
|
141
145
|
: path.join(appPath, schemaBasePath, systemFileName);
|
|
142
146
|
|
|
143
147
|
if (!fs.existsSync(systemFilePath)) {
|
|
144
|
-
|
|
148
|
+
const hint = systemFileName.endsWith('-deploy.json')
|
|
149
|
+
? ' Use the system definition file (e.g. <app>-system.yaml) in externalIntegration.systems, not the deploy manifest (-deploy.json).'
|
|
150
|
+
: '';
|
|
151
|
+
throw new Error(`System file not found: ${systemFilePath}.${hint}`);
|
|
145
152
|
}
|
|
146
153
|
|
|
147
|
-
const
|
|
148
|
-
const systemJson = JSON.parse(systemContent);
|
|
154
|
+
const systemJson = loadConfigFile(systemFilePath);
|
|
149
155
|
|
|
150
156
|
// Load rbac.yaml from app directory and merge if present
|
|
151
157
|
const rbacPath = path.join(appPath, 'rbac.yaml');
|
|
@@ -183,8 +189,7 @@ async function loadDatasourceFiles(appPath, schemaBasePath, datasourceFiles) {
|
|
|
183
189
|
throw new Error(`Datasource file not found: ${datasourcePath}`);
|
|
184
190
|
}
|
|
185
191
|
|
|
186
|
-
const
|
|
187
|
-
const datasourceJson = JSON.parse(datasourceContent);
|
|
192
|
+
const datasourceJson = loadConfigFile(datasourcePath);
|
|
188
193
|
datasourceJsons.push(datasourceJson);
|
|
189
194
|
}
|
|
190
195
|
|
|
@@ -297,11 +302,11 @@ function validateDatasourceSchemas(datasourceJsons, externalDatasourceSchema, aj
|
|
|
297
302
|
* @throws {Error} If configuration is invalid
|
|
298
303
|
*/
|
|
299
304
|
async function loadExternalIntegrationConfig(appPath) {
|
|
300
|
-
const
|
|
301
|
-
const { parsed: variables } = loadVariables(
|
|
305
|
+
const configPath = resolveApplicationConfigPath(appPath);
|
|
306
|
+
const { parsed: variables } = loadVariables(configPath);
|
|
302
307
|
|
|
303
308
|
if (!variables.externalIntegration) {
|
|
304
|
-
throw new Error('externalIntegration block not found in
|
|
309
|
+
throw new Error('externalIntegration block not found in application.yaml');
|
|
305
310
|
}
|
|
306
311
|
|
|
307
312
|
const schemaBasePath = variables.externalIntegration.schemaBasePath || './';
|
package/lib/generator/github.js
CHANGED
|
@@ -45,7 +45,7 @@ async function loadStepTemplates(stepNames = []) {
|
|
|
45
45
|
/**
|
|
46
46
|
* Generate GitHub Actions workflow files from templates
|
|
47
47
|
* @param {string} appPath - Path to application directory
|
|
48
|
-
* @param {Object} config - Configuration from
|
|
48
|
+
* @param {Object} config - Configuration from application.yaml
|
|
49
49
|
* @param {Object} options - Generation options
|
|
50
50
|
* @returns {Promise<string[]>} Array of generated file paths
|
|
51
51
|
*/
|
package/lib/generator/helpers.js
CHANGED
|
@@ -10,24 +10,17 @@
|
|
|
10
10
|
|
|
11
11
|
const fs = require('fs');
|
|
12
12
|
const yaml = require('js-yaml');
|
|
13
|
+
const { loadConfigFile } = require('../utils/config-format');
|
|
13
14
|
|
|
14
15
|
/**
|
|
15
|
-
* Loads
|
|
16
|
-
* @param {string}
|
|
17
|
-
* @returns {Object}
|
|
18
|
-
* @throws {Error} If file not found or invalid YAML
|
|
16
|
+
* Loads application config file (application.yaml, application.json, or legacy path) via converter.
|
|
17
|
+
* @param {string} configPath - Path to application config file
|
|
18
|
+
* @returns {Object} Object with parsed config: { parsed }
|
|
19
|
+
* @throws {Error} If file not found or invalid YAML/JSON
|
|
19
20
|
*/
|
|
20
|
-
function loadVariables(
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
const variablesContent = fs.readFileSync(variablesPath, 'utf8');
|
|
26
|
-
try {
|
|
27
|
-
return { content: variablesContent, parsed: yaml.load(variablesContent) };
|
|
28
|
-
} catch (error) {
|
|
29
|
-
throw new Error(`Invalid YAML syntax in variables.yaml: ${error.message}`);
|
|
30
|
-
}
|
|
21
|
+
function loadVariables(configPath) {
|
|
22
|
+
const parsed = loadConfigFile(configPath);
|
|
23
|
+
return { parsed };
|
|
31
24
|
}
|
|
32
25
|
|
|
33
26
|
/**
|
|
@@ -151,16 +144,16 @@ function validatePortalInput(portalInput, variableName) {
|
|
|
151
144
|
}
|
|
152
145
|
|
|
153
146
|
/**
|
|
154
|
-
* Parses environment variables from env.template and merges portalInput from
|
|
147
|
+
* Parses environment variables from env.template and merges portalInput from application config
|
|
155
148
|
* @param {string} envTemplate - Content of env.template file
|
|
156
|
-
* @param {Object|null} [variablesConfig=null] - Optional configuration from
|
|
149
|
+
* @param {Object|null} [variablesConfig=null] - Optional configuration from application.yaml
|
|
157
150
|
* @returns {Array<Object>} Configuration array with merged portalInput
|
|
158
151
|
* @throws {Error} If portalInput structure is invalid
|
|
159
152
|
*/
|
|
160
153
|
/**
|
|
161
154
|
* Creates a map of portalInput configurations from variables config
|
|
162
155
|
* @function createPortalInputMap
|
|
163
|
-
* @param {Object|null} variablesConfig - Configuration from
|
|
156
|
+
* @param {Object|null} variablesConfig - Configuration from application.yaml
|
|
164
157
|
* @returns {Map} Map of variable names to portalInput configurations
|
|
165
158
|
*/
|
|
166
159
|
function createPortalInputMap(variablesConfig) {
|
|
@@ -250,7 +243,7 @@ function createConfigItem(key, value, location, required, portalInputMap) {
|
|
|
250
243
|
required
|
|
251
244
|
};
|
|
252
245
|
|
|
253
|
-
// Merge portalInput if it exists in
|
|
246
|
+
// Merge portalInput if it exists in application config
|
|
254
247
|
if (portalInputMap.has(key)) {
|
|
255
248
|
configItem.portalInput = portalInputMap.get(key);
|
|
256
249
|
}
|
package/lib/generator/index.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* AI Fabrix Builder Deployment JSON Generator
|
|
3
3
|
*
|
|
4
4
|
* This module generates deployment JSON manifests for Miso Controller.
|
|
5
|
-
* Combines
|
|
5
|
+
* Combines application.yaml, env.template, and rbac.yaml into deployment configuration.
|
|
6
6
|
*
|
|
7
7
|
* @fileoverview Deployment JSON generation for AI Fabrix Builder
|
|
8
8
|
* @author AI Fabrix Team
|
|
@@ -13,7 +13,8 @@ const fs = require('fs');
|
|
|
13
13
|
const path = require('path');
|
|
14
14
|
const _validator = require('../validation/validator');
|
|
15
15
|
const builders = require('./builders');
|
|
16
|
-
const { detectAppType, getDeployJsonPath } = require('../utils/paths');
|
|
16
|
+
const { detectAppType, getDeployJsonPath, resolveApplicationConfigPath } = require('../utils/paths');
|
|
17
|
+
const { logOfflinePathWhenType } = require('../utils/cli-utils');
|
|
17
18
|
const splitFunctions = require('./split');
|
|
18
19
|
const { loadVariables, loadEnvTemplate, loadRbac, parseEnvironmentVariables } = require('./helpers');
|
|
19
20
|
const { generateExternalSystemApplicationSchema, splitExternalApplicationSchema } = require('./external');
|
|
@@ -24,13 +25,13 @@ const { resolveVersionForApp } = require('../utils/image-version');
|
|
|
24
25
|
* Generates deployment JSON from application configuration files
|
|
25
26
|
* Creates <app-name>-deploy.json for regular apps (consistent naming)
|
|
26
27
|
* For external systems, generates application-schema.json
|
|
27
|
-
* For regular apps, generates deployment manifest from
|
|
28
|
+
* For regular apps, generates deployment manifest from application.yaml, env.template, rbac.yaml
|
|
28
29
|
*
|
|
29
30
|
* @async
|
|
30
31
|
* @function generateDeployJson
|
|
31
32
|
* @param {string} appName - Name of the application
|
|
32
33
|
* @param {Object} [options] - Generation options
|
|
33
|
-
*
|
|
34
|
+
*
|
|
34
35
|
* @returns {Promise<string>} Path to generated deployment JSON file
|
|
35
36
|
* @throws {Error} If generation fails or configuration is invalid
|
|
36
37
|
*
|
|
@@ -46,7 +47,7 @@ const { resolveVersionForApp } = require('../utils/image-version');
|
|
|
46
47
|
* @returns {Object} Loaded configuration files
|
|
47
48
|
*/
|
|
48
49
|
function loadDeploymentConfigFiles(appPath, appType, appName) {
|
|
49
|
-
const variablesPath =
|
|
50
|
+
const variablesPath = resolveApplicationConfigPath(appPath);
|
|
50
51
|
const templatePath = path.join(appPath, 'env.template');
|
|
51
52
|
const rbacPath = path.join(appPath, 'rbac.yaml');
|
|
52
53
|
const jsonPath = getDeployJsonPath(appName, appType, true); // Use new naming
|
|
@@ -69,7 +70,7 @@ function loadDeploymentConfigFiles(appPath, appType, appName) {
|
|
|
69
70
|
* @throws {Error} If validation fails
|
|
70
71
|
*/
|
|
71
72
|
function buildAndValidateDeployment(appName, variables, envTemplate, rbac) {
|
|
72
|
-
// Parse environment variables from template and merge portalInput from
|
|
73
|
+
// Parse environment variables from template and merge portalInput from application config
|
|
73
74
|
const configuration = parseEnvironmentVariables(envTemplate, variables);
|
|
74
75
|
|
|
75
76
|
// Build deployment manifest (Controller computes deploymentKey from schema)
|
|
@@ -93,17 +94,17 @@ function buildAndValidateDeployment(appName, variables, envTemplate, rbac) {
|
|
|
93
94
|
* @param {string} appName - Application name
|
|
94
95
|
* @param {Object} [options] - Options (e.g. type for external)
|
|
95
96
|
* @returns {Promise<{ deployment: Object, appPath: string }>} Manifest and app path
|
|
96
|
-
* @throws {Error} If
|
|
97
|
+
* @throws {Error} If application config/env.template missing or generation fails
|
|
97
98
|
*/
|
|
98
99
|
async function buildDeploymentManifestInMemory(appName, options = {}) {
|
|
99
100
|
if (!appName || typeof appName !== 'string') {
|
|
100
101
|
throw new Error('App name is required and must be a string');
|
|
101
102
|
}
|
|
102
103
|
|
|
103
|
-
const { isExternal, appPath, appType } = await detectAppType(appName
|
|
104
|
+
const { isExternal, appPath, appType } = await detectAppType(appName);
|
|
104
105
|
|
|
105
106
|
if (isExternal) {
|
|
106
|
-
const manifest = await generateControllerManifest(appName);
|
|
107
|
+
const manifest = await generateControllerManifest(appName, options);
|
|
107
108
|
return { deployment: manifest, appPath };
|
|
108
109
|
}
|
|
109
110
|
|
|
@@ -124,13 +125,13 @@ async function generateDeployJson(appName, options = {}) {
|
|
|
124
125
|
throw new Error('App name is required and must be a string');
|
|
125
126
|
}
|
|
126
127
|
|
|
127
|
-
// Detect app type and get correct path (integration
|
|
128
|
-
const { isExternal, appPath, appType } = await detectAppType(appName
|
|
128
|
+
// Detect app type and get correct path (integration first, then builder)
|
|
129
|
+
const { isExternal, appPath, appType } = await detectAppType(appName);
|
|
130
|
+
logOfflinePathWhenType(appPath);
|
|
129
131
|
|
|
130
132
|
// Check if app type is external
|
|
131
133
|
if (isExternal) {
|
|
132
|
-
|
|
133
|
-
const manifest = await generateControllerManifest(appName);
|
|
134
|
+
const manifest = await generateControllerManifest(appName, options);
|
|
134
135
|
|
|
135
136
|
// Determine system key for file naming
|
|
136
137
|
const systemKey = manifest.key || appName;
|
|
@@ -161,8 +162,7 @@ async function generateDeployJsonWithValidation(appName, options = {}) {
|
|
|
161
162
|
const jsonContent = fs.readFileSync(jsonPath, 'utf8');
|
|
162
163
|
const deployment = JSON.parse(jsonContent);
|
|
163
164
|
|
|
164
|
-
|
|
165
|
-
const { isExternal } = await detectAppType(appName, options);
|
|
165
|
+
const { isExternal } = await detectAppType(appName);
|
|
166
166
|
|
|
167
167
|
// For external systems, skip deployment JSON validation (they use external system JSON structure)
|
|
168
168
|
if (isExternal) {
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Parses image reference string into components.
|
|
3
|
+
* @fileoverview Image reference parser for deployment JSON
|
|
4
|
+
* @author AI Fabrix Team
|
|
5
|
+
* @version 2.0.0
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Parses image reference string into components
|
|
10
|
+
* @function parseImageReference
|
|
11
|
+
* @param {string} imageString - Full image string (e.g., "registry/name:tag")
|
|
12
|
+
* @returns {Object} Object with registry, name, and tag
|
|
13
|
+
*/
|
|
14
|
+
function parseImageReference(imageString) {
|
|
15
|
+
if (!imageString || typeof imageString !== 'string') {
|
|
16
|
+
return { registry: null, name: null, tag: 'latest' };
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const parts = imageString.split('/');
|
|
20
|
+
let registry = null;
|
|
21
|
+
let nameAndTag = imageString;
|
|
22
|
+
|
|
23
|
+
if (parts.length > 1 && parts[0].includes('.')) {
|
|
24
|
+
registry = parts[0];
|
|
25
|
+
nameAndTag = parts.slice(1).join('/');
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const tagIndex = nameAndTag.lastIndexOf(':');
|
|
29
|
+
const name = tagIndex !== -1 ? nameAndTag.substring(0, tagIndex) : nameAndTag;
|
|
30
|
+
const tag = tagIndex !== -1 ? nameAndTag.substring(tagIndex + 1) : 'latest';
|
|
31
|
+
|
|
32
|
+
return { registry, name, tag };
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
module.exports = { parseImageReference };
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* README generation from deployment JSON for split flow.
|
|
3
|
+
* @fileoverview Builds readme config and generates README from deployment JSON
|
|
4
|
+
* @author AI Fabrix Team
|
|
5
|
+
* @version 2.0.0
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const { generateReadmeMd } = require('../app/readme');
|
|
9
|
+
const { parseImageReference } = require('./parse-image');
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Builds config for external-system README from deployment
|
|
13
|
+
* @param {Object} deployment - Deployment JSON object
|
|
14
|
+
* @returns {{ appName: string, config: Object }}
|
|
15
|
+
*/
|
|
16
|
+
function buildReadmeConfigForExternal(deployment) {
|
|
17
|
+
const system = deployment.system;
|
|
18
|
+
const appName = system.key || deployment.key || 'external-system';
|
|
19
|
+
const dataSources = deployment.dataSources || deployment.datasources || [];
|
|
20
|
+
return {
|
|
21
|
+
appName,
|
|
22
|
+
config: {
|
|
23
|
+
type: 'external',
|
|
24
|
+
systemKey: appName,
|
|
25
|
+
systemType: system.type || 'openapi',
|
|
26
|
+
systemDisplayName: system.displayName || appName,
|
|
27
|
+
systemDescription: system.description || `External system integration for ${appName}`,
|
|
28
|
+
datasourceCount: dataSources.length,
|
|
29
|
+
datasources: dataSources
|
|
30
|
+
}
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Builds config for application README from deployment
|
|
36
|
+
* @param {Object} deployment - Deployment JSON object
|
|
37
|
+
* @returns {{ appName: string, config: Object }}
|
|
38
|
+
*/
|
|
39
|
+
function buildReadmeConfigForApp(deployment) {
|
|
40
|
+
const appName = deployment.key || 'application';
|
|
41
|
+
const imageParts = parseImageReference(deployment.image || '');
|
|
42
|
+
const port = deployment.port !== undefined ? deployment.port : 3000;
|
|
43
|
+
const imageName = imageParts.name || appName;
|
|
44
|
+
const registry = imageParts.registry || 'myacr.azurecr.io';
|
|
45
|
+
|
|
46
|
+
const config = {
|
|
47
|
+
type: deployment.type || 'webapp',
|
|
48
|
+
displayName: deployment.displayName,
|
|
49
|
+
description: deployment.description,
|
|
50
|
+
port,
|
|
51
|
+
build: { localPort: port },
|
|
52
|
+
image: { name: imageName, registry },
|
|
53
|
+
registry,
|
|
54
|
+
database: deployment.requiresDatabase,
|
|
55
|
+
requires: {
|
|
56
|
+
database: deployment.requiresDatabase,
|
|
57
|
+
redis: deployment.requiresRedis,
|
|
58
|
+
storage: deployment.requiresStorage
|
|
59
|
+
},
|
|
60
|
+
redis: deployment.requiresRedis,
|
|
61
|
+
storage: deployment.requiresStorage,
|
|
62
|
+
authentication: !!deployment.authentication
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
if (config.type === 'external') {
|
|
66
|
+
config.systemKey = appName;
|
|
67
|
+
config.systemType = deployment.systemType || 'openapi';
|
|
68
|
+
config.systemDisplayName = deployment.displayName || appName;
|
|
69
|
+
config.systemDescription = deployment.description || `External system integration for ${appName}`;
|
|
70
|
+
config.datasourceCount = 0;
|
|
71
|
+
config.datasources = deployment.dataSources || deployment.datasources || [];
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
return { appName, config };
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Builds application config shape from deployment JSON for README template context.
|
|
79
|
+
* @param {Object} deployment - Deployment JSON object
|
|
80
|
+
* @returns {{ appName: string, config: Object }}
|
|
81
|
+
*/
|
|
82
|
+
function buildReadmeConfigFromDeployment(deployment) {
|
|
83
|
+
if (deployment.system && typeof deployment.system === 'object') {
|
|
84
|
+
return buildReadmeConfigForExternal(deployment);
|
|
85
|
+
}
|
|
86
|
+
return buildReadmeConfigForApp(deployment);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Generates README.md content from deployment JSON.
|
|
91
|
+
* @param {Object} deployment - Deployment JSON object
|
|
92
|
+
* @returns {string} README.md content
|
|
93
|
+
*/
|
|
94
|
+
function generateReadmeFromDeployJson(deployment) {
|
|
95
|
+
if (!deployment || typeof deployment !== 'object') {
|
|
96
|
+
throw new Error('Deployment object is required');
|
|
97
|
+
}
|
|
98
|
+
const { appName, config } = buildReadmeConfigFromDeployment(deployment);
|
|
99
|
+
return generateReadmeMd(appName, config);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
module.exports = {
|
|
103
|
+
buildReadmeConfigFromDeployment,
|
|
104
|
+
generateReadmeFromDeployJson
|
|
105
|
+
};
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Variables YAML extraction for deployment JSON split.
|
|
3
|
+
* Extracts application.yaml (variables) structure from deployment JSON.
|
|
4
|
+
*
|
|
5
|
+
* @fileoverview Split variables extraction for deployment JSON reverse conversion
|
|
6
|
+
* @author AI Fabrix Team
|
|
7
|
+
* @version 2.0.0
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
const { parseImageReference } = require('./parse-image');
|
|
11
|
+
|
|
12
|
+
function extractAppSection(deployment) {
|
|
13
|
+
if (!deployment.key && !deployment.displayName && !deployment.description && !deployment.type) {
|
|
14
|
+
return undefined;
|
|
15
|
+
}
|
|
16
|
+
const app = {};
|
|
17
|
+
if (deployment.key) app.key = deployment.key;
|
|
18
|
+
if (deployment.displayName) app.displayName = deployment.displayName;
|
|
19
|
+
if (deployment.description) app.description = deployment.description;
|
|
20
|
+
if (deployment.type) app.type = deployment.type;
|
|
21
|
+
if (deployment.version) app.version = deployment.version;
|
|
22
|
+
return app;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function extractImageSection(deployment) {
|
|
26
|
+
if (!deployment.image) return undefined;
|
|
27
|
+
const imageParts = parseImageReference(deployment.image);
|
|
28
|
+
const image = {};
|
|
29
|
+
if (imageParts.name) image.name = imageParts.name;
|
|
30
|
+
if (imageParts.registry) image.registry = imageParts.registry;
|
|
31
|
+
if (imageParts.tag) image.tag = imageParts.tag;
|
|
32
|
+
if (deployment.registryMode) image.registryMode = deployment.registryMode;
|
|
33
|
+
return image;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function extractRequirementsSection(deployment) {
|
|
37
|
+
if (!deployment.requiresDatabase && !deployment.requiresRedis && !deployment.requiresStorage && !deployment.databases) {
|
|
38
|
+
return undefined;
|
|
39
|
+
}
|
|
40
|
+
const requires = {};
|
|
41
|
+
if (deployment.requiresDatabase !== undefined) requires.database = deployment.requiresDatabase;
|
|
42
|
+
if (deployment.requiresRedis !== undefined) requires.redis = deployment.requiresRedis;
|
|
43
|
+
if (deployment.requiresStorage !== undefined) requires.storage = deployment.requiresStorage;
|
|
44
|
+
if (deployment.databases) requires.databases = deployment.databases;
|
|
45
|
+
return requires;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function extractOptionalSection(deployment, sectionName, optional) {
|
|
49
|
+
if (!deployment[sectionName]) return;
|
|
50
|
+
optional[sectionName] = sectionName === 'authentication'
|
|
51
|
+
? { ...deployment[sectionName] }
|
|
52
|
+
: deployment[sectionName];
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function extractOptionalSections(deployment) {
|
|
56
|
+
const optional = {};
|
|
57
|
+
const names = [
|
|
58
|
+
'healthCheck', 'authentication', 'build', 'repository', 'deployment',
|
|
59
|
+
'startupCommand', 'runtimeVersion', 'scaling', 'frontDoorRouting'
|
|
60
|
+
];
|
|
61
|
+
for (const sectionName of names) {
|
|
62
|
+
extractOptionalSection(deployment, sectionName, optional);
|
|
63
|
+
}
|
|
64
|
+
return optional;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Portal-only configuration for application.yaml (name + portalInput per entry).
|
|
69
|
+
* @param {Array} configuration - Configuration array from deployment JSON
|
|
70
|
+
* @returns {Array<{ name: string, portalInput: Object }>}
|
|
71
|
+
*/
|
|
72
|
+
function extractPortalInputConfiguration(configuration) {
|
|
73
|
+
if (!Array.isArray(configuration) || configuration.length === 0) return [];
|
|
74
|
+
return configuration
|
|
75
|
+
.filter(item => item && item.portalInput && typeof item.name === 'string')
|
|
76
|
+
.map(item => ({ name: item.name, portalInput: item.portalInput }));
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Datasource filename for externalIntegration.dataSources.
|
|
81
|
+
* @param {string} systemKey - System key
|
|
82
|
+
* @param {Object} datasource - Datasource from deployment.dataSources
|
|
83
|
+
* @param {number} index - Index
|
|
84
|
+
* @returns {string} Filename e.g. test-hubspot-datasource-companies-data.yaml
|
|
85
|
+
*/
|
|
86
|
+
function getExternalDatasourceFileName(systemKey, datasource, index) {
|
|
87
|
+
const key = datasource.key || '';
|
|
88
|
+
let suffix;
|
|
89
|
+
if (key.startsWith(`${systemKey}-deploy-`)) suffix = key.slice(`${systemKey}-deploy-`.length);
|
|
90
|
+
else if (key.startsWith(`${systemKey}-`)) suffix = key.slice(systemKey.length + 1);
|
|
91
|
+
else if (key) suffix = key;
|
|
92
|
+
else suffix = datasource.entityType || datasource.entityKey || `entity${index + 1}`;
|
|
93
|
+
return `${systemKey}-datasource-${suffix}.yaml`;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function extractVariablesYamlForExternal(deployment) {
|
|
97
|
+
const system = deployment.system;
|
|
98
|
+
const systemKey = system.key || 'external-system';
|
|
99
|
+
const dataSourcesList = deployment.dataSources || deployment.datasources || [];
|
|
100
|
+
const variables = {
|
|
101
|
+
app: {
|
|
102
|
+
key: systemKey,
|
|
103
|
+
displayName: system.displayName || systemKey,
|
|
104
|
+
description: system.description || `External system integration for ${systemKey}`,
|
|
105
|
+
type: 'external'
|
|
106
|
+
},
|
|
107
|
+
externalIntegration: {
|
|
108
|
+
schemaBasePath: './',
|
|
109
|
+
systems: [`${systemKey}-system.yaml`],
|
|
110
|
+
dataSources: dataSourcesList.map((ds, i) => getExternalDatasourceFileName(systemKey, ds, i)),
|
|
111
|
+
autopublish: true,
|
|
112
|
+
version: '1.0.0'
|
|
113
|
+
}
|
|
114
|
+
};
|
|
115
|
+
const portalOnlyConfig = extractPortalInputConfiguration(deployment.configuration);
|
|
116
|
+
if (portalOnlyConfig.length > 0) variables.configuration = portalOnlyConfig;
|
|
117
|
+
return variables;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Extracts deployment JSON into application config (variables) structure.
|
|
122
|
+
* @param {Object} deployment - Deployment JSON object
|
|
123
|
+
* @returns {Object} Variables YAML structure
|
|
124
|
+
*/
|
|
125
|
+
function extractVariablesYaml(deployment) {
|
|
126
|
+
if (!deployment || typeof deployment !== 'object') {
|
|
127
|
+
throw new Error('Deployment object is required');
|
|
128
|
+
}
|
|
129
|
+
if (deployment.system && typeof deployment.system === 'object') {
|
|
130
|
+
return extractVariablesYamlForExternal(deployment);
|
|
131
|
+
}
|
|
132
|
+
const variables = {};
|
|
133
|
+
const appSection = extractAppSection(deployment);
|
|
134
|
+
if (appSection) variables.app = appSection;
|
|
135
|
+
const imageSection = extractImageSection(deployment);
|
|
136
|
+
if (imageSection) variables.image = imageSection;
|
|
137
|
+
if (deployment.port !== undefined) variables.port = deployment.port;
|
|
138
|
+
const requirementsSection = extractRequirementsSection(deployment);
|
|
139
|
+
if (requirementsSection) variables.requires = requirementsSection;
|
|
140
|
+
Object.assign(variables, extractOptionalSections(deployment));
|
|
141
|
+
const portalOnlyConfig = extractPortalInputConfiguration(deployment.configuration);
|
|
142
|
+
if (portalOnlyConfig.length > 0) variables.configuration = portalOnlyConfig;
|
|
143
|
+
return variables;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
module.exports = {
|
|
147
|
+
extractVariablesYaml,
|
|
148
|
+
getExternalDatasourceFileName
|
|
149
|
+
};
|