@aifabrix/builder 2.22.1 → 2.31.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/jest.config.coverage.js +37 -0
- package/lib/api/pipeline.api.js +10 -9
- package/lib/app-deploy.js +36 -14
- package/lib/app-list.js +191 -71
- package/lib/app-prompts.js +77 -26
- package/lib/app-readme.js +123 -5
- package/lib/app-rotate-secret.js +101 -57
- package/lib/app-run-helpers.js +200 -172
- package/lib/app-run.js +137 -68
- package/lib/audit-logger.js +8 -7
- package/lib/build.js +161 -250
- package/lib/cli.js +73 -65
- package/lib/commands/login.js +45 -31
- package/lib/commands/logout.js +181 -0
- package/lib/commands/secrets-set.js +2 -2
- package/lib/commands/secure.js +61 -26
- package/lib/config.js +79 -45
- package/lib/datasource-deploy.js +89 -29
- package/lib/deployer.js +164 -129
- package/lib/diff.js +63 -21
- package/lib/environment-deploy.js +36 -19
- package/lib/external-system-deploy.js +134 -66
- package/lib/external-system-download.js +244 -171
- package/lib/external-system-test.js +199 -164
- package/lib/generator-external.js +145 -72
- package/lib/generator-helpers.js +49 -17
- package/lib/generator-split.js +105 -58
- package/lib/infra.js +101 -131
- package/lib/schema/application-schema.json +895 -896
- package/lib/schema/env-config.yaml +11 -4
- package/lib/template-validator.js +13 -4
- package/lib/utils/api.js +8 -8
- package/lib/utils/app-register-auth.js +36 -18
- package/lib/utils/app-run-containers.js +140 -0
- package/lib/utils/auth-headers.js +6 -6
- package/lib/utils/build-copy.js +60 -2
- package/lib/utils/build-helpers.js +94 -0
- package/lib/utils/cli-utils.js +177 -76
- package/lib/utils/compose-generator.js +12 -2
- package/lib/utils/config-tokens.js +151 -9
- package/lib/utils/deployment-errors.js +137 -69
- package/lib/utils/deployment-validation-helpers.js +103 -0
- package/lib/utils/docker-build.js +57 -0
- package/lib/utils/dockerfile-utils.js +13 -3
- package/lib/utils/env-copy.js +163 -94
- package/lib/utils/env-map.js +226 -86
- package/lib/utils/environment-checker.js +2 -2
- package/lib/utils/error-formatters/network-errors.js +0 -1
- package/lib/utils/external-system-display.js +14 -19
- package/lib/utils/external-system-env-helpers.js +107 -0
- package/lib/utils/external-system-test-helpers.js +144 -0
- package/lib/utils/health-check.js +10 -8
- package/lib/utils/infra-status.js +123 -0
- package/lib/utils/local-secrets.js +3 -2
- package/lib/utils/paths.js +228 -49
- package/lib/utils/schema-loader.js +125 -57
- package/lib/utils/token-manager.js +10 -7
- package/lib/utils/yaml-preserve.js +55 -16
- package/lib/validate.js +87 -89
- package/package.json +4 -4
- package/scripts/ci-fix.sh +19 -0
- package/scripts/ci-simulate.sh +19 -0
- package/templates/applications/miso-controller/test.yaml +1 -0
- package/templates/python/Dockerfile.hbs +8 -45
- package/templates/typescript/Dockerfile.hbs +8 -42
package/lib/app-run.js
CHANGED
|
@@ -17,6 +17,131 @@ const composeGenerator = require('./utils/compose-generator');
|
|
|
17
17
|
// Helper functions extracted to reduce file size and complexity
|
|
18
18
|
const helpers = require('./app-run-helpers');
|
|
19
19
|
|
|
20
|
+
/**
|
|
21
|
+
* Validate app for run and check if it's an external system
|
|
22
|
+
* @async
|
|
23
|
+
* @param {string} appName - Application name
|
|
24
|
+
* @param {boolean} _debug - Debug flag (unused)
|
|
25
|
+
* @returns {Promise<boolean>} True if should continue, false if external system
|
|
26
|
+
* @throws {Error} If app name is invalid
|
|
27
|
+
*/
|
|
28
|
+
async function validateAppForRun(appName, _debug) {
|
|
29
|
+
if (!appName || typeof appName !== 'string') {
|
|
30
|
+
throw new Error('Application name is required');
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// Check if app type is external - skip Docker run
|
|
34
|
+
const { detectAppType } = require('./utils/paths');
|
|
35
|
+
try {
|
|
36
|
+
const { isExternal } = await detectAppType(appName);
|
|
37
|
+
if (isExternal) {
|
|
38
|
+
logger.log(chalk.yellow('⚠️ External systems don\'t run as Docker containers.'));
|
|
39
|
+
logger.log(chalk.blue('Use "aifabrix build" to deploy to dataplane, then test via OpenAPI endpoints.'));
|
|
40
|
+
return false;
|
|
41
|
+
}
|
|
42
|
+
} catch (error) {
|
|
43
|
+
// If detection fails, continue with normal run
|
|
44
|
+
// (detectAppType throws if app doesn't exist, which is fine for run command)
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
return true;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Check if container is running and stop it if needed
|
|
52
|
+
* @async
|
|
53
|
+
* @param {string} appName - Application name
|
|
54
|
+
* @param {number|string} developerId - Developer ID
|
|
55
|
+
* @param {boolean} debug - Debug flag
|
|
56
|
+
* @returns {Promise<void>}
|
|
57
|
+
*/
|
|
58
|
+
async function checkAndStopContainer(appName, developerId, debug) {
|
|
59
|
+
const containerRunning = await helpers.checkContainerRunning(appName, developerId, debug);
|
|
60
|
+
if (!containerRunning) {
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// Dev 0: aifabrix-{appName} (no dev-0 suffix), Dev > 0: aifabrix-dev{id}-{appName}
|
|
65
|
+
const idNum = typeof developerId === 'string' ? parseInt(developerId, 10) : developerId;
|
|
66
|
+
const containerName = idNum === 0 ? `aifabrix-${appName}` : `aifabrix-dev${developerId}-${appName}`;
|
|
67
|
+
logger.log(chalk.yellow(`Container ${containerName} is already running`));
|
|
68
|
+
await helpers.stopAndRemoveContainer(appName, developerId, debug);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Calculate host port and validate it's available
|
|
73
|
+
* @async
|
|
74
|
+
* @param {Object} appConfig - Application configuration
|
|
75
|
+
* @param {Object} options - Run options
|
|
76
|
+
* @param {number} [options.port] - Override port
|
|
77
|
+
* @param {boolean} debug - Debug flag
|
|
78
|
+
* @returns {Promise<number>} Host port
|
|
79
|
+
* @throws {Error} If port is not available
|
|
80
|
+
*/
|
|
81
|
+
async function calculateHostPort(appConfig, options, debug) {
|
|
82
|
+
const basePort = appConfig.port || 3000;
|
|
83
|
+
const idNum = typeof appConfig.developerId === 'string' ? parseInt(appConfig.developerId, 10) : appConfig.developerId;
|
|
84
|
+
const hostPort = options.port || (idNum === 0 ? basePort : basePort + (idNum * 100));
|
|
85
|
+
|
|
86
|
+
if (debug) {
|
|
87
|
+
logger.log(chalk.gray(`[DEBUG] Host port: ${hostPort} (${options.port ? 'CLI override' : 'dev-specific'}), Container port: ${appConfig.build?.containerPort || appConfig.port || 3000} (unchanged)`));
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const portAvailable = await checkPortAvailable(hostPort);
|
|
91
|
+
if (debug) {
|
|
92
|
+
logger.log(chalk.gray(`[DEBUG] Port ${hostPort} available: ${portAvailable}`));
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
if (!portAvailable) {
|
|
96
|
+
throw new Error(`Port ${hostPort} is already in use. Try --port <alternative>`);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
return hostPort;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Load and configure application
|
|
104
|
+
* @async
|
|
105
|
+
* @param {string} appName - Application name
|
|
106
|
+
* @param {boolean} debug - Debug flag
|
|
107
|
+
* @returns {Promise<Object>} Application configuration with developerId
|
|
108
|
+
*/
|
|
109
|
+
async function loadAndConfigureApp(appName, debug) {
|
|
110
|
+
const appConfig = await helpers.validateAppConfiguration(appName);
|
|
111
|
+
const developerId = await config.getDeveloperId();
|
|
112
|
+
appConfig.developerId = developerId;
|
|
113
|
+
|
|
114
|
+
if (debug) {
|
|
115
|
+
logger.log(chalk.gray(`[DEBUG] Configuration loaded: port=${appConfig.port || 'default'}, healthCheck.path=${appConfig.healthCheck?.path || '/health'}, developerId=${appConfig.developerId}`));
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
return appConfig;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Start application container and display status
|
|
123
|
+
* @async
|
|
124
|
+
* @param {string} appName - Application name
|
|
125
|
+
* @param {string} tempComposePath - Path to compose file
|
|
126
|
+
* @param {number} hostPort - Host port
|
|
127
|
+
* @param {Object} appConfig - Application configuration
|
|
128
|
+
* @param {boolean} debug - Debug flag
|
|
129
|
+
* @throws {Error} If container start fails
|
|
130
|
+
*/
|
|
131
|
+
async function startAppContainer(appName, tempComposePath, hostPort, appConfig, debug) {
|
|
132
|
+
try {
|
|
133
|
+
await helpers.startContainer(appName, tempComposePath, hostPort, appConfig, debug);
|
|
134
|
+
await helpers.displayRunStatus(appName, hostPort, appConfig);
|
|
135
|
+
} catch (error) {
|
|
136
|
+
logger.log(chalk.yellow(`\n⚠️ Compose file preserved at: ${tempComposePath}`));
|
|
137
|
+
logger.log(chalk.yellow(' Review the file to debug issues'));
|
|
138
|
+
if (debug) {
|
|
139
|
+
logger.log(chalk.gray(`[DEBUG] Error during container start: ${error.message}`));
|
|
140
|
+
}
|
|
141
|
+
throw error;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
20
145
|
/**
|
|
21
146
|
* Runs the application locally using Docker
|
|
22
147
|
* Starts container with proper port mapping and environment
|
|
@@ -43,65 +168,23 @@ async function runApp(appName, options = {}) {
|
|
|
43
168
|
}
|
|
44
169
|
|
|
45
170
|
try {
|
|
46
|
-
// Validate app
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
// Check if app type is external - skip Docker run
|
|
52
|
-
const { detectAppType } = require('./utils/paths');
|
|
53
|
-
try {
|
|
54
|
-
const { isExternal } = await detectAppType(appName);
|
|
55
|
-
if (isExternal) {
|
|
56
|
-
logger.log(chalk.yellow('⚠️ External systems don\'t run as Docker containers.'));
|
|
57
|
-
logger.log(chalk.blue('Use "aifabrix build" to deploy to dataplane, then test via OpenAPI endpoints.'));
|
|
58
|
-
return;
|
|
59
|
-
}
|
|
60
|
-
} catch (error) {
|
|
61
|
-
// If detection fails, continue with normal run
|
|
62
|
-
// (detectAppType throws if app doesn't exist, which is fine for run command)
|
|
171
|
+
// Validate app for run and check if external
|
|
172
|
+
const shouldContinue = await validateAppForRun(appName, debug);
|
|
173
|
+
if (!shouldContinue) {
|
|
174
|
+
return;
|
|
63
175
|
}
|
|
64
176
|
|
|
65
|
-
//
|
|
66
|
-
const appConfig = await
|
|
67
|
-
|
|
68
|
-
// Load developer ID once from config module - it's now cached and available as config.developerId
|
|
69
|
-
// Developer ID: 0 = default infra, > 0 = developer-specific
|
|
70
|
-
const developerId = await config.getDeveloperId(); // Load and cache developer ID
|
|
71
|
-
appConfig.developerId = developerId; // Use developer ID in config
|
|
72
|
-
|
|
73
|
-
if (debug) {
|
|
74
|
-
logger.log(chalk.gray(`[DEBUG] Configuration loaded: port=${appConfig.port || 'default'}, healthCheck.path=${appConfig.healthCheck?.path || '/health'}, developerId=${appConfig.developerId}`));
|
|
75
|
-
}
|
|
177
|
+
// Load and configure application
|
|
178
|
+
const appConfig = await loadAndConfigureApp(appName, debug);
|
|
76
179
|
|
|
77
180
|
// Check prerequisites: image and infrastructure
|
|
78
181
|
await helpers.checkPrerequisites(appName, appConfig, debug);
|
|
79
182
|
|
|
80
|
-
// Check if container is already running
|
|
81
|
-
|
|
82
|
-
if (containerRunning) {
|
|
83
|
-
// Dev 0: aifabrix-{appName} (no dev-0 suffix), Dev > 0: aifabrix-dev{id}-{appName}
|
|
84
|
-
const idNum2 = typeof appConfig.developerId === 'string' ? parseInt(appConfig.developerId, 10) : appConfig.developerId;
|
|
85
|
-
const containerName = idNum2 === 0 ? `aifabrix-${appName}` : `aifabrix-dev${appConfig.developerId}-${appName}`;
|
|
86
|
-
logger.log(chalk.yellow(`Container ${containerName} is already running`));
|
|
87
|
-
await helpers.stopAndRemoveContainer(appName, appConfig.developerId, debug);
|
|
88
|
-
}
|
|
183
|
+
// Check if container is already running and stop it if needed
|
|
184
|
+
await checkAndStopContainer(appName, appConfig.developerId, debug);
|
|
89
185
|
|
|
90
|
-
// Calculate host port
|
|
91
|
-
|
|
92
|
-
const basePort = appConfig.port || 3000;
|
|
93
|
-
const idNum3 = typeof appConfig.developerId === 'string' ? parseInt(appConfig.developerId, 10) : appConfig.developerId;
|
|
94
|
-
const hostPort = options.port || (idNum3 === 0 ? basePort : basePort + (idNum3 * 100));
|
|
95
|
-
if (debug) {
|
|
96
|
-
logger.log(chalk.gray(`[DEBUG] Host port: ${hostPort} (${options.port ? 'CLI override' : 'dev-specific'}), Container port: ${appConfig.build?.containerPort || appConfig.port || 3000} (unchanged)`));
|
|
97
|
-
}
|
|
98
|
-
const portAvailable = await checkPortAvailable(hostPort);
|
|
99
|
-
if (debug) {
|
|
100
|
-
logger.log(chalk.gray(`[DEBUG] Port ${hostPort} available: ${portAvailable}`));
|
|
101
|
-
}
|
|
102
|
-
if (!portAvailable) {
|
|
103
|
-
throw new Error(`Port ${hostPort} is already in use. Try --port <alternative>`);
|
|
104
|
-
}
|
|
186
|
+
// Calculate host port and validate it's available
|
|
187
|
+
const hostPort = await calculateHostPort(appConfig, options, debug);
|
|
105
188
|
|
|
106
189
|
// Prepare environment: ensure .env file and generate Docker Compose
|
|
107
190
|
const tempComposePath = await helpers.prepareEnvironment(appName, appConfig, options);
|
|
@@ -109,22 +192,8 @@ async function runApp(appName, options = {}) {
|
|
|
109
192
|
logger.log(chalk.gray(`[DEBUG] Compose file generated: ${tempComposePath}`));
|
|
110
193
|
}
|
|
111
194
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
await helpers.startContainer(appName, tempComposePath, hostPort, appConfig, debug);
|
|
115
|
-
|
|
116
|
-
// Display success message
|
|
117
|
-
await helpers.displayRunStatus(appName, hostPort, appConfig);
|
|
118
|
-
|
|
119
|
-
} catch (error) {
|
|
120
|
-
// Keep the compose file for debugging - don't delete on error
|
|
121
|
-
logger.log(chalk.yellow(`\n⚠️ Compose file preserved at: ${tempComposePath}`));
|
|
122
|
-
logger.log(chalk.yellow(' Review the file to debug issues'));
|
|
123
|
-
if (debug) {
|
|
124
|
-
logger.log(chalk.gray(`[DEBUG] Error during container start: ${error.message}`));
|
|
125
|
-
}
|
|
126
|
-
throw error;
|
|
127
|
-
}
|
|
195
|
+
// Start container and display status
|
|
196
|
+
await startAppContainer(appName, tempComposePath, hostPort, appConfig, debug);
|
|
128
197
|
|
|
129
198
|
} catch (error) {
|
|
130
199
|
if (debug) {
|
package/lib/audit-logger.js
CHANGED
|
@@ -258,14 +258,15 @@ async function logApplicationCreation(appName, options = {}) {
|
|
|
258
258
|
* Logs API call attempt with full details for audit trail
|
|
259
259
|
* Logs both successful and failed API calls for troubleshooting
|
|
260
260
|
*
|
|
261
|
-
* @param {
|
|
262
|
-
* @param {
|
|
263
|
-
* @param {
|
|
264
|
-
* @param {number}
|
|
265
|
-
* @param {
|
|
266
|
-
* @param {
|
|
261
|
+
* @param {Object} params - Function parameters
|
|
262
|
+
* @param {string} params.url - API endpoint URL
|
|
263
|
+
* @param {Object} params.options - Fetch options (method, headers, etc.)
|
|
264
|
+
* @param {number} params.statusCode - HTTP status code
|
|
265
|
+
* @param {number} params.duration - Request duration in milliseconds
|
|
266
|
+
* @param {boolean} params.success - Whether the request was successful
|
|
267
|
+
* @param {Object} [params.errorInfo] - Error information (if failed)
|
|
267
268
|
*/
|
|
268
|
-
async function logApiCall(url, options, statusCode, duration, success, errorInfo = {}) {
|
|
269
|
+
async function logApiCall({ url, options, statusCode, duration, success, errorInfo = {} }) {
|
|
269
270
|
const method = options.method || 'GET';
|
|
270
271
|
const path = extractPathFromUrl(url);
|
|
271
272
|
|