@aifabrix/builder 2.31.1 → 2.32.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/README.md +9 -9
- package/integration/hubspot/README.md +2 -2
- package/integration/hubspot/hubspot-deploy-company.json +17 -14
- package/integration/hubspot/hubspot-deploy-contact.json +19 -16
- package/integration/hubspot/hubspot-deploy-deal.json +21 -18
- package/lib/api/types/datasources.types.js +31 -5
- package/lib/api/types/wizard.types.js +142 -0
- package/lib/api/wizard.api.js +177 -0
- package/lib/{app-config.js → app/config.js} +4 -4
- package/lib/{app-deploy.js → app/deploy.js} +8 -8
- package/lib/app/display.js +90 -0
- package/lib/{app-dockerfile.js → app/dockerfile.js} +4 -4
- package/lib/{app-down.js → app/down.js} +4 -4
- package/lib/app/helpers.js +218 -0
- package/lib/app/index.js +298 -0
- package/lib/{app-list.js → app/list.js} +6 -6
- package/lib/{app-push.js → app/push.js} +4 -4
- package/lib/{app-readme.js → app/readme.js} +34 -13
- package/lib/{app-register.js → app/register.js} +9 -9
- package/lib/{app-rotate-secret.js → app/rotate-secret.js} +10 -10
- package/lib/{app-run-helpers.js → app/run-helpers.js} +10 -10
- package/lib/{app-run.js → app/run.js} +6 -6
- package/lib/{build.js → build/index.js} +59 -32
- package/lib/build/package.json +7 -0
- package/lib/cli.js +245 -179
- package/lib/commands/app.js +3 -3
- package/lib/commands/datasource.js +4 -4
- package/lib/commands/login-credentials.js +209 -0
- package/lib/commands/login-device.js +254 -0
- package/lib/commands/login.js +67 -378
- package/lib/commands/logout.js +1 -1
- package/lib/commands/secrets-set.js +1 -1
- package/lib/commands/secure.js +2 -2
- package/lib/commands/wizard.js +498 -0
- package/lib/{audit-logger.js → core/audit-logger.js} +1 -1
- package/lib/{config.js → core/config.js} +28 -26
- package/lib/{diff.js → core/diff.js} +157 -72
- package/lib/{secrets.js → core/secrets.js} +86 -49
- package/lib/{templates.js → core/templates-env.js} +14 -222
- package/lib/core/templates.js +279 -0
- package/lib/{datasource-deploy.js → datasource/deploy.js} +6 -6
- package/lib/{datasource-diff.js → datasource/diff.js} +2 -2
- package/lib/datasource/list.js +223 -0
- package/lib/{datasource-validate.js → datasource/validate.js} +2 -2
- package/lib/{deployer.js → deployment/deployer.js} +48 -18
- package/lib/{environment-deploy.js → deployment/environment.js} +163 -84
- package/lib/{push.js → deployment/push.js} +1 -1
- package/lib/external-system/deploy-helpers.js +145 -0
- package/lib/{external-system-deploy.js → external-system/deploy.js} +156 -111
- package/lib/external-system/download-helpers.js +114 -0
- package/lib/{external-system-download.js → external-system/download.js} +92 -135
- package/lib/{external-system-generator.js → external-system/generator.js} +15 -11
- package/lib/external-system/test-auth.js +40 -0
- package/lib/external-system/test-execution.js +84 -0
- package/lib/external-system/test-helpers.js +109 -0
- package/lib/{external-system-test.js → external-system/test.js} +174 -192
- package/lib/{generator-builders.js → generator/builders.js} +87 -10
- package/lib/{generator-external.js → generator/external.js} +115 -52
- package/lib/{github-generator.js → generator/github.js} +116 -15
- package/lib/{generator-helpers.js → generator/helpers.js} +92 -42
- package/lib/{generator.js → generator/index.js} +49 -22
- package/lib/{generator-split.js → generator/split.js} +108 -55
- package/lib/generator/wizard-prompts.js +357 -0
- package/lib/generator/wizard.js +490 -0
- package/lib/{infra.js → infrastructure/index.js} +49 -22
- package/lib/schema/external-datasource.schema.json +145 -133
- package/lib/schema/external-system.schema.json +42 -0
- package/lib/utils/api.js +9 -5
- package/lib/utils/app-register-api.js +60 -32
- package/lib/utils/app-register-auth.js +172 -47
- package/lib/utils/app-register-config.js +130 -59
- package/lib/utils/app-run-containers.js +29 -8
- package/lib/utils/build-helpers.js +1 -1
- package/lib/utils/cli-utils.js +78 -30
- package/lib/utils/compose-generator.js +145 -65
- package/lib/utils/config-paths.js +2 -0
- package/lib/utils/deployment-errors.js +1 -1
- package/lib/utils/device-code.js +99 -41
- package/lib/utils/env-config-loader.js +1 -1
- package/lib/utils/env-copy.js +21 -18
- package/lib/utils/env-endpoints.js +115 -67
- package/lib/utils/env-map.js +13 -14
- package/lib/utils/env-ports.js +45 -25
- package/lib/utils/env-template.js +84 -42
- package/lib/utils/error-formatter.js +26 -9
- package/lib/utils/error-formatters/error-parser.js +90 -4
- package/lib/utils/error-formatters/http-status-errors.js +54 -17
- package/lib/utils/error-formatters/network-errors.js +103 -26
- package/lib/utils/external-system-display.js +184 -90
- package/lib/utils/external-system-validators.js +164 -42
- package/lib/utils/file-upload.js +109 -0
- package/lib/utils/health-check.js +199 -83
- package/lib/utils/infra-containers.js +1 -1
- package/lib/utils/infra-status.js +66 -15
- package/lib/utils/local-secrets.js +45 -25
- package/lib/utils/paths.js +45 -33
- package/lib/utils/schema-loader.js +42 -25
- package/lib/utils/schema-resolver.js +123 -74
- package/lib/utils/secrets-encryption.js +62 -25
- package/lib/utils/secrets-helpers.js +126 -63
- package/lib/utils/secrets-path.js +1 -1
- package/lib/utils/secrets-url.js +1 -1
- package/lib/utils/token-manager-refresh.js +181 -0
- package/lib/utils/token-manager.js +76 -123
- package/lib/utils/variable-transformer.js +154 -77
- package/lib/utils/yaml-preserve.js +41 -47
- package/lib/{template-validator.js → validation/template.js} +54 -23
- package/lib/{validate.js → validation/validate.js} +205 -125
- package/lib/{validator.js → validation/validator.js} +58 -39
- package/package.json +31 -2
- package/templates/external-system/deploy.ps1.hbs +34 -0
- package/templates/external-system/deploy.sh.hbs +34 -0
- package/templates/external-system/external-datasource.json.hbs +31 -12
- package/lib/app.js +0 -467
- package/lib/datasource-list.js +0 -141
- /package/lib/{app-prompts.js → app/prompts.js} +0 -0
- /package/lib/{env-reader.js → core/env-reader.js} +0 -0
- /package/lib/{key-generator.js → core/key-generator.js} +0 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aifabrix/builder",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.32.1",
|
|
4
4
|
"description": "AI Fabrix Local Fabric & Deployment SDK",
|
|
5
5
|
"main": "lib/index.js",
|
|
6
6
|
"bin": {
|
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
"scripts": {
|
|
10
10
|
"test": "node tests/scripts/test-wrapper.js",
|
|
11
11
|
"test:coverage": "jest --config jest.config.coverage.js --coverage --runInBand",
|
|
12
|
+
"test:coverage:nyc": "nyc --reporter=text --reporter=lcov --reporter=html jest --config jest.config.coverage.js --runInBand",
|
|
12
13
|
"test:watch": "jest --watch",
|
|
13
14
|
"test:integration": "jest --config jest.config.integration.js --runInBand",
|
|
14
15
|
"test:integration:python": "cross-env TEST_LANGUAGE=python jest --config jest.config.integration.js --runInBand",
|
|
@@ -38,6 +39,32 @@
|
|
|
38
39
|
"engines": {
|
|
39
40
|
"node": ">=18.0.0"
|
|
40
41
|
},
|
|
42
|
+
"nyc": {
|
|
43
|
+
"all": true,
|
|
44
|
+
"check-coverage": false,
|
|
45
|
+
"reporter": [
|
|
46
|
+
"text",
|
|
47
|
+
"lcov",
|
|
48
|
+
"html"
|
|
49
|
+
],
|
|
50
|
+
"include": [
|
|
51
|
+
"lib/**/*.js",
|
|
52
|
+
"bin/**/*.js"
|
|
53
|
+
],
|
|
54
|
+
"exclude": [
|
|
55
|
+
"**/node_modules/**",
|
|
56
|
+
"**/tests/**",
|
|
57
|
+
"lib/infra.js",
|
|
58
|
+
"bin/aifabrix.js"
|
|
59
|
+
],
|
|
60
|
+
"cache": true,
|
|
61
|
+
"temp-dir": ".nyc_output"
|
|
62
|
+
},
|
|
63
|
+
"pnpm": {
|
|
64
|
+
"overrides": {
|
|
65
|
+
"@bcoe/v8-coverage": "^1.0.2"
|
|
66
|
+
}
|
|
67
|
+
},
|
|
41
68
|
"dependencies": {
|
|
42
69
|
"ajv": "^8.12.0",
|
|
43
70
|
"axios": "^1.6.0",
|
|
@@ -49,10 +76,12 @@
|
|
|
49
76
|
"ora": "^5.4.1"
|
|
50
77
|
},
|
|
51
78
|
"devDependencies": {
|
|
79
|
+
"@bcoe/v8-coverage": "^1.0.2",
|
|
52
80
|
"@types/node": "^20.10.0",
|
|
53
81
|
"cross-env": "^10.1.0",
|
|
54
82
|
"eslint": "^8.55.0",
|
|
55
|
-
"jest": "^
|
|
83
|
+
"jest": "^30.2.0",
|
|
84
|
+
"nyc": "^17.1.0"
|
|
56
85
|
},
|
|
57
86
|
"repository": {
|
|
58
87
|
"type": "git",
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# Deploy {{systemKey}} external system and datasources using aifabrix CLI
|
|
2
|
+
|
|
3
|
+
$ErrorActionPreference = "Stop"
|
|
4
|
+
|
|
5
|
+
$SCRIPT_DIR = $PSScriptRoot
|
|
6
|
+
$env:ENVIRONMENT = if ($env:ENVIRONMENT) { $env:ENVIRONMENT } else { "dev" }
|
|
7
|
+
$env:CONTROLLER = if ($env:CONTROLLER) { $env:CONTROLLER } else { "http://localhost:3000" }
|
|
8
|
+
|
|
9
|
+
Write-Host "🔍 Validating {{systemKey}} configuration files..."
|
|
10
|
+
{{#each allJsonFiles}}
|
|
11
|
+
aifabrix validate "${SCRIPT_DIR}\{{this}}"
|
|
12
|
+
if ($LASTEXITCODE -ne 0) { exit 1 }
|
|
13
|
+
{{/each}}
|
|
14
|
+
|
|
15
|
+
Write-Host "✅ Validation passed"
|
|
16
|
+
|
|
17
|
+
Write-Host "🚀 Deploying {{systemKey}} external system and datasources..."
|
|
18
|
+
Write-Host " Environment: $env:ENVIRONMENT"
|
|
19
|
+
Write-Host " Controller: $env:CONTROLLER"
|
|
20
|
+
|
|
21
|
+
# Deploy datasources
|
|
22
|
+
{{#each datasourceFileNames}}
|
|
23
|
+
aifabrix datasource deploy {{../systemKey}} "${SCRIPT_DIR}\{{this}}" --environment $env:ENVIRONMENT --controller $env:CONTROLLER
|
|
24
|
+
if ($LASTEXITCODE -ne 0) { exit 1 }
|
|
25
|
+
{{/each}}
|
|
26
|
+
|
|
27
|
+
Write-Host "✅ Deployment complete"
|
|
28
|
+
|
|
29
|
+
# Optional: Run tests
|
|
30
|
+
if ($env:RUN_TESTS -eq "true") {
|
|
31
|
+
Write-Host "🧪 Running integration tests..."
|
|
32
|
+
aifabrix test-integration {{systemKey}} --environment $env:ENVIRONMENT --controller $env:CONTROLLER
|
|
33
|
+
}
|
|
34
|
+
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# Deploy {{systemKey}} external system and datasources using aifabrix CLI
|
|
3
|
+
|
|
4
|
+
set -e
|
|
5
|
+
|
|
6
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
7
|
+
ENVIRONMENT="${ENVIRONMENT:-dev}"
|
|
8
|
+
CONTROLLER="${CONTROLLER:-http://localhost:3000}"
|
|
9
|
+
|
|
10
|
+
echo "🔍 Validating {{systemKey}} configuration files..."
|
|
11
|
+
{{#each allJsonFiles}}
|
|
12
|
+
aifabrix validate "${SCRIPT_DIR}/{{this}}" || exit 1
|
|
13
|
+
{{/each}}
|
|
14
|
+
|
|
15
|
+
echo "✅ Validation passed"
|
|
16
|
+
|
|
17
|
+
echo "🚀 Deploying {{systemKey}} external system and datasources..."
|
|
18
|
+
echo " Environment: ${ENVIRONMENT}"
|
|
19
|
+
echo " Controller: ${CONTROLLER}"
|
|
20
|
+
|
|
21
|
+
# Deploy datasources
|
|
22
|
+
{{#each datasourceFileNames}}
|
|
23
|
+
aifabrix datasource deploy {{../systemKey}} "${SCRIPT_DIR}/{{this}}" \
|
|
24
|
+
--environment "${ENVIRONMENT}" --controller "${CONTROLLER}" || exit 1
|
|
25
|
+
{{/each}}
|
|
26
|
+
|
|
27
|
+
echo "✅ Deployment complete"
|
|
28
|
+
|
|
29
|
+
# Optional: Run tests
|
|
30
|
+
if [ "${RUN_TESTS:-false}" = "true" ]; then
|
|
31
|
+
echo "🧪 Running integration tests..."
|
|
32
|
+
aifabrix test-integration {{systemKey}} --environment "${ENVIRONMENT}" --controller "${CONTROLLER}"
|
|
33
|
+
fi
|
|
34
|
+
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"displayName": "{{datasourceDisplayName}}",
|
|
4
4
|
"description": "{{datasourceDescription}}",
|
|
5
5
|
"systemKey": "{{systemKey}}",
|
|
6
|
-
"
|
|
6
|
+
"entityType": "{{entityType}}",
|
|
7
7
|
"resourceType": "{{resourceType}}",
|
|
8
8
|
"enabled": true,
|
|
9
9
|
"version": "1.0.0",
|
|
@@ -12,41 +12,60 @@
|
|
|
12
12
|
"properties": {}
|
|
13
13
|
},
|
|
14
14
|
"fieldMappings": {
|
|
15
|
-
"
|
|
16
|
-
|
|
15
|
+
"dimensions": {
|
|
16
|
+
{{#if dimensions}}
|
|
17
|
+
{{#each dimensions}}
|
|
18
|
+
"{{@key}}": "{{this}}"{{#unless @last}},{{/unless}}
|
|
19
|
+
{{/each}}
|
|
20
|
+
{{else}}
|
|
21
|
+
"country": "metadata.country",
|
|
22
|
+
"department": "metadata.department"
|
|
23
|
+
{{/if}}
|
|
24
|
+
},
|
|
25
|
+
"attributes": {
|
|
26
|
+
{{#if attributes}}
|
|
27
|
+
{{#each attributes}}
|
|
28
|
+
"{{@key}}": {
|
|
29
|
+
{{#each this}}
|
|
30
|
+
"{{@key}}": {{#if (eq @key "indexed")}}{{this}}{{else}}"{{this}}"{{/if}}{{#unless @last}},{{/unless}}
|
|
31
|
+
{{/each}}
|
|
32
|
+
}{{#unless @last}},{{/unless}}
|
|
33
|
+
{{/each}}
|
|
34
|
+
{{else}}
|
|
17
35
|
"id": {
|
|
18
36
|
"expression": "{{raw.id}}",
|
|
19
37
|
"type": "string",
|
|
20
38
|
"description": "Unique identifier",
|
|
21
|
-
"
|
|
39
|
+
"indexed": true
|
|
22
40
|
},
|
|
23
41
|
"name": {
|
|
24
42
|
"expression": "{{raw.name}}",
|
|
25
43
|
"type": "string",
|
|
26
44
|
"description": "Display name",
|
|
27
|
-
"
|
|
45
|
+
"indexed": false
|
|
28
46
|
}
|
|
47
|
+
{{/if}}
|
|
29
48
|
}
|
|
30
49
|
},
|
|
31
50
|
"exposed": {
|
|
32
|
-
"
|
|
33
|
-
"description": "Exposed
|
|
51
|
+
"attributes": ["id", "name"],
|
|
52
|
+
"description": "Exposed attributes for {{datasourceDisplayName}}"
|
|
34
53
|
}{{#if (eq systemType "openapi")}},
|
|
35
54
|
"openapi": {
|
|
36
55
|
"enabled": true,
|
|
37
56
|
"documentKey": "{{systemKey}}-api",
|
|
38
57
|
"baseUrl": "https://api.example.com",
|
|
39
|
-
"resourcePath": "/{{
|
|
58
|
+
"resourcePath": "/{{entityType}}",
|
|
40
59
|
"operations": {
|
|
41
60
|
"list": {
|
|
42
|
-
"operationId": "list{{
|
|
61
|
+
"operationId": "list{{entityType}}",
|
|
43
62
|
"method": "GET",
|
|
44
|
-
"path": "/{{
|
|
63
|
+
"path": "/{{entityType}}"
|
|
45
64
|
},
|
|
46
65
|
"get": {
|
|
47
|
-
"operationId": "get{{
|
|
66
|
+
"operationId": "get{{entityType}}",
|
|
48
67
|
"method": "GET",
|
|
49
|
-
"path": "/{{
|
|
68
|
+
"path": "/{{entityType}}/{id}"
|
|
50
69
|
}
|
|
51
70
|
},
|
|
52
71
|
"autoRbac": true
|
package/lib/app.js
DELETED
|
@@ -1,467 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* AI Fabrix Builder Application Management
|
|
3
|
-
*
|
|
4
|
-
* This module handles application building, running, and deployment.
|
|
5
|
-
* Includes runtime detection, Dockerfile generation, and container management.
|
|
6
|
-
*
|
|
7
|
-
* @fileoverview Application build and run management for AI Fabrix Builder
|
|
8
|
-
* @author AI Fabrix Team
|
|
9
|
-
* @version 2.0.0
|
|
10
|
-
*/
|
|
11
|
-
|
|
12
|
-
const fs = require('fs').promises;
|
|
13
|
-
const path = require('path');
|
|
14
|
-
const chalk = require('chalk');
|
|
15
|
-
const { readExistingEnv } = require('./env-reader');
|
|
16
|
-
const build = require('./build');
|
|
17
|
-
const appRun = require('./app-run');
|
|
18
|
-
const { validateTemplate, copyTemplateFiles, copyAppFiles } = require('./template-validator');
|
|
19
|
-
const { promptForOptions } = require('./app-prompts');
|
|
20
|
-
const { generateConfigFiles } = require('./app-config');
|
|
21
|
-
const { validateAppName, pushApp } = require('./app-push');
|
|
22
|
-
const { generateDockerfileForApp } = require('./app-dockerfile');
|
|
23
|
-
const { loadTemplateVariables, updateTemplateVariables, mergeTemplateVariables } = require('./utils/template-helpers');
|
|
24
|
-
const logger = require('./utils/logger');
|
|
25
|
-
const auditLogger = require('./audit-logger');
|
|
26
|
-
const { downApp } = require('./app-down');
|
|
27
|
-
const { getAppPath } = require('./utils/paths');
|
|
28
|
-
|
|
29
|
-
/**
|
|
30
|
-
* Displays success message after app creation
|
|
31
|
-
* @param {string} appName - Application name
|
|
32
|
-
* @param {Object} config - Final configuration
|
|
33
|
-
* @param {string} envConversionMessage - Environment conversion message
|
|
34
|
-
*/
|
|
35
|
-
function displaySuccessMessage(appName, config, envConversionMessage, hasAppFiles = false, appPath = null) {
|
|
36
|
-
logger.log(chalk.green('\n✓ Application created successfully!'));
|
|
37
|
-
logger.log(chalk.blue(`\nApplication: ${appName}`));
|
|
38
|
-
|
|
39
|
-
// Determine location based on app type
|
|
40
|
-
const baseDir = config.type === 'external' ? 'integration' : 'builder';
|
|
41
|
-
const location = appPath ? path.relative(process.cwd(), appPath) : `${baseDir}/${appName}/`;
|
|
42
|
-
logger.log(chalk.blue(`Location: ${location}`));
|
|
43
|
-
|
|
44
|
-
if (hasAppFiles) {
|
|
45
|
-
logger.log(chalk.blue(`Application files: apps/${appName}/`));
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
if (config.type === 'external') {
|
|
49
|
-
logger.log(chalk.blue('Type: External System'));
|
|
50
|
-
logger.log(chalk.blue(`System Key: ${config.systemKey || appName}`));
|
|
51
|
-
logger.log(chalk.green('\nNext steps:'));
|
|
52
|
-
logger.log(chalk.white('1. Edit external system JSON files in ' + location));
|
|
53
|
-
logger.log(chalk.white('2. Run: aifabrix app register ' + appName + ' --environment dev'));
|
|
54
|
-
logger.log(chalk.white('3. Run: aifabrix build ' + appName + ' (deploys to dataplane)'));
|
|
55
|
-
logger.log(chalk.white('4. Run: aifabrix deploy ' + appName + ' (publishes to dataplane)'));
|
|
56
|
-
} else {
|
|
57
|
-
logger.log(chalk.blue(`Language: ${config.language}`));
|
|
58
|
-
logger.log(chalk.blue(`Port: ${config.port}`));
|
|
59
|
-
|
|
60
|
-
if (config.database) logger.log(chalk.yellow(' - Database enabled'));
|
|
61
|
-
if (config.redis) logger.log(chalk.yellow(' - Redis enabled'));
|
|
62
|
-
if (config.storage) logger.log(chalk.yellow(' - Storage enabled'));
|
|
63
|
-
if (config.authentication) logger.log(chalk.yellow(' - Authentication enabled'));
|
|
64
|
-
|
|
65
|
-
logger.log(chalk.gray(envConversionMessage));
|
|
66
|
-
|
|
67
|
-
logger.log(chalk.green('\nNext steps:'));
|
|
68
|
-
logger.log(chalk.white('1. Copy env.template to .env and fill in your values'));
|
|
69
|
-
logger.log(chalk.white('2. Run: aifabrix build ' + appName));
|
|
70
|
-
logger.log(chalk.white('3. Run: aifabrix run ' + appName));
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
/**
|
|
75
|
-
* Validates that app directory doesn't already exist
|
|
76
|
-
* @async
|
|
77
|
-
* @param {string} appPath - Application directory path
|
|
78
|
-
* @param {string} appName - Application name
|
|
79
|
-
* @throws {Error} If directory already exists
|
|
80
|
-
*/
|
|
81
|
-
async function validateAppDirectoryNotExists(appPath, appName, baseDir = 'builder') {
|
|
82
|
-
try {
|
|
83
|
-
await fs.access(appPath);
|
|
84
|
-
throw new Error(`Application '${appName}' already exists in ${baseDir}/${appName}/`);
|
|
85
|
-
} catch (error) {
|
|
86
|
-
if (error.code !== 'ENOENT') {
|
|
87
|
-
throw error;
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
/**
|
|
93
|
-
* Gets the base directory path for an app based on its type
|
|
94
|
-
* @param {string} appName - Application name
|
|
95
|
-
* @param {string} appType - Application type ('external' or other)
|
|
96
|
-
* @returns {string} Base directory path ('integration' or 'builder')
|
|
97
|
-
*/
|
|
98
|
-
function getBaseDirForAppType(appType) {
|
|
99
|
-
return appType === 'external' ? 'integration' : 'builder';
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
/**
|
|
103
|
-
* Handles GitHub workflow generation if requested
|
|
104
|
-
* @async
|
|
105
|
-
* @param {Object} options - Creation options
|
|
106
|
-
* @param {Object} config - Final configuration
|
|
107
|
-
*/
|
|
108
|
-
async function handleGitHubWorkflows(options, config) {
|
|
109
|
-
if (!options.github) {
|
|
110
|
-
return;
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
const githubGen = require('./github-generator');
|
|
114
|
-
|
|
115
|
-
// Parse github-steps if provided
|
|
116
|
-
const githubSteps = options.githubSteps
|
|
117
|
-
? options.githubSteps.split(',').map(s => s.trim()).filter(s => s.length > 0)
|
|
118
|
-
: [];
|
|
119
|
-
|
|
120
|
-
const workflowFiles = await githubGen.generateGithubWorkflows(
|
|
121
|
-
process.cwd(),
|
|
122
|
-
config,
|
|
123
|
-
{
|
|
124
|
-
mainBranch: options.mainBranch || 'main',
|
|
125
|
-
uploadCoverage: true,
|
|
126
|
-
githubSteps: githubSteps
|
|
127
|
-
}
|
|
128
|
-
);
|
|
129
|
-
|
|
130
|
-
logger.log(chalk.green('✓ Generated GitHub Actions workflows:'));
|
|
131
|
-
workflowFiles.forEach(file => logger.log(chalk.gray(` - ${file}`)));
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
/**
|
|
135
|
-
* Validates app creation prerequisites
|
|
136
|
-
* @async
|
|
137
|
-
* @function validateAppCreation
|
|
138
|
-
* @param {string} appName - Application name
|
|
139
|
-
* @param {Object} options - Creation options
|
|
140
|
-
* @param {string} appPath - Application directory path
|
|
141
|
-
* @throws {Error} If validation fails
|
|
142
|
-
*/
|
|
143
|
-
async function validateAppCreation(appName, options, appPath, baseDir = 'builder') {
|
|
144
|
-
validateAppName(appName);
|
|
145
|
-
await validateAppDirectoryNotExists(appPath, appName, baseDir);
|
|
146
|
-
|
|
147
|
-
if (!options.app) {
|
|
148
|
-
return;
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
const appsPath = path.join(process.cwd(), 'apps', appName);
|
|
152
|
-
try {
|
|
153
|
-
await fs.access(appsPath);
|
|
154
|
-
throw new Error(`Application '${appName}' already exists in apps/${appName}/`);
|
|
155
|
-
} catch (error) {
|
|
156
|
-
if (error.code !== 'ENOENT') {
|
|
157
|
-
throw error;
|
|
158
|
-
}
|
|
159
|
-
}
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
/**
|
|
163
|
-
* Processes template files if template is specified
|
|
164
|
-
* @async
|
|
165
|
-
* @function processTemplateFiles
|
|
166
|
-
* @param {string} template - Template name
|
|
167
|
-
* @param {string} appPath - Application directory path
|
|
168
|
-
* @param {string} appName - Application name
|
|
169
|
-
* @param {Object} options - Creation options
|
|
170
|
-
* @param {Object} config - Final configuration
|
|
171
|
-
* @throws {Error} If template processing fails
|
|
172
|
-
*/
|
|
173
|
-
async function processTemplateFiles(template, appPath, appName, options, config) {
|
|
174
|
-
if (!template) {
|
|
175
|
-
return;
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
await validateTemplate(template);
|
|
179
|
-
const copiedFiles = await copyTemplateFiles(template, appPath);
|
|
180
|
-
logger.log(chalk.green(`✓ Copied ${copiedFiles.length} file(s) from template '${template}'`));
|
|
181
|
-
await updateTemplateVariables(appPath, appName, options, config);
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
/**
|
|
185
|
-
* Updates variables.yaml for --app flag
|
|
186
|
-
* @async
|
|
187
|
-
* @function updateVariablesForAppFlag
|
|
188
|
-
* @param {string} appPath - Application directory path
|
|
189
|
-
* @param {string} appName - Application name
|
|
190
|
-
* @throws {Error} If update fails
|
|
191
|
-
*/
|
|
192
|
-
async function updateVariablesForAppFlag(appPath, appName) {
|
|
193
|
-
try {
|
|
194
|
-
const yaml = require('js-yaml');
|
|
195
|
-
const variablesPath = path.join(appPath, 'variables.yaml');
|
|
196
|
-
const variablesContent = await fs.readFile(variablesPath, 'utf8');
|
|
197
|
-
const variables = yaml.load(variablesContent);
|
|
198
|
-
|
|
199
|
-
if (variables.build) {
|
|
200
|
-
variables.build.context = '../..';
|
|
201
|
-
variables.build.envOutputPath = `../../apps/${appName}/.env`;
|
|
202
|
-
} else {
|
|
203
|
-
variables.build = {
|
|
204
|
-
context: '../..',
|
|
205
|
-
envOutputPath: `../../apps/${appName}/.env`
|
|
206
|
-
};
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
await fs.writeFile(variablesPath, yaml.dump(variables, { indent: 2, lineWidth: 120, noRefs: true }));
|
|
210
|
-
} catch (error) {
|
|
211
|
-
logger.warn(chalk.yellow(`⚠️ Warning: Could not update variables.yaml: ${error.message}`));
|
|
212
|
-
}
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
/**
|
|
216
|
-
* Gets language from config or variables.yaml
|
|
217
|
-
* @async
|
|
218
|
-
* @function getLanguageForAppFiles
|
|
219
|
-
* @param {string} language - Language from config
|
|
220
|
-
* @param {string} appPath - Application directory path
|
|
221
|
-
* @returns {Promise<string>} Language to use
|
|
222
|
-
* @throws {Error} If language cannot be determined
|
|
223
|
-
*/
|
|
224
|
-
async function getLanguageForAppFiles(language, appPath) {
|
|
225
|
-
if (language) {
|
|
226
|
-
return language;
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
const yaml = require('js-yaml');
|
|
230
|
-
const variablesPath = path.join(appPath, 'variables.yaml');
|
|
231
|
-
const variablesContent = await fs.readFile(variablesPath, 'utf8');
|
|
232
|
-
const variables = yaml.load(variablesContent);
|
|
233
|
-
const languageFromYaml = variables?.build?.language;
|
|
234
|
-
|
|
235
|
-
if (!languageFromYaml) {
|
|
236
|
-
throw new Error('Language not specified and could not be determined from variables.yaml. Use --language flag or ensure variables.yaml contains build.language');
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
return languageFromYaml;
|
|
240
|
-
}
|
|
241
|
-
|
|
242
|
-
/**
|
|
243
|
-
* Sets up apps directory and copies application files
|
|
244
|
-
* @async
|
|
245
|
-
* @function setupAppFiles
|
|
246
|
-
* @param {string} appName - Application name
|
|
247
|
-
* @param {string} appPath - Application directory path
|
|
248
|
-
* @param {Object} config - Final configuration
|
|
249
|
-
* @param {Object} options - Creation options
|
|
250
|
-
* @throws {Error} If setup fails
|
|
251
|
-
*/
|
|
252
|
-
async function setupAppFiles(appName, appPath, config, options) {
|
|
253
|
-
const appsPath = path.join(process.cwd(), 'apps', appName);
|
|
254
|
-
await fs.mkdir(appsPath, { recursive: true });
|
|
255
|
-
await updateVariablesForAppFlag(appPath, appName);
|
|
256
|
-
|
|
257
|
-
const language = await getLanguageForAppFiles(config.language || options.language, appPath);
|
|
258
|
-
const copiedFiles = await copyAppFiles(language, appsPath);
|
|
259
|
-
logger.log(chalk.green(`✓ Copied ${copiedFiles.length} application file(s) to apps/${appName}/`));
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
/**
|
|
263
|
-
* Creates new application with scaffolded configuration files
|
|
264
|
-
* Prompts for configuration options and generates builder/ folder structure
|
|
265
|
-
*
|
|
266
|
-
* @async
|
|
267
|
-
* @function createApp
|
|
268
|
-
* @param {string} appName - Name of the application to create
|
|
269
|
-
* @param {Object} options - Creation options
|
|
270
|
-
* @param {number} [options.port] - Application port
|
|
271
|
-
* @param {boolean} [options.database] - Requires database
|
|
272
|
-
* @param {boolean} [options.redis] - Requires Redis
|
|
273
|
-
* @param {boolean} [options.storage] - Requires file storage
|
|
274
|
-
* @param {boolean} [options.authentication] - Requires authentication/RBAC
|
|
275
|
-
* @param {string} [options.language] - Runtime language (typescript/python)
|
|
276
|
-
* @param {string} [options.template] - Template to use (e.g., controller, keycloak)
|
|
277
|
-
* @returns {Promise<void>} Resolves when app is created
|
|
278
|
-
* @throws {Error} If creation fails
|
|
279
|
-
*
|
|
280
|
-
* @example
|
|
281
|
-
* await createApp('myapp', { port: 3000, database: true, language: 'typescript' });
|
|
282
|
-
* // Creates builder/ with variables.yaml, env.template, rbac.yaml
|
|
283
|
-
*/
|
|
284
|
-
async function createApp(appName, options = {}) {
|
|
285
|
-
try {
|
|
286
|
-
// Validate appName early
|
|
287
|
-
if (!appName || typeof appName !== 'string') {
|
|
288
|
-
throw new Error('Application name is required');
|
|
289
|
-
}
|
|
290
|
-
|
|
291
|
-
// Determine app type from options (will be confirmed during prompts)
|
|
292
|
-
// For now, check if type is explicitly set in options
|
|
293
|
-
const initialType = options.type || 'webapp';
|
|
294
|
-
const baseDir = getBaseDirForAppType(initialType);
|
|
295
|
-
const appPath = getAppPath(appName, initialType);
|
|
296
|
-
|
|
297
|
-
await validateAppCreation(appName, options, appPath, baseDir);
|
|
298
|
-
|
|
299
|
-
if (options.template) {
|
|
300
|
-
await validateTemplate(options.template);
|
|
301
|
-
}
|
|
302
|
-
|
|
303
|
-
const templateVariables = await loadTemplateVariables(options.template);
|
|
304
|
-
const mergedOptions = mergeTemplateVariables(options, templateVariables);
|
|
305
|
-
const config = await promptForOptions(appName, mergedOptions);
|
|
306
|
-
|
|
307
|
-
// Update appPath based on final config type (may have changed during prompts)
|
|
308
|
-
const finalBaseDir = getBaseDirForAppType(config.type);
|
|
309
|
-
const finalAppPath = getAppPath(appName, config.type);
|
|
310
|
-
|
|
311
|
-
// If path changed, validate the new path
|
|
312
|
-
if (finalAppPath !== appPath) {
|
|
313
|
-
await validateAppDirectoryNotExists(finalAppPath, appName, finalBaseDir);
|
|
314
|
-
}
|
|
315
|
-
|
|
316
|
-
await fs.mkdir(finalAppPath, { recursive: true });
|
|
317
|
-
await processTemplateFiles(options.template, finalAppPath, appName, options, config);
|
|
318
|
-
|
|
319
|
-
const existingEnv = await readExistingEnv(process.cwd());
|
|
320
|
-
const envConversionMessage = existingEnv
|
|
321
|
-
? '\n✓ Found existing .env file - sensitive values will be converted to kv:// references'
|
|
322
|
-
: '';
|
|
323
|
-
|
|
324
|
-
await generateConfigFiles(finalAppPath, appName, config, existingEnv);
|
|
325
|
-
|
|
326
|
-
// Generate external system files if type is external
|
|
327
|
-
if (config.type === 'external') {
|
|
328
|
-
const externalGenerator = require('./external-system-generator');
|
|
329
|
-
await externalGenerator.generateExternalSystemFiles(finalAppPath, appName, config);
|
|
330
|
-
}
|
|
331
|
-
|
|
332
|
-
if (options.app) {
|
|
333
|
-
await setupAppFiles(appName, finalAppPath, config, options);
|
|
334
|
-
}
|
|
335
|
-
|
|
336
|
-
await handleGitHubWorkflows(options, config);
|
|
337
|
-
displaySuccessMessage(appName, config, envConversionMessage, options.app, finalAppPath);
|
|
338
|
-
|
|
339
|
-
// Log application creation for audit trail
|
|
340
|
-
await auditLogger.logApplicationCreation(appName, {
|
|
341
|
-
language: config.language,
|
|
342
|
-
port: config.port,
|
|
343
|
-
database: config.database,
|
|
344
|
-
redis: config.redis,
|
|
345
|
-
storage: config.storage,
|
|
346
|
-
authentication: config.authentication,
|
|
347
|
-
template: options.template,
|
|
348
|
-
api: null // Local operation, no API involved
|
|
349
|
-
});
|
|
350
|
-
} catch (error) {
|
|
351
|
-
throw new Error(`Failed to create application: ${error.message}`);
|
|
352
|
-
}
|
|
353
|
-
}
|
|
354
|
-
|
|
355
|
-
/**
|
|
356
|
-
* Builds a container image for the specified application
|
|
357
|
-
* Auto-detects runtime and generates Dockerfile if needed
|
|
358
|
-
*
|
|
359
|
-
* @async
|
|
360
|
-
* @function buildApp
|
|
361
|
-
* @param {string} appName - Name of the application to build
|
|
362
|
-
* @param {Object} options - Build options
|
|
363
|
-
* @param {string} [options.language] - Override language detection
|
|
364
|
-
* @param {boolean} [options.forceTemplate] - Force rebuild from template
|
|
365
|
-
* @param {string} [options.tag] - Image tag (default: latest)
|
|
366
|
-
* @returns {Promise<string>} Image tag that was built
|
|
367
|
-
* @throws {Error} If build fails or app configuration is invalid
|
|
368
|
-
*
|
|
369
|
-
* @example
|
|
370
|
-
* const imageTag = await buildApp('myapp', { language: 'typescript' });
|
|
371
|
-
* // Returns: 'myapp:latest'
|
|
372
|
-
*/
|
|
373
|
-
async function buildApp(appName, options = {}) {
|
|
374
|
-
return build.buildApp(appName, options);
|
|
375
|
-
}
|
|
376
|
-
|
|
377
|
-
/**
|
|
378
|
-
* Detects the runtime language of an application
|
|
379
|
-
* Analyzes project files to determine TypeScript, Python, etc.
|
|
380
|
-
*
|
|
381
|
-
* @function detectLanguage
|
|
382
|
-
* @param {string} appPath - Path to application directory
|
|
383
|
-
* @returns {string} Detected language ('typescript', 'python', etc.)
|
|
384
|
-
* @throws {Error} If language cannot be detected
|
|
385
|
-
*
|
|
386
|
-
* @example
|
|
387
|
-
* const language = detectLanguage('./myapp');
|
|
388
|
-
* // Returns: 'typescript'
|
|
389
|
-
*/
|
|
390
|
-
function detectLanguage(appPath) {
|
|
391
|
-
return build.detectLanguage(appPath);
|
|
392
|
-
}
|
|
393
|
-
|
|
394
|
-
/**
|
|
395
|
-
* Generates a Dockerfile from template based on detected language
|
|
396
|
-
* Uses Handlebars templates to create optimized Dockerfiles
|
|
397
|
-
*
|
|
398
|
-
* @async
|
|
399
|
-
* @function generateDockerfile
|
|
400
|
-
* @param {string} appPath - Path to application directory
|
|
401
|
-
* @param {string} language - Target language ('typescript', 'python')
|
|
402
|
-
* @param {Object} config - Application configuration from variables.yaml
|
|
403
|
-
* @returns {Promise<string>} Path to generated Dockerfile
|
|
404
|
-
* @throws {Error} If template generation fails
|
|
405
|
-
*
|
|
406
|
-
* @example
|
|
407
|
-
* const dockerfilePath = await generateDockerfile('./myapp', 'typescript', config);
|
|
408
|
-
* // Returns: './myapp/.aifabrix/Dockerfile.typescript'
|
|
409
|
-
*/
|
|
410
|
-
async function generateDockerfile(appPath, language, config) {
|
|
411
|
-
return build.generateDockerfile(appPath, language, config);
|
|
412
|
-
}
|
|
413
|
-
|
|
414
|
-
/**
|
|
415
|
-
* Runs the application locally using Docker
|
|
416
|
-
* Starts container with proper port mapping and environment
|
|
417
|
-
*
|
|
418
|
-
* @async
|
|
419
|
-
* @function runApp
|
|
420
|
-
* @param {string} appName - Name of the application to run
|
|
421
|
-
* @param {Object} options - Run options
|
|
422
|
-
* @param {number} [options.port] - Override local port
|
|
423
|
-
* @param {boolean} [options.debug] - Enable debug output
|
|
424
|
-
* @returns {Promise<void>} Resolves when app is running
|
|
425
|
-
* @throws {Error} If run fails or app is not built
|
|
426
|
-
*
|
|
427
|
-
* @example
|
|
428
|
-
* await runApp('myapp', { port: 3001 });
|
|
429
|
-
* // Application is now running on localhost:3001
|
|
430
|
-
*/
|
|
431
|
-
async function runApp(appName, options = {}) {
|
|
432
|
-
return appRun.runApp(appName, options);
|
|
433
|
-
}
|
|
434
|
-
|
|
435
|
-
/**
|
|
436
|
-
* Deploys application to controller
|
|
437
|
-
* @async
|
|
438
|
-
* @function deployApp
|
|
439
|
-
* @param {string} appName - Name of the application
|
|
440
|
-
* @param {Object} options - Deployment options
|
|
441
|
-
* @returns {Promise<void>} Resolves when deployment is complete
|
|
442
|
-
*/
|
|
443
|
-
async function deployApp(appName, options = {}) {
|
|
444
|
-
const appDeploy = require('./app-deploy');
|
|
445
|
-
return appDeploy.deployApp(appName, options);
|
|
446
|
-
}
|
|
447
|
-
|
|
448
|
-
module.exports = {
|
|
449
|
-
createApp,
|
|
450
|
-
buildApp,
|
|
451
|
-
runApp,
|
|
452
|
-
downApp,
|
|
453
|
-
detectLanguage,
|
|
454
|
-
generateDockerfile,
|
|
455
|
-
generateDockerfileForApp,
|
|
456
|
-
pushApp,
|
|
457
|
-
deployApp,
|
|
458
|
-
loadTemplateVariables,
|
|
459
|
-
updateTemplateVariables,
|
|
460
|
-
mergeTemplateVariables,
|
|
461
|
-
checkImageExists: appRun.checkImageExists,
|
|
462
|
-
checkContainerRunning: appRun.checkContainerRunning,
|
|
463
|
-
stopAndRemoveContainer: appRun.stopAndRemoveContainer,
|
|
464
|
-
checkPortAvailable: appRun.checkPortAvailable,
|
|
465
|
-
generateDockerCompose: appRun.generateDockerCompose,
|
|
466
|
-
waitForHealthCheck: appRun.waitForHealthCheck
|
|
467
|
-
};
|