@aifabrix/builder 2.22.2 → 2.31.1
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 +210 -80
- 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/secure.js +59 -24
- 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/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/paths.js +228 -49
- package/lib/utils/schema-loader.js +125 -57
- package/lib/utils/token-manager.js +3 -3
- package/lib/utils/yaml-preserve.js +55 -16
- package/lib/validate.js +87 -89
- package/package.json +7 -5
- package/scripts/ci-fix.sh +19 -0
- package/scripts/ci-simulate.sh +19 -0
- package/scripts/install-local.js +210 -0
- package/templates/applications/miso-controller/test.yaml +1 -0
- package/templates/python/Dockerfile.hbs +8 -45
- package/templates/typescript/Dockerfile.hbs +8 -42
|
@@ -42,127 +42,195 @@ function handleDeploymentError(error) {
|
|
|
42
42
|
}
|
|
43
43
|
|
|
44
44
|
/**
|
|
45
|
-
*
|
|
46
|
-
*
|
|
47
|
-
* @
|
|
48
|
-
* @param {string} appName - Application name for audit logging
|
|
49
|
-
* @param {string} url - Controller URL for audit logging
|
|
50
|
-
* @param {boolean} [alreadyLogged=false] - Whether error has already been logged
|
|
51
|
-
* @throws {Error} User-friendly error message
|
|
45
|
+
* Extract error message from different error types
|
|
46
|
+
* @param {Error|string|Object} error - Error object, string, or object
|
|
47
|
+
* @returns {string} Error message
|
|
52
48
|
*/
|
|
53
|
-
|
|
54
|
-
// For validation errors (like URL validation), just re-throw them directly
|
|
55
|
-
// They already have user-friendly messages
|
|
56
|
-
// Handle both Error objects and strings
|
|
57
|
-
let errorMessage = '';
|
|
49
|
+
function extractErrorMessage(error) {
|
|
58
50
|
if (error instanceof Error) {
|
|
59
|
-
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
errorMessage = error.message;
|
|
51
|
+
return error.message || '';
|
|
52
|
+
}
|
|
53
|
+
if (typeof error === 'string') {
|
|
54
|
+
return error;
|
|
64
55
|
}
|
|
56
|
+
if (error && typeof error === 'object' && error.message) {
|
|
57
|
+
return error.message;
|
|
58
|
+
}
|
|
59
|
+
return '';
|
|
60
|
+
}
|
|
65
61
|
|
|
66
|
-
|
|
62
|
+
/**
|
|
63
|
+
* Check if error is a validation error that should be re-thrown directly
|
|
64
|
+
* @param {string} errorMessage - Error message
|
|
65
|
+
* @returns {boolean} True if validation error
|
|
66
|
+
*/
|
|
67
|
+
function isValidationError(errorMessage) {
|
|
68
|
+
return errorMessage && (
|
|
67
69
|
errorMessage.includes('Controller URL must use HTTPS') ||
|
|
68
70
|
errorMessage.includes('Invalid environment key') ||
|
|
69
71
|
errorMessage.includes('Environment key is required') ||
|
|
70
72
|
errorMessage.includes('Authentication configuration is required') ||
|
|
71
73
|
errorMessage.includes('Invalid controller URL format') ||
|
|
72
74
|
errorMessage.includes('Controller URL is required')
|
|
73
|
-
)
|
|
74
|
-
|
|
75
|
-
if (typeof error === 'string') {
|
|
76
|
-
throw new Error(error);
|
|
77
|
-
}
|
|
78
|
-
// If it's already an Error object, re-throw it directly
|
|
79
|
-
if (error instanceof Error) {
|
|
80
|
-
throw error;
|
|
81
|
-
}
|
|
82
|
-
// Otherwise, create a new Error with the message
|
|
83
|
-
throw new Error(errorMessage);
|
|
84
|
-
}
|
|
75
|
+
);
|
|
76
|
+
}
|
|
85
77
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
78
|
+
/**
|
|
79
|
+
* Re-throw validation errors directly
|
|
80
|
+
* @param {Error|string|Object} error - Error object
|
|
81
|
+
* @param {string} errorMessage - Error message
|
|
82
|
+
* @throws {Error} Validation error
|
|
83
|
+
*/
|
|
84
|
+
function throwValidationError(error, errorMessage) {
|
|
85
|
+
if (typeof error === 'string') {
|
|
86
|
+
throw new Error(error);
|
|
87
|
+
}
|
|
88
|
+
if (error instanceof Error) {
|
|
89
|
+
throw error;
|
|
95
90
|
}
|
|
91
|
+
throw new Error(errorMessage);
|
|
92
|
+
}
|
|
96
93
|
|
|
97
|
-
|
|
94
|
+
/**
|
|
95
|
+
* Log deployment failure to audit log
|
|
96
|
+
* @async
|
|
97
|
+
* @param {string} appName - Application name
|
|
98
|
+
* @param {string} url - Controller URL
|
|
99
|
+
* @param {Error|string|Object} error - Error object
|
|
100
|
+
* @returns {Promise<void>}
|
|
101
|
+
*/
|
|
102
|
+
async function logDeploymentFailure(appName, url, error) {
|
|
103
|
+
try {
|
|
104
|
+
await auditLogger.logDeploymentFailure(appName, url, error);
|
|
105
|
+
} catch (logError) {
|
|
106
|
+
// Don't fail if audit logging fails, but log to console
|
|
107
|
+
// eslint-disable-next-line no-console
|
|
108
|
+
console.error(`[AUDIT LOG ERROR] Failed to log deployment failure: ${logError.message}`);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
98
111
|
|
|
99
|
-
|
|
112
|
+
/**
|
|
113
|
+
* Extract and normalize error response data
|
|
114
|
+
* @param {Object} safeError - Safe error object
|
|
115
|
+
* @param {Error|Object} error - Original error object
|
|
116
|
+
* @returns {string|Object} Normalized error response
|
|
117
|
+
*/
|
|
118
|
+
function extractErrorResponse(safeError, error) {
|
|
100
119
|
let errorData = safeError.data;
|
|
101
120
|
if (error.response && error.response.data !== undefined) {
|
|
102
121
|
errorData = error.response.data;
|
|
103
122
|
}
|
|
104
123
|
|
|
105
|
-
// Ensure errorData is not undefined before parsing
|
|
106
|
-
// If errorData is undefined, use the error message instead
|
|
107
124
|
let errorResponse = errorData !== undefined ? errorData : safeError.message;
|
|
108
125
|
|
|
109
|
-
// Ensure errorResponse is a string or object, not an Error object
|
|
110
126
|
if (errorResponse instanceof Error) {
|
|
111
127
|
errorResponse = errorResponse.message || 'Unknown error occurred';
|
|
112
128
|
}
|
|
113
129
|
|
|
114
|
-
// Ensure errorResponse is not null or undefined
|
|
115
130
|
if (errorResponse === null || errorResponse === undefined) {
|
|
116
131
|
errorResponse = safeError.message || 'Unknown error occurred';
|
|
117
132
|
}
|
|
118
133
|
|
|
119
|
-
|
|
120
|
-
|
|
134
|
+
return errorResponse;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Determine if error is a network error
|
|
139
|
+
* @param {Object} safeError - Safe error object
|
|
140
|
+
* @returns {boolean} True if network error
|
|
141
|
+
*/
|
|
142
|
+
function isNetworkError(safeError) {
|
|
143
|
+
return safeError.code === 'ECONNREFUSED' ||
|
|
121
144
|
safeError.code === 'ENOTFOUND' ||
|
|
122
145
|
safeError.code === 'ECONNABORTED' ||
|
|
123
146
|
safeError.timeout;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Parse error response and ensure valid result
|
|
151
|
+
* @param {string|Object} errorResponse - Error response
|
|
152
|
+
* @param {Object} safeError - Safe error object
|
|
153
|
+
* @returns {Object} Parsed error object
|
|
154
|
+
*/
|
|
155
|
+
function parseErrorResponseSafely(errorResponse, safeError) {
|
|
156
|
+
const isNetwork = isNetworkError(safeError);
|
|
124
157
|
|
|
125
|
-
// Parse error using error handler
|
|
126
|
-
let parsedError;
|
|
127
158
|
try {
|
|
128
|
-
parsedError = parseErrorResponse(errorResponse, safeError.status || 0,
|
|
129
|
-
// Ensure parsedError is a valid object
|
|
159
|
+
const parsedError = parseErrorResponse(errorResponse, safeError.status || 0, isNetwork);
|
|
130
160
|
if (!parsedError || typeof parsedError !== 'object') {
|
|
131
161
|
throw new Error('parseErrorResponse returned invalid result');
|
|
132
162
|
}
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
message: safeError.message || 'Unknown error occurred',
|
|
137
|
-
formatted: safeError.message || 'Unknown error occurred',
|
|
138
|
-
data: undefined
|
|
139
|
-
};
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
// Ensure parsedError is always a valid object with required properties
|
|
143
|
-
if (!parsedError || typeof parsedError !== 'object') {
|
|
144
|
-
parsedError = {
|
|
163
|
+
return parsedError;
|
|
164
|
+
} catch {
|
|
165
|
+
return {
|
|
145
166
|
message: safeError.message || 'Unknown error occurred',
|
|
146
167
|
formatted: safeError.message || 'Unknown error occurred',
|
|
147
168
|
data: undefined
|
|
148
169
|
};
|
|
149
170
|
}
|
|
171
|
+
}
|
|
150
172
|
|
|
151
|
-
|
|
173
|
+
/**
|
|
174
|
+
* Create formatted error object
|
|
175
|
+
* @param {Object} parsedError - Parsed error object
|
|
176
|
+
* @param {Object} safeError - Safe error object
|
|
177
|
+
* @param {Error|string|Object} originalError - Original error
|
|
178
|
+
* @returns {Error} Formatted error object
|
|
179
|
+
*/
|
|
180
|
+
function createFormattedError(parsedError, safeError, originalError) {
|
|
152
181
|
const finalErrorMessage = (parsedError && parsedError.message) ? parsedError.message : (safeError.message || 'Unknown error occurred');
|
|
153
182
|
|
|
154
|
-
// Validate finalErrorMessage is a string
|
|
155
183
|
if (typeof finalErrorMessage !== 'string') {
|
|
156
|
-
throw new Error(`Invalid error message type: ${typeof finalErrorMessage}. Error: ${JSON.stringify(
|
|
184
|
+
throw new Error(`Invalid error message type: ${typeof finalErrorMessage}. Error: ${JSON.stringify(originalError)}`);
|
|
157
185
|
}
|
|
158
186
|
|
|
159
|
-
// Throw clean error message (without emoji) - CLI will format it
|
|
160
187
|
const formattedError = new Error(finalErrorMessage);
|
|
161
188
|
formattedError.formatted = parsedError?.formatted || finalErrorMessage;
|
|
162
189
|
formattedError.status = safeError.status;
|
|
163
190
|
formattedError.data = parsedError?.data;
|
|
164
|
-
formattedError._logged = true;
|
|
165
|
-
|
|
191
|
+
formattedError._logged = true;
|
|
192
|
+
|
|
193
|
+
return formattedError;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* Unified error handler for deployment errors
|
|
198
|
+
* Handles audit logging, error formatting, and user-friendly messages
|
|
199
|
+
* @param {Error} error - Error object
|
|
200
|
+
* @param {string} appName - Application name for audit logging
|
|
201
|
+
* @param {string} url - Controller URL for audit logging
|
|
202
|
+
* @param {boolean} [alreadyLogged=false] - Whether error has already been logged
|
|
203
|
+
* @throws {Error} User-friendly error message
|
|
204
|
+
*/
|
|
205
|
+
async function handleDeploymentErrors(error, appName, url, alreadyLogged = false) {
|
|
206
|
+
// Extract error message
|
|
207
|
+
const errorMessage = extractErrorMessage(error);
|
|
208
|
+
|
|
209
|
+
// For validation errors (like URL validation), just re-throw them directly
|
|
210
|
+
if (isValidationError(errorMessage)) {
|
|
211
|
+
throwValidationError(error, errorMessage);
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
// Log to audit log if not already logged
|
|
215
|
+
if (!alreadyLogged) {
|
|
216
|
+
await logDeploymentFailure(appName, url, error);
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
const safeError = handleDeploymentError(error);
|
|
220
|
+
const errorResponse = extractErrorResponse(safeError, error);
|
|
221
|
+
const parsedError = parseErrorResponseSafely(errorResponse, safeError);
|
|
222
|
+
|
|
223
|
+
// Ensure parsedError is always a valid object
|
|
224
|
+
const validParsedError = (!parsedError || typeof parsedError !== 'object')
|
|
225
|
+
? {
|
|
226
|
+
message: safeError.message || 'Unknown error occurred',
|
|
227
|
+
formatted: safeError.message || 'Unknown error occurred',
|
|
228
|
+
data: undefined
|
|
229
|
+
}
|
|
230
|
+
: parsedError;
|
|
231
|
+
|
|
232
|
+
// Create and throw formatted error
|
|
233
|
+
throw createFormattedError(validParsedError, safeError, error);
|
|
166
234
|
}
|
|
167
235
|
|
|
168
236
|
module.exports = {
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Deployment Validation Helper Functions
|
|
3
|
+
*
|
|
4
|
+
* Helper functions for processing deployment validation responses.
|
|
5
|
+
* Separated from deployer.js to maintain file size limits.
|
|
6
|
+
*
|
|
7
|
+
* @fileoverview Deployment validation response processing helpers
|
|
8
|
+
* @author AI Fabrix Team
|
|
9
|
+
* @version 2.0.0
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Process successful validation response
|
|
14
|
+
* @param {Object} responseData - Response data
|
|
15
|
+
* @returns {Object} Validation result
|
|
16
|
+
*/
|
|
17
|
+
function processSuccessfulValidation(responseData) {
|
|
18
|
+
return {
|
|
19
|
+
success: true,
|
|
20
|
+
validateToken: responseData.validateToken,
|
|
21
|
+
draftDeploymentId: responseData.draftDeploymentId,
|
|
22
|
+
imageServer: responseData.imageServer,
|
|
23
|
+
imageUsername: responseData.imageUsername,
|
|
24
|
+
imagePassword: responseData.imagePassword,
|
|
25
|
+
expiresAt: responseData.expiresAt
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Process validation failure response
|
|
31
|
+
* @param {Object} responseData - Response data
|
|
32
|
+
* @throws {Error} Validation error
|
|
33
|
+
*/
|
|
34
|
+
function processValidationFailure(responseData) {
|
|
35
|
+
const errorMessage = responseData.errors && responseData.errors.length > 0
|
|
36
|
+
? `Validation failed: ${responseData.errors.join(', ')}`
|
|
37
|
+
: 'Validation failed: Invalid configuration';
|
|
38
|
+
const error = new Error(errorMessage);
|
|
39
|
+
error.status = 400;
|
|
40
|
+
error.data = responseData;
|
|
41
|
+
throw error;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Process validation error response
|
|
46
|
+
* @param {Object} response - API response
|
|
47
|
+
* @throws {Error} Validation error
|
|
48
|
+
*/
|
|
49
|
+
function processValidationError(response) {
|
|
50
|
+
const error = new Error(`Validation request failed: ${response.formattedError || response.error || 'Unknown error'}`);
|
|
51
|
+
error.status = response.status || 400;
|
|
52
|
+
error.data = response.data;
|
|
53
|
+
throw error;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Process unexpected validation response state
|
|
58
|
+
* @param {Object} responseData - Response data
|
|
59
|
+
* @throws {Error} Unexpected state error
|
|
60
|
+
*/
|
|
61
|
+
function processUnexpectedValidationState(responseData) {
|
|
62
|
+
const error = new Error('Validation response is in an unexpected state');
|
|
63
|
+
error.status = 400;
|
|
64
|
+
error.data = responseData;
|
|
65
|
+
throw error;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Handle validation response
|
|
70
|
+
* @param {Object} response - API response
|
|
71
|
+
* @returns {Object|null} Validation result or null if needs retry
|
|
72
|
+
* @throws {Error} If validation fails
|
|
73
|
+
*/
|
|
74
|
+
function handleValidationResponse(response) {
|
|
75
|
+
// Handle successful validation (200 OK with valid: true)
|
|
76
|
+
if (response.success && response.data) {
|
|
77
|
+
const responseData = response.data.data || response.data;
|
|
78
|
+
if (responseData.valid === true) {
|
|
79
|
+
return processSuccessfulValidation(responseData);
|
|
80
|
+
}
|
|
81
|
+
// Handle validation failure (valid: false)
|
|
82
|
+
if (responseData.valid === false) {
|
|
83
|
+
processValidationFailure(responseData);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// Handle validation errors (non-success responses)
|
|
88
|
+
if (!response.success) {
|
|
89
|
+
processValidationError(response);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// If we get here, response.success is true but valid is not true and not false
|
|
93
|
+
processUnexpectedValidationState(response.data);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
module.exports = {
|
|
97
|
+
processSuccessfulValidation,
|
|
98
|
+
processValidationFailure,
|
|
99
|
+
processValidationError,
|
|
100
|
+
processUnexpectedValidationState,
|
|
101
|
+
handleValidationResponse
|
|
102
|
+
};
|
|
103
|
+
|
|
@@ -190,8 +190,65 @@ async function executeDockerBuild(imageName, dockerfilePath, contextPath, tag) {
|
|
|
190
190
|
});
|
|
191
191
|
}
|
|
192
192
|
|
|
193
|
+
/**
|
|
194
|
+
* Execute Docker build with additional tagging
|
|
195
|
+
* @async
|
|
196
|
+
* @param {string} imageName - Image name to build
|
|
197
|
+
* @param {string} dockerfilePath - Path to Dockerfile
|
|
198
|
+
* @param {string} contextPath - Build context path
|
|
199
|
+
* @param {string} tag - Image tag
|
|
200
|
+
* @param {Object} options - Build options
|
|
201
|
+
* @returns {Promise<void>} Resolves when build completes
|
|
202
|
+
* @throws {Error} If build fails
|
|
203
|
+
*/
|
|
204
|
+
async function executeBuild(imageName, dockerfilePath, contextPath, tag, options) {
|
|
205
|
+
await executeDockerBuild(imageName, dockerfilePath, contextPath, tag);
|
|
206
|
+
|
|
207
|
+
// Tag image if additional tag provided
|
|
208
|
+
if (options && options.tag && options.tag !== 'latest') {
|
|
209
|
+
const { promisify } = require('util');
|
|
210
|
+
const { exec } = require('child_process');
|
|
211
|
+
const run = promisify(exec);
|
|
212
|
+
await run(`docker tag ${imageName}:${tag} ${imageName}:latest`);
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
/**
|
|
217
|
+
* Execute Docker build and tag with compatibility tag
|
|
218
|
+
* @async
|
|
219
|
+
* @param {string} effectiveImageName - Effective image name
|
|
220
|
+
* @param {string} imageName - Base image name
|
|
221
|
+
* @param {string} dockerfilePath - Dockerfile path
|
|
222
|
+
* @param {string} contextPath - Build context path
|
|
223
|
+
* @param {string} tag - Image tag
|
|
224
|
+
* @param {Object} options - Build options
|
|
225
|
+
* @returns {Promise<void>}
|
|
226
|
+
*/
|
|
227
|
+
async function executeDockerBuildWithTag(effectiveImageName, imageName, dockerfilePath, contextPath, tag, options) {
|
|
228
|
+
const logger = require('../utils/logger');
|
|
229
|
+
const chalk = require('chalk');
|
|
230
|
+
|
|
231
|
+
logger.log(chalk.blue(`Using Dockerfile: ${dockerfilePath}`));
|
|
232
|
+
logger.log(chalk.blue(`Using build context: ${contextPath}`));
|
|
233
|
+
|
|
234
|
+
await executeBuild(effectiveImageName, dockerfilePath, contextPath, tag, options);
|
|
235
|
+
|
|
236
|
+
// Back-compat: also tag the built dev image as the base image name
|
|
237
|
+
try {
|
|
238
|
+
const { promisify } = require('util');
|
|
239
|
+
const { exec } = require('child_process');
|
|
240
|
+
const run = promisify(exec);
|
|
241
|
+
await run(`docker tag ${effectiveImageName}:${tag} ${imageName}:${tag}`);
|
|
242
|
+
logger.log(chalk.green(`✓ Tagged image: ${imageName}:${tag}`));
|
|
243
|
+
} catch (err) {
|
|
244
|
+
logger.log(chalk.yellow(`⚠️ Warning: Could not create compatibility tag ${imageName}:${tag} - ${err.message}`));
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
|
|
193
248
|
module.exports = {
|
|
194
249
|
executeDockerBuild,
|
|
250
|
+
executeBuild,
|
|
251
|
+
executeDockerBuildWithTag,
|
|
195
252
|
isDockerNotAvailableError
|
|
196
253
|
};
|
|
197
254
|
|
|
@@ -22,10 +22,19 @@ const handlebars = require('handlebars');
|
|
|
22
22
|
* @throws {Error} If template not found
|
|
23
23
|
*/
|
|
24
24
|
function loadDockerfileTemplate(language) {
|
|
25
|
-
|
|
25
|
+
// Use getProjectRoot to reliably find templates in all environments
|
|
26
|
+
const { getProjectRoot } = require('./paths');
|
|
27
|
+
const projectRoot = getProjectRoot();
|
|
28
|
+
const templatePath = path.join(projectRoot, 'templates', language, 'Dockerfile.hbs');
|
|
26
29
|
|
|
27
30
|
if (!fsSync.existsSync(templatePath)) {
|
|
28
|
-
|
|
31
|
+
// Provide helpful error message with actual paths checked
|
|
32
|
+
const errorMessage = `Template not found for language: ${language}\n` +
|
|
33
|
+
` Expected path: ${templatePath}\n` +
|
|
34
|
+
` Project root: ${projectRoot}\n` +
|
|
35
|
+
` Templates directory: ${path.join(projectRoot, 'templates', language)}\n` +
|
|
36
|
+
` Global PROJECT_ROOT: ${typeof global !== 'undefined' && global.PROJECT_ROOT ? global.PROJECT_ROOT : 'not set'}`;
|
|
37
|
+
throw new Error(errorMessage);
|
|
29
38
|
}
|
|
30
39
|
|
|
31
40
|
const templateContent = fsSync.readFileSync(templatePath, 'utf8');
|
|
@@ -114,7 +123,8 @@ function checkProjectDockerfile(builderPath, appName, buildConfig, contextPath,
|
|
|
114
123
|
return customPath;
|
|
115
124
|
}
|
|
116
125
|
|
|
117
|
-
|
|
126
|
+
// Use builderPath instead of process.cwd() to handle test scenarios
|
|
127
|
+
const builderCustomPath = path.join(builderPath, customDockerfile);
|
|
118
128
|
if (fsSync.existsSync(builderCustomPath)) {
|
|
119
129
|
return builderCustomPath;
|
|
120
130
|
}
|