@aifabrix/builder 2.36.2 → 2.37.5
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/.cursor/rules/project-rules.mdc +19 -0
- package/README.md +68 -104
- package/integration/hubspot/test.js +1 -1
- package/lib/api/wizard.api.js +24 -1
- package/lib/app/deploy.js +43 -7
- package/lib/app/display.js +1 -1
- package/lib/app/list.js +3 -1
- package/lib/app/run-helpers.js +1 -1
- package/lib/build/index.js +3 -4
- package/lib/cli/index.js +45 -0
- package/lib/cli/setup-app.js +230 -0
- package/lib/cli/setup-auth.js +88 -0
- package/lib/cli/setup-dev.js +101 -0
- package/lib/cli/setup-environment.js +53 -0
- package/lib/cli/setup-external-system.js +87 -0
- package/lib/cli/setup-infra.js +219 -0
- package/lib/cli/setup-secrets.js +48 -0
- package/lib/cli/setup-utility.js +202 -0
- package/lib/cli.js +7 -961
- package/lib/commands/up-common.js +31 -1
- package/lib/commands/up-miso.js +6 -2
- package/lib/commands/wizard-core.js +32 -7
- package/lib/core/config.js +10 -0
- package/lib/core/ensure-encryption-key.js +56 -0
- package/lib/deployment/deployer-status.js +101 -0
- package/lib/deployment/deployer.js +62 -110
- package/lib/deployment/environment.js +133 -34
- package/lib/external-system/deploy.js +5 -1
- package/lib/external-system/test-auth.js +14 -7
- package/lib/generator/wizard.js +37 -41
- package/lib/infrastructure/helpers.js +1 -1
- package/lib/schema/environment-deploy-request.schema.json +64 -0
- package/lib/utils/help-builder.js +5 -2
- package/lib/utils/paths.js +22 -4
- package/lib/utils/secrets-generator.js +23 -8
- package/lib/utils/secrets-helpers.js +46 -21
- package/package.json +1 -1
- package/scripts/install-local.js +11 -2
- package/templates/applications/README.md.hbs +3 -3
- package/templates/applications/dataplane/variables.yaml +0 -2
- package/templates/applications/miso-controller/variables.yaml +0 -2
- package/templates/external-system/deploy.js.hbs +69 -0
- package/templates/infra/environment-dev.json +10 -0
|
@@ -33,7 +33,17 @@ function interpolateEnvVars(content, envVars) {
|
|
|
33
33
|
}
|
|
34
34
|
|
|
35
35
|
/**
|
|
36
|
-
*
|
|
36
|
+
* Returns true if the line is a comment or empty (should be skipped for kv:// resolution)
|
|
37
|
+
* @param {string} line - Single line
|
|
38
|
+
* @returns {boolean}
|
|
39
|
+
*/
|
|
40
|
+
function isCommentOrEmptyLine(line) {
|
|
41
|
+
const t = line.trim();
|
|
42
|
+
return t === '' || t.startsWith('#');
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Collect missing kv:// secrets referenced in content (skips commented and empty lines)
|
|
37
47
|
* @function collectMissingSecrets
|
|
38
48
|
* @param {string} content - Text content
|
|
39
49
|
* @param {Object} secrets - Available secrets
|
|
@@ -42,11 +52,16 @@ function interpolateEnvVars(content, envVars) {
|
|
|
42
52
|
function collectMissingSecrets(content, secrets) {
|
|
43
53
|
const kvPattern = /kv:\/\/([a-zA-Z0-9-_]+)/g;
|
|
44
54
|
const missing = [];
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
55
|
+
const lines = content.split('\n');
|
|
56
|
+
for (const line of lines) {
|
|
57
|
+
if (isCommentOrEmptyLine(line)) continue;
|
|
58
|
+
let match;
|
|
59
|
+
kvPattern.lastIndex = 0;
|
|
60
|
+
while ((match = kvPattern.exec(line)) !== null) {
|
|
61
|
+
const secretKey = match[1];
|
|
62
|
+
if (!(secretKey in secrets)) {
|
|
63
|
+
missing.push(`kv://${secretKey}`);
|
|
64
|
+
}
|
|
50
65
|
}
|
|
51
66
|
}
|
|
52
67
|
return missing;
|
|
@@ -76,7 +91,7 @@ function formatMissingSecretsFileInfo(secretsFilePaths) {
|
|
|
76
91
|
}
|
|
77
92
|
|
|
78
93
|
/**
|
|
79
|
-
* Replace kv:// references with actual values
|
|
94
|
+
* Replace kv:// references with actual values (skips commented and empty lines)
|
|
80
95
|
* @function replaceKvInContent
|
|
81
96
|
* @param {string} content - Text content containing kv:// references
|
|
82
97
|
* @param {Object} secrets - Secrets map
|
|
@@ -85,15 +100,20 @@ function formatMissingSecretsFileInfo(secretsFilePaths) {
|
|
|
85
100
|
*/
|
|
86
101
|
function replaceKvInContent(content, secrets, envVars) {
|
|
87
102
|
const kvPattern = /kv:\/\/([a-zA-Z0-9-_]+)/g;
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
if (
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
103
|
+
const lines = content.split('\n');
|
|
104
|
+
const result = lines.map(line => {
|
|
105
|
+
if (isCommentOrEmptyLine(line)) return line;
|
|
106
|
+
return line.replace(kvPattern, (match, secretKey) => {
|
|
107
|
+
let value = secrets[secretKey];
|
|
108
|
+
if (typeof value === 'string') {
|
|
109
|
+
value = value.replace(/\$\{([A-Z_]+)\}/g, (m, envVar) => {
|
|
110
|
+
return envVars[envVar] || m;
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
return value;
|
|
114
|
+
});
|
|
96
115
|
});
|
|
116
|
+
return result.join('\n');
|
|
97
117
|
}
|
|
98
118
|
|
|
99
119
|
/**
|
|
@@ -420,7 +440,7 @@ function ensureNonEmptySecrets(secrets) {
|
|
|
420
440
|
}
|
|
421
441
|
|
|
422
442
|
/**
|
|
423
|
-
* Validate secrets against the env template
|
|
443
|
+
* Validate secrets against the env template (skips commented and empty lines)
|
|
424
444
|
* @function validateSecrets
|
|
425
445
|
* @param {string} envTemplate - Environment template content
|
|
426
446
|
* @param {Object} secrets - Available secrets
|
|
@@ -429,11 +449,16 @@ function ensureNonEmptySecrets(secrets) {
|
|
|
429
449
|
function validateSecrets(envTemplate, secrets) {
|
|
430
450
|
const kvPattern = /kv:\/\/([a-zA-Z0-9-_]+)/g;
|
|
431
451
|
const missing = [];
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
452
|
+
const lines = envTemplate.split('\n');
|
|
453
|
+
for (const line of lines) {
|
|
454
|
+
if (isCommentOrEmptyLine(line)) continue;
|
|
455
|
+
let match;
|
|
456
|
+
kvPattern.lastIndex = 0;
|
|
457
|
+
while ((match = kvPattern.exec(line)) !== null) {
|
|
458
|
+
const secretKey = match[1];
|
|
459
|
+
if (!(secretKey in secrets)) {
|
|
460
|
+
missing.push(`kv://${secretKey}`);
|
|
461
|
+
}
|
|
437
462
|
}
|
|
438
463
|
}
|
|
439
464
|
return {
|
package/package.json
CHANGED
package/scripts/install-local.js
CHANGED
|
@@ -114,10 +114,19 @@ function installLocal() {
|
|
|
114
114
|
console.log('Linking @aifabrix/builder globally...\n');
|
|
115
115
|
|
|
116
116
|
try {
|
|
117
|
+
const projectRoot = path.join(__dirname, '..');
|
|
117
118
|
if (pm === 'pnpm') {
|
|
118
|
-
|
|
119
|
+
// Update pnpm global.
|
|
120
|
+
execSync('pnpm link --global', { stdio: 'inherit', cwd: projectRoot });
|
|
121
|
+
// Also run npm link so npm's global bin points here; often PATH has
|
|
122
|
+
// npm's global bin before pnpm's, so "aifabrix" would otherwise stay old.
|
|
123
|
+
try {
|
|
124
|
+
execSync('npm link', { stdio: 'inherit', cwd: projectRoot });
|
|
125
|
+
} catch {
|
|
126
|
+
// npm may not be available or may fail; pnpm link already ran
|
|
127
|
+
}
|
|
119
128
|
} else {
|
|
120
|
-
execSync('npm link', { stdio: 'inherit' });
|
|
129
|
+
execSync('npm link', { stdio: 'inherit', cwd: projectRoot });
|
|
121
130
|
}
|
|
122
131
|
|
|
123
132
|
// Get new version after linking
|
|
@@ -47,8 +47,8 @@ docker logs aifabrix-{{appName}} -f
|
|
|
47
47
|
|
|
48
48
|
**Stop:**
|
|
49
49
|
```bash
|
|
50
|
-
aifabrix down {{appName}}
|
|
51
|
-
# aifabrix down {{appName}} --volumes # also remove data volume
|
|
50
|
+
aifabrix down-infra {{appName}}
|
|
51
|
+
# aifabrix down-infra {{appName}} --volumes # also remove data volume
|
|
52
52
|
```
|
|
53
53
|
|
|
54
54
|
### 4. Deploy to Azure
|
|
@@ -87,7 +87,7 @@ aifabrix app rotate-secret {{appName}}
|
|
|
87
87
|
# Development
|
|
88
88
|
aifabrix build {{appName}} # Build app
|
|
89
89
|
aifabrix run {{appName}} # Run locally
|
|
90
|
-
aifabrix down {{appName}} [--volumes] # Stop app (optionally remove volume)
|
|
90
|
+
aifabrix down-infra {{appName}} [--volumes] # Stop app (optionally remove volume)
|
|
91
91
|
aifabrix dockerfile {{appName}} --force # Generate Dockerfile
|
|
92
92
|
aifabrix resolve {{appName}} # Generate .env file
|
|
93
93
|
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/* eslint-disable no-console */
|
|
3
|
+
/**
|
|
4
|
+
* Deploy {{systemKey}} external system and datasources using aifabrix CLI.
|
|
5
|
+
* Run: node deploy.js
|
|
6
|
+
* Flow: check auth → login if needed → deploy → run integration tests.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
const { execSync } = require('child_process');
|
|
10
|
+
const path = require('path');
|
|
11
|
+
|
|
12
|
+
const scriptDir = __dirname;
|
|
13
|
+
const appKey = '{{systemKey}}';
|
|
14
|
+
const env = process.env.ENVIRONMENT || 'dev';
|
|
15
|
+
// Controller URL: from config (aifabrix auth config) or set CONTROLLER env before running
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Run a shell command with optional options.
|
|
19
|
+
* @param {string} cmd - Command to run
|
|
20
|
+
* @param {Object} [options={}] - Options (cwd, stdio, ignoreExit)
|
|
21
|
+
* @returns {boolean} True if command succeeded
|
|
22
|
+
*/
|
|
23
|
+
function run(cmd, options = {}) {
|
|
24
|
+
const opts = { cwd: scriptDir, stdio: 'inherit', ...options };
|
|
25
|
+
try {
|
|
26
|
+
execSync(cmd, opts);
|
|
27
|
+
return true;
|
|
28
|
+
} catch (err) {
|
|
29
|
+
if (options.ignoreExit) return false;
|
|
30
|
+
process.exit(err.status ?? 1);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Check if aifabrix auth is logged in.
|
|
36
|
+
* @returns {boolean} True if logged in
|
|
37
|
+
*/
|
|
38
|
+
function isLoggedIn() {
|
|
39
|
+
try {
|
|
40
|
+
execSync('aifabrix auth status', { cwd: scriptDir, stdio: 'pipe' });
|
|
41
|
+
return true;
|
|
42
|
+
} catch {
|
|
43
|
+
return false;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
console.log('🔍 Checking authentication...');
|
|
48
|
+
if (!isLoggedIn()) {
|
|
49
|
+
console.log('⚠️ Not logged in. Run login (e.g. aifabrix login --controller <url> --method device --environment ' + env + ').');
|
|
50
|
+
run('aifabrix login --environment ' + env);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
console.log('🔍 Validating configuration...');
|
|
54
|
+
{{#each allJsonFiles}}
|
|
55
|
+
run('aifabrix validate "' + path.join(scriptDir, '{{this}}') + '"');
|
|
56
|
+
{{/each}}
|
|
57
|
+
console.log('✅ Validation passed');
|
|
58
|
+
|
|
59
|
+
console.log('🚀 Deploying ' + appKey + '...');
|
|
60
|
+
run('aifabrix deploy ' + appKey);
|
|
61
|
+
console.log('✅ Deployment complete');
|
|
62
|
+
|
|
63
|
+
if (process.env.RUN_TESTS !== 'false') {
|
|
64
|
+
console.log('🧪 Running integration tests...');
|
|
65
|
+
run('aifabrix test-integration ' + appKey);
|
|
66
|
+
console.log('✅ Tests passed');
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
console.log('✅ Done.');
|