@aifabrix/builder 2.42.1 → 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 +2 -2
- 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 +157 -0
- package/integration/{hubspot → hubspot-test}/application.json +6 -6
- package/integration/{hubspot → hubspot-test}/create-hubspot.js +10 -10
- package/integration/hubspot-test/env.template +4 -0
- package/integration/hubspot-test/hubspot-test-datasource-company.json +138 -0
- package/integration/hubspot-test/hubspot-test-datasource-contact.json +146 -0
- package/integration/hubspot-test/hubspot-test-datasource-deal.json +146 -0
- package/integration/hubspot-test/hubspot-test-datasource-users.json +76 -0
- package/integration/{hubspot/hubspot-deploy.json → hubspot-test/hubspot-test-deploy.json} +201 -24
- package/integration/{hubspot/hubspot-system.json → hubspot-test/hubspot-test-system.json} +8 -7
- package/integration/hubspot-test/rbac.json +166 -0
- package/integration/{hubspot → hubspot-test}/test-artifacts/wizard-hubspot-credential-real.yaml +3 -3
- package/integration/{hubspot → hubspot-test}/test-artifacts/wizard-hubspot-env-vars.yaml +2 -2
- package/integration/{hubspot → hubspot-test}/test-artifacts/wizard-invalid-add-datasource.yaml +1 -1
- package/integration/{hubspot → hubspot-test}/test-artifacts/wizard-invalid-credential-create.yaml +1 -1
- package/integration/{hubspot → hubspot-test}/test-artifacts/wizard-invalid-credential-select.yaml +1 -1
- package/integration/{hubspot → hubspot-test}/test-artifacts/wizard-invalid-known-platform.yaml +1 -1
- package/integration/hubspot-test/test-artifacts/wizard-invalid-missing-source.yaml +2 -0
- package/integration/{hubspot → hubspot-test}/test-artifacts/wizard-invalid-mode.yaml +1 -1
- package/integration/{hubspot → hubspot-test}/test-artifacts/wizard-invalid-openapi-file.yaml +1 -1
- package/integration/{hubspot → hubspot-test}/test-artifacts/wizard-invalid-openapi-url.yaml +1 -1
- package/integration/{hubspot → hubspot-test}/test-artifacts/wizard-invalid-source.yaml +1 -1
- package/integration/{hubspot → hubspot-test}/test-artifacts/wizard-valid-for-dimension-array-test.yaml +1 -1
- package/integration/hubspot-test/test-artifacts/wizard-valid-for-dimension-key-test.yaml +5 -0
- package/integration/hubspot-test/test-artifacts/wizard-valid-for-dimension-path-test.yaml +5 -0
- package/integration/{hubspot → hubspot-test}/test-artifacts/wizard-valid-for-dimension-test.yaml +1 -1
- package/integration/{hubspot → hubspot-test}/test-artifacts/wizard-valid-for-rbac-test.yaml +1 -1
- package/integration/{hubspot → hubspot-test}/test-artifacts/wizard-valid-for-rbac-yaml-test.yaml +1 -1
- package/integration/{hubspot → hubspot-test}/test-dataplane-down-tests.js +1 -7
- package/integration/{hubspot → hubspot-test}/test-dataplane-down.js +3 -3
- package/integration/{hubspot → hubspot-test}/test.js +137 -102
- package/integration/{hubspot → hubspot-test}/wizard-hubspot-e2e.yaml +2 -2
- package/integration/{hubspot → hubspot-test}/wizard-hubspot-platform.yaml +1 -1
- 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/service-users.api.js +111 -2
- package/lib/api/types/dev.types.js +4 -3
- package/lib/api/types/pipeline.types.js +8 -5
- package/lib/api/types/service-users.types.js +41 -0
- 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 +19 -8
- package/lib/app/rotate-secret.js +17 -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 +59 -123
- package/lib/cli/setup-app.test-commands.js +179 -0
- package/lib/cli/setup-auth.js +36 -14
- 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 +190 -103
- package/lib/cli/setup-environment.js +11 -20
- package/lib/cli/setup-external-system.js +62 -22
- package/lib/cli/setup-infra.js +139 -47
- package/lib/cli/setup-parameters.js +32 -0
- package/lib/cli/setup-secrets.js +147 -10
- package/lib/cli/setup-service-user.js +146 -20
- package/lib/cli/setup-utility.js +47 -19
- 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 +10 -14
- 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 +123 -71
- 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 +16 -10
- package/lib/commands/repair-rbac.js +25 -19
- package/lib/commands/repair.js +116 -32
- package/lib/commands/secrets-list.js +23 -12
- package/lib/commands/secrets-remove-all.js +220 -0
- package/lib/commands/secrets-remove.js +22 -13
- package/lib/commands/secrets-set.js +21 -12
- package/lib/commands/secrets-validate.js +20 -7
- package/lib/commands/secure.js +10 -9
- package/lib/commands/service-user.js +243 -13
- package/lib/commands/test-e2e-external.js +27 -1
- package/lib/commands/up-common.js +28 -2
- package/lib/commands/up-dataplane.js +31 -18
- 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 +16 -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 +59 -58
- package/lib/core/diff.js +3 -2
- package/lib/core/ensure-encryption-key.js +2 -4
- 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 +228 -42
- package/lib/core/templates-env.js +4 -3
- package/lib/core/templates.js +1 -1
- package/lib/datasource/abac-validator.js +148 -0
- package/lib/datasource/deploy.js +75 -53
- package/lib/datasource/field-reference-validator.js +77 -36
- package/lib/datasource/integration-context.js +63 -0
- package/lib/datasource/list.js +8 -7
- package/lib/datasource/log-viewer.js +252 -0
- package/lib/datasource/resolve-app.js +109 -0
- package/lib/datasource/test-e2e.js +95 -155
- package/lib/datasource/test-integration.js +121 -109
- 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 +162 -15
- 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 +104 -14
- 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-controller-manifest.js +3 -3
- package/lib/generator/external.js +23 -11
- package/lib/generator/helpers.js +71 -12
- package/lib/generator/index.js +8 -4
- package/lib/generator/split-readme.js +12 -7
- package/lib/generator/split-variables.js +2 -1
- package/lib/generator/split.js +46 -11
- package/lib/generator/wizard-readme.js +3 -3
- package/lib/generator/wizard.js +16 -13
- package/lib/infrastructure/compose.js +60 -6
- package/lib/infrastructure/helpers.js +238 -51
- package/lib/infrastructure/index.js +64 -37
- 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 +1201 -433
- 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-config-resolver.js +23 -1
- 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 +209 -6
- 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/credential-secrets-env.js +16 -1
- 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 +42 -11
- package/lib/utils/env-template.js +2 -2
- package/lib/utils/environment-scoped-resources.js +144 -0
- package/lib/utils/error-formatter.js +125 -9
- 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-env-template.js +180 -0
- package/lib/utils/external-readme.js +8 -1
- package/lib/utils/external-system-display.js +277 -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 +32 -14
- package/lib/utils/health-check-url.js +119 -0
- package/lib/utils/health-check.js +59 -25
- package/lib/utils/help-builder.js +14 -13
- 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 +29 -7
- package/lib/utils/paths.js +136 -48
- 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 +171 -51
- package/lib/utils/secrets-helpers.js +70 -59
- package/lib/utils/secrets-kv-scope.js +60 -0
- package/lib/utils/secrets-utils.js +35 -37
- package/lib/utils/secrets-validation.js +3 -1
- package/lib/utils/secrets-yaml-preserve.js +109 -0
- package/lib/utils/secure-file-permissions.js +91 -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 +37 -5
- 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 +78 -1
- package/lib/validation/datasource-warnings.js +56 -0
- package/lib/validation/env-template-auth.js +50 -2
- package/lib/validation/external-manifest-validator.js +35 -7
- package/lib/validation/validate-display.js +37 -31
- package/lib/validation/validate.js +9 -10
- package/lib/validation/validator-unresolved-placeholders.js +98 -0
- package/lib/validation/validator.js +32 -78
- package/lib/validation/wizard-config-validator.js +2 -1
- package/package.json +11 -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 +80 -18
- 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 +55 -14
- 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/env.template.hbs +22 -0
- 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/integration/hubspot/README.md +0 -102
- package/integration/hubspot/env.template +0 -4
- package/integration/hubspot/hubspot-datasource-company.json +0 -541
- package/integration/hubspot/hubspot-datasource-contact.json +0 -639
- package/integration/hubspot/hubspot-datasource-deal.json +0 -588
- package/integration/hubspot/hubspot-datasource-users.json +0 -116
- package/integration/hubspot/test-artifacts/wizard-invalid-missing-source.yaml +0 -2
- package/integration/hubspot/test-artifacts/wizard-valid-for-dimension-key-test.yaml +0 -5
- package/integration/hubspot/test-artifacts/wizard-valid-for-dimension-path-test.yaml +0 -5
- package/lib/api/external-test.api.js +0 -111
- package/lib/schema/env-config.yaml +0 -43
- /package/integration/{hubspot → hubspot-test}/companies.json +0 -0
- /package/integration/{hubspot → hubspot-test}/test-artifacts/wizard-invalid-app-name.yaml +0 -0
- /package/integration/{hubspot → hubspot-test}/test-artifacts/wizard-invalid-missing-app.yaml +0 -0
- /package/integration/{hubspot → hubspot-test}/test-dataplane-down-helpers.js +0 -0
package/lib/app/index.js
CHANGED
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
12
|
const fs = require('fs').promises;
|
|
13
|
-
const
|
|
13
|
+
const envReaderModule = require('../core/env-reader');
|
|
14
14
|
const build = require('../build');
|
|
15
15
|
const appRun = require('./run');
|
|
16
16
|
const { promptForOptions } = require('./prompts');
|
|
@@ -34,6 +34,29 @@ const {
|
|
|
34
34
|
const path = require('path');
|
|
35
35
|
const secretsEnsure = require('../core/secrets-ensure');
|
|
36
36
|
|
|
37
|
+
/**
|
|
38
|
+
* Ensures secrets from env.template. On EACCES (e.g. secrets path under a read-only tree), uses getPrimaryUserSecretsLocalPath().
|
|
39
|
+
* @param {string} envTemplatePath - Path to env.template
|
|
40
|
+
* @returns {Promise<void>}
|
|
41
|
+
*/
|
|
42
|
+
async function ensureSecretsFromNewAppEnvTemplate(envTemplatePath) {
|
|
43
|
+
try {
|
|
44
|
+
await secretsEnsure.ensureSecretsFromEnvTemplate(envTemplatePath, {});
|
|
45
|
+
} catch (err) {
|
|
46
|
+
if (err.code === 'ENOENT') {
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
if (err.code === 'EACCES' || (err.message && String(err.message).includes('EACCES'))) {
|
|
50
|
+
const { getPrimaryUserSecretsLocalPath } = require('../utils/paths');
|
|
51
|
+
await secretsEnsure.ensureSecretsFromEnvTemplate(envTemplatePath, {
|
|
52
|
+
preferredFilePath: getPrimaryUserSecretsLocalPath()
|
|
53
|
+
});
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
throw err;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
37
60
|
/**
|
|
38
61
|
* Creates new application with scaffolded configuration files
|
|
39
62
|
* Prompts for configuration options and generates builder/ folder structure
|
|
@@ -125,19 +148,15 @@ async function generateApplicationFiles(finalAppPath, appName, config, options)
|
|
|
125
148
|
await fs.mkdir(finalAppPath, { recursive: true });
|
|
126
149
|
await processTemplateFiles(options.template, finalAppPath, appName, options, config);
|
|
127
150
|
|
|
128
|
-
const existingEnv = await readExistingEnv(
|
|
151
|
+
const existingEnv = await envReaderModule.readExistingEnv(finalAppPath);
|
|
129
152
|
const envConversionMessage = existingEnv
|
|
130
|
-
? '\n
|
|
153
|
+
? '\n✔ Found existing .env file - sensitive values will be converted to kv:// references'
|
|
131
154
|
: '';
|
|
132
155
|
|
|
133
156
|
await generateConfigFiles(finalAppPath, appName, config, existingEnv);
|
|
134
157
|
|
|
135
158
|
const envTemplatePath = path.join(finalAppPath, 'env.template');
|
|
136
|
-
|
|
137
|
-
await secretsEnsure.ensureSecretsFromEnvTemplate(envTemplatePath, {});
|
|
138
|
-
} catch (err) {
|
|
139
|
-
if (err.code !== 'ENOENT') throw err;
|
|
140
|
-
}
|
|
159
|
+
await ensureSecretsFromNewAppEnvTemplate(envTemplatePath);
|
|
141
160
|
|
|
142
161
|
// Generate external system files if type is external
|
|
143
162
|
if (config.type === 'external') {
|
package/lib/app/list.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
const { formatBlockingError } = require('../utils/cli-test-layout-chalk');
|
|
1
2
|
/**
|
|
2
3
|
* AI Fabrix Builder - App List Command
|
|
3
4
|
*
|
|
@@ -85,7 +86,7 @@ function extractApplications(response) {
|
|
|
85
86
|
extractWrappedPaginatedItems(apiResponse);
|
|
86
87
|
|
|
87
88
|
if (!applications) {
|
|
88
|
-
logger.error(
|
|
89
|
+
logger.error(formatBlockingError('Invalid response: expected data array or items array'));
|
|
89
90
|
logger.error(chalk.gray('\nAPI response type:'), typeof apiResponse);
|
|
90
91
|
logger.error(chalk.gray('API response:'), JSON.stringify(apiResponse, null, 2));
|
|
91
92
|
logger.error(chalk.gray('\nFull response for debugging:'));
|
|
@@ -166,11 +167,11 @@ function displayApplications(applications, environment, controllerUrl) {
|
|
|
166
167
|
applications.forEach((app) => {
|
|
167
168
|
const isExternal = app.configuration?.type === 'external';
|
|
168
169
|
const externalIcon = isExternal ? '🔗 ' : '';
|
|
169
|
-
const hasPipeline = app.configuration?.pipeline?.isActive ? '
|
|
170
|
+
const hasPipeline = app.configuration?.pipeline?.isActive ? '✔' : '✖';
|
|
170
171
|
const urlAndPort = formatUrlAndPort(app);
|
|
171
172
|
logger.log(`${externalIcon}${hasPipeline} ${chalk.cyan(app.key)} - ${app.displayName} (${app.status || 'unknown'})${urlAndPort}`);
|
|
172
173
|
});
|
|
173
|
-
logger.log(chalk.gray(' To show details for an app: aifabrix app show <
|
|
174
|
+
logger.log(chalk.gray(' To show details for an app: aifabrix app show <app>\n'));
|
|
174
175
|
}
|
|
175
176
|
|
|
176
177
|
/**
|
|
@@ -246,7 +247,7 @@ async function getListAuthToken(controllerUrl, config) {
|
|
|
246
247
|
const authResult = await tryGetTokenFromController(controllerUrl);
|
|
247
248
|
if (!authResult || !authResult.token) {
|
|
248
249
|
// No token found for explicitly provided controller URL
|
|
249
|
-
logger.error(
|
|
250
|
+
logger.error(formatBlockingError(`No authentication token found for controller: ${controllerUrl}`));
|
|
250
251
|
logger.error(chalk.gray('Please login to this controller using: aifabrix login'));
|
|
251
252
|
process.exit(1);
|
|
252
253
|
// Return to prevent further execution in tests where process.exit is mocked
|
|
@@ -300,7 +301,7 @@ async function listApplications(options = {}) {
|
|
|
300
301
|
|
|
301
302
|
const controllerUrl = options.controller || (await resolveControllerUrl());
|
|
302
303
|
if (!controllerUrl) {
|
|
303
|
-
logger.error(
|
|
304
|
+
logger.error(formatBlockingError('Controller URL is required. Run "aifabrix login" to set the controller URL in config.yaml'));
|
|
304
305
|
process.exit(1);
|
|
305
306
|
return;
|
|
306
307
|
}
|
|
@@ -321,7 +322,7 @@ async function listApplications(options = {}) {
|
|
|
321
322
|
const applications = handleListResponse(response, actualControllerUrl);
|
|
322
323
|
displayApplications(applications, environment, actualControllerUrl);
|
|
323
324
|
} catch (error) {
|
|
324
|
-
logger.error(
|
|
325
|
+
logger.error(formatBlockingError(`Failed to list applications from controller: ${actualControllerUrl}`));
|
|
325
326
|
logger.error(chalk.gray(`Error: ${error.message}`));
|
|
326
327
|
process.exit(1);
|
|
327
328
|
}
|
package/lib/app/push.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
const { formatSuccessLine, formatSuccessParagraph } = require('../utils/cli-test-layout-chalk');
|
|
1
2
|
/**
|
|
2
3
|
* Application Push Utilities
|
|
3
4
|
*
|
|
@@ -143,7 +144,7 @@ async function validatePushConfig(registry, imageName, appName) {
|
|
|
143
144
|
*/
|
|
144
145
|
async function authenticateWithRegistry(registry) {
|
|
145
146
|
if (await pushUtils.checkACRAuthentication(registry)) {
|
|
146
|
-
logger.log(
|
|
147
|
+
logger.log(formatSuccessLine(`Already authenticated with ${registry}`));
|
|
147
148
|
} else {
|
|
148
149
|
await pushUtils.authenticateACR(registry);
|
|
149
150
|
}
|
|
@@ -189,7 +190,7 @@ async function pushImageTags(imageName, registry, tags) {
|
|
|
189
190
|
* @param {Array<string>} tags - Image tags
|
|
190
191
|
*/
|
|
191
192
|
function displayPushResults(registry, imageName, tags) {
|
|
192
|
-
logger.log(
|
|
193
|
+
logger.log(formatSuccessParagraph(`Successfully pushed ${tags.length} tag(s) to ${registry}`));
|
|
193
194
|
logger.log(chalk.gray(`Image: ${registry}/${imageName}:*`));
|
|
194
195
|
logger.log(chalk.gray(`Tags: ${tags.join(', ')}`));
|
|
195
196
|
}
|
|
@@ -208,7 +209,7 @@ async function pushApp(appName, options = {}) {
|
|
|
208
209
|
try {
|
|
209
210
|
const { isExternal } = await detectAppType(appName);
|
|
210
211
|
if (isExternal) {
|
|
211
|
-
logger.log(chalk.yellow('
|
|
212
|
+
logger.log(chalk.yellow('⚠ External systems don\'t require Docker images. Skipping push...'));
|
|
212
213
|
return;
|
|
213
214
|
}
|
|
214
215
|
} catch (error) {
|
package/lib/app/register.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
const { formatSuccessLine, formatSuccessParagraph } = require('../utils/cli-test-layout-chalk');
|
|
1
2
|
/**
|
|
2
3
|
* AI Fabrix Builder - App Register Command
|
|
3
4
|
*
|
|
@@ -22,6 +23,8 @@ const {
|
|
|
22
23
|
const { checkAuthentication } = require('../utils/app-register-auth');
|
|
23
24
|
const { callRegisterApi } = require('../utils/app-register-api');
|
|
24
25
|
const { displayRegistrationResults, getEnvironmentPrefix } = require('../utils/app-register-display');
|
|
26
|
+
const pathsUtil = require('../utils/paths');
|
|
27
|
+
const { refreshUrlsLocalRegistryFromBuilder } = require('../utils/urls-local-registry');
|
|
25
28
|
|
|
26
29
|
/**
|
|
27
30
|
* Build registration data payload from app configuration
|
|
@@ -62,8 +65,8 @@ function buildRegistrationData(appConfig, options) {
|
|
|
62
65
|
registrationData.image = imageValue;
|
|
63
66
|
}
|
|
64
67
|
|
|
65
|
-
// URL:
|
|
66
|
-
const portForUrl = appConfig.
|
|
68
|
+
// URL: default uses manifest listen port; host mapping uses url:// in env.template at resolve time
|
|
69
|
+
const portForUrl = appConfig.port;
|
|
67
70
|
if (portForUrl) {
|
|
68
71
|
registrationData.url = options.url || appConfig.url || `http://localhost:${portForUrl}`;
|
|
69
72
|
}
|
|
@@ -97,15 +100,15 @@ async function saveLocalCredentials(responseData, apiUrl) {
|
|
|
97
100
|
// Regenerate .env file with updated credentials
|
|
98
101
|
try {
|
|
99
102
|
await generateEnvFile(registeredAppKey, null, 'local');
|
|
100
|
-
logger.log(
|
|
103
|
+
logger.log(formatSuccessLine('.env file updated with new credentials'));
|
|
101
104
|
} catch (error) {
|
|
102
|
-
logger.warn(chalk.yellow(
|
|
105
|
+
logger.warn(chalk.yellow(`⚠ Could not regenerate .env file: ${error.message}`));
|
|
103
106
|
}
|
|
104
107
|
|
|
105
|
-
logger.log(
|
|
106
|
-
logger.log(
|
|
108
|
+
logger.log(formatSuccessParagraph('Credentials saved to ~/.aifabrix/secrets.local.yaml'));
|
|
109
|
+
logger.log(formatSuccessLine('env.template updated with MISO_CLIENTID, MISO_CLIENTSECRET, and MISO_CONTROLLER_URL\n'));
|
|
107
110
|
} catch (error) {
|
|
108
|
-
logger.warn(chalk.yellow(
|
|
111
|
+
logger.warn(chalk.yellow(`⚠ Could not save credentials locally: ${error.message}`));
|
|
109
112
|
}
|
|
110
113
|
}
|
|
111
114
|
|
|
@@ -150,7 +153,9 @@ async function registerApplication(appKey, options = {}) {
|
|
|
150
153
|
logger.log(chalk.blue('📋 Registering application...\n'));
|
|
151
154
|
|
|
152
155
|
const { resolveControllerUrl } = require('../utils/controller-url');
|
|
153
|
-
const
|
|
156
|
+
const config = require('../core/config');
|
|
157
|
+
await config.ensureSecretsEncryptionKey();
|
|
158
|
+
const { resolveEnvironment } = config;
|
|
154
159
|
|
|
155
160
|
// Load application config
|
|
156
161
|
const { variables, created } = await loadVariablesYaml(appKey);
|
|
@@ -177,6 +182,12 @@ async function registerApplication(appKey, options = {}) {
|
|
|
177
182
|
registrationData
|
|
178
183
|
);
|
|
179
184
|
|
|
185
|
+
try {
|
|
186
|
+
refreshUrlsLocalRegistryFromBuilder(pathsUtil.getProjectRoot());
|
|
187
|
+
} catch (error) {
|
|
188
|
+
logger.warn(chalk.yellow(`⚠ Could not refresh URLs registry: ${error.message}`));
|
|
189
|
+
}
|
|
190
|
+
|
|
180
191
|
// Save credentials and display results (pass display name we sent so output shows it when API returns key as displayName)
|
|
181
192
|
await saveLocalCredentials(responseData, authConfig.apiUrl);
|
|
182
193
|
displayRegistrationResults(responseData, authConfig.apiUrl, environment, registrationData.displayName);
|
package/lib/app/rotate-secret.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
const { formatBlockingError, formatSuccessLine, formatSuccessParagraph } = require('../utils/cli-test-layout-chalk');
|
|
1
2
|
/**
|
|
2
3
|
* AI Fabrix Builder - App Rotate Secret Command
|
|
3
4
|
*
|
|
@@ -70,7 +71,7 @@ function validateEnvironment(environment) {
|
|
|
70
71
|
async function resolveControllerAndEnvironment() {
|
|
71
72
|
const controllerUrl = await resolveControllerUrl();
|
|
72
73
|
if (!controllerUrl) {
|
|
73
|
-
logger.error(
|
|
74
|
+
logger.error(formatBlockingError('Controller URL is required. Run "aifabrix login" to set the controller URL in config.yaml'));
|
|
74
75
|
process.exit(1);
|
|
75
76
|
}
|
|
76
77
|
const environment = await resolveEnvironment();
|
|
@@ -135,7 +136,7 @@ function extractCredentials(response) {
|
|
|
135
136
|
*/
|
|
136
137
|
function validateResponse(response) {
|
|
137
138
|
if (!response.data || typeof response.data !== 'object') {
|
|
138
|
-
logger.error(
|
|
139
|
+
logger.error(formatBlockingError('Invalid response: missing data'));
|
|
139
140
|
logger.error(chalk.gray('\nAPI response type:'), typeof response.data);
|
|
140
141
|
logger.error(chalk.gray('API response:'), JSON.stringify(response.data, null, 2));
|
|
141
142
|
logger.error(chalk.gray('\nFull response for debugging:'));
|
|
@@ -146,7 +147,7 @@ function validateResponse(response) {
|
|
|
146
147
|
const result = extractCredentials(response);
|
|
147
148
|
|
|
148
149
|
if (!result) {
|
|
149
|
-
logger.error(
|
|
150
|
+
logger.error(formatBlockingError('Invalid response: missing or invalid credentials'));
|
|
150
151
|
logger.error(chalk.gray('\nAPI response type:'), typeof response.data);
|
|
151
152
|
logger.error(chalk.gray('API response:'), JSON.stringify(response.data, null, 2));
|
|
152
153
|
logger.error(chalk.gray('\nFull response for debugging:'));
|
|
@@ -166,7 +167,7 @@ function validateResponse(response) {
|
|
|
166
167
|
* @param {string} [message] - Optional message from API
|
|
167
168
|
*/
|
|
168
169
|
function displayRotationResults(appKey, environment, credentials, apiUrl, message) {
|
|
169
|
-
logger.log(
|
|
170
|
+
logger.log(formatSuccessLine('Secret rotated successfully!\n'));
|
|
170
171
|
logger.log(chalk.bold('📋 Application Details:'));
|
|
171
172
|
logger.log(` Key: ${appKey}`);
|
|
172
173
|
logger.log(` Environment: ${environment}`);
|
|
@@ -206,7 +207,7 @@ async function getTokenFromUrl(controllerUrl) {
|
|
|
206
207
|
};
|
|
207
208
|
}
|
|
208
209
|
} catch (error) {
|
|
209
|
-
logger.error(
|
|
210
|
+
logger.error(formatBlockingError(`Failed to authenticate with controller: ${controllerUrl}`));
|
|
210
211
|
logger.error(chalk.gray(`Error: ${error.message}`));
|
|
211
212
|
process.exit(1);
|
|
212
213
|
}
|
|
@@ -287,18 +288,18 @@ async function saveCredentialsLocally(appKey, credentials, actualControllerUrl)
|
|
|
287
288
|
// Regenerate .env file with updated credentials
|
|
288
289
|
try {
|
|
289
290
|
await generateEnvFile(appKey, null, 'local');
|
|
290
|
-
logger.log(
|
|
291
|
+
logger.log(formatSuccessLine('.env file updated with new credentials'));
|
|
291
292
|
} catch (error) {
|
|
292
|
-
logger.warn(chalk.yellow(
|
|
293
|
+
logger.warn(chalk.yellow(`⚠ Could not regenerate .env file: ${error.message}`));
|
|
293
294
|
}
|
|
294
295
|
|
|
295
|
-
logger.log(
|
|
296
|
-
logger.log(
|
|
296
|
+
logger.log(formatSuccessParagraph('Credentials saved to ~/.aifabrix/secrets.local.yaml'));
|
|
297
|
+
logger.log(formatSuccessLine('env.template updated with MISO_CLIENTID, MISO_CLIENTSECRET, and MISO_CONTROLLER_URL\n'));
|
|
297
298
|
} else {
|
|
298
|
-
logger.log(
|
|
299
|
+
logger.log(formatSuccessParagraph('Credentials saved to ~/.aifabrix/secrets.local.yaml\n'));
|
|
299
300
|
}
|
|
300
301
|
} catch (error) {
|
|
301
|
-
logger.warn(chalk.yellow(
|
|
302
|
+
logger.warn(chalk.yellow(`⚠ Could not save credentials locally: ${error.message}`));
|
|
302
303
|
}
|
|
303
304
|
}
|
|
304
305
|
|
|
@@ -337,7 +338,10 @@ async function executeRotation(appKey, actualControllerUrl, environment, token)
|
|
|
337
338
|
* @throws {Error} If rotation fails
|
|
338
339
|
*/
|
|
339
340
|
async function rotateSecret(appKey, _options = {}) {
|
|
340
|
-
logger.log(chalk.yellow('
|
|
341
|
+
logger.log(chalk.yellow('⚠ This will invalidate the old ClientSecret!\n'));
|
|
342
|
+
|
|
343
|
+
const { ensureSecretsEncryptionKey } = require('../core/config');
|
|
344
|
+
await ensureSecretsEncryptionKey();
|
|
341
345
|
|
|
342
346
|
const { controllerUrl, environment } = await resolveControllerAndEnvironment();
|
|
343
347
|
const config = await getConfig();
|
|
@@ -346,7 +350,7 @@ async function rotateSecret(appKey, _options = {}) {
|
|
|
346
350
|
try {
|
|
347
351
|
await executeRotation(appKey, actualControllerUrl, environment, token);
|
|
348
352
|
} catch (error) {
|
|
349
|
-
logger.error(
|
|
353
|
+
logger.error(formatBlockingError(`Failed to rotate secret via controller: ${actualControllerUrl}`));
|
|
350
354
|
logger.error(chalk.gray(`Error: ${error.message}`));
|
|
351
355
|
process.exit(1);
|
|
352
356
|
}
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Docker Compose up and docker-run fallback for `aifabrix run`.
|
|
3
|
+
*
|
|
4
|
+
* @fileoverview Extracted from run-helpers to keep file size within limits
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
'use strict';
|
|
8
|
+
const { formatSuccessLine } = require('../utils/cli-test-layout-chalk');
|
|
9
|
+
|
|
10
|
+
const fs = require('fs').promises;
|
|
11
|
+
const chalk = require('chalk');
|
|
12
|
+
const { exec } = require('child_process');
|
|
13
|
+
const { promisify } = require('util');
|
|
14
|
+
const logger = require('../utils/logger');
|
|
15
|
+
const dockerUtils = require('../utils/docker');
|
|
16
|
+
const containerHelpers = require('../utils/app-run-containers');
|
|
17
|
+
const healthCheck = require('../utils/health-check');
|
|
18
|
+
const runDockerFallback = require('./run-docker-fallback');
|
|
19
|
+
const { resolveRunImage } = require('./run-resolve-image');
|
|
20
|
+
|
|
21
|
+
const execAsync = promisify(exec);
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Logs and runs docker when Compose CLI is missing (narrow eligibility).
|
|
25
|
+
* @async
|
|
26
|
+
* @param {string} appName
|
|
27
|
+
* @param {Object} appConfig
|
|
28
|
+
* @param {number} port
|
|
29
|
+
* @param {Object} opts
|
|
30
|
+
*/
|
|
31
|
+
async function emitAndRunDockerFallback(appName, appConfig, port, opts) {
|
|
32
|
+
const { debug, runEnvPath, runOptions, misoEnvironment } = opts;
|
|
33
|
+
logger.log(
|
|
34
|
+
chalk.yellow(
|
|
35
|
+
'Docker Compose not found; using docker run (apps without database/redis only). ' +
|
|
36
|
+
'Install docker-compose-plugin for full compose support.'
|
|
37
|
+
)
|
|
38
|
+
);
|
|
39
|
+
const { imageName, imageTag } = resolveRunImage(appName, appConfig, runOptions);
|
|
40
|
+
await runDockerFallback.executeDockerRunUp({
|
|
41
|
+
appName,
|
|
42
|
+
appConfig,
|
|
43
|
+
hostPort: port,
|
|
44
|
+
fullImage: `${imageName}:${imageTag}`,
|
|
45
|
+
runEnvPath,
|
|
46
|
+
misoEnvironment,
|
|
47
|
+
debug
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Prepares env for docker compose child process (UID/GID + remote docker).
|
|
53
|
+
* @async
|
|
54
|
+
* @param {boolean} debug
|
|
55
|
+
* @returns {Promise<Object>}
|
|
56
|
+
*/
|
|
57
|
+
async function prepareContainerEnv(debug) {
|
|
58
|
+
const { getDockerExecEnv } = require('../utils/remote-docker-env');
|
|
59
|
+
const env = await getDockerExecEnv();
|
|
60
|
+
|
|
61
|
+
if (typeof process.getuid === 'function' && typeof process.getgid === 'function') {
|
|
62
|
+
env.AIFABRIX_UID = String(process.getuid());
|
|
63
|
+
env.AIFABRIX_GID = String(process.getgid());
|
|
64
|
+
} else {
|
|
65
|
+
env.AIFABRIX_UID = '1000';
|
|
66
|
+
env.AIFABRIX_GID = '1000';
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
if (debug) {
|
|
70
|
+
logger.log(chalk.gray('[DEBUG] Container env prepared (secrets via env_file)'));
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
return env;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* @async
|
|
78
|
+
* @param {string} composeCmdBase
|
|
79
|
+
* @param {string} composePath
|
|
80
|
+
* @param {Object} env
|
|
81
|
+
* @param {boolean} debug
|
|
82
|
+
*/
|
|
83
|
+
async function executeComposeUp(composeCmdBase, composePath, env, debug) {
|
|
84
|
+
const composeCmd = `${composeCmdBase} -f "${composePath}" up -d`;
|
|
85
|
+
if (debug) {
|
|
86
|
+
logger.log(chalk.gray(`[DEBUG] Executing: ${composeCmd}`));
|
|
87
|
+
logger.log(chalk.gray(`[DEBUG] Compose file: ${composePath}`));
|
|
88
|
+
}
|
|
89
|
+
await execAsync(composeCmd, { env });
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Log status, wait for HTTP health, delete run .env files.
|
|
94
|
+
* @async
|
|
95
|
+
* @param {string} appName
|
|
96
|
+
* @param {number} port
|
|
97
|
+
* @param {Object} appConfig
|
|
98
|
+
* @param {{ debug: boolean, runEnvPath: string|null, runEnvAdminPath: string|null }} o
|
|
99
|
+
*/
|
|
100
|
+
async function waitForHealthyAndCleanupEnvFiles(appName, port, appConfig, o) {
|
|
101
|
+
const { debug, runEnvPath, runEnvAdminPath, runOptions = {} } = o;
|
|
102
|
+
const ro = runOptions || {};
|
|
103
|
+
const scopeOpts =
|
|
104
|
+
ro.effectiveEnvironmentScopedResources === true && ro.env
|
|
105
|
+
? { effectiveEnvironmentScopedResources: true, env: String(ro.env).toLowerCase() }
|
|
106
|
+
: null;
|
|
107
|
+
const containerName = containerHelpers.getContainerName(appName, appConfig.developerId, scopeOpts);
|
|
108
|
+
logger.log(formatSuccessLine(`Container ${containerName} started`));
|
|
109
|
+
await containerHelpers.logContainerStatus(containerName, debug);
|
|
110
|
+
|
|
111
|
+
const healthCheckPath = appConfig?.healthCheck?.path || '/health';
|
|
112
|
+
const healthUrl = (typeof healthCheck.computeHealthCheckUrl === 'function')
|
|
113
|
+
? await healthCheck.computeHealthCheckUrl(appName, port, appConfig, { runOptions: ro })
|
|
114
|
+
: null;
|
|
115
|
+
// Keep a stable fallback in the message if URL computation fails for any reason.
|
|
116
|
+
const displayUrl = (healthUrl && typeof healthUrl === 'string')
|
|
117
|
+
? healthUrl
|
|
118
|
+
: `http://localhost:${port}${healthCheckPath}`;
|
|
119
|
+
logger.log(chalk.blue(`Waiting for application to be healthy at ${displayUrl}...`));
|
|
120
|
+
await healthCheck.waitForHealthCheck(appName, 90, port, appConfig, debug, ro);
|
|
121
|
+
|
|
122
|
+
for (const p of [runEnvPath, runEnvAdminPath]) {
|
|
123
|
+
if (p && typeof p === 'string') {
|
|
124
|
+
try {
|
|
125
|
+
await fs.unlink(p);
|
|
126
|
+
} catch (err) {
|
|
127
|
+
if (err.code !== 'ENOENT') logger.log(chalk.yellow(`Warning: could not remove run .env: ${err.message}`));
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Starts the container and waits for health check. Deletes run .env files after success (ISO 27K).
|
|
135
|
+
* @async
|
|
136
|
+
* @param {string} appName
|
|
137
|
+
* @param {string} composePath
|
|
138
|
+
* @param {number} port
|
|
139
|
+
* @param {Object} appConfig
|
|
140
|
+
* @param {Object} [opts]
|
|
141
|
+
*/
|
|
142
|
+
async function startContainer(appName, composePath, port, appConfig = null, opts = {}) {
|
|
143
|
+
const {
|
|
144
|
+
debug = false,
|
|
145
|
+
runEnvPath = null,
|
|
146
|
+
runEnvAdminPath = null,
|
|
147
|
+
runOptions = {},
|
|
148
|
+
devMountPath = null,
|
|
149
|
+
misoEnvironment = 'dev'
|
|
150
|
+
} = opts;
|
|
151
|
+
logger.log(chalk.blue(`Starting ${appName}...`));
|
|
152
|
+
|
|
153
|
+
let composeCmdBase;
|
|
154
|
+
let usedDockerRunFallback = false;
|
|
155
|
+
try {
|
|
156
|
+
composeCmdBase = await dockerUtils.ensureDockerAndCompose();
|
|
157
|
+
} catch (composeErr) {
|
|
158
|
+
if (runDockerFallback.canUseDockerRunWithoutCompose(appConfig, { devMountPath })) {
|
|
159
|
+
await emitAndRunDockerFallback(appName, appConfig, port, {
|
|
160
|
+
debug,
|
|
161
|
+
runEnvPath,
|
|
162
|
+
runOptions,
|
|
163
|
+
misoEnvironment
|
|
164
|
+
});
|
|
165
|
+
usedDockerRunFallback = true;
|
|
166
|
+
} else {
|
|
167
|
+
throw composeErr;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
if (!usedDockerRunFallback) {
|
|
172
|
+
const env = await prepareContainerEnv(debug);
|
|
173
|
+
await executeComposeUp(composeCmdBase, composePath, env, debug);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
await waitForHealthyAndCleanupEnvFiles(appName, port, appConfig, {
|
|
177
|
+
debug,
|
|
178
|
+
runEnvPath,
|
|
179
|
+
runEnvAdminPath,
|
|
180
|
+
runOptions
|
|
181
|
+
});
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
module.exports = { startContainer, prepareContainerEnv, executeComposeUp };
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Fallback `docker run` when Docker Compose CLI is missing — narrow case only.
|
|
3
|
+
*
|
|
4
|
+
* @fileoverview Apps that declare no Postgres/Redis in application.yaml can start
|
|
5
|
+
* with plain Docker; same eligibility as skipping local infra health.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
'use strict';
|
|
9
|
+
|
|
10
|
+
const chalk = require('chalk');
|
|
11
|
+
const logger = require('../utils/logger');
|
|
12
|
+
const { execWithDockerEnv } = require('../utils/docker-exec');
|
|
13
|
+
const { getContainerPort } = require('../utils/port-resolver');
|
|
14
|
+
const { getAppInfraRequirements } = require('./run-infra-requirements');
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Shell-single-quote a string for use inside a POSIX sh -c style command.
|
|
18
|
+
* @param {string} value
|
|
19
|
+
* @returns {string}
|
|
20
|
+
*/
|
|
21
|
+
function shellSingleQuote(value) {
|
|
22
|
+
const s = String(value);
|
|
23
|
+
return '\'' + s.replace(/'/g, '\'\\\'\'') + '\'';
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Named volume for /mnt/data (must match templates/typescript/docker-compose.hbs).
|
|
28
|
+
* @param {string} appName
|
|
29
|
+
* @param {string|number} developerId
|
|
30
|
+
* @returns {string}
|
|
31
|
+
*/
|
|
32
|
+
function storageVolumeName(appName, developerId) {
|
|
33
|
+
const idNum = typeof developerId === 'string' ? parseInt(developerId, 10) : developerId;
|
|
34
|
+
if (idNum === 0) {
|
|
35
|
+
return `aifabrix_${appName}_data`;
|
|
36
|
+
}
|
|
37
|
+
return `aifabrix_dev${developerId}_${appName}_data`;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* True when safe to emulate compose with `docker run` if Compose is unavailable.
|
|
42
|
+
* @param {Object} appConfig
|
|
43
|
+
* @param {{ devMountPath?: string|null }} startOpts
|
|
44
|
+
* @returns {boolean}
|
|
45
|
+
*/
|
|
46
|
+
function canUseDockerRunWithoutCompose(appConfig, startOpts) {
|
|
47
|
+
const req = getAppInfraRequirements(appConfig);
|
|
48
|
+
if (!req || req.needsPostgres || req.needsRedis) {
|
|
49
|
+
return false;
|
|
50
|
+
}
|
|
51
|
+
if (appConfig.frontDoorRouting && appConfig.frontDoorRouting.enabled === true) {
|
|
52
|
+
return false;
|
|
53
|
+
}
|
|
54
|
+
if (startOpts.devMountPath) {
|
|
55
|
+
return false;
|
|
56
|
+
}
|
|
57
|
+
return true;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* @param {Object} o
|
|
62
|
+
* @param {string} o.appName
|
|
63
|
+
* @param {Object} o.appConfig
|
|
64
|
+
* @param {number} o.hostPort
|
|
65
|
+
* @param {string} o.fullImage image:tag
|
|
66
|
+
* @param {string|null} o.runEnvPath
|
|
67
|
+
* @param {string} o.misoEnvironment
|
|
68
|
+
* @param {boolean} o.debug
|
|
69
|
+
* @returns {Promise<void>}
|
|
70
|
+
*/
|
|
71
|
+
async function executeDockerRunUp(o) {
|
|
72
|
+
const { appName, appConfig, hostPort, fullImage, runEnvPath, misoEnvironment, debug } = o;
|
|
73
|
+
const idNum = typeof appConfig.developerId === 'string' ? parseInt(appConfig.developerId, 10) : appConfig.developerId;
|
|
74
|
+
const containerName = idNum === 0 ? `aifabrix-${appName}` : `aifabrix-dev${appConfig.developerId}-${appName}`;
|
|
75
|
+
const containerPort = getContainerPort(appConfig, 3000);
|
|
76
|
+
|
|
77
|
+
const needsStorage =
|
|
78
|
+
appConfig.requires?.storage === true ||
|
|
79
|
+
appConfig.services?.storage === true;
|
|
80
|
+
|
|
81
|
+
const parts = [
|
|
82
|
+
'docker run -d',
|
|
83
|
+
`--name ${shellSingleQuote(containerName)}`,
|
|
84
|
+
'--restart unless-stopped',
|
|
85
|
+
`-p ${hostPort}:${containerPort}`,
|
|
86
|
+
`-e MISO_ENVIRONMENT=${shellSingleQuote(misoEnvironment)}`
|
|
87
|
+
];
|
|
88
|
+
|
|
89
|
+
if (needsStorage) {
|
|
90
|
+
parts.push(`-v ${shellSingleQuote(storageVolumeName(appName, appConfig.developerId))}:/mnt/data`);
|
|
91
|
+
}
|
|
92
|
+
if (runEnvPath && typeof runEnvPath === 'string') {
|
|
93
|
+
parts.push(`--env-file ${shellSingleQuote(runEnvPath)}`);
|
|
94
|
+
}
|
|
95
|
+
parts.push(shellSingleQuote(fullImage));
|
|
96
|
+
|
|
97
|
+
const cmd = parts.join(' ');
|
|
98
|
+
if (debug) {
|
|
99
|
+
logger.log(chalk.gray(`[DEBUG] Docker run fallback: ${cmd.replace(/env-file [^ ]+/, 'env-file <redacted>')}`));
|
|
100
|
+
}
|
|
101
|
+
await execWithDockerEnv(cmd);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
module.exports = {
|
|
105
|
+
canUseDockerRunWithoutCompose,
|
|
106
|
+
executeDockerRunUp,
|
|
107
|
+
storageVolumeName
|
|
108
|
+
};
|