@aifabrix/builder 2.22.2 → 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/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 +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/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.0",
|
|
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,7 +17,7 @@
|
|
|
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",
|
|
@@ -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 @@
|
|
|
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}}
|