@aifabrix/builder 2.43.0 → 2.44.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/.cursor/rules/anchor-docs.mdc +15 -0
- package/README.md +1 -1
- package/anchor-docs/README.md +10 -0
- package/anchor-docs/_TEMPLATE +24 -0
- package/bin/aifabrix.js +13 -4
- package/integration/hubspot-test/README.md +31 -0
- package/integration/hubspot-test/create-hubspot.js +5 -5
- package/integration/hubspot-test/hubspot-test-datasource-company.json +58 -462
- package/integration/hubspot-test/hubspot-test-datasource-contact.json +61 -555
- package/integration/hubspot-test/hubspot-test-datasource-deal.json +63 -506
- package/integration/hubspot-test/hubspot-test-datasource-users.json +42 -83
- package/integration/hubspot-test/hubspot-test-deploy.json +3 -3
- package/integration/hubspot-test/test-dataplane-down-tests.js +1 -7
- package/integration/hubspot-test/test-dataplane-down.js +3 -3
- package/integration/hubspot-test/test.js +35 -43
- package/integration/hubspot-test/wizard-hubspot-test-headless.yaml +23 -0
- package/integration/roundtrip-test-local/README.md +144 -0
- package/integration/roundtrip-test-local/application.yaml +13 -0
- package/integration/roundtrip-test-local/env.template +15 -0
- package/integration/roundtrip-test-local/roundtrip-test-local-datasource-roundtrip-test-company.yaml +14 -0
- package/integration/roundtrip-test-local/roundtrip-test-local-deploy.json +61 -0
- package/integration/roundtrip-test-local/roundtrip-test-local-system.yaml +25 -0
- package/integration/roundtrip-test-local2/README.md +144 -0
- package/integration/roundtrip-test-local2/application.yaml +13 -0
- package/integration/roundtrip-test-local2/env.template +15 -0
- package/integration/roundtrip-test-local2/roundtrip-test-local2-datasource-company.yaml +31 -0
- package/integration/roundtrip-test-local2/roundtrip-test-local2-deploy.json +86 -0
- package/integration/roundtrip-test-local2/roundtrip-test-local2-system.yaml +25 -0
- package/integration/test/wizard.yaml +8 -0
- package/jest.config.default.js +10 -0
- package/jest.config.integration.fixtures.js +22 -0
- package/jest.config.integration.js +21 -18
- package/jest.config.isolated.js +10 -0
- package/jest.projects.js +288 -0
- package/lib/api/datasources-core.api.js +3 -3
- package/lib/api/dev-mtls-request.js +110 -0
- package/lib/api/dev-server-https.js +145 -0
- package/lib/api/dev.api.js +133 -144
- package/lib/api/index.js +0 -1
- package/lib/api/pipeline.api.js +67 -20
- package/lib/api/types/dev.types.js +4 -3
- package/lib/api/types/pipeline.types.js +8 -5
- package/lib/api/types/validation-run.types.js +56 -0
- package/lib/api/validation-run.api.js +99 -0
- package/lib/api/validation-runner.js +99 -0
- package/lib/app/config.js +1 -1
- package/lib/app/deploy-status-display.js +2 -2
- package/lib/app/deploy.js +7 -6
- package/lib/app/display.js +2 -1
- package/lib/app/dockerfile.js +3 -2
- package/lib/app/down.js +2 -1
- package/lib/app/helpers.js +6 -5
- package/lib/app/index.js +27 -8
- package/lib/app/list.js +7 -6
- package/lib/app/push.js +4 -3
- package/lib/app/register.js +16 -7
- package/lib/app/rotate-secret.js +14 -13
- package/lib/app/run-container-start.js +184 -0
- package/lib/app/run-docker-fallback.js +108 -0
- package/lib/app/run-env-compose.js +30 -42
- package/lib/app/run-helpers.js +49 -126
- package/lib/app/run-infra-requirements.js +30 -0
- package/lib/app/run-resolve-image.js +21 -0
- package/lib/app/run.js +74 -21
- package/lib/app/show-display.js +1 -1
- package/lib/app/show.js +1 -1
- package/lib/build/index.js +13 -10
- package/lib/cli/index.js +2 -0
- package/lib/cli/setup-app.help.js +67 -0
- package/lib/cli/setup-app.js +57 -121
- package/lib/cli/setup-app.test-commands.js +179 -0
- package/lib/cli/setup-auth.js +19 -5
- package/lib/cli/setup-credential-deployment.js +22 -8
- package/lib/cli/setup-dev-path-commands.js +124 -0
- package/lib/cli/setup-dev.js +170 -113
- package/lib/cli/setup-environment.js +7 -1
- package/lib/cli/setup-external-system.js +62 -22
- package/lib/cli/setup-infra.js +126 -47
- package/lib/cli/setup-parameters.js +32 -0
- package/lib/cli/setup-secrets.js +106 -8
- package/lib/cli/setup-service-user.js +1 -1
- package/lib/cli/setup-utility.js +36 -20
- package/lib/commands/app-down.js +5 -7
- package/lib/commands/app-install.js +14 -7
- package/lib/commands/app-logs.js +13 -10
- package/lib/commands/app-shell.js +4 -1
- package/lib/commands/app-test.js +25 -19
- package/lib/commands/app.js +22 -10
- package/lib/commands/auth-config.js +6 -6
- package/lib/commands/auth-status.js +4 -3
- package/lib/commands/credential-env.js +4 -3
- package/lib/commands/credential-list.js +5 -4
- package/lib/commands/credential-push.js +4 -3
- package/lib/commands/datasource-unified-test-cli.js +495 -0
- package/lib/commands/datasource-unified-test-cli.options.js +149 -0
- package/lib/commands/datasource-validation-cli.js +129 -0
- package/lib/commands/datasource.js +105 -98
- package/lib/commands/deployment-list.js +6 -5
- package/lib/commands/dev-cli-handlers.js +122 -18
- package/lib/commands/dev-down.js +4 -3
- package/lib/commands/dev-init.js +231 -116
- package/lib/commands/dev-show-display.js +473 -0
- package/lib/commands/login-credentials.js +3 -2
- package/lib/commands/login-device.js +4 -3
- package/lib/commands/login.js +5 -4
- package/lib/commands/logout.js +8 -7
- package/lib/commands/parameters-validate.js +54 -0
- package/lib/commands/repair-datasource.js +314 -68
- package/lib/commands/repair-env-template.js +2 -2
- package/lib/commands/repair.js +21 -3
- package/lib/commands/secrets-list.js +23 -12
- package/lib/commands/secrets-remove-all.js +220 -0
- package/lib/commands/secrets-remove.js +21 -12
- package/lib/commands/secrets-set.js +21 -12
- package/lib/commands/secrets-validate.js +4 -4
- package/lib/commands/secure.js +10 -9
- package/lib/commands/service-user.js +26 -25
- package/lib/commands/test-e2e-external.js +27 -1
- package/lib/commands/up-common.js +3 -2
- package/lib/commands/up-dataplane.js +29 -16
- package/lib/commands/up-miso.js +19 -29
- package/lib/commands/upload.js +138 -39
- package/lib/commands/wizard-core-helpers.js +1 -1
- package/lib/commands/wizard-dataplane.js +4 -3
- package/lib/commands/wizard-helpers.js +3 -3
- package/lib/commands/wizard.js +2 -2
- package/lib/core/admin-secrets.js +14 -5
- package/lib/core/audit-logger.js +12 -4
- package/lib/core/config-attach-extensions.js +46 -0
- package/lib/core/config-runtime-paths.js +29 -0
- package/lib/core/config.js +55 -56
- package/lib/core/diff.js +3 -2
- package/lib/core/ensure-encryption-key.js +1 -1
- package/lib/core/secrets-ensure-infra.js +77 -0
- package/lib/core/secrets-ensure.js +120 -64
- package/lib/core/secrets-env-write.js +35 -7
- package/lib/core/secrets-infra-placeholder-sync.js +61 -0
- package/lib/core/secrets.js +200 -37
- package/lib/core/templates-env.js +4 -3
- package/lib/datasource/abac-validator.js +1 -10
- package/lib/datasource/deploy.js +75 -53
- package/lib/datasource/field-reference-validator.js +9 -6
- package/lib/datasource/integration-context.js +63 -0
- package/lib/datasource/list.js +8 -7
- package/lib/datasource/log-viewer.js +84 -53
- package/lib/datasource/resolve-app.js +4 -4
- package/lib/datasource/test-e2e.js +95 -146
- package/lib/datasource/test-integration.js +114 -122
- package/lib/datasource/unified-validation-run-body.js +65 -0
- package/lib/datasource/unified-validation-run-post.js +23 -0
- package/lib/datasource/unified-validation-run-resolve.js +43 -0
- package/lib/datasource/unified-validation-run.js +92 -0
- package/lib/datasource/validate.js +157 -13
- package/lib/deployment/deployer.js +4 -3
- package/lib/deployment/environment.js +7 -6
- package/lib/deployment/push.js +17 -8
- package/lib/external-system/delete.js +4 -3
- package/lib/external-system/deploy.js +131 -53
- package/lib/external-system/download-helpers.js +1 -1
- package/lib/external-system/download.js +7 -6
- package/lib/external-system/generator.js +92 -6
- package/lib/external-system/integration-test-dispatch.js +26 -0
- package/lib/external-system/test-execution.js +5 -1
- package/lib/external-system/test-helpers.js +0 -4
- package/lib/external-system/test-system-level-helpers.js +110 -0
- package/lib/external-system/test-system-level.js +83 -44
- package/lib/external-system/test.js +59 -8
- package/lib/generator/builders.js +23 -11
- package/lib/generator/deploy-manifest-azure-kv.js +81 -0
- package/lib/generator/external.js +16 -4
- package/lib/generator/helpers.js +58 -3
- package/lib/generator/index.js +4 -0
- package/lib/generator/split-readme.js +12 -7
- package/lib/generator/split-variables.js +2 -1
- package/lib/generator/split.js +1 -1
- package/lib/generator/wizard-readme.js +3 -3
- package/lib/generator/wizard.js +8 -8
- package/lib/infrastructure/compose.js +60 -6
- package/lib/infrastructure/helpers.js +201 -29
- package/lib/infrastructure/index.js +28 -17
- package/lib/infrastructure/services.js +21 -15
- package/lib/internal/fs-real-sync.js +104 -0
- package/lib/internal/node-fs.js +98 -0
- package/lib/parameters/database-secret-values.js +173 -0
- package/lib/parameters/infra-kv-discovery.js +121 -0
- package/lib/parameters/infra-parameter-catalog.js +458 -0
- package/lib/parameters/infra-parameter-validate.js +64 -0
- package/lib/schema/application-schema.json +37 -17
- package/lib/schema/datasource-test-run.schema.json +493 -0
- package/lib/schema/deployment-rules.yaml +102 -63
- package/lib/schema/external-datasource.schema.json +1200 -442
- package/lib/schema/external-system.schema.json +181 -5
- package/lib/schema/flag-map-validation-run.json +31 -0
- package/lib/schema/infra-parameter.schema.json +106 -0
- package/lib/schema/infra.parameter.yaml +421 -0
- package/lib/schema/type/credential-auth-templates.json +40 -0
- package/lib/schema/type/document-storage.json +213 -0
- package/lib/schema/type/message-service.json +123 -0
- package/lib/schema/type/vector-store.json +88 -0
- package/lib/utils/aifabrix-runtime-config-dir.js +132 -0
- package/lib/utils/api-error-handler.js +2 -2
- package/lib/utils/api.js +49 -14
- package/lib/utils/app-register-api.js +3 -2
- package/lib/utils/app-register-auth.js +1 -1
- package/lib/utils/app-register-config.js +4 -4
- package/lib/utils/app-register-display.js +3 -2
- package/lib/utils/app-register-validator.js +3 -2
- package/lib/utils/app-run-containers.js +26 -22
- package/lib/utils/app-scoped-config.js +31 -0
- package/lib/utils/app-service-env-from-builder.js +164 -0
- package/lib/utils/build-copy.js +1 -1
- package/lib/utils/build-helpers.js +20 -20
- package/lib/utils/build-resolve-image.js +165 -0
- package/lib/utils/cli-layout-chalk.js +8 -0
- package/lib/utils/cli-test-layout-chalk.js +267 -0
- package/lib/utils/cli-utils.js +88 -11
- package/lib/utils/compose-db-passwords.js +138 -0
- package/lib/utils/compose-generate-docker-compose.js +216 -0
- package/lib/utils/compose-generator.js +197 -291
- package/lib/utils/compose-miso-env.js +18 -0
- package/lib/utils/compose-traefik-ingress-base.js +158 -0
- package/lib/utils/config-paths.js +166 -7
- package/lib/utils/config-scoped-resources-preference.js +41 -0
- package/lib/utils/controller-deployment-outcome.js +68 -0
- package/lib/utils/credential-display.js +2 -2
- package/lib/utils/dataplane-pipeline-warning.js +4 -3
- package/lib/utils/datasource-test-run-capability-scope.js +43 -0
- package/lib/utils/datasource-test-run-debug-display.js +137 -0
- package/lib/utils/datasource-test-run-debug-slice.js +93 -0
- package/lib/utils/datasource-test-run-display.js +442 -0
- package/lib/utils/datasource-test-run-exit.js +58 -0
- package/lib/utils/datasource-test-run-legacy-adapter.js +93 -0
- package/lib/utils/datasource-test-run-report-version.js +51 -0
- package/lib/utils/datasource-test-run-schema-sync.js +59 -0
- package/lib/utils/datasource-test-run-tty-log.js +81 -0
- package/lib/utils/datasource-validation-watch.js +266 -0
- package/lib/utils/declarative-url-ports.js +47 -0
- package/lib/utils/derive-env-key-from-client-id.js +41 -0
- package/lib/utils/dev-ca-install.js +185 -23
- package/lib/utils/dev-cert-helper.js +266 -17
- package/lib/utils/dev-hosts-helper.js +307 -0
- package/lib/utils/dev-init-cert-hints.js +37 -0
- package/lib/utils/dev-init-health-messages.js +52 -0
- package/lib/utils/dev-init-resolve.js +86 -0
- package/lib/utils/dev-init-ssh-merge.js +65 -0
- package/lib/utils/dev-ssh-config-helper.js +196 -0
- package/lib/utils/dev-user-groups.js +93 -0
- package/lib/utils/docker-build.js +42 -17
- package/lib/utils/docker-exec.js +28 -0
- package/lib/utils/docker-manifest-public-port.js +116 -0
- package/lib/utils/docker-not-running-hint.js +52 -0
- package/lib/utils/docker.js +98 -11
- package/lib/utils/ensure-dev-certs-for-remote-docker.js +192 -0
- package/lib/utils/env-config-loader.js +10 -91
- package/lib/utils/env-copy.js +19 -10
- package/lib/utils/env-map.js +35 -8
- package/lib/utils/env-template.js +2 -2
- package/lib/utils/environment-scoped-resources.js +144 -0
- package/lib/utils/error-formatter.js +92 -13
- package/lib/utils/error-formatters/http-status-errors.js +6 -5
- package/lib/utils/error-formatters/network-errors.js +2 -1
- package/lib/utils/error-formatters/permission-errors.js +2 -1
- package/lib/utils/error-formatters/validation-errors.js +2 -1
- package/lib/utils/external-readme.js +8 -1
- package/lib/utils/external-system-display.js +234 -136
- package/lib/utils/external-system-local-test-tty.js +389 -0
- package/lib/utils/external-system-readiness-core.js +377 -0
- package/lib/utils/external-system-readiness-deploy-display.js +270 -0
- package/lib/utils/external-system-readiness-display-internals.js +150 -0
- package/lib/utils/external-system-readiness-display.js +186 -0
- package/lib/utils/external-system-test-helpers.js +24 -6
- package/lib/utils/external-system-validators.js +30 -12
- package/lib/utils/health-check-url.js +119 -0
- package/lib/utils/health-check.js +59 -25
- package/lib/utils/help-builder.js +11 -8
- package/lib/utils/image-version.js +4 -8
- package/lib/utils/infra-containers.js +4 -7
- package/lib/utils/infra-env-defaults.js +162 -0
- package/lib/utils/infra-status-display.js +167 -0
- package/lib/utils/infra-status.js +16 -8
- package/lib/utils/local-secrets.js +3 -4
- package/lib/utils/paths.js +134 -47
- package/lib/utils/port-resolver.js +10 -23
- package/lib/utils/redis-env-scope.js +62 -0
- package/lib/utils/register-aifabrix-shell-env.js +204 -0
- package/lib/utils/remote-builder-validation.js +99 -0
- package/lib/utils/remote-dev-auth.js +117 -21
- package/lib/utils/remote-docker-env.js +67 -15
- package/lib/utils/remote-secrets-loader.js +13 -4
- package/lib/utils/resolve-docker-image-ref.js +124 -0
- package/lib/utils/schema-loader.js +22 -9
- package/lib/utils/secrets-bash-kv.js +25 -0
- package/lib/utils/secrets-generator.js +169 -49
- package/lib/utils/secrets-helpers.js +70 -59
- package/lib/utils/secrets-kv-scope.js +60 -0
- package/lib/utils/secrets-utils.js +32 -38
- package/lib/utils/secrets-validation.js +3 -1
- package/lib/utils/secrets-yaml-preserve.js +109 -0
- package/lib/utils/ssh-key-helper.js +4 -2
- package/lib/utils/template-helpers.js +2 -2
- package/lib/utils/test-log-writer.js +3 -3
- package/lib/utils/token-manager.js +1 -2
- package/lib/utils/url-declarative-public-base.js +188 -0
- package/lib/utils/url-declarative-resolve-build.js +493 -0
- package/lib/utils/url-declarative-resolve-load-doc.js +51 -0
- package/lib/utils/url-declarative-resolve.js +220 -0
- package/lib/utils/url-declarative-token-parse.js +74 -0
- package/lib/utils/url-declarative-url-flags.js +50 -0
- package/lib/utils/url-declarative-vdir-inactive-env.js +99 -0
- package/lib/utils/url-public-path-prefix.js +34 -0
- package/lib/utils/urls-local-registry.js +220 -0
- package/lib/utils/validation-report-tty-kit.js +77 -0
- package/lib/utils/validation-run-poll.js +89 -0
- package/lib/utils/validation-run-post-retry.js +73 -0
- package/lib/utils/validation-run-request.js +98 -0
- package/lib/utils/variable-transformer.js +21 -4
- package/lib/utils/yaml-preserve.js +33 -14
- package/lib/validation/datasource-warnings.js +56 -0
- package/lib/validation/env-template-auth.js +1 -1
- package/lib/validation/external-manifest-validator.js +27 -7
- package/lib/validation/validate-display.js +37 -31
- package/lib/validation/validate.js +4 -13
- package/lib/validation/validator-unresolved-placeholders.js +98 -0
- package/lib/validation/validator.js +22 -65
- package/lib/validation/wizard-config-validator.js +2 -1
- package/package.json +7 -3
- package/scripts/check-datasource-test-run-schema-sync.js +34 -0
- package/scripts/diagnose-cli.js +150 -0
- package/scripts/install-local.js +304 -55
- package/templates/README.md +15 -2
- package/templates/applications/dataplane/application.yaml +52 -2
- package/templates/applications/dataplane/env.template +75 -17
- package/templates/applications/dataplane/rbac.yaml +8 -0
- package/templates/applications/keycloak/application.yaml +9 -1
- package/templates/applications/keycloak/env.template +15 -6
- package/templates/applications/miso-controller/application.yaml +10 -2
- package/templates/applications/miso-controller/env.template +42 -12
- package/templates/applications/miso-controller/rbac.yaml +5 -0
- package/templates/external-system/README.md.hbs +20 -7
- package/templates/external-system/deploy.js.hbs +5 -5
- package/templates/external-system/external-datasource.yaml.hbs +197 -118
- package/templates/infra/compose.yaml.hbs +20 -4
- package/templates/python/docker-compose.hbs +16 -0
- package/templates/typescript/docker-compose.hbs +16 -0
- package/lib/api/external-test.api.js +0 -111
- package/lib/schema/env-config.yaml +0 -60
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Merge a Builder dev SSH Host alias into ~/.ssh/config (Mutagen / interactive SSH).
|
|
3
|
+
* @author AI Fabrix Team
|
|
4
|
+
* @version 2.0.0
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
const nodeFsLib = require('../internal/node-fs');
|
|
8
|
+
const path = require('path');
|
|
9
|
+
const { ensureSshDir } = require('./ssh-key-helper');
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Derive SSH config Host alias: sync user + real hostname (e.g. dev01.builder02.local).
|
|
13
|
+
* @param {string} sshUser - sync-ssh-user
|
|
14
|
+
* @param {string} sshHost - sync-ssh-host (HostName)
|
|
15
|
+
* @returns {string}
|
|
16
|
+
*/
|
|
17
|
+
function getDevSshHostAlias(sshUser, sshHost) {
|
|
18
|
+
return `${sshUser}.${sshHost}`;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Parse ssh config into Host blocks (lines before the first Host are ignored).
|
|
23
|
+
* @param {string} content
|
|
24
|
+
* @returns {{ aliases: string[], body: string[] }[]}
|
|
25
|
+
*/
|
|
26
|
+
function parseSshHostBlocks(content) {
|
|
27
|
+
const lines = (content || '').split(/\r?\n/);
|
|
28
|
+
const blocks = [];
|
|
29
|
+
let i = 0;
|
|
30
|
+
while (i < lines.length) {
|
|
31
|
+
const m = lines[i].match(/^Host\s+(.+?)\s*$/i);
|
|
32
|
+
if (!m) {
|
|
33
|
+
i++;
|
|
34
|
+
continue;
|
|
35
|
+
}
|
|
36
|
+
const aliases = m[1].trim().split(/\s+/).filter(Boolean);
|
|
37
|
+
i++;
|
|
38
|
+
const body = [];
|
|
39
|
+
while (i < lines.length && !/^Host\s/i.test(lines[i])) {
|
|
40
|
+
body.push(lines[i]);
|
|
41
|
+
i++;
|
|
42
|
+
}
|
|
43
|
+
blocks.push({ aliases, body });
|
|
44
|
+
}
|
|
45
|
+
return blocks;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* First value for a directive inside a Host block body (e.g. HostName, User).
|
|
50
|
+
* @param {string[]} bodyLines
|
|
51
|
+
* @param {string} directive - e.g. HostName
|
|
52
|
+
* @returns {string|null}
|
|
53
|
+
*/
|
|
54
|
+
function getHostBlockDirective(bodyLines, directive) {
|
|
55
|
+
const re = new RegExp(`^\\s+${directive}\\s+(.+)$`, 'i');
|
|
56
|
+
for (const line of bodyLines) {
|
|
57
|
+
const m = line.match(re);
|
|
58
|
+
if (m) return m[1].trim();
|
|
59
|
+
}
|
|
60
|
+
return null;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Find a Host block whose HostName and User match (exact trim match).
|
|
65
|
+
* @param {string} content
|
|
66
|
+
* @param {string} hostname
|
|
67
|
+
* @param {string} user
|
|
68
|
+
* @returns {{ aliases: string[], body: string[] }|null}
|
|
69
|
+
*/
|
|
70
|
+
function findMatchingHostBlockForUserHost(content, hostname, user) {
|
|
71
|
+
for (const block of parseSshHostBlocks(content)) {
|
|
72
|
+
const hn = getHostBlockDirective(block.body, 'HostName');
|
|
73
|
+
const u = getHostBlockDirective(block.body, 'User');
|
|
74
|
+
if (hn === hostname && u === user) {
|
|
75
|
+
return block;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
return null;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* SSH Host alias to suggest for ssh(1) when a block already exists.
|
|
83
|
+
* @param {{ aliases: string[] }} matchedBlock
|
|
84
|
+
* @param {string} canonicalAlias - e.g. dev01.builder02.local
|
|
85
|
+
* @returns {string}
|
|
86
|
+
*/
|
|
87
|
+
function resolveConnectAlias(matchedBlock, canonicalAlias) {
|
|
88
|
+
if (matchedBlock.aliases.includes(canonicalAlias)) {
|
|
89
|
+
return canonicalAlias;
|
|
90
|
+
}
|
|
91
|
+
return matchedBlock.aliases[0] || canonicalAlias;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Replace an existing Host block for hostAlias, or append a new block at EOF.
|
|
96
|
+
* @param {string} content - Existing ssh config (may be empty)
|
|
97
|
+
* @param {string} hostAlias - Host keyword value (single alias)
|
|
98
|
+
* @param {string} hostname - HostName directive
|
|
99
|
+
* @param {string} user - User directive
|
|
100
|
+
* @returns {string} Updated config
|
|
101
|
+
*/
|
|
102
|
+
function upsertSshHostBlock(content, hostAlias, hostname, user) {
|
|
103
|
+
const lines = (content || '').split(/\r?\n/);
|
|
104
|
+
const out = [];
|
|
105
|
+
let i = 0;
|
|
106
|
+
while (i < lines.length) {
|
|
107
|
+
const line = lines[i];
|
|
108
|
+
const m = line.match(/^Host\s+(.+?)\s*$/i);
|
|
109
|
+
if (m) {
|
|
110
|
+
const aliases = m[1].trim().split(/\s+/).filter(Boolean);
|
|
111
|
+
if (aliases.includes(hostAlias)) {
|
|
112
|
+
i++;
|
|
113
|
+
while (i < lines.length && !/^Host\s/i.test(lines[i])) {
|
|
114
|
+
i++;
|
|
115
|
+
}
|
|
116
|
+
continue;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
out.push(line);
|
|
120
|
+
i++;
|
|
121
|
+
}
|
|
122
|
+
const block = [
|
|
123
|
+
`Host ${hostAlias}`,
|
|
124
|
+
` HostName ${hostname}`,
|
|
125
|
+
` User ${user}`,
|
|
126
|
+
' IdentitiesOnly yes'
|
|
127
|
+
].join('\n');
|
|
128
|
+
const trimmed = out.join('\n').replace(/\s+$/, '');
|
|
129
|
+
const prefix = trimmed.length ? `${trimmed}\n\n` : '';
|
|
130
|
+
return `${prefix}${block}\n`;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* @param {string} sshUser
|
|
135
|
+
* @param {string} sshHost
|
|
136
|
+
* @returns {{ error: string }|{ user: string, host: string, hostAlias: string }}
|
|
137
|
+
*/
|
|
138
|
+
function parseEnsureSshInputs(sshUser, sshHost) {
|
|
139
|
+
if (!sshUser || typeof sshUser !== 'string' || !sshUser.trim()) {
|
|
140
|
+
return { error: 'missing ssh user' };
|
|
141
|
+
}
|
|
142
|
+
if (!sshHost || typeof sshHost !== 'string' || !sshHost.trim()) {
|
|
143
|
+
return { error: 'missing ssh host' };
|
|
144
|
+
}
|
|
145
|
+
const user = sshUser.trim();
|
|
146
|
+
const host = sshHost.trim();
|
|
147
|
+
return { user, host, hostAlias: getDevSshHostAlias(user, host) };
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Ensure ~/.ssh/config contains a Host entry for interactive SSH / tooling.
|
|
152
|
+
* @param {string} sshUser - SSH user (sync-ssh-user)
|
|
153
|
+
* @param {string} sshHost - Real hostname (sync-ssh-host)
|
|
154
|
+
* @param {string} [sshDir] - SSH directory (default ~/.ssh)
|
|
155
|
+
* @returns {Promise<{ ok: boolean, configPath?: string, hostAlias?: string, error?: string, skippedDuplicate?: boolean }>}
|
|
156
|
+
*/
|
|
157
|
+
async function ensureDevSshConfigBlock(sshUser, sshHost, sshDir) {
|
|
158
|
+
const parsed = parseEnsureSshInputs(sshUser, sshHost);
|
|
159
|
+
if ('error' in parsed) {
|
|
160
|
+
return { ok: false, error: parsed.error };
|
|
161
|
+
}
|
|
162
|
+
const { user, host, hostAlias } = parsed;
|
|
163
|
+
const dir = ensureSshDir(sshDir);
|
|
164
|
+
const configPath = path.join(dir, 'config');
|
|
165
|
+
let existing = '';
|
|
166
|
+
try {
|
|
167
|
+
existing = await nodeFsLib.nodeFs().promises.readFile(configPath, 'utf8');
|
|
168
|
+
} catch (e) {
|
|
169
|
+
if (e.code !== 'ENOENT') {
|
|
170
|
+
return { ok: false, error: e.message || String(e) };
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
const matched = findMatchingHostBlockForUserHost(existing, host, user);
|
|
174
|
+
if (matched) {
|
|
175
|
+
return {
|
|
176
|
+
ok: true,
|
|
177
|
+
configPath,
|
|
178
|
+
hostAlias: resolveConnectAlias(matched, hostAlias),
|
|
179
|
+
skippedDuplicate: true
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
const next = upsertSshHostBlock(existing, hostAlias, host, user);
|
|
183
|
+
if (next === existing) {
|
|
184
|
+
return { ok: true, configPath, hostAlias };
|
|
185
|
+
}
|
|
186
|
+
await nodeFsLib.nodeFs().promises.writeFile(configPath, next, { mode: 0o600 });
|
|
187
|
+
return { ok: true, configPath, hostAlias };
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
module.exports = {
|
|
191
|
+
getDevSshHostAlias,
|
|
192
|
+
findMatchingHostBlockForUserHost,
|
|
193
|
+
resolveConnectAlias,
|
|
194
|
+
upsertSshHostBlock,
|
|
195
|
+
ensureDevSshConfigBlock
|
|
196
|
+
};
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Allowed dev user groups for Builder Server user APIs (must match server DTOs).
|
|
3
|
+
* @fileoverview
|
|
4
|
+
* @author AI Fabrix Team
|
|
5
|
+
* @version 2.0.0
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const ALLOWED_DEV_GROUPS = Object.freeze([
|
|
9
|
+
'admin',
|
|
10
|
+
'secret-manager',
|
|
11
|
+
'developer',
|
|
12
|
+
'docker'
|
|
13
|
+
]);
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Parse --groups CLI value into normalized lowercase tokens.
|
|
17
|
+
* Uses commas and/or whitespace so PowerShell (which often turns `a,b,c` into
|
|
18
|
+
* separate argv tokens later joined as `a b c`) still works.
|
|
19
|
+
* @param {string|string[]} raw - e.g. "admin, developer,docker" or ["admin","developer"]
|
|
20
|
+
* @returns {string[]}
|
|
21
|
+
*/
|
|
22
|
+
function parseDevGroupsOption(raw) {
|
|
23
|
+
if (raw === null || raw === undefined) {
|
|
24
|
+
return [];
|
|
25
|
+
}
|
|
26
|
+
if (Array.isArray(raw)) {
|
|
27
|
+
return raw
|
|
28
|
+
.map(s => String(s).trim().toLowerCase())
|
|
29
|
+
.filter(Boolean);
|
|
30
|
+
}
|
|
31
|
+
if (typeof raw !== 'string') {
|
|
32
|
+
return [];
|
|
33
|
+
}
|
|
34
|
+
return raw
|
|
35
|
+
.split(/[\s,]+/)
|
|
36
|
+
.map(s => s.trim().toLowerCase())
|
|
37
|
+
.filter(Boolean);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Ensure every group is allowed; throws with an actionable message if not.
|
|
42
|
+
* @param {string[]} groups - Normalized group names
|
|
43
|
+
* @throws {Error} When an unknown group is present
|
|
44
|
+
* @returns {string[]} Same array reference
|
|
45
|
+
*/
|
|
46
|
+
function validateDevGroups(groups) {
|
|
47
|
+
if (!Array.isArray(groups)) {
|
|
48
|
+
throw new Error('groups must be an array');
|
|
49
|
+
}
|
|
50
|
+
const invalid = groups.filter(g => !ALLOWED_DEV_GROUPS.includes(g));
|
|
51
|
+
if (invalid.length > 0) {
|
|
52
|
+
throw new Error(
|
|
53
|
+
`Invalid group(s): ${invalid.join(', ')}. Each value must be one of: ${ALLOWED_DEV_GROUPS.join(', ')}`
|
|
54
|
+
);
|
|
55
|
+
}
|
|
56
|
+
return groups;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* When the Builder Server rejects `groups` with an enum message that omits `docker`,
|
|
61
|
+
* but the CLI sent `docker`, append a short hint (the server must be upgraded).
|
|
62
|
+
* Mutates err.message when applicable.
|
|
63
|
+
* @param {Error} err - API error
|
|
64
|
+
* @param {string[]|undefined} requestedGroups - groups sent in the request body
|
|
65
|
+
* @returns {Error}
|
|
66
|
+
*/
|
|
67
|
+
function augmentDevUserGroupsServerError(err, requestedGroups) {
|
|
68
|
+
if (!err || typeof err.message !== 'string') {
|
|
69
|
+
return err;
|
|
70
|
+
}
|
|
71
|
+
if (!Array.isArray(requestedGroups) || !requestedGroups.includes('docker')) {
|
|
72
|
+
return err;
|
|
73
|
+
}
|
|
74
|
+
const msg = err.message;
|
|
75
|
+
const looksLikeGroupEnum =
|
|
76
|
+
/each value in groups must be one of/i.test(msg) ||
|
|
77
|
+
(/groups/i.test(msg) && /must be one of the following values/i.test(msg));
|
|
78
|
+
if (!looksLikeGroupEnum) {
|
|
79
|
+
return err;
|
|
80
|
+
}
|
|
81
|
+
if (msg.toLowerCase().includes('docker')) {
|
|
82
|
+
return err;
|
|
83
|
+
}
|
|
84
|
+
err.message = `${msg} The Builder Server that handled this request does not accept the \`docker\` group yet; update that service (user DTO enum), or omit docker from --groups until it is deployed.`;
|
|
85
|
+
return err;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
module.exports = {
|
|
89
|
+
ALLOWED_DEV_GROUPS,
|
|
90
|
+
parseDevGroupsOption,
|
|
91
|
+
validateDevGroups,
|
|
92
|
+
augmentDevUserGroupsServerError
|
|
93
|
+
};
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
const { formatSuccessLine } = require('./cli-test-layout-chalk');
|
|
1
2
|
/**
|
|
2
3
|
* Docker Build Utilities
|
|
3
4
|
*
|
|
@@ -11,6 +12,7 @@
|
|
|
11
12
|
|
|
12
13
|
const { spawn } = require('child_process');
|
|
13
14
|
const ora = require('ora');
|
|
15
|
+
const { getDockerNotRunningErrorMessage } = require('./docker-not-running-hint');
|
|
14
16
|
|
|
15
17
|
/**
|
|
16
18
|
* Checks if error indicates Docker is not running or not installed
|
|
@@ -86,7 +88,7 @@ function handleDockerClose(code, ctx) {
|
|
|
86
88
|
spinner.fail('Build failed');
|
|
87
89
|
const errorMessage = stderrBuffer || stdoutBuffer || 'Docker build failed';
|
|
88
90
|
if (isDockerNotAvailableError(errorMessage)) {
|
|
89
|
-
reject(new Error(
|
|
91
|
+
reject(new Error(getDockerNotRunningErrorMessage()));
|
|
90
92
|
} else {
|
|
91
93
|
const errorLines = errorMessage.split('\n').filter(line => line.trim());
|
|
92
94
|
reject(new Error(`Docker build failed: ${errorLines.slice(-5).join('\n')}`));
|
|
@@ -94,17 +96,35 @@ function handleDockerClose(code, ctx) {
|
|
|
94
96
|
}
|
|
95
97
|
}
|
|
96
98
|
|
|
97
|
-
function
|
|
98
|
-
const { imageName, tag, dockerfilePath, contextPath, spinner, resolve, reject, env = {}, buildArgs = {} } = buildOpts;
|
|
99
|
-
const spawnEnv = { ...process.env, ...env };
|
|
99
|
+
function buildDockerCliArgs(imageName, tag, dockerfilePath, contextPath, buildArgs, noCache) {
|
|
100
100
|
const args = ['build', '-t', `${imageName}:${tag}`, '-f', dockerfilePath];
|
|
101
|
-
// Pass NPM_TOKEN/PYPI_TOKEN etc. so private registry auth works during RUN npm install / pip install
|
|
102
101
|
for (const [key, value] of Object.entries(buildArgs)) {
|
|
103
102
|
if (value !== null && value !== undefined && String(value).length > 0) {
|
|
104
103
|
args.push('--build-arg', `${key}=${String(value)}`);
|
|
105
104
|
}
|
|
106
105
|
}
|
|
106
|
+
if (noCache) {
|
|
107
|
+
args.push('--no-cache');
|
|
108
|
+
}
|
|
107
109
|
args.push(contextPath);
|
|
110
|
+
return args;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
function runDockerBuildProcess(buildOpts) {
|
|
114
|
+
const {
|
|
115
|
+
imageName,
|
|
116
|
+
tag,
|
|
117
|
+
dockerfilePath,
|
|
118
|
+
contextPath,
|
|
119
|
+
spinner,
|
|
120
|
+
resolve,
|
|
121
|
+
reject,
|
|
122
|
+
env = {},
|
|
123
|
+
buildArgs = {},
|
|
124
|
+
noCache = false
|
|
125
|
+
} = buildOpts;
|
|
126
|
+
const spawnEnv = { ...process.env, ...env };
|
|
127
|
+
const args = buildDockerCliArgs(imageName, tag, dockerfilePath, contextPath, buildArgs, noCache);
|
|
108
128
|
const dockerProcess = spawn('docker', args, {
|
|
109
129
|
shell: process.platform === 'win32',
|
|
110
130
|
env: spawnEnv
|
|
@@ -135,7 +155,7 @@ function runDockerBuildProcess(buildOpts) {
|
|
|
135
155
|
spinner.fail('Build failed');
|
|
136
156
|
const msg = error.message || String(error);
|
|
137
157
|
if (isDockerNotAvailableError(msg)) {
|
|
138
|
-
reject(new Error(
|
|
158
|
+
reject(new Error(getDockerNotRunningErrorMessage()));
|
|
139
159
|
} else {
|
|
140
160
|
reject(new Error(`Docker build failed: ${msg}`));
|
|
141
161
|
}
|
|
@@ -149,10 +169,11 @@ function runDockerBuildProcess(buildOpts) {
|
|
|
149
169
|
* @param {string} contextPath - Build context path
|
|
150
170
|
* @param {string} tag - Image tag
|
|
151
171
|
* @param {Object} [buildArgs={}] - Optional build args (e.g. NPM_TOKEN, PYPI_TOKEN) for private registries
|
|
172
|
+
* @param {boolean} [noCache=false] - When true, pass `docker build --no-cache` (full rebuild)
|
|
152
173
|
* @returns {Promise<void>} Resolves when build completes
|
|
153
174
|
* @throws {Error} If build fails
|
|
154
175
|
*/
|
|
155
|
-
async function executeDockerBuild(imageName, dockerfilePath, contextPath, tag, buildArgs = {}) {
|
|
176
|
+
async function executeDockerBuild(imageName, dockerfilePath, contextPath, tag, buildArgs = {}, noCache = false) {
|
|
156
177
|
const spinner = ora({ text: 'Starting Docker build...', spinner: 'dots' }).start();
|
|
157
178
|
const fsSync = require('fs');
|
|
158
179
|
const path = require('path');
|
|
@@ -187,7 +208,8 @@ async function executeDockerBuild(imageName, dockerfilePath, contextPath, tag, b
|
|
|
187
208
|
resolve,
|
|
188
209
|
reject,
|
|
189
210
|
env: remoteEnv,
|
|
190
|
-
buildArgs: resolvedBuildArgs
|
|
211
|
+
buildArgs: resolvedBuildArgs,
|
|
212
|
+
noCache
|
|
191
213
|
});
|
|
192
214
|
});
|
|
193
215
|
}
|
|
@@ -205,16 +227,20 @@ async function executeDockerBuild(imageName, dockerfilePath, contextPath, tag, b
|
|
|
205
227
|
*/
|
|
206
228
|
async function executeBuild(imageName, dockerfilePath, contextPath, tag, options) {
|
|
207
229
|
const buildArgs = (options && options.buildArgs) || {};
|
|
208
|
-
|
|
230
|
+
// Commander maps `--no-cache` to `cache: false` (boolean negation), not `noCache: true`.
|
|
231
|
+
const noCache = Boolean(
|
|
232
|
+
options &&
|
|
233
|
+
(options.cache === false || options.noCache === true || options['no-cache'] === true)
|
|
234
|
+
);
|
|
235
|
+
await executeDockerBuild(imageName, dockerfilePath, contextPath, tag, buildArgs, noCache);
|
|
209
236
|
|
|
210
237
|
// Tag image if additional tag provided
|
|
211
238
|
if (options && options.tag && options.tag !== 'latest') {
|
|
212
239
|
const { promisify } = require('util');
|
|
213
240
|
const { exec } = require('child_process');
|
|
214
241
|
const run = promisify(exec);
|
|
215
|
-
const {
|
|
216
|
-
const
|
|
217
|
-
const env = { ...process.env, ...remoteEnv };
|
|
242
|
+
const { getDockerExecEnv } = require('./remote-docker-env');
|
|
243
|
+
const env = await getDockerExecEnv();
|
|
218
244
|
await run(`docker tag ${imageName}:${tag} ${imageName}:latest`, { env });
|
|
219
245
|
}
|
|
220
246
|
}
|
|
@@ -244,13 +270,12 @@ async function executeDockerBuildWithTag(effectiveImageName, imageName, dockerfi
|
|
|
244
270
|
const { promisify } = require('util');
|
|
245
271
|
const { exec } = require('child_process');
|
|
246
272
|
const run = promisify(exec);
|
|
247
|
-
const {
|
|
248
|
-
const
|
|
249
|
-
const env = { ...process.env, ...remoteEnv };
|
|
273
|
+
const { getDockerExecEnv } = require('./remote-docker-env');
|
|
274
|
+
const env = await getDockerExecEnv();
|
|
250
275
|
await run(`docker tag ${effectiveImageName}:${tag} ${imageName}:${tag}`, { env });
|
|
251
|
-
logger.log(
|
|
276
|
+
logger.log(formatSuccessLine(`Tagged image: ${imageName}:${tag}`));
|
|
252
277
|
} catch (err) {
|
|
253
|
-
logger.log(chalk.yellow(
|
|
278
|
+
logger.log(chalk.yellow(`⚠ Warning: Could not create compatibility tag ${imageName}:${tag} - ${err.message}`));
|
|
254
279
|
}
|
|
255
280
|
}
|
|
256
281
|
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Docker CLI exec with dev-config remote Docker (docker-endpoint + TLS) applied.
|
|
3
|
+
*
|
|
4
|
+
* @fileoverview Wraps child_process.exec with getDockerExecEnv for consistent remote daemon use
|
|
5
|
+
* @author AI Fabrix Team
|
|
6
|
+
* @version 2.0.0
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
'use strict';
|
|
10
|
+
|
|
11
|
+
const { promisify } = require('util');
|
|
12
|
+
const { exec } = require('child_process');
|
|
13
|
+
|
|
14
|
+
const execAsync = promisify(exec);
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Run a shell command with process.env merged with remote Docker client settings when docker-endpoint is set.
|
|
18
|
+
* @param {string} command
|
|
19
|
+
* @param {import('child_process').ExecOptions} [options]
|
|
20
|
+
* @returns {Promise<{ stdout: string, stderr: string }>}
|
|
21
|
+
*/
|
|
22
|
+
async function execWithDockerEnv(command, options = {}) {
|
|
23
|
+
const { getDockerExecEnv } = require('./remote-docker-env');
|
|
24
|
+
const env = { ...(await getDockerExecEnv()), ...(options.env || {}) };
|
|
25
|
+
return execAsync(command, { ...options, env });
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
module.exports = { execWithDockerEnv };
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Docker .env generation: manifest `port` is the host-published port; env-config `*_PORT` is
|
|
3
|
+
* often internal. After resolveKvReferences, ${*_PUBLIC_PORT} may already be interpolated from
|
|
4
|
+
* schema before manifest port is applied — align *_PUBLIC_PORT with application.yaml `port`.
|
|
5
|
+
*
|
|
6
|
+
* Matching rule (no app-specific keys in callers): find env-config `*_HOST` whose value equals
|
|
7
|
+
* `application.yaml` `app.key` (Docker Compose service name), then update the paired *_PUBLIC_PORT.
|
|
8
|
+
*
|
|
9
|
+
* @fileoverview Manifest published port for docker env interpolation
|
|
10
|
+
* @author AI Fabrix Team
|
|
11
|
+
* @version 1.0.0
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
'use strict';
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* @param {string} s
|
|
18
|
+
* @returns {string}
|
|
19
|
+
*/
|
|
20
|
+
function escapeRegExp(s) {
|
|
21
|
+
return String(s).replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* @param {Object} envVars
|
|
26
|
+
* @param {string} appKey - application.yaml app.key (trimmed)
|
|
27
|
+
* @returns {string|null} e.g. KEYCLOAK_HOST
|
|
28
|
+
*/
|
|
29
|
+
function findDockerHostKeyForAppKey(envVars, appKey) {
|
|
30
|
+
if (!envVars || !appKey) {
|
|
31
|
+
return null;
|
|
32
|
+
}
|
|
33
|
+
for (const [k, v] of Object.entries(envVars)) {
|
|
34
|
+
if (!/_HOST$/.test(k)) {
|
|
35
|
+
continue;
|
|
36
|
+
}
|
|
37
|
+
if (String(v) === appKey) {
|
|
38
|
+
return k;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
return null;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* @param {Object} envVars
|
|
46
|
+
* @param {string} appKey
|
|
47
|
+
* @returns {string|null} e.g. KEYCLOAK_PUBLIC_PORT
|
|
48
|
+
*/
|
|
49
|
+
function publicPortKeyForAppKey(envVars, appKey) {
|
|
50
|
+
const hostKey = findDockerHostKeyForAppKey(envVars, appKey);
|
|
51
|
+
if (!hostKey) {
|
|
52
|
+
return null;
|
|
53
|
+
}
|
|
54
|
+
return hostKey.replace(/_HOST$/, '_PUBLIC_PORT');
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Set *_PUBLIC_PORT from manifest `port` + developer id when docker service name matches app.key.
|
|
59
|
+
* @param {Object} envVars - Mutated map from buildEnvVarMap
|
|
60
|
+
* @param {Object|null|undefined} appDoc - application.yaml root
|
|
61
|
+
* @returns {Promise<void>}
|
|
62
|
+
*/
|
|
63
|
+
async function mergeDockerManifestPublishedPort(envVars, appDoc) {
|
|
64
|
+
if (!appDoc || !appDoc.app || typeof appDoc.app.key !== 'string') {
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
const appKey = appDoc.app.key.trim();
|
|
68
|
+
if (!appKey) {
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
const publicPortKey = publicPortKeyForAppKey(envVars, appKey);
|
|
72
|
+
if (!publicPortKey) {
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
const rawPub = appDoc.port;
|
|
76
|
+
const pubBase = rawPub !== undefined && rawPub !== null ? Number(rawPub) : NaN;
|
|
77
|
+
if (!Number.isFinite(pubBase)) {
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
const devIdNum = await require('./env-map').getDeveloperIdNumber(null);
|
|
81
|
+
envVars[publicPortKey] = String(devIdNum > 0 ? pubBase + devIdNum * 100 : pubBase);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Rewrite first line for the matched *_PUBLIC_PORT after interpolateEnvVars (early kv pass may have wrong value).
|
|
86
|
+
* @param {string} resolved
|
|
87
|
+
* @param {Object} envVars
|
|
88
|
+
* @param {Object|null|undefined} appDoc
|
|
89
|
+
* @returns {string}
|
|
90
|
+
*/
|
|
91
|
+
function rewriteDockerManifestPublicPortEnvLine(resolved, envVars, appDoc) {
|
|
92
|
+
if (!resolved || !appDoc || !appDoc.app || typeof appDoc.app.key !== 'string') {
|
|
93
|
+
return resolved;
|
|
94
|
+
}
|
|
95
|
+
const appKey = appDoc.app.key.trim();
|
|
96
|
+
if (!appKey) {
|
|
97
|
+
return resolved;
|
|
98
|
+
}
|
|
99
|
+
const publicPortKey = publicPortKeyForAppKey(envVars, appKey);
|
|
100
|
+
if (!publicPortKey) {
|
|
101
|
+
return resolved;
|
|
102
|
+
}
|
|
103
|
+
const publicPort = envVars[publicPortKey];
|
|
104
|
+
if (publicPort === undefined || publicPort === null || publicPort === '') {
|
|
105
|
+
return resolved;
|
|
106
|
+
}
|
|
107
|
+
const re = new RegExp(`^${escapeRegExp(publicPortKey)}\\s*=\\s*.*$`, 'm');
|
|
108
|
+
return resolved.replace(re, `${publicPortKey}=${publicPort}`);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
module.exports = {
|
|
112
|
+
findDockerHostKeyForAppKey,
|
|
113
|
+
publicPortKeyForAppKey,
|
|
114
|
+
mergeDockerManifestPublishedPort,
|
|
115
|
+
rewriteDockerManifestPublicPortEnvLine
|
|
116
|
+
};
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Platform-specific hints when the Docker CLI cannot reach the daemon.
|
|
3
|
+
* @author AI Fabrix Team
|
|
4
|
+
* @version 2.0.0
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
'use strict';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Short sentence: how to get a working Docker daemon for this OS (no trailing period in fragment use).
|
|
11
|
+
* @returns {string}
|
|
12
|
+
*/
|
|
13
|
+
function getDockerDaemonStartHintSentence() {
|
|
14
|
+
if (process.platform === 'linux') {
|
|
15
|
+
return (
|
|
16
|
+
'Start the Docker daemon (e.g. sudo systemctl start docker), install Docker Engine if needed, ' +
|
|
17
|
+
'and ensure your user can use the socket (e.g. sudo usermod -aG docker $USER, then log out and back in). ' +
|
|
18
|
+
'On Windows or macOS, start Docker Desktop.'
|
|
19
|
+
);
|
|
20
|
+
}
|
|
21
|
+
if (process.platform === 'darwin') {
|
|
22
|
+
return 'Start Docker Desktop (or Colima / another runtime) and try again.';
|
|
23
|
+
}
|
|
24
|
+
return 'Start Docker Desktop and try again.';
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Single-line error for thrown Error.message (build spawn failures, etc.).
|
|
29
|
+
* @returns {string}
|
|
30
|
+
*/
|
|
31
|
+
function getDockerNotRunningErrorMessage() {
|
|
32
|
+
return `Docker is not running or not installed. ${getDockerDaemonStartHintSentence()}`;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* When the user cannot use the local unix socket (no docker group) but Docker runs on the same or another host
|
|
37
|
+
* exposing the Engine API — the CLI talks to that API via DOCKER_HOST (docker-endpoint in ~/.aifabrix/config.yaml).
|
|
38
|
+
* @returns {string[]}
|
|
39
|
+
*/
|
|
40
|
+
function getDockerApiOverTcpHintLines() {
|
|
41
|
+
return [
|
|
42
|
+
' Without unix socket access: set docker-endpoint to the Docker Engine API (e.g. tcp://builder02.local:2376). Builder settings often merge this after aifabrix dev init or dev refresh.',
|
|
43
|
+
' The CLI still runs docker/docker compose locally as a thin client to that API (not the Builder HTTP API). TLS: cert.pem, key.pem, ca.pem in ~/.aifabrix/certs/<developer-id>/ (match daemon client-auth).',
|
|
44
|
+
' Run: aifabrix dev show'
|
|
45
|
+
];
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
module.exports = {
|
|
49
|
+
getDockerDaemonStartHintSentence,
|
|
50
|
+
getDockerNotRunningErrorMessage,
|
|
51
|
+
getDockerApiOverTcpHintLines
|
|
52
|
+
};
|