@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
|
@@ -11,11 +11,10 @@
|
|
|
11
11
|
const fs = require('fs').promises;
|
|
12
12
|
const fsSync = require('fs');
|
|
13
13
|
const path = require('path');
|
|
14
|
-
const yaml = require('js-yaml');
|
|
15
14
|
const { detectAppType, getDeployJsonPath } = require('../utils/paths');
|
|
16
15
|
|
|
17
16
|
/**
|
|
18
|
-
* Loads
|
|
17
|
+
* Loads application config for an application
|
|
19
18
|
* @async
|
|
20
19
|
* @function loadVariablesYaml
|
|
21
20
|
* @param {string} appName - Application name
|
|
@@ -23,11 +22,11 @@ const { detectAppType, getDeployJsonPath } = require('../utils/paths');
|
|
|
23
22
|
* @throws {Error} If file cannot be loaded
|
|
24
23
|
*/
|
|
25
24
|
async function loadVariablesYaml(appName) {
|
|
26
|
-
// Detect app type and get correct path (integration or builder)
|
|
27
25
|
const { appPath } = await detectAppType(appName);
|
|
28
|
-
const
|
|
29
|
-
const
|
|
30
|
-
|
|
26
|
+
const { resolveApplicationConfigPath } = require('../utils/app-config-resolver');
|
|
27
|
+
const { loadConfigFile } = require('../utils/config-format');
|
|
28
|
+
const configPath = resolveApplicationConfigPath(appPath);
|
|
29
|
+
return loadConfigFile(configPath);
|
|
31
30
|
}
|
|
32
31
|
|
|
33
32
|
/**
|
|
@@ -13,6 +13,8 @@ const chalk = require('chalk');
|
|
|
13
13
|
const { getDeploymentAuth } = require('../utils/token-manager');
|
|
14
14
|
const logger = require('../utils/logger');
|
|
15
15
|
const { resolveControllerUrl } = require('../utils/controller-url');
|
|
16
|
+
const { detectAppType } = require('../utils/paths');
|
|
17
|
+
const { logOfflinePathWhenType } = require('../utils/cli-utils');
|
|
16
18
|
const { resolveDataplaneUrl } = require('../utils/dataplane-resolver');
|
|
17
19
|
const { getExternalSystem } = require('../api/external-systems.api');
|
|
18
20
|
const { generateControllerManifest } = require('../generator/external-controller-manifest');
|
|
@@ -132,11 +134,14 @@ async function prepareDeploymentConfig(appName, _options) {
|
|
|
132
134
|
*/
|
|
133
135
|
async function deployExternalSystem(appName, options = {}) {
|
|
134
136
|
try {
|
|
137
|
+
const { appPath } = await detectAppType(appName);
|
|
138
|
+
logOfflinePathWhenType(appPath);
|
|
139
|
+
|
|
135
140
|
logger.log(chalk.blue(`\n🚀 Deploying external system: ${appName}`));
|
|
136
141
|
|
|
137
142
|
// Step 0: Validate before deployment (same as validate command)
|
|
138
143
|
logger.log(chalk.blue('🔍 Validating external system before deployment...'));
|
|
139
|
-
const validationResult = await validateExternalSystemComplete(appName);
|
|
144
|
+
const validationResult = await validateExternalSystemComplete(appName, options);
|
|
140
145
|
|
|
141
146
|
if (!validationResult.valid) {
|
|
142
147
|
displayValidationResults(validationResult);
|
|
@@ -146,7 +151,7 @@ async function deployExternalSystem(appName, options = {}) {
|
|
|
146
151
|
logger.log(chalk.green('✓ Validation passed, proceeding with deployment...'));
|
|
147
152
|
|
|
148
153
|
// Step 1: Generate controller manifest (validated, ready for deployment)
|
|
149
|
-
const manifest = await generateControllerManifest(appName);
|
|
154
|
+
const manifest = await generateControllerManifest(appName, options);
|
|
150
155
|
|
|
151
156
|
// Step 2: Get deployment configuration (auth, controller URL, etc.)
|
|
152
157
|
const { environment, controllerUrl, authConfig } = await prepareDeploymentConfig(appName, options);
|
|
@@ -11,14 +11,14 @@
|
|
|
11
11
|
const { generateExternalReadmeContent } = require('../utils/external-readme');
|
|
12
12
|
|
|
13
13
|
/**
|
|
14
|
-
* Generates
|
|
14
|
+
* Generates application.yaml content for downloaded system
|
|
15
15
|
* @param {string} systemKey - System key
|
|
16
16
|
* @param {Object} application - External system configuration
|
|
17
17
|
* @param {Array} dataSources - Array of datasource configurations
|
|
18
18
|
* @returns {Object} Variables YAML object
|
|
19
19
|
*/
|
|
20
20
|
function generateVariablesYaml(systemKey, application, dataSources) {
|
|
21
|
-
const systemFileName = `${systemKey}-system.
|
|
21
|
+
const systemFileName = `${systemKey}-system.yaml`;
|
|
22
22
|
const datasourceFiles = dataSources.map(ds => {
|
|
23
23
|
// Extract datasource key (remove system key prefix if present)
|
|
24
24
|
const datasourceKey = ds.key || '';
|
|
@@ -29,7 +29,7 @@ function generateVariablesYaml(systemKey, application, dataSources) {
|
|
|
29
29
|
const entityType = ds.entityType || ds.entityKey || datasourceKey.split('-').pop();
|
|
30
30
|
datasourceKeyOnly = entityType;
|
|
31
31
|
}
|
|
32
|
-
return `${systemKey}-datasource-${datasourceKeyOnly}.
|
|
32
|
+
return `${systemKey}-datasource-${datasourceKeyOnly}.yaml`;
|
|
33
33
|
});
|
|
34
34
|
|
|
35
35
|
return {
|
|
@@ -73,7 +73,7 @@ function generateReadme(systemKey, application, dataSources) {
|
|
|
73
73
|
return {
|
|
74
74
|
entityType: datasourceKeyOnly,
|
|
75
75
|
displayName: ds.displayName || ds.name || ds.key || `Datasource ${index + 1}`,
|
|
76
|
-
fileName: `${systemKey}-datasource-${datasourceKeyOnly}.
|
|
76
|
+
fileName: `${systemKey}-datasource-${datasourceKeyOnly}.yaml`
|
|
77
77
|
};
|
|
78
78
|
});
|
|
79
79
|
|
|
@@ -19,6 +19,7 @@ const { getDeploymentAuth } = require('../utils/token-manager');
|
|
|
19
19
|
const { getConfig } = require('../core/config');
|
|
20
20
|
const { detectAppType } = require('../utils/paths');
|
|
21
21
|
const logger = require('../utils/logger');
|
|
22
|
+
const { writeConfigFile } = require('../utils/config-format');
|
|
22
23
|
const { generateEnvTemplate } = require('../utils/external-system-env-helpers');
|
|
23
24
|
const { generateVariablesYaml, generateReadme } = require('./download-helpers');
|
|
24
25
|
const { resolveControllerUrl } = require('../utils/controller-url');
|
|
@@ -201,9 +202,9 @@ async function downloadSystemConfiguration(dataplaneUrl, systemKey, authConfig)
|
|
|
201
202
|
* @returns {Promise<string>} System file path
|
|
202
203
|
*/
|
|
203
204
|
async function generateSystemFile(tempDir, systemKey, application) {
|
|
204
|
-
const systemFileName = `${systemKey}-system.
|
|
205
|
+
const systemFileName = `${systemKey}-system.yaml`;
|
|
205
206
|
const systemFilePath = path.join(tempDir, systemFileName);
|
|
206
|
-
|
|
207
|
+
writeConfigFile(systemFilePath, application);
|
|
207
208
|
return systemFilePath;
|
|
208
209
|
}
|
|
209
210
|
|
|
@@ -230,9 +231,9 @@ async function generateDatasourceFiles(tempDir, systemKey, dataSources) {
|
|
|
230
231
|
const entityType = datasource.entityType || datasource.entityKey || datasourceKey.split('-').pop();
|
|
231
232
|
datasourceKeyOnly = entityType;
|
|
232
233
|
}
|
|
233
|
-
const datasourceFileName = `${systemKey}-datasource-${datasourceKeyOnly}.
|
|
234
|
+
const datasourceFileName = `${systemKey}-datasource-${datasourceKeyOnly}.yaml`;
|
|
234
235
|
const datasourceFilePath = path.join(tempDir, datasourceFileName);
|
|
235
|
-
|
|
236
|
+
writeConfigFile(datasourceFilePath, datasource);
|
|
236
237
|
datasourceFiles.push(datasourceFilePath);
|
|
237
238
|
} catch (error) {
|
|
238
239
|
datasourceErrors.push(new Error(`Failed to write datasource ${datasource.key}: ${error.message}`));
|
|
@@ -242,7 +243,7 @@ async function generateDatasourceFiles(tempDir, systemKey, dataSources) {
|
|
|
242
243
|
}
|
|
243
244
|
|
|
244
245
|
/**
|
|
245
|
-
* Generates configuration files (
|
|
246
|
+
* Generates configuration files (application.yaml, env.template, README.md)
|
|
246
247
|
* @async
|
|
247
248
|
* @function generateConfigFiles
|
|
248
249
|
* @param {string} tempDir - Temporary directory
|
|
@@ -252,9 +253,9 @@ async function generateDatasourceFiles(tempDir, systemKey, dataSources) {
|
|
|
252
253
|
* @returns {Promise<Object>} Object with file paths
|
|
253
254
|
*/
|
|
254
255
|
async function generateConfigFiles(tempDir, systemKey, application, dataSources) {
|
|
255
|
-
// Generate
|
|
256
|
+
// Generate application.yaml
|
|
256
257
|
const variables = generateVariablesYaml(systemKey, application, dataSources);
|
|
257
|
-
const variablesPath = path.join(tempDir, '
|
|
258
|
+
const variablesPath = path.join(tempDir, 'application.yaml');
|
|
258
259
|
await fs.writeFile(variablesPath, yaml.dump(variables, { indent: 2, lineWidth: 120, noRefs: true }), 'utf8');
|
|
259
260
|
|
|
260
261
|
// Generate env.template
|
|
@@ -307,7 +308,7 @@ async function moveFilesToFinalLocation(tempDir, finalPath, systemKey, filePaths
|
|
|
307
308
|
const systemFileName = `${systemKey}-system.json`;
|
|
308
309
|
const filesToMove = [
|
|
309
310
|
{ from: filePaths.systemFilePath, to: path.join(finalPath, systemFileName) },
|
|
310
|
-
{ from: filePaths.variablesPath, to: path.join(finalPath, '
|
|
311
|
+
{ from: filePaths.variablesPath, to: path.join(finalPath, 'application.yaml') },
|
|
311
312
|
{ from: filePaths.envTemplatePath, to: path.join(finalPath, 'env.template') },
|
|
312
313
|
{ from: filePaths.readmePath, to: path.join(finalPath, 'README.md') }
|
|
313
314
|
];
|
|
@@ -347,8 +348,8 @@ function handleDryRun(systemKey, dataplaneUrl) {
|
|
|
347
348
|
logger.log(chalk.gray(` ${dataplaneUrl}/api/v1/external/systems/${systemKey}/config`));
|
|
348
349
|
logger.log(chalk.yellow('\nWould create:'));
|
|
349
350
|
logger.log(chalk.gray(` integration/${systemKey}/`));
|
|
350
|
-
logger.log(chalk.gray(` integration/${systemKey}/
|
|
351
|
-
logger.log(chalk.gray(` integration/${systemKey}/${systemKey}-system.
|
|
351
|
+
logger.log(chalk.gray(` integration/${systemKey}/application.yaml`));
|
|
352
|
+
logger.log(chalk.gray(` integration/${systemKey}/${systemKey}-system.yaml`));
|
|
352
353
|
logger.log(chalk.gray(` integration/${systemKey}/env.template`));
|
|
353
354
|
logger.log(chalk.gray(` integration/${systemKey}/README.md`));
|
|
354
355
|
}
|
|
@@ -12,9 +12,10 @@
|
|
|
12
12
|
const fs = require('fs').promises;
|
|
13
13
|
const path = require('path');
|
|
14
14
|
const handlebars = require('handlebars');
|
|
15
|
-
const yaml = require('js-yaml');
|
|
16
15
|
const chalk = require('chalk');
|
|
17
16
|
const logger = require('../utils/logger');
|
|
17
|
+
const { resolveApplicationConfigPath } = require('../utils/app-config-resolver');
|
|
18
|
+
const { loadConfigFile, writeConfigFile } = require('../utils/config-format');
|
|
18
19
|
|
|
19
20
|
// Register Handlebars helper for equality check
|
|
20
21
|
handlebars.registerHelper('eq', (a, b) => a === b);
|
|
@@ -52,11 +53,12 @@ async function generateExternalSystemTemplate(appPath, systemKey, config) {
|
|
|
52
53
|
};
|
|
53
54
|
|
|
54
55
|
const rendered = template(context);
|
|
56
|
+
const parsed = JSON.parse(rendered);
|
|
55
57
|
|
|
56
|
-
// Generate in same folder as
|
|
57
|
-
// Use naming: <app-name>-system.
|
|
58
|
-
const outputPath = path.join(appPath, `${systemKey}-system.
|
|
59
|
-
|
|
58
|
+
// Generate in same folder as application.yaml (new structure)
|
|
59
|
+
// Use naming: <app-name>-system.yaml
|
|
60
|
+
const outputPath = path.join(appPath, `${systemKey}-system.yaml`);
|
|
61
|
+
writeConfigFile(outputPath, parsed);
|
|
60
62
|
|
|
61
63
|
return outputPath;
|
|
62
64
|
} catch (error) {
|
|
@@ -98,15 +100,16 @@ async function generateExternalDataSourceTemplate(appPath, datasourceKey, config
|
|
|
98
100
|
};
|
|
99
101
|
|
|
100
102
|
const rendered = template(context);
|
|
103
|
+
const datasourceConfig = JSON.parse(rendered);
|
|
101
104
|
|
|
102
|
-
// Generate in same folder as
|
|
103
|
-
// Use naming: <app-name>-datasource-<datasource-key>.
|
|
105
|
+
// Generate in same folder as application.yaml (new structure)
|
|
106
|
+
// Use naming: <app-name>-datasource-<datasource-key>.yaml
|
|
104
107
|
// Extract datasource key (remove system key prefix if present)
|
|
105
108
|
const datasourceKeyOnly = datasourceKey.includes('-') && datasourceKey.startsWith(`${config.systemKey}-`)
|
|
106
109
|
? datasourceKey.substring(config.systemKey.length + 1)
|
|
107
110
|
: datasourceKey;
|
|
108
|
-
const outputPath = path.join(appPath, `${config.systemKey}-datasource-${datasourceKeyOnly}.
|
|
109
|
-
|
|
111
|
+
const outputPath = path.join(appPath, `${config.systemKey}-datasource-${datasourceKeyOnly}.yaml`);
|
|
112
|
+
writeConfigFile(outputPath, datasourceConfig);
|
|
110
113
|
|
|
111
114
|
return outputPath;
|
|
112
115
|
} catch (error) {
|
|
@@ -160,7 +163,7 @@ async function generateExternalSystemFiles(appPath, appName, config) {
|
|
|
160
163
|
logger.log(chalk.green(`✓ Generated datasource: ${path.basename(datasourcePath)}`));
|
|
161
164
|
}
|
|
162
165
|
|
|
163
|
-
// Update
|
|
166
|
+
// Update application.yaml with externalIntegration block
|
|
164
167
|
await updateVariablesYamlWithExternalIntegration(appPath, systemKey, datasourcePaths);
|
|
165
168
|
|
|
166
169
|
return {
|
|
@@ -173,7 +176,7 @@ async function generateExternalSystemFiles(appPath, appName, config) {
|
|
|
173
176
|
}
|
|
174
177
|
|
|
175
178
|
/**
|
|
176
|
-
* Updates
|
|
179
|
+
* Updates application.yaml with externalIntegration block
|
|
177
180
|
* @async
|
|
178
181
|
* @function updateVariablesYamlWithExternalIntegration
|
|
179
182
|
* @param {string} appPath - Application directory path
|
|
@@ -183,23 +186,22 @@ async function generateExternalSystemFiles(appPath, appName, config) {
|
|
|
183
186
|
*/
|
|
184
187
|
async function updateVariablesYamlWithExternalIntegration(appPath, systemKey, datasourcePaths) {
|
|
185
188
|
try {
|
|
186
|
-
const
|
|
187
|
-
const
|
|
188
|
-
const variables = yaml.load(variablesContent);
|
|
189
|
+
const configPath = resolveApplicationConfigPath(appPath);
|
|
190
|
+
const variables = loadConfigFile(configPath);
|
|
189
191
|
|
|
190
192
|
// Add externalIntegration block
|
|
191
193
|
// Files are in same folder, so schemaBasePath is './'
|
|
192
194
|
variables.externalIntegration = {
|
|
193
195
|
schemaBasePath: './',
|
|
194
|
-
systems: [`${systemKey}-system.
|
|
196
|
+
systems: [`${systemKey}-system.yaml`],
|
|
195
197
|
dataSources: datasourcePaths.map(p => path.basename(p)),
|
|
196
198
|
autopublish: true,
|
|
197
199
|
version: '1.0.0'
|
|
198
200
|
};
|
|
199
201
|
|
|
200
|
-
|
|
202
|
+
writeConfigFile(configPath, variables);
|
|
201
203
|
} catch (error) {
|
|
202
|
-
throw new Error(`Failed to update
|
|
204
|
+
throw new Error(`Failed to update application config: ${error.message}`);
|
|
203
205
|
}
|
|
204
206
|
}
|
|
205
207
|
|
|
@@ -12,7 +12,6 @@
|
|
|
12
12
|
const fs = require('fs').promises;
|
|
13
13
|
const fsSync = require('fs');
|
|
14
14
|
const path = require('path');
|
|
15
|
-
const yaml = require('js-yaml');
|
|
16
15
|
const chalk = require('chalk');
|
|
17
16
|
const testHelpers = require('../utils/external-system-test-helpers');
|
|
18
17
|
const { retryApiCall } = require('../utils/external-system-test-helpers');
|
|
@@ -39,30 +38,26 @@ const {
|
|
|
39
38
|
} = require('./test-execution');
|
|
40
39
|
|
|
41
40
|
/**
|
|
42
|
-
* Loads and parses
|
|
41
|
+
* Loads and parses application config file
|
|
43
42
|
* @async
|
|
44
43
|
* @function loadVariablesYamlFile
|
|
45
|
-
* @param {string} variablesPath - Path to
|
|
44
|
+
* @param {string} variablesPath - Path to application config
|
|
46
45
|
* @returns {Promise<Object>} Parsed variables
|
|
47
|
-
* @throws {Error} If file not found or invalid
|
|
46
|
+
* @throws {Error} If file not found or invalid
|
|
48
47
|
*/
|
|
49
48
|
async function loadVariablesYamlFile(variablesPath) {
|
|
50
|
-
|
|
51
|
-
throw new Error(`variables.yaml not found: ${variablesPath}`);
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
const variablesContent = await fs.readFile(variablesPath, 'utf8');
|
|
49
|
+
const { loadConfigFile } = require('../utils/config-format');
|
|
55
50
|
try {
|
|
56
|
-
const variables =
|
|
51
|
+
const variables = loadConfigFile(variablesPath);
|
|
57
52
|
if (!variables.externalIntegration) {
|
|
58
|
-
throw new Error('externalIntegration block not found in
|
|
53
|
+
throw new Error('externalIntegration block not found in application config');
|
|
59
54
|
}
|
|
60
55
|
return variables;
|
|
61
56
|
} catch (error) {
|
|
62
57
|
if (error.message.includes('externalIntegration')) {
|
|
63
58
|
throw error;
|
|
64
59
|
}
|
|
65
|
-
throw new Error(`
|
|
60
|
+
throw new Error(`Application config: ${error.message}`);
|
|
66
61
|
}
|
|
67
62
|
}
|
|
68
63
|
|
|
@@ -152,9 +147,9 @@ async function loadDatasourceFiles(datasourceFiles, schemaBasePath, appPath) {
|
|
|
152
147
|
*/
|
|
153
148
|
async function loadExternalSystemFiles(appName) {
|
|
154
149
|
const { appPath } = await detectAppType(appName);
|
|
155
|
-
const
|
|
156
|
-
|
|
157
|
-
const variables = await loadVariablesYamlFile(
|
|
150
|
+
const { resolveApplicationConfigPath } = require('../utils/app-config-resolver');
|
|
151
|
+
const configPath = resolveApplicationConfigPath(appPath);
|
|
152
|
+
const variables = await loadVariablesYamlFile(configPath);
|
|
158
153
|
|
|
159
154
|
const schemaBasePath = variables.externalIntegration.schemaBasePath || './';
|
|
160
155
|
const systemFiles = variables.externalIntegration.systems || [];
|
|
@@ -290,7 +290,7 @@ function addHealthCheckToDeployment(deployment, variables) {
|
|
|
290
290
|
* @param {Object|null} rbac - RBAC configuration
|
|
291
291
|
*/
|
|
292
292
|
function addRolesAndPermissions(deployment, variables, rbac) {
|
|
293
|
-
// Priority:
|
|
293
|
+
// Priority: application.yaml > rbac.yaml
|
|
294
294
|
if (variables.roles) {
|
|
295
295
|
deployment.roles = variables.roles;
|
|
296
296
|
} else if (rbac && rbac.roles) {
|
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
|
|
12
12
|
const path = require('path');
|
|
13
13
|
const { detectAppType } = require('../utils/paths');
|
|
14
|
+
const { resolveApplicationConfigPath } = require('../utils/app-config-resolver');
|
|
14
15
|
const { loadSystemFile, loadDatasourceFiles } = require('./external');
|
|
15
16
|
const { loadVariables, loadRbac } = require('./helpers');
|
|
16
17
|
|
|
@@ -42,18 +43,18 @@ function mergeRbacIntoSystemJson(systemJson, rbac) {
|
|
|
42
43
|
* @param {Object} options - Options with optional appPath
|
|
43
44
|
* @returns {Promise<string>} Application path
|
|
44
45
|
*/
|
|
45
|
-
async function resolveAppPath(appName, options) {
|
|
46
|
-
if (options.appPath) {
|
|
46
|
+
async function resolveAppPath(appName, options = {}) {
|
|
47
|
+
if (options && options.appPath) {
|
|
47
48
|
return options.appPath;
|
|
48
49
|
}
|
|
49
|
-
const detected = await detectAppType(appName
|
|
50
|
+
const detected = await detectAppType(appName);
|
|
50
51
|
return detected.appPath;
|
|
51
52
|
}
|
|
52
53
|
|
|
53
54
|
/**
|
|
54
55
|
* Extracts app metadata from variables
|
|
55
56
|
* @function extractAppMetadata
|
|
56
|
-
* @param {Object} variables - Parsed
|
|
57
|
+
* @param {Object} variables - Parsed application config
|
|
57
58
|
* @param {string} appName - Application name
|
|
58
59
|
* @returns {Object} App metadata { appKey, displayName, description }
|
|
59
60
|
*/
|
|
@@ -98,59 +99,55 @@ async function loadSystemWithRbac(appPath, schemaBasePath, systemFile) {
|
|
|
98
99
|
* const manifest = await generateControllerManifest('my-hubspot');
|
|
99
100
|
* // Returns: { key, displayName, description, type: "external", system: {...}, dataSources: [...] }
|
|
100
101
|
*/
|
|
102
|
+
function normalizeSchemaBasePath(schemaBasePath, appPath, appName) {
|
|
103
|
+
const base = path.normalize(schemaBasePath || './').replace(/[/\\]+$/, '');
|
|
104
|
+
return base === path.join('integration', appName) ? './' : (schemaBasePath || './');
|
|
105
|
+
}
|
|
106
|
+
|
|
101
107
|
async function generateControllerManifest(appName, options = {}) {
|
|
102
108
|
if (!appName || typeof appName !== 'string') {
|
|
103
109
|
throw new Error('App name is required and must be a string');
|
|
104
110
|
}
|
|
105
|
-
|
|
106
111
|
const appPath = await resolveAppPath(appName, options);
|
|
107
|
-
const
|
|
108
|
-
const { parsed: variables } = loadVariables(variablesPath);
|
|
109
|
-
|
|
112
|
+
const { parsed: variables } = loadVariables(resolveApplicationConfigPath(appPath));
|
|
110
113
|
if (!variables.externalIntegration) {
|
|
111
|
-
throw new Error('externalIntegration block not found in
|
|
114
|
+
throw new Error('externalIntegration block not found in application.yaml');
|
|
112
115
|
}
|
|
113
|
-
|
|
114
116
|
const metadata = extractAppMetadata(variables, appName);
|
|
115
|
-
const schemaBasePath =
|
|
117
|
+
const schemaBasePath = normalizeSchemaBasePath(
|
|
118
|
+
variables.externalIntegration.schemaBasePath,
|
|
119
|
+
appPath,
|
|
120
|
+
appName
|
|
121
|
+
);
|
|
116
122
|
const systemFiles = variables.externalIntegration.systems || [];
|
|
117
|
-
|
|
118
123
|
if (systemFiles.length === 0) {
|
|
119
124
|
throw new Error('No system files specified in externalIntegration.systems');
|
|
120
125
|
}
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
+
const [systemJson, datasourceJsons] = await Promise.all([
|
|
127
|
+
loadSystemWithRbac(appPath, schemaBasePath, systemFiles[0]),
|
|
128
|
+
loadDatasourceFiles(appPath, schemaBasePath, variables.externalIntegration.dataSources || [])
|
|
129
|
+
]);
|
|
126
130
|
const appVersion = variables.app?.version || variables.externalIntegration?.version || '1.0.0';
|
|
127
|
-
|
|
128
|
-
// Build externalIntegration block (required by application schema for type: "external")
|
|
129
131
|
const externalIntegration = {
|
|
130
|
-
schemaBasePath
|
|
132
|
+
schemaBasePath,
|
|
131
133
|
systems: systemFiles,
|
|
132
|
-
dataSources:
|
|
133
|
-
autopublish: variables.externalIntegration.autopublish !== false,
|
|
134
|
+
dataSources: variables.externalIntegration.dataSources || [],
|
|
135
|
+
autopublish: variables.externalIntegration.autopublish !== false,
|
|
134
136
|
version: appVersion
|
|
135
137
|
};
|
|
136
|
-
|
|
137
|
-
const manifest = {
|
|
138
|
+
return {
|
|
138
139
|
key: metadata.appKey,
|
|
139
140
|
displayName: metadata.displayName,
|
|
140
141
|
description: metadata.description,
|
|
141
142
|
type: 'external',
|
|
142
143
|
version: appVersion,
|
|
143
|
-
externalIntegration
|
|
144
|
-
// Inline system and dataSources for atomic deployment (optional but recommended)
|
|
144
|
+
externalIntegration,
|
|
145
145
|
system: systemJson,
|
|
146
146
|
dataSources: datasourceJsons,
|
|
147
|
-
// Explicitly set to false to satisfy conditional schema requirements
|
|
148
147
|
requiresDatabase: false,
|
|
149
148
|
requiresRedis: false,
|
|
150
149
|
requiresStorage: false
|
|
151
150
|
};
|
|
152
|
-
|
|
153
|
-
return manifest;
|
|
154
151
|
}
|
|
155
152
|
|
|
156
153
|
module.exports = {
|
|
@@ -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
|
*/
|