@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
package/lib/validate.js
CHANGED
|
@@ -19,6 +19,81 @@ const { formatValidationErrors } = require('./utils/error-formatter');
|
|
|
19
19
|
const { detectAppType } = require('./utils/paths');
|
|
20
20
|
const logger = require('./utils/logger');
|
|
21
21
|
|
|
22
|
+
/**
|
|
23
|
+
* Validates a file path (detects type and validates)
|
|
24
|
+
* @async
|
|
25
|
+
* @param {string} filePath - Path to file
|
|
26
|
+
* @returns {Promise<Object>} Validation result
|
|
27
|
+
*/
|
|
28
|
+
async function validateFilePath(filePath) {
|
|
29
|
+
if (!fs.existsSync(filePath)) {
|
|
30
|
+
throw new Error(`File not found: ${filePath}`);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const content = fs.readFileSync(filePath, 'utf8');
|
|
34
|
+
const type = detectSchemaType(filePath, content);
|
|
35
|
+
|
|
36
|
+
return await validateExternalFile(filePath, type);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Validates external files for an application
|
|
41
|
+
* @async
|
|
42
|
+
* @param {string} appName - Application name
|
|
43
|
+
* @returns {Promise<Array>} Array of validation results
|
|
44
|
+
*/
|
|
45
|
+
async function validateExternalFilesForApp(appName) {
|
|
46
|
+
const files = await resolveExternalFiles(appName);
|
|
47
|
+
const validations = [];
|
|
48
|
+
|
|
49
|
+
for (const file of files) {
|
|
50
|
+
const result = await validateExternalFile(file.path, file.type);
|
|
51
|
+
validations.push({
|
|
52
|
+
file: file.fileName,
|
|
53
|
+
path: file.path,
|
|
54
|
+
type: file.type,
|
|
55
|
+
...result
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
return validations;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Aggregates validation results from multiple sources
|
|
64
|
+
* @param {Object} appValidation - Application validation result
|
|
65
|
+
* @param {Array} externalValidations - External file validation results
|
|
66
|
+
* @param {Object|null} rbacValidation - RBAC validation result (optional)
|
|
67
|
+
* @returns {Object} Aggregated validation result
|
|
68
|
+
*/
|
|
69
|
+
function aggregateValidationResults(appValidation, externalValidations, rbacValidation) {
|
|
70
|
+
const allErrors = [...(appValidation.errors || [])];
|
|
71
|
+
const allWarnings = [...(appValidation.warnings || [])];
|
|
72
|
+
|
|
73
|
+
if (rbacValidation && !rbacValidation.valid) {
|
|
74
|
+
allErrors.push(...(rbacValidation.errors || []));
|
|
75
|
+
allWarnings.push(...(rbacValidation.warnings || []));
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
externalValidations.forEach(validation => {
|
|
79
|
+
if (!validation.valid) {
|
|
80
|
+
allErrors.push(`External ${validation.type} file "${validation.file}": ${validation.errors.join(', ')}`);
|
|
81
|
+
}
|
|
82
|
+
if (validation.warnings && validation.warnings.length > 0) {
|
|
83
|
+
allWarnings.push(`External ${validation.type} file "${validation.file}": ${validation.warnings.join(', ')}`);
|
|
84
|
+
}
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
return {
|
|
88
|
+
valid: appValidation.valid && (!rbacValidation || rbacValidation.valid) && externalValidations.every(v => v.valid),
|
|
89
|
+
application: appValidation,
|
|
90
|
+
externalFiles: externalValidations,
|
|
91
|
+
rbac: rbacValidation,
|
|
92
|
+
errors: allErrors,
|
|
93
|
+
warnings: allWarnings
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
|
|
22
97
|
/**
|
|
23
98
|
* Validates a single external file against its schema
|
|
24
99
|
*
|
|
@@ -47,9 +122,11 @@ async function validateExternalFile(filePath, type) {
|
|
|
47
122
|
}
|
|
48
123
|
|
|
49
124
|
let validate;
|
|
50
|
-
|
|
125
|
+
// Normalize type: handle both 'external-system'/'external-datasource' and 'system'/'datasource'
|
|
126
|
+
const normalizedType = type === 'external-system' ? 'system' : (type === 'external-datasource' ? 'datasource' : type);
|
|
127
|
+
if (normalizedType === 'system') {
|
|
51
128
|
validate = loadExternalSystemSchema();
|
|
52
|
-
} else if (
|
|
129
|
+
} else if (normalizedType === 'datasource') {
|
|
53
130
|
validate = loadExternalDataSourceSchema();
|
|
54
131
|
} else {
|
|
55
132
|
throw new Error(`Unknown file type: ${type}`);
|
|
@@ -61,7 +138,7 @@ async function validateExternalFile(filePath, type) {
|
|
|
61
138
|
const warnings = [];
|
|
62
139
|
|
|
63
140
|
// Additional validation for external system files: check role references in permissions
|
|
64
|
-
if (
|
|
141
|
+
if (normalizedType === 'system' && parsed.permissions && Array.isArray(parsed.permissions)) {
|
|
65
142
|
const roles = parsed.roles || [];
|
|
66
143
|
const roleValues = new Set(roles.map(r => r.value));
|
|
67
144
|
|
|
@@ -79,7 +156,9 @@ async function validateExternalFile(filePath, type) {
|
|
|
79
156
|
return {
|
|
80
157
|
valid: errors.length === 0,
|
|
81
158
|
errors,
|
|
82
|
-
warnings
|
|
159
|
+
warnings,
|
|
160
|
+
file: filePath,
|
|
161
|
+
type: type
|
|
83
162
|
};
|
|
84
163
|
}
|
|
85
164
|
|
|
@@ -106,45 +185,7 @@ async function validateAppOrFile(appOrFile) {
|
|
|
106
185
|
const isFilePath = fs.existsSync(appOrFile) && fs.statSync(appOrFile).isFile();
|
|
107
186
|
|
|
108
187
|
if (isFilePath) {
|
|
109
|
-
|
|
110
|
-
const schemaType = detectSchemaType(appOrFile);
|
|
111
|
-
let result;
|
|
112
|
-
|
|
113
|
-
if (schemaType === 'application') {
|
|
114
|
-
// For application files, we'd need to transform them first
|
|
115
|
-
// For now, just validate JSON structure
|
|
116
|
-
const content = fs.readFileSync(appOrFile, 'utf8');
|
|
117
|
-
try {
|
|
118
|
-
JSON.parse(content);
|
|
119
|
-
} catch (error) {
|
|
120
|
-
return {
|
|
121
|
-
valid: false,
|
|
122
|
-
errors: [`Invalid JSON syntax: ${error.message}`],
|
|
123
|
-
warnings: []
|
|
124
|
-
};
|
|
125
|
-
}
|
|
126
|
-
// Note: Full application validation requires variables.yaml transformation
|
|
127
|
-
// This is a simplified validation for direct JSON files
|
|
128
|
-
result = {
|
|
129
|
-
valid: true,
|
|
130
|
-
errors: [],
|
|
131
|
-
warnings: ['Application file validation is simplified. Use app name for full validation.']
|
|
132
|
-
};
|
|
133
|
-
} else if (schemaType === 'external-system') {
|
|
134
|
-
result = await validateExternalFile(appOrFile, 'system');
|
|
135
|
-
} else if (schemaType === 'external-datasource') {
|
|
136
|
-
result = await validateExternalFile(appOrFile, 'datasource');
|
|
137
|
-
} else {
|
|
138
|
-
throw new Error(`Unknown schema type: ${schemaType}`);
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
return {
|
|
142
|
-
valid: result.valid,
|
|
143
|
-
file: appOrFile,
|
|
144
|
-
type: schemaType,
|
|
145
|
-
errors: result.errors,
|
|
146
|
-
warnings: result.warnings
|
|
147
|
-
};
|
|
188
|
+
return await validateFilePath(appOrFile);
|
|
148
189
|
}
|
|
149
190
|
|
|
150
191
|
// Treat as app name
|
|
@@ -204,54 +245,11 @@ async function validateAppOrFile(appOrFile) {
|
|
|
204
245
|
};
|
|
205
246
|
}
|
|
206
247
|
|
|
207
|
-
//
|
|
208
|
-
const
|
|
209
|
-
const externalValidations = [];
|
|
210
|
-
|
|
211
|
-
for (const fileInfo of externalFiles) {
|
|
212
|
-
try {
|
|
213
|
-
const validation = await validateExternalFile(fileInfo.path, fileInfo.type);
|
|
214
|
-
externalValidations.push({
|
|
215
|
-
file: fileInfo.fileName,
|
|
216
|
-
path: fileInfo.path,
|
|
217
|
-
type: fileInfo.type,
|
|
218
|
-
...validation
|
|
219
|
-
});
|
|
220
|
-
} catch (error) {
|
|
221
|
-
externalValidations.push({
|
|
222
|
-
file: fileInfo.fileName,
|
|
223
|
-
path: fileInfo.path,
|
|
224
|
-
type: fileInfo.type,
|
|
225
|
-
valid: false,
|
|
226
|
-
errors: [error.message],
|
|
227
|
-
warnings: []
|
|
228
|
-
});
|
|
229
|
-
}
|
|
230
|
-
}
|
|
248
|
+
// Validate external files
|
|
249
|
+
const externalValidations = await validateExternalFilesForApp(appName);
|
|
231
250
|
|
|
232
251
|
// Aggregate results
|
|
233
|
-
|
|
234
|
-
const rbacWarnings = rbacValidation ? rbacValidation.warnings : [];
|
|
235
|
-
const allValid = appValidation.valid && externalValidations.every(v => v.valid) && (!rbacValidation || rbacValidation.valid);
|
|
236
|
-
const allErrors = [
|
|
237
|
-
...appValidation.errors,
|
|
238
|
-
...externalValidations.flatMap(v => v.errors.map(e => `${v.file}: ${e}`)),
|
|
239
|
-
...rbacErrors.map(e => `rbac.yaml: ${e}`)
|
|
240
|
-
];
|
|
241
|
-
const allWarnings = [
|
|
242
|
-
...appValidation.warnings,
|
|
243
|
-
...externalValidations.flatMap(v => v.warnings),
|
|
244
|
-
...rbacWarnings
|
|
245
|
-
];
|
|
246
|
-
|
|
247
|
-
return {
|
|
248
|
-
valid: allValid,
|
|
249
|
-
application: appValidation,
|
|
250
|
-
externalFiles: externalValidations,
|
|
251
|
-
rbac: rbacValidation,
|
|
252
|
-
errors: allErrors,
|
|
253
|
-
warnings: allWarnings
|
|
254
|
-
};
|
|
252
|
+
return aggregateValidationResults(appValidation, externalValidations, rbacValidation);
|
|
255
253
|
}
|
|
256
254
|
|
|
257
255
|
/**
|
package/package.json
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aifabrix/builder",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.31.1",
|
|
4
4
|
"description": "AI Fabrix Local Fabric & Deployment SDK",
|
|
5
5
|
"main": "lib/index.js",
|
|
6
6
|
"bin": {
|
|
7
7
|
"aifabrix": "bin/aifabrix.js"
|
|
8
8
|
},
|
|
9
9
|
"scripts": {
|
|
10
|
-
"test": "
|
|
10
|
+
"test": "node tests/scripts/test-wrapper.js",
|
|
11
|
+
"test:coverage": "jest --config jest.config.coverage.js --coverage --runInBand",
|
|
11
12
|
"test:watch": "jest --watch",
|
|
12
|
-
"test:ci": "jest --ci --coverage --watchAll=false",
|
|
13
13
|
"test:integration": "jest --config jest.config.integration.js --runInBand",
|
|
14
14
|
"test:integration:python": "cross-env TEST_LANGUAGE=python jest --config jest.config.integration.js --runInBand",
|
|
15
15
|
"test:integration:typescript": "cross-env TEST_LANGUAGE=typescript jest --config jest.config.integration.js --runInBand",
|
|
@@ -17,11 +17,13 @@
|
|
|
17
17
|
"lint:fix": "eslint . --ext .js --fix",
|
|
18
18
|
"lint:ci": "eslint . --ext .js --format json --output-file eslint-report.json",
|
|
19
19
|
"dev": "node bin/aifabrix.js",
|
|
20
|
-
"build": "npm run lint && npm run test
|
|
20
|
+
"build": "npm run lint && npm run test",
|
|
21
21
|
"pack": "npm run build && npm pack",
|
|
22
22
|
"validate": "npm run build",
|
|
23
23
|
"prepublishOnly": "npm run validate",
|
|
24
|
-
"precommit": "npm run lint:fix && npm run test"
|
|
24
|
+
"precommit": "npm run lint:fix && npm run test",
|
|
25
|
+
"install:local": "node scripts/install-local.js",
|
|
26
|
+
"uninstall:local": "node scripts/install-local.js uninstall"
|
|
25
27
|
},
|
|
26
28
|
"keywords": [
|
|
27
29
|
"aifabrix",
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# CI Fix Script Wrapper
|
|
3
|
+
# This script is a wrapper that calls the actual CI fix script
|
|
4
|
+
# located in tests/scripts/ci-fix.sh
|
|
5
|
+
|
|
6
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
7
|
+
ACTUAL_SCRIPT="$SCRIPT_DIR/../tests/scripts/ci-fix.sh"
|
|
8
|
+
|
|
9
|
+
if [ ! -f "$ACTUAL_SCRIPT" ]; then
|
|
10
|
+
echo "Error: CI fix script not found at $ACTUAL_SCRIPT" >&2
|
|
11
|
+
exit 1
|
|
12
|
+
fi
|
|
13
|
+
|
|
14
|
+
# Make sure the script is executable
|
|
15
|
+
chmod +x "$ACTUAL_SCRIPT" 2>/dev/null || true
|
|
16
|
+
|
|
17
|
+
# Execute the actual script
|
|
18
|
+
exec "$ACTUAL_SCRIPT" "$@"
|
|
19
|
+
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# CI Simulation Script Wrapper
|
|
3
|
+
# This script is a wrapper that calls the actual CI simulation script
|
|
4
|
+
# located in tests/scripts/ci-simulate.sh
|
|
5
|
+
|
|
6
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
7
|
+
ACTUAL_SCRIPT="$SCRIPT_DIR/../tests/scripts/ci-simulate.sh"
|
|
8
|
+
|
|
9
|
+
if [ ! -f "$ACTUAL_SCRIPT" ]; then
|
|
10
|
+
echo "Error: CI simulation script not found at $ACTUAL_SCRIPT" >&2
|
|
11
|
+
exit 1
|
|
12
|
+
fi
|
|
13
|
+
|
|
14
|
+
# Make sure the script is executable
|
|
15
|
+
chmod +x "$ACTUAL_SCRIPT" 2>/dev/null || true
|
|
16
|
+
|
|
17
|
+
# Execute the actual script
|
|
18
|
+
exec "$ACTUAL_SCRIPT" "$@"
|
|
19
|
+
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/* eslint-disable no-console */
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Install local package globally using npm link or pnpm link
|
|
6
|
+
* Automatically detects which package manager is being used
|
|
7
|
+
*
|
|
8
|
+
* @fileoverview Local installation script for @aifabrix/builder
|
|
9
|
+
* @author AI Fabrix Team
|
|
10
|
+
* @version 2.0.0
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
const { execSync } = require('child_process');
|
|
14
|
+
const fs = require('fs');
|
|
15
|
+
const path = require('path');
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Detect which package manager is being used (pnpm or npm)
|
|
19
|
+
* @returns {string} 'pnpm' or 'npm'
|
|
20
|
+
*/
|
|
21
|
+
function detectPackageManager() {
|
|
22
|
+
try {
|
|
23
|
+
// Check if pnpm is available
|
|
24
|
+
execSync('which pnpm', { encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'] });
|
|
25
|
+
return 'pnpm';
|
|
26
|
+
} catch {
|
|
27
|
+
// Fall back to npm
|
|
28
|
+
return 'npm';
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Get currently installed version of aifabrix CLI
|
|
34
|
+
* @returns {string|null} Version string or null if not installed
|
|
35
|
+
*/
|
|
36
|
+
function getCurrentVersion() {
|
|
37
|
+
try {
|
|
38
|
+
const version = execSync('aifabrix --version', { encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'] }).trim();
|
|
39
|
+
return version;
|
|
40
|
+
} catch {
|
|
41
|
+
return null;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Get version from local package.json
|
|
47
|
+
* @returns {string|null} Version string or null if not found
|
|
48
|
+
*/
|
|
49
|
+
function getPackageVersion() {
|
|
50
|
+
try {
|
|
51
|
+
const packageJsonPath = path.join(__dirname, '..', 'package.json');
|
|
52
|
+
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
|
|
53
|
+
return packageJson.version;
|
|
54
|
+
} catch {
|
|
55
|
+
return null;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Display version comparison information
|
|
61
|
+
* @param {string|null} currentVersion - Currently installed version
|
|
62
|
+
* @param {string|null} packageVersion - Version being linked
|
|
63
|
+
* @returns {void}
|
|
64
|
+
*/
|
|
65
|
+
function displayVersionInfo(currentVersion, packageVersion) {
|
|
66
|
+
if (currentVersion) {
|
|
67
|
+
console.log(`📦 Current installed version: ${currentVersion}`);
|
|
68
|
+
} else {
|
|
69
|
+
console.log('📦 No previous version detected (first install)');
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
if (packageVersion) {
|
|
73
|
+
if (currentVersion && currentVersion !== packageVersion) {
|
|
74
|
+
console.log(`🔄 Linking new version: ${packageVersion}`);
|
|
75
|
+
console.log(` Version change: ${currentVersion} → ${packageVersion}\n`);
|
|
76
|
+
} else if (currentVersion && currentVersion === packageVersion) {
|
|
77
|
+
console.log(`🔄 Linking version: ${packageVersion} (same version)\n`);
|
|
78
|
+
} else {
|
|
79
|
+
console.log(`🔄 Linking version: ${packageVersion}\n`);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Display success message with version information
|
|
86
|
+
* @param {string|null} currentVersion - Version before linking
|
|
87
|
+
* @param {string|null} newVersion - Version after linking
|
|
88
|
+
* @returns {void}
|
|
89
|
+
*/
|
|
90
|
+
function displaySuccessMessage(currentVersion, newVersion) {
|
|
91
|
+
console.log('\n✅ Successfully linked!');
|
|
92
|
+
if (currentVersion && newVersion && currentVersion !== newVersion) {
|
|
93
|
+
console.log(`📊 Version updated: ${currentVersion} → ${newVersion}`);
|
|
94
|
+
} else if (newVersion) {
|
|
95
|
+
console.log(`📊 Installed version: ${newVersion}`);
|
|
96
|
+
}
|
|
97
|
+
console.log('Run "aifabrix --version" to verify.');
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Install local package globally
|
|
102
|
+
* @returns {void}
|
|
103
|
+
*/
|
|
104
|
+
function installLocal() {
|
|
105
|
+
const pm = detectPackageManager();
|
|
106
|
+
const packageVersion = getPackageVersion();
|
|
107
|
+
const currentVersion = getCurrentVersion();
|
|
108
|
+
|
|
109
|
+
console.log(`Detected package manager: ${pm}\n`);
|
|
110
|
+
|
|
111
|
+
// Show version comparison
|
|
112
|
+
displayVersionInfo(currentVersion, packageVersion);
|
|
113
|
+
|
|
114
|
+
console.log('Linking @aifabrix/builder globally...\n');
|
|
115
|
+
|
|
116
|
+
try {
|
|
117
|
+
if (pm === 'pnpm') {
|
|
118
|
+
execSync('pnpm link --global', { stdio: 'inherit' });
|
|
119
|
+
} else {
|
|
120
|
+
execSync('npm link', { stdio: 'inherit' });
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// Get new version after linking
|
|
124
|
+
const newVersion = getCurrentVersion();
|
|
125
|
+
|
|
126
|
+
displaySuccessMessage(currentVersion, newVersion);
|
|
127
|
+
} catch (error) {
|
|
128
|
+
console.error('\n❌ Failed to link package:', error.message);
|
|
129
|
+
process.exit(1);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Display version information before unlinking
|
|
135
|
+
* @param {string|null} currentVersion - Currently installed version
|
|
136
|
+
* @param {string|null} packageVersion - Local package version
|
|
137
|
+
* @returns {void}
|
|
138
|
+
*/
|
|
139
|
+
function displayUninstallVersionInfo(currentVersion, packageVersion) {
|
|
140
|
+
if (currentVersion) {
|
|
141
|
+
console.log(`📦 Current installed version: ${currentVersion}`);
|
|
142
|
+
} else {
|
|
143
|
+
console.log('📦 No installed version detected');
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
if (packageVersion) {
|
|
147
|
+
console.log(`📋 Local package version: ${packageVersion}`);
|
|
148
|
+
if (currentVersion && currentVersion === packageVersion) {
|
|
149
|
+
console.log(' (matches installed version)\n');
|
|
150
|
+
} else if (currentVersion && currentVersion !== packageVersion) {
|
|
151
|
+
console.log(` (installed: ${currentVersion}, local: ${packageVersion})\n`);
|
|
152
|
+
} else {
|
|
153
|
+
console.log('\n');
|
|
154
|
+
}
|
|
155
|
+
} else {
|
|
156
|
+
console.log('\n');
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* Display success message after unlinking
|
|
162
|
+
* @param {string} pm - Package manager ('pnpm' or 'npm')
|
|
163
|
+
* @param {string|null} currentVersion - Version that was uninstalled
|
|
164
|
+
* @returns {void}
|
|
165
|
+
*/
|
|
166
|
+
function displayUninstallSuccess(pm, currentVersion) {
|
|
167
|
+
console.log(`\n✅ Successfully unlinked with ${pm}!`);
|
|
168
|
+
if (currentVersion) {
|
|
169
|
+
console.log(`📊 Uninstalled version: ${currentVersion}`);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* Uninstall local package from global installation
|
|
175
|
+
* @returns {void}
|
|
176
|
+
*/
|
|
177
|
+
function uninstallLocal() {
|
|
178
|
+
const pm = detectPackageManager();
|
|
179
|
+
const currentVersion = getCurrentVersion();
|
|
180
|
+
const packageVersion = getPackageVersion();
|
|
181
|
+
|
|
182
|
+
console.log(`Detected package manager: ${pm}\n`);
|
|
183
|
+
|
|
184
|
+
// Show version information before unlinking
|
|
185
|
+
displayUninstallVersionInfo(currentVersion, packageVersion);
|
|
186
|
+
|
|
187
|
+
console.log('Unlinking @aifabrix/builder globally...\n');
|
|
188
|
+
|
|
189
|
+
try {
|
|
190
|
+
if (pm === 'pnpm') {
|
|
191
|
+
execSync('pnpm unlink --global @aifabrix/builder', { stdio: 'inherit' });
|
|
192
|
+
displayUninstallSuccess(pm, currentVersion);
|
|
193
|
+
} else {
|
|
194
|
+
execSync('npm unlink -g @aifabrix/builder', { stdio: 'inherit' });
|
|
195
|
+
displayUninstallSuccess(pm, currentVersion);
|
|
196
|
+
}
|
|
197
|
+
} catch (error) {
|
|
198
|
+
console.error('\n❌ Failed to unlink package:', error.message);
|
|
199
|
+
process.exit(1);
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
// Main execution
|
|
204
|
+
const command = process.argv[2];
|
|
205
|
+
|
|
206
|
+
if (command === 'uninstall' || command === 'unlink') {
|
|
207
|
+
uninstallLocal();
|
|
208
|
+
} else {
|
|
209
|
+
installLocal();
|
|
210
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
test content
|
|
@@ -1,49 +1,12 @@
|
|
|
1
|
-
# Python Dockerfile Template
|
|
2
|
-
# Generated by AI Fabrix Builder SDK
|
|
3
|
-
# Base image: Python 3.11 Alpine for optimal size and security
|
|
4
|
-
|
|
5
1
|
FROM python:3.11-alpine
|
|
6
|
-
|
|
7
|
-
# Set working directory
|
|
8
2
|
WORKDIR /app
|
|
9
|
-
|
|
10
|
-
# Install system dependencies
|
|
11
|
-
RUN apk add --no-cache \
|
|
12
|
-
dumb-init \
|
|
13
|
-
curl \
|
|
14
|
-
gcc \
|
|
15
|
-
musl-dev \
|
|
16
|
-
libffi-dev \
|
|
17
|
-
&& rm -rf /var/cache/apk/*
|
|
18
|
-
|
|
19
|
-
# Copy requirements first for better layer caching
|
|
20
|
-
COPY requirements*.txt ./
|
|
21
|
-
|
|
22
|
-
# Install Python dependencies
|
|
3
|
+
COPY {{appSourcePath}}requirements*.txt ./
|
|
23
4
|
RUN pip install --no-cache-dir -r requirements.txt
|
|
24
|
-
|
|
25
|
-
# Copy application code
|
|
26
|
-
COPY . .
|
|
27
|
-
|
|
28
|
-
# Create non-root user for security
|
|
29
|
-
RUN addgroup -g 1001 -S python && \
|
|
30
|
-
adduser -S python -u 1001
|
|
31
|
-
|
|
32
|
-
# Change ownership of app directory
|
|
33
|
-
RUN chown -R python:python /app
|
|
34
|
-
USER python
|
|
35
|
-
|
|
36
|
-
# Expose application port
|
|
37
|
-
# Template variable: {{port}} will be replaced with actual port number
|
|
5
|
+
COPY {{appSourcePath}} .
|
|
38
6
|
EXPOSE {{port}}
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
# Use dumb-init to handle signals properly
|
|
46
|
-
ENTRYPOINT ["dumb-init", "--"]
|
|
47
|
-
|
|
48
|
-
# Start application
|
|
49
|
-
CMD {{#if startupCommand}}["{{startupCommand}}"]{{else}}["python", "main.py"]{{/if}}
|
|
7
|
+
{{#if healthCheck}}
|
|
8
|
+
HEALTHCHECK --interval={{healthCheck.interval}}s CMD curl -f http://localhost:{{port}}{{healthCheck.path}} || exit 1
|
|
9
|
+
{{/if}}
|
|
10
|
+
{{#if startupCommand}}
|
|
11
|
+
CMD {{startupCommand}}
|
|
12
|
+
{{/if}}
|
|
@@ -1,46 +1,12 @@
|
|
|
1
|
-
# TypeScript/Node.js Dockerfile Template
|
|
2
|
-
# Generated by AI Fabrix Builder SDK
|
|
3
|
-
# Base image: Node 20 Alpine for optimal size and security
|
|
4
|
-
|
|
5
1
|
FROM node:20-alpine
|
|
6
|
-
|
|
7
|
-
# Set working directory
|
|
8
2
|
WORKDIR /app
|
|
9
|
-
|
|
10
|
-
# Install system dependencies
|
|
11
|
-
RUN apk add --no-cache \
|
|
12
|
-
dumb-init \
|
|
13
|
-
curl \
|
|
14
|
-
&& rm -rf /var/cache/apk/*
|
|
15
|
-
|
|
16
|
-
# Copy package files first for better layer caching
|
|
17
|
-
COPY package*.json ./
|
|
18
|
-
|
|
19
|
-
# Install dependencies (including devDependencies for ts-node)
|
|
3
|
+
COPY {{appSourcePath}}package*.json ./
|
|
20
4
|
RUN npm install && npm cache clean --force
|
|
21
|
-
|
|
22
|
-
# Copy application code
|
|
23
|
-
COPY . .
|
|
24
|
-
|
|
25
|
-
# Create non-root user for security
|
|
26
|
-
RUN addgroup -g 1001 -S nodejs && \
|
|
27
|
-
adduser -S nextjs -u 1001
|
|
28
|
-
|
|
29
|
-
# Change ownership of app directory
|
|
30
|
-
RUN chown -R nextjs:nodejs /app
|
|
31
|
-
USER nextjs
|
|
32
|
-
|
|
33
|
-
# Expose application port
|
|
34
|
-
# Template variable: {{port}} will be replaced with actual port number
|
|
5
|
+
COPY {{appSourcePath}} .
|
|
35
6
|
EXPOSE {{port}}
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
# Use dumb-init to handle signals properly
|
|
43
|
-
ENTRYPOINT ["dumb-init", "--"]
|
|
44
|
-
|
|
45
|
-
# Start application
|
|
46
|
-
CMD {{#if startupCommand}}["{{startupCommand}}"]{{else}}["npm", "start"]{{/if}}
|
|
7
|
+
{{#if healthCheck}}
|
|
8
|
+
HEALTHCHECK --interval={{healthCheck.interval}}s CMD curl -f http://localhost:{{port}}{{healthCheck.path}} || exit 1
|
|
9
|
+
{{/if}}
|
|
10
|
+
{{#if startupCommand}}
|
|
11
|
+
CMD {{startupCommand}}
|
|
12
|
+
{{/if}}
|