@aifabrix/builder 2.0.0 → 2.0.3
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/README.md +5 -3
- package/bin/aifabrix.js +9 -3
- package/jest.config.integration.js +30 -0
- package/lib/app-config.js +157 -0
- package/lib/app-deploy.js +233 -82
- package/lib/app-dockerfile.js +112 -0
- package/lib/app-prompts.js +244 -0
- package/lib/app-push.js +172 -0
- package/lib/app-run.js +235 -144
- package/lib/app.js +208 -274
- package/lib/audit-logger.js +2 -0
- package/lib/build.js +177 -125
- package/lib/cli.js +76 -86
- package/lib/commands/app.js +414 -0
- package/lib/commands/login.js +304 -0
- package/lib/config.js +78 -0
- package/lib/deployer.js +225 -81
- package/lib/env-reader.js +45 -30
- package/lib/generator.js +308 -191
- package/lib/github-generator.js +67 -7
- package/lib/infra.js +156 -61
- package/lib/push.js +105 -10
- package/lib/schema/application-schema.json +30 -2
- package/lib/schema/env-config.yaml +9 -1
- package/lib/schema/infrastructure-schema.json +589 -0
- package/lib/secrets.js +229 -24
- package/lib/template-validator.js +205 -0
- package/lib/templates.js +305 -170
- package/lib/utils/api.js +329 -0
- package/lib/utils/cli-utils.js +97 -0
- package/lib/utils/compose-generator.js +185 -0
- package/lib/utils/docker-build.js +173 -0
- package/lib/utils/dockerfile-utils.js +131 -0
- package/lib/utils/environment-checker.js +125 -0
- package/lib/utils/error-formatter.js +61 -0
- package/lib/utils/health-check.js +187 -0
- package/lib/utils/logger.js +53 -0
- package/lib/utils/template-helpers.js +223 -0
- package/lib/utils/variable-transformer.js +271 -0
- package/lib/validator.js +27 -112
- package/package.json +14 -10
- package/templates/README.md +75 -3
- package/templates/applications/keycloak/Dockerfile +36 -0
- package/templates/applications/keycloak/env.template +32 -0
- package/templates/applications/keycloak/rbac.yaml +37 -0
- package/templates/applications/keycloak/variables.yaml +56 -0
- package/templates/applications/miso-controller/Dockerfile +125 -0
- package/templates/applications/miso-controller/env.template +129 -0
- package/templates/applications/miso-controller/rbac.yaml +214 -0
- package/templates/applications/miso-controller/variables.yaml +56 -0
- package/templates/github/release.yaml.hbs +5 -26
- package/templates/github/steps/npm.hbs +24 -0
- package/templates/infra/compose.yaml +6 -6
- package/templates/python/docker-compose.hbs +19 -12
- package/templates/python/main.py +80 -0
- package/templates/python/requirements.txt +4 -0
- package/templates/typescript/Dockerfile.hbs +2 -2
- package/templates/typescript/docker-compose.hbs +19 -12
- package/templates/typescript/index.ts +116 -0
- package/templates/typescript/package.json +26 -0
- package/templates/typescript/tsconfig.json +24 -0
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Logger Utility
|
|
3
|
+
*
|
|
4
|
+
* Centralized logging utility that wraps console methods
|
|
5
|
+
* Allows disabling eslint warnings in one place
|
|
6
|
+
*
|
|
7
|
+
* @fileoverview Logger utility for AI Fabrix Builder
|
|
8
|
+
* @author AI Fabrix Team
|
|
9
|
+
* @version 2.0.0
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
/* eslint-disable no-console */
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Logger utility that wraps console methods
|
|
16
|
+
* All console statements should use this logger to avoid eslint warnings
|
|
17
|
+
*/
|
|
18
|
+
const logger = {
|
|
19
|
+
/**
|
|
20
|
+
* Log informational message
|
|
21
|
+
* @param {...any} args - Arguments to log
|
|
22
|
+
*/
|
|
23
|
+
log: (...args) => {
|
|
24
|
+
console.log(...args);
|
|
25
|
+
},
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Log error message
|
|
29
|
+
* @param {...any} args - Arguments to log
|
|
30
|
+
*/
|
|
31
|
+
error: (...args) => {
|
|
32
|
+
console.error(...args);
|
|
33
|
+
},
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Log warning message
|
|
37
|
+
* @param {...any} args - Arguments to log
|
|
38
|
+
*/
|
|
39
|
+
warn: (...args) => {
|
|
40
|
+
console.warn(...args);
|
|
41
|
+
},
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Log informational message (alias for log)
|
|
45
|
+
* @param {...any} args - Arguments to log
|
|
46
|
+
*/
|
|
47
|
+
info: (...args) => {
|
|
48
|
+
console.log(...args);
|
|
49
|
+
}
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
module.exports = logger;
|
|
53
|
+
|
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Template Helper Utilities
|
|
3
|
+
*
|
|
4
|
+
* Handles template variable loading, updating, and merging
|
|
5
|
+
*
|
|
6
|
+
* @fileoverview Template helper utilities for AI Fabrix Builder
|
|
7
|
+
* @author AI Fabrix Team
|
|
8
|
+
* @version 2.0.0
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
const fs = require('fs').promises;
|
|
12
|
+
const path = require('path');
|
|
13
|
+
const chalk = require('chalk');
|
|
14
|
+
const logger = require('./logger');
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Loads template variables from template's variables.yaml file
|
|
18
|
+
* @async
|
|
19
|
+
* @function loadTemplateVariables
|
|
20
|
+
* @param {string} templateName - Template name
|
|
21
|
+
* @returns {Promise<Object|null>} Template variables or null if not found
|
|
22
|
+
*/
|
|
23
|
+
async function loadTemplateVariables(templateName) {
|
|
24
|
+
if (!templateName) {
|
|
25
|
+
return null;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const yaml = require('js-yaml');
|
|
29
|
+
const templatePath = path.join(__dirname, '..', '..', 'templates', 'applications', templateName);
|
|
30
|
+
const templateVariablesPath = path.join(templatePath, 'variables.yaml');
|
|
31
|
+
|
|
32
|
+
try {
|
|
33
|
+
const templateContent = await fs.readFile(templateVariablesPath, 'utf8');
|
|
34
|
+
return yaml.load(templateContent);
|
|
35
|
+
} catch (error) {
|
|
36
|
+
// Template variables.yaml not found or invalid, continue without it
|
|
37
|
+
if (error.code !== 'ENOENT') {
|
|
38
|
+
logger.warn(chalk.yellow(`⚠️ Warning: Could not load template variables.yaml: ${error.message}`));
|
|
39
|
+
}
|
|
40
|
+
return null;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Updates app key and display name in variables
|
|
46
|
+
* @function updateAppMetadata
|
|
47
|
+
* @param {Object} variables - Variables object
|
|
48
|
+
* @param {string} appName - Application name
|
|
49
|
+
*/
|
|
50
|
+
function updateAppMetadata(variables, appName) {
|
|
51
|
+
if (variables.app) {
|
|
52
|
+
variables.app.key = appName;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
if (variables.app?.displayName && variables.app.displayName.toLowerCase().includes('miso')) {
|
|
56
|
+
variables.app.displayName = appName.replace(/-/g, ' ').replace(/\b\w/g, l => l.toUpperCase());
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Updates port in variables if provided
|
|
62
|
+
* @function updatePort
|
|
63
|
+
* @param {Object} variables - Variables object
|
|
64
|
+
* @param {Object} options - CLI options
|
|
65
|
+
* @param {Object} config - Final configuration
|
|
66
|
+
*/
|
|
67
|
+
function updatePort(variables, options, config) {
|
|
68
|
+
if (options.port && config.port && variables.port !== config.port) {
|
|
69
|
+
variables.port = config.port;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Updates build configuration
|
|
75
|
+
* @function updateBuildConfig
|
|
76
|
+
* @param {Object} variables - Variables object
|
|
77
|
+
*/
|
|
78
|
+
function updateBuildConfig(variables) {
|
|
79
|
+
if (variables.build && variables.build.envOutputPath) {
|
|
80
|
+
variables.build.envOutputPath = null;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Updates database configuration for --app flag
|
|
86
|
+
* @function updateDatabaseConfig
|
|
87
|
+
* @param {Object} variables - Variables object
|
|
88
|
+
* @param {Object} options - CLI options
|
|
89
|
+
* @param {string} appName - Application name
|
|
90
|
+
*/
|
|
91
|
+
function updateDatabaseConfig(variables, options, appName) {
|
|
92
|
+
if (!options.app || !variables.requires) {
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
if (variables.requires.databases) {
|
|
97
|
+
variables.requires.databases = [{ name: appName }];
|
|
98
|
+
} else if (variables.requires.database && !variables.requires.databases) {
|
|
99
|
+
variables.requires.databases = [{ name: appName }];
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Updates variables.yaml file after copying from template
|
|
105
|
+
* Updates app.key, displayName, and port with actual values
|
|
106
|
+
* @async
|
|
107
|
+
* @function updateTemplateVariables
|
|
108
|
+
* @param {string} appPath - Application directory path
|
|
109
|
+
* @param {string} appName - Application name
|
|
110
|
+
* @param {Object} options - CLI options
|
|
111
|
+
* @param {Object} config - Final configuration
|
|
112
|
+
*/
|
|
113
|
+
async function updateTemplateVariables(appPath, appName, options, config) {
|
|
114
|
+
const variablesPath = path.join(appPath, 'variables.yaml');
|
|
115
|
+
try {
|
|
116
|
+
const yaml = require('js-yaml');
|
|
117
|
+
const variablesContent = await fs.readFile(variablesPath, 'utf8');
|
|
118
|
+
const variables = yaml.load(variablesContent);
|
|
119
|
+
|
|
120
|
+
updateAppMetadata(variables, appName);
|
|
121
|
+
updatePort(variables, options, config);
|
|
122
|
+
updateBuildConfig(variables);
|
|
123
|
+
updateDatabaseConfig(variables, options, appName);
|
|
124
|
+
|
|
125
|
+
await fs.writeFile(variablesPath, yaml.dump(variables, { indent: 2, lineWidth: 120, noRefs: true }));
|
|
126
|
+
} catch (error) {
|
|
127
|
+
if (error.code !== 'ENOENT') {
|
|
128
|
+
logger.warn(chalk.yellow(`⚠️ Warning: Could not update variables.yaml: ${error.message}`));
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Merges port from template variables if not in options
|
|
135
|
+
* @function mergePort
|
|
136
|
+
* @param {Object} merged - Merged options object
|
|
137
|
+
* @param {Object} templateVariables - Template variables
|
|
138
|
+
*/
|
|
139
|
+
function mergePort(merged, templateVariables) {
|
|
140
|
+
if (!merged.port && templateVariables.port) {
|
|
141
|
+
merged.port = templateVariables.port;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Merges language from template variables if not in options
|
|
147
|
+
* @function mergeLanguage
|
|
148
|
+
* @param {Object} merged - Merged options object
|
|
149
|
+
* @param {Object} templateVariables - Template variables
|
|
150
|
+
*/
|
|
151
|
+
function mergeLanguage(merged, templateVariables) {
|
|
152
|
+
if (!merged.language && templateVariables.build?.language) {
|
|
153
|
+
merged.language = templateVariables.build.language;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Merges service requirements from template variables if not in options
|
|
159
|
+
* @function mergeServices
|
|
160
|
+
* @param {Object} merged - Merged options object
|
|
161
|
+
* @param {Object} templateVariables - Template variables
|
|
162
|
+
*/
|
|
163
|
+
function mergeServices(merged, templateVariables) {
|
|
164
|
+
// Database: use template requires.database if not specified in options
|
|
165
|
+
if (!Object.prototype.hasOwnProperty.call(merged, 'database') &&
|
|
166
|
+
templateVariables.requires?.database !== undefined) {
|
|
167
|
+
merged.database = templateVariables.requires.database;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
// Redis: use template requires.redis if not specified in options
|
|
171
|
+
if (!Object.prototype.hasOwnProperty.call(merged, 'redis') &&
|
|
172
|
+
templateVariables.requires?.redis !== undefined) {
|
|
173
|
+
merged.redis = templateVariables.requires.redis;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// Storage: use template requires.storage if not specified in options
|
|
177
|
+
if (!Object.prototype.hasOwnProperty.call(merged, 'storage') &&
|
|
178
|
+
templateVariables.requires?.storage !== undefined) {
|
|
179
|
+
merged.storage = templateVariables.requires.storage;
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* Merges authentication from template variables if not in options
|
|
185
|
+
* @function mergeAuthentication
|
|
186
|
+
* @param {Object} merged - Merged options object
|
|
187
|
+
* @param {Object} templateVariables - Template variables
|
|
188
|
+
*/
|
|
189
|
+
function mergeAuthentication(merged, templateVariables) {
|
|
190
|
+
if (!Object.prototype.hasOwnProperty.call(merged, 'authentication') &&
|
|
191
|
+
templateVariables.authentication !== undefined) {
|
|
192
|
+
merged.authentication = !!templateVariables.authentication;
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* Merges template variables into options
|
|
198
|
+
* @function mergeTemplateVariables
|
|
199
|
+
* @param {Object} options - User-provided options
|
|
200
|
+
* @param {Object} templateVariables - Template variables from variables.yaml
|
|
201
|
+
* @returns {Object} Merged options object
|
|
202
|
+
*/
|
|
203
|
+
function mergeTemplateVariables(options, templateVariables) {
|
|
204
|
+
if (!templateVariables) {
|
|
205
|
+
return options;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
const merged = { ...options };
|
|
209
|
+
|
|
210
|
+
mergePort(merged, templateVariables);
|
|
211
|
+
mergeLanguage(merged, templateVariables);
|
|
212
|
+
mergeServices(merged, templateVariables);
|
|
213
|
+
mergeAuthentication(merged, templateVariables);
|
|
214
|
+
|
|
215
|
+
return merged;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
module.exports = {
|
|
219
|
+
loadTemplateVariables,
|
|
220
|
+
updateTemplateVariables,
|
|
221
|
+
mergeTemplateVariables
|
|
222
|
+
};
|
|
223
|
+
|
|
@@ -0,0 +1,271 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Variable Transformation Utilities
|
|
3
|
+
*
|
|
4
|
+
* Transforms nested variables.yaml structure to flat schema format
|
|
5
|
+
* Converts app.*, image.*, requires.* to schema-compatible structure
|
|
6
|
+
*
|
|
7
|
+
* @fileoverview Variable transformation utilities for AI Fabrix Builder
|
|
8
|
+
* @author AI Fabrix Team
|
|
9
|
+
* @version 2.0.0
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Sanitizes authentication type - map keycloak to azure (schema allows: azure, local, none)
|
|
14
|
+
* @function sanitizeAuthType
|
|
15
|
+
* @param {string} authType - Authentication type
|
|
16
|
+
* @returns {string} Sanitized authentication type
|
|
17
|
+
*/
|
|
18
|
+
function sanitizeAuthType(authType) {
|
|
19
|
+
if (authType === 'keycloak') {
|
|
20
|
+
return 'azure';
|
|
21
|
+
}
|
|
22
|
+
if (authType && !['azure', 'local', 'none'].includes(authType)) {
|
|
23
|
+
return 'azure'; // Default to azure if invalid type
|
|
24
|
+
}
|
|
25
|
+
return authType;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Transforms flat structure to schema-compatible format
|
|
30
|
+
* @function transformFlatStructure
|
|
31
|
+
* @param {Object} variables - Raw variables from YAML
|
|
32
|
+
* @param {string} appName - Application name (fallback)
|
|
33
|
+
* @returns {Object} Transformed variables matching schema
|
|
34
|
+
*/
|
|
35
|
+
function transformFlatStructure(variables, appName) {
|
|
36
|
+
const result = {
|
|
37
|
+
key: variables.key || appName,
|
|
38
|
+
displayName: variables.displayName || appName,
|
|
39
|
+
description: variables.description || '',
|
|
40
|
+
type: variables.type || 'webapp',
|
|
41
|
+
image: variables.image,
|
|
42
|
+
registryMode: variables.registryMode || 'external',
|
|
43
|
+
port: variables.port || 3000,
|
|
44
|
+
requiresDatabase: variables.requiresDatabase || false,
|
|
45
|
+
requiresRedis: variables.requiresRedis || false,
|
|
46
|
+
requiresStorage: variables.requiresStorage || false,
|
|
47
|
+
databases: variables.databases || [],
|
|
48
|
+
...variables
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
// Sanitize authentication if present
|
|
52
|
+
if (result.authentication && result.authentication.type) {
|
|
53
|
+
result.authentication = {
|
|
54
|
+
...result.authentication,
|
|
55
|
+
type: sanitizeAuthType(result.authentication.type)
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
return result;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Builds image reference string from variables
|
|
64
|
+
* @function buildImageReference
|
|
65
|
+
* @param {Object} variables - Variables configuration
|
|
66
|
+
* @param {string} appName - Application name (fallback)
|
|
67
|
+
* @returns {string} Image reference string
|
|
68
|
+
*/
|
|
69
|
+
function buildImageReference(variables, appName) {
|
|
70
|
+
const imageName = variables.image?.name || variables.app?.key || appName;
|
|
71
|
+
const registry = variables.image?.registry;
|
|
72
|
+
const tag = variables.image?.tag || 'latest';
|
|
73
|
+
return registry ? `${registry}/${imageName}:${tag}` : `${imageName}:${tag}`;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Validates repository URL format
|
|
78
|
+
* @function validateRepositoryUrl
|
|
79
|
+
* @param {string} url - Repository URL
|
|
80
|
+
* @returns {boolean} True if valid
|
|
81
|
+
*/
|
|
82
|
+
function validateRepositoryUrl(url) {
|
|
83
|
+
if (!url || url.trim() === '') {
|
|
84
|
+
return false;
|
|
85
|
+
}
|
|
86
|
+
const repoPattern = /^(https:\/\/github\.com\/[^/]+\/[^/]+|https:\/\/gitlab\.com\/[^/]+\/[^/]+|https:\/\/dev\.azure\.com\/[^/]+\/[^/]+\/[^/]+)$/;
|
|
87
|
+
return repoPattern.test(url);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Validates and transforms repository configuration
|
|
92
|
+
* @function validateRepositoryConfig
|
|
93
|
+
* @param {Object} repository - Repository configuration
|
|
94
|
+
* @returns {Object|null} Validated repository config or null
|
|
95
|
+
*/
|
|
96
|
+
function validateRepositoryConfig(repository) {
|
|
97
|
+
if (!repository) {
|
|
98
|
+
return null;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
const repo = { enabled: repository.enabled || false };
|
|
102
|
+
if (validateRepositoryUrl(repository.repositoryUrl)) {
|
|
103
|
+
repo.repositoryUrl = repository.repositoryUrl;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
if (repo.enabled || repo.repositoryUrl) {
|
|
107
|
+
return repo;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
return null;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Validates and transforms build configuration
|
|
115
|
+
* @function validateBuildConfig
|
|
116
|
+
* @param {Object} build - Build configuration
|
|
117
|
+
* @returns {Object|null} Validated build config or null
|
|
118
|
+
*/
|
|
119
|
+
function validateBuildConfig(build) {
|
|
120
|
+
if (!build) {
|
|
121
|
+
return null;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
const buildConfig = {};
|
|
125
|
+
if (build.envOutputPath) {
|
|
126
|
+
buildConfig.envOutputPath = build.envOutputPath;
|
|
127
|
+
}
|
|
128
|
+
if (build.secrets !== null && build.secrets !== undefined && build.secrets !== '') {
|
|
129
|
+
buildConfig.secrets = build.secrets;
|
|
130
|
+
}
|
|
131
|
+
if (build.localPort) {
|
|
132
|
+
buildConfig.localPort = build.localPort;
|
|
133
|
+
}
|
|
134
|
+
if (build.language) {
|
|
135
|
+
buildConfig.language = build.language;
|
|
136
|
+
}
|
|
137
|
+
if (build.context) {
|
|
138
|
+
buildConfig.context = build.context;
|
|
139
|
+
}
|
|
140
|
+
if (build.dockerfile && build.dockerfile.trim() !== '') {
|
|
141
|
+
buildConfig.dockerfile = build.dockerfile;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
return Object.keys(buildConfig).length > 0 ? buildConfig : null;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* Validates and transforms deployment configuration
|
|
149
|
+
* @function validateDeploymentConfig
|
|
150
|
+
* @param {Object} deployment - Deployment configuration
|
|
151
|
+
* @returns {Object|null} Validated deployment config or null
|
|
152
|
+
*/
|
|
153
|
+
function validateDeploymentConfig(deployment) {
|
|
154
|
+
if (!deployment) {
|
|
155
|
+
return null;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
const deploymentConfig = {};
|
|
159
|
+
if (deployment.controllerUrl && deployment.controllerUrl.trim() !== '' && /^https:\/\/.*$/.test(deployment.controllerUrl)) {
|
|
160
|
+
deploymentConfig.controllerUrl = deployment.controllerUrl;
|
|
161
|
+
}
|
|
162
|
+
if (deployment.clientId && deployment.clientId.trim() !== '' && /^[a-z0-9-]+$/.test(deployment.clientId)) {
|
|
163
|
+
deploymentConfig.clientId = deployment.clientId;
|
|
164
|
+
}
|
|
165
|
+
if (deployment.clientSecret && deployment.clientSecret.trim() !== '' && /^(kv:\/\/.*|.+)$/.test(deployment.clientSecret)) {
|
|
166
|
+
deploymentConfig.clientSecret = deployment.clientSecret;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
return Object.keys(deploymentConfig).length > 0 ? deploymentConfig : null;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* Transforms optional fields from variables
|
|
174
|
+
* @function transformOptionalFields
|
|
175
|
+
* @param {Object} variables - Raw variables from YAML
|
|
176
|
+
* @param {Object} transformed - Base transformed object
|
|
177
|
+
* @returns {Object} Transformed object with optional fields added
|
|
178
|
+
*/
|
|
179
|
+
function transformOptionalFields(variables, transformed) {
|
|
180
|
+
if (variables.healthCheck) {
|
|
181
|
+
transformed.healthCheck = variables.healthCheck;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
if (variables.authentication) {
|
|
185
|
+
transformed.authentication = {
|
|
186
|
+
...variables.authentication,
|
|
187
|
+
type: sanitizeAuthType(variables.authentication.type)
|
|
188
|
+
};
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
const repository = validateRepositoryConfig(variables.repository);
|
|
192
|
+
if (repository) {
|
|
193
|
+
transformed.repository = repository;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
const build = validateBuildConfig(variables.build);
|
|
197
|
+
if (build) {
|
|
198
|
+
transformed.build = build;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
const deployment = validateDeploymentConfig(variables.deployment);
|
|
202
|
+
if (deployment) {
|
|
203
|
+
transformed.deployment = deployment;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
if (variables.startupCommand) {
|
|
207
|
+
transformed.startupCommand = variables.startupCommand;
|
|
208
|
+
}
|
|
209
|
+
if (variables.runtimeVersion) {
|
|
210
|
+
transformed.runtimeVersion = variables.runtimeVersion;
|
|
211
|
+
}
|
|
212
|
+
if (variables.scaling) {
|
|
213
|
+
transformed.scaling = variables.scaling;
|
|
214
|
+
}
|
|
215
|
+
if (variables.frontDoorRouting) {
|
|
216
|
+
transformed.frontDoorRouting = variables.frontDoorRouting;
|
|
217
|
+
}
|
|
218
|
+
if (variables.roles) {
|
|
219
|
+
transformed.roles = variables.roles;
|
|
220
|
+
}
|
|
221
|
+
if (variables.permissions) {
|
|
222
|
+
transformed.permissions = variables.permissions;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
return transformed;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
/**
|
|
229
|
+
* Transforms nested variables.yaml structure to flat schema format
|
|
230
|
+
* Converts app.*, image.*, requires.* to schema-compatible structure
|
|
231
|
+
* Handles both flat and nested structures
|
|
232
|
+
*
|
|
233
|
+
* @function transformVariablesForValidation
|
|
234
|
+
* @param {Object} variables - Raw variables from YAML
|
|
235
|
+
* @param {string} appName - Application name (fallback)
|
|
236
|
+
* @returns {Object} Transformed variables matching schema
|
|
237
|
+
*/
|
|
238
|
+
function transformVariablesForValidation(variables, appName) {
|
|
239
|
+
// Check if structure is already flat (has key, displayName, image as string)
|
|
240
|
+
const isFlat = variables.key && variables.image && typeof variables.image === 'string';
|
|
241
|
+
|
|
242
|
+
if (isFlat) {
|
|
243
|
+
return transformFlatStructure(variables, appName);
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
// Nested structure - transform it
|
|
247
|
+
const requires = variables.requires || {};
|
|
248
|
+
const imageRef = buildImageReference(variables, appName);
|
|
249
|
+
|
|
250
|
+
// Transform to flat schema structure
|
|
251
|
+
const transformed = {
|
|
252
|
+
key: variables.app?.key || appName,
|
|
253
|
+
displayName: variables.app?.displayName || appName,
|
|
254
|
+
description: variables.app?.description || '',
|
|
255
|
+
type: variables.app?.type || 'webapp',
|
|
256
|
+
image: imageRef,
|
|
257
|
+
registryMode: variables.image?.registryMode || 'external',
|
|
258
|
+
port: variables.port || 3000,
|
|
259
|
+
requiresDatabase: requires.database || false,
|
|
260
|
+
requiresRedis: requires.redis || false,
|
|
261
|
+
requiresStorage: requires.storage || false,
|
|
262
|
+
databases: requires.databases || (requires.database ? [{ name: variables.app?.key || appName }] : [])
|
|
263
|
+
};
|
|
264
|
+
|
|
265
|
+
return transformOptionalFields(variables, transformed);
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
module.exports = {
|
|
269
|
+
transformVariablesForValidation
|
|
270
|
+
};
|
|
271
|
+
|