@aifabrix/builder 2.45.0 → 2.46.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/cli-layout.mdc +2 -0
- package/.cursor/rules/cli-product-neutral.mdc +43 -0
- package/.cursor/rules/docs-rules.mdc +3 -1
- package/.cursor/rules/project-rules.mdc +1 -0
- package/README.md +43 -17
- package/integration/hubspot-test/wizard-hubspot-e2e.yaml +1 -1
- package/jest.config.default.js +2 -1
- package/jest.config.isolated.js +2 -1
- package/jest.global-live-fabrix-hooks.js +36 -0
- package/jest.isolated-projects.js +130 -0
- package/jest.projects.js +69 -121
- package/lib/api/agent-metadata-validation.api.js +93 -0
- package/lib/api/api-response-errors.js +124 -0
- package/lib/api/audit-api-helpers.js +68 -0
- package/lib/api/audit.api.js +119 -0
- package/lib/api/auth-cache.api.js +44 -0
- package/lib/api/certificates.api.js +4 -4
- package/lib/api/credential.api.js +2 -2
- package/lib/api/dataplane-health.api.js +137 -0
- package/lib/api/dataplane-sync.api.js +27 -0
- package/lib/api/datasources-core.api.js +21 -18
- package/lib/api/datasources-extended.api.js +22 -22
- package/lib/api/dimensions.api.js +3 -0
- package/lib/api/external-systems.api.js +13 -13
- package/lib/api/governance-scenario-pack.api.js +159 -0
- package/lib/api/governance-scenarios.api.js +39 -0
- package/lib/api/groups.api.js +79 -0
- package/lib/api/index.js +88 -5
- package/lib/api/lifecycle.api.js +77 -0
- package/lib/api/pipeline.api.js +7 -5
- package/lib/api/protection.api.js +157 -0
- package/lib/api/records-bulk.api.js +43 -0
- package/lib/api/records-search-parse.js +49 -0
- package/lib/api/records-search.api.js +29 -0
- package/lib/api/types/agent-metadata-validation.types.js +67 -0
- package/lib/api/types/audit.types.js +72 -0
- package/lib/api/types/dataplane-health.types.js +27 -0
- package/lib/api/types/dimensions.types.js +2 -0
- package/lib/api/types/governance-scenarios.types.js +39 -0
- package/lib/api/types/protection.types.js +69 -0
- package/lib/api/types/records-bulk.types.js +35 -0
- package/lib/api/types/records-search.types.js +28 -0
- package/lib/api/user-groups.api.js +50 -0
- package/lib/api/users.api.js +95 -0
- package/lib/api/validation-run.api.js +3 -3
- package/lib/api/validation-runner.js +5 -3
- package/lib/api/wizard-platform.api.js +3 -3
- package/lib/api/wizard.api.js +21 -21
- package/lib/app/deploy-config.js +4 -1
- package/lib/app/deploy.js +34 -19
- package/lib/app/down.js +20 -1
- package/lib/app/push.js +4 -5
- package/lib/app/register.js +7 -4
- package/lib/app/run-env-compose.js +84 -30
- package/lib/build/index.js +5 -0
- package/lib/certification/sync-system-certification.js +13 -6
- package/lib/cli/index.js +8 -2
- package/lib/cli/infra-guided-auth.js +109 -0
- package/lib/cli/infra-guided.js +91 -69
- package/lib/cli/setup-app.help.js +19 -2
- package/lib/cli/setup-app.js +58 -23
- package/lib/cli/setup-app.test-commands.js +28 -21
- package/lib/cli/setup-auth.js +55 -3
- package/lib/cli/setup-certification-commands.js +147 -0
- package/lib/cli/setup-certification.help.js +141 -0
- package/lib/cli/setup-environment.js +19 -2
- package/lib/cli/setup-external-system-help.js +62 -0
- package/lib/cli/setup-external-system.js +62 -6
- package/lib/cli/setup-infra.js +9 -1
- package/lib/cli/setup-platform.js +6 -1
- package/lib/cli/setup-secrets.js +33 -1
- package/lib/cli/setup-utility-help-after.js +65 -0
- package/lib/cli/setup-utility-repair.js +198 -0
- package/lib/cli/setup-utility-resolve.js +42 -6
- package/lib/cli/setup-utility-validate.js +197 -0
- package/lib/cli/setup-utility.js +41 -258
- package/lib/commands/auth-status-client-tokens.js +214 -0
- package/lib/commands/auth-status-dataplane-version.js +269 -0
- package/lib/commands/auth-status-display.js +94 -0
- package/lib/commands/auth-status.js +111 -53
- package/lib/commands/credential-push.js +10 -5
- package/lib/commands/datasource-load-export-cli.js +178 -0
- package/lib/commands/datasource-test-trust-cli.js +118 -0
- package/lib/commands/datasource-unified-test-cli.js +31 -20
- package/lib/commands/datasource-unified-test-cli.options.js +22 -3
- package/lib/commands/datasource-unified-test-e2e-cli-helpers.js +5 -5
- package/lib/commands/datasource-verify-audit-cli.js +96 -0
- package/lib/commands/datasource.js +68 -1
- package/lib/commands/dev-show-display.js +8 -1
- package/lib/commands/dimension-value.js +43 -13
- package/lib/commands/dimension.js +38 -7
- package/lib/commands/governance-cli-shared.js +94 -0
- package/lib/commands/governance-command-action.js +132 -0
- package/lib/commands/governance-download-external.js +34 -0
- package/lib/commands/governance-generate-external.js +71 -0
- package/lib/commands/governance-upload-external.js +36 -0
- package/lib/commands/governance-verify-external.js +64 -0
- package/lib/commands/governance.js +144 -0
- package/lib/commands/identity-apply-sync.js +131 -0
- package/lib/commands/identity-group-cli.js +146 -0
- package/lib/commands/identity-membership-role-cache.js +175 -0
- package/lib/commands/identity-shared.js +94 -0
- package/lib/commands/identity-user-cli.js +158 -0
- package/lib/commands/identity.js +43 -0
- package/lib/commands/lifecycle-command-action.js +123 -0
- package/lib/commands/lifecycle-external.js +268 -0
- package/lib/commands/login.js +56 -17
- package/lib/commands/protection-cli-leaves.js +286 -0
- package/lib/commands/protection.js +50 -0
- package/lib/commands/repair-auth-apply.js +127 -0
- package/lib/commands/repair-changed-files.js +41 -0
- package/lib/commands/repair-datasource-keys.js +4 -1
- package/lib/commands/repair-datasource-run.js +59 -0
- package/lib/commands/repair-datasource-test-payload.js +259 -0
- package/lib/commands/repair-datasource.js +1 -51
- package/lib/commands/repair-env-template.js +64 -13
- package/lib/commands/repair-external-run.js +216 -0
- package/lib/commands/repair-openapi-sync.js +23 -0
- package/lib/commands/repair-persist.js +4 -0
- package/lib/commands/repair-rbac.js +52 -1
- package/lib/commands/repair-steps.js +145 -0
- package/lib/commands/repair.js +29 -286
- package/lib/commands/secrets-get.js +184 -0
- package/lib/commands/secrets-remove-all.js +3 -1
- package/lib/commands/setup-image-refresh.js +146 -0
- package/lib/commands/setup-modes.js +158 -162
- package/lib/commands/setup-platform-auth.js +76 -0
- package/lib/commands/setup-prompts-admin.js +244 -0
- package/lib/commands/setup-prompts-platform-mode.js +67 -0
- package/lib/commands/setup-prompts-validators.js +36 -0
- package/lib/commands/setup-prompts.js +24 -76
- package/lib/commands/setup-run-context.js +45 -0
- package/lib/commands/setup-spinners.js +39 -0
- package/lib/commands/setup.js +27 -5
- package/lib/commands/teardown.js +39 -0
- package/lib/commands/test-e2e-external.js +61 -14
- package/lib/commands/test-governance-cli-options.js +47 -0
- package/lib/commands/test-governance-external.js +67 -0
- package/lib/commands/test-trust-cli-options.js +32 -0
- package/lib/commands/test-trust-external.js +126 -0
- package/lib/commands/up-common.js +25 -13
- package/lib/commands/up-dataplane.js +50 -13
- package/lib/commands/upload-publish-context.js +75 -0
- package/lib/commands/upload-sidecar-followups.js +89 -0
- package/lib/commands/upload.js +108 -105
- package/lib/commands/verify-governance-combined-external.js +61 -0
- package/lib/commands/verify-governance-command-action.js +64 -0
- package/lib/commands/verify-governance-db.js +102 -0
- package/lib/commands/verify-governance-external.js +77 -0
- package/lib/commands/verify-governance-result-builders.js +71 -0
- package/lib/commands/verify-operations-command-action.js +68 -0
- package/lib/commands/verify-operations-external.js +268 -0
- package/lib/commands/verify-operations-persist.js +75 -0
- package/lib/commands/verify-operations-step-plan.js +68 -0
- package/lib/commands/verify-operations-steps.js +111 -0
- package/lib/commands/verify-trust-command-action.js +70 -0
- package/lib/commands/verify-trust-external.js +67 -0
- package/lib/commands/wizard-config-normalizer.js +43 -23
- package/lib/commands/wizard-core.js +5 -0
- package/lib/core/admin-password-generator.js +38 -0
- package/lib/core/admin-secrets-bundle.js +111 -0
- package/lib/core/admin-secrets-env-overlay.js +62 -0
- package/lib/core/admin-secrets.js +32 -4
- package/lib/core/config-attach-extensions.js +3 -0
- package/lib/core/config-device-dataplane.js +185 -0
- package/lib/core/config-platform-controller.js +44 -0
- package/lib/core/config-setup-installation-profile.js +42 -0
- package/lib/core/config-setup-platform-mode.js +46 -0
- package/lib/core/config.js +46 -36
- package/lib/core/secrets-admin-env.js +6 -2
- package/lib/core/secrets-ensure.js +4 -0
- package/lib/core/secrets-env-content.js +54 -48
- package/lib/core/secrets-env-write.js +10 -1
- package/lib/core/secrets-infra-placeholder-sync.js +2 -0
- package/lib/core/setup-installation-profile.js +43 -0
- package/lib/core/setup-platform-mode.js +55 -0
- package/lib/datasource/agent-trust-debug-log.js +45 -0
- package/lib/datasource/agent-trust-fetch.js +115 -0
- package/lib/datasource/agent-trust-map.js +101 -0
- package/lib/datasource/agent-trust-mock.js +44 -0
- package/lib/datasource/agent-trust-run.js +95 -0
- package/lib/datasource/agent-trust-timeout.js +25 -0
- package/lib/datasource/audit-evidence-display.js +80 -0
- package/lib/datasource/audit-evidence-extract-debug.js +115 -0
- package/lib/datasource/audit-evidence-extract.js +180 -0
- package/lib/datasource/audit-evidence-matrix-rows.js +158 -0
- package/lib/datasource/audit-evidence-operation-aliases.js +63 -0
- package/lib/datasource/audit-evidence-poll.js +109 -0
- package/lib/datasource/audit-evidence-run.js +177 -0
- package/lib/datasource/audit-evidence-verify-helpers.js +167 -0
- package/lib/datasource/audit-evidence-verify-matrix.js +205 -0
- package/lib/datasource/audit-evidence-verify.js +108 -0
- package/lib/datasource/bulk-loader-run-batches.js +100 -0
- package/lib/datasource/bulk-loader-service-retry.js +52 -0
- package/lib/datasource/bulk-loader-service.js +127 -0
- package/lib/datasource/capability/run-capability-dimension.js +2 -1
- package/lib/datasource/datasource-exporter-resolve.js +82 -0
- package/lib/datasource/datasource-exporter-service.js +144 -0
- package/lib/datasource/datasource-identifier.js +148 -0
- package/lib/datasource/datasource-load-export-context.js +95 -0
- package/lib/datasource/integration-context.js +6 -4
- package/lib/datasource/local-data-paths.js +145 -0
- package/lib/datasource/log-cleaner.js +241 -0
- package/lib/datasource/log-viewer.js +111 -6
- package/lib/datasource/record-file-parser.js +85 -0
- package/lib/datasource/record-mapper.js +129 -0
- package/lib/datasource/resolve-app.js +74 -55
- package/lib/datasource/test-e2e.js +29 -2
- package/lib/datasource/unified-validation-run-body.js +3 -1
- package/lib/datasource/unified-validation-run-resolve.js +45 -12
- package/lib/datasource/unified-validation-run.js +47 -18
- package/lib/datasource/validate.js +37 -20
- package/lib/deployment/environment-poll-status.js +138 -0
- package/lib/deployment/environment.js +42 -34
- package/lib/external-system/download.js +21 -20
- package/lib/external-system/generator.js +5 -1
- package/lib/external-system/integration-auth-context.js +45 -0
- package/lib/external-system/test-auth.js +12 -5
- package/lib/external-system/test-system-level.js +25 -4
- package/lib/external-system/test.js +9 -7
- package/lib/generator/external-controller-manifest.js +15 -0
- package/lib/governance/governance-cli-errors.js +96 -0
- package/lib/governance/governance-download-display.js +38 -0
- package/lib/governance/governance-pack-loader.js +70 -0
- package/lib/governance/governance-run-display.js +309 -0
- package/lib/identity/identity-apply-core.js +108 -0
- package/lib/identity/identity-apply-phases.js +244 -0
- package/lib/identity/identity-apply-service.js +77 -0
- package/lib/identity/identity-csv-parser.js +114 -0
- package/lib/infrastructure/admin-secrets-apply.js +84 -0
- package/lib/infrastructure/admin-secrets-ensure.js +130 -0
- package/lib/infrastructure/admin-secrets-resolve.js +95 -0
- package/lib/infrastructure/admin-secrets-sync.js +91 -0
- package/lib/infrastructure/helpers.js +23 -129
- package/lib/infrastructure/index.js +5 -6
- package/lib/infrastructure/orphan-infra-docker-teardown.js +11 -5
- package/lib/internal/fs-real-sync.js +29 -1
- package/lib/lifecycle/certification-progress.js +231 -0
- package/lib/lifecycle/dataplane-ui-url.js +145 -0
- package/lib/lifecycle/exit-codes.js +24 -0
- package/lib/lifecycle/json-envelope.js +25 -0
- package/lib/lifecycle/lifecycle-governance-options.js +51 -0
- package/lib/lifecycle/logger-quiet.js +29 -0
- package/lib/lifecycle/open-api-published.js +59 -0
- package/lib/lifecycle/operations-readiness-metrics.js +101 -0
- package/lib/lifecycle/product-model.js +196 -0
- package/lib/lifecycle/report-display.js +471 -0
- package/lib/lifecycle/scenario-upload.js +191 -0
- package/lib/lifecycle/verify-step-progress.js +87 -0
- package/lib/parameters/infra-kv-discovery.js +27 -2
- package/lib/protection/auth-context.js +51 -0
- package/lib/protection/convert-batch.js +90 -0
- package/lib/protection/load.js +41 -0
- package/lib/protection/paths.js +158 -0
- package/lib/protection/preflight-datasource-ready.js +46 -0
- package/lib/protection/protection-create-probe-lines.js +142 -0
- package/lib/protection/protection-create-probes.js +95 -0
- package/lib/protection/protection-create-scaffold.js +281 -0
- package/lib/protection/protection-display-helpers.js +445 -0
- package/lib/protection/protection-display.js +215 -0
- package/lib/protection/protection-preset-registry.js +97 -0
- package/lib/protection/report-exit.js +62 -0
- package/lib/protection/resolve.js +202 -0
- package/lib/protection/run-commands-validate.js +130 -0
- package/lib/protection/run-commands.js +216 -0
- package/lib/protection/run-protection-create-helpers.js +134 -0
- package/lib/protection/run-protection-create-probes-flow.js +43 -0
- package/lib/protection/run-protection-create.js +191 -0
- package/lib/protection/scope.js +22 -0
- package/lib/protection/sync-after-upload.js +64 -0
- package/lib/protection/upload-batch.js +127 -0
- package/lib/protection/validate-batch.js +113 -0
- package/lib/protection/validate-local.js +65 -0
- package/lib/resolvers/dimension-file.js +29 -4
- package/lib/schema/application-schema.json +1 -1
- package/lib/schema/datasource-test-run.schema.json +89 -6
- package/lib/schema/external-datasource.schema.json +2636 -2588
- package/lib/schema/external-system.schema.json +1089 -761
- package/lib/schema/flag-map-validation-run.json +2 -2
- package/lib/schema/infra.parameter.yaml +45 -0
- package/lib/schema/protection.schema.json +374 -0
- package/lib/schema/type/credential-field-registry.json +642 -0
- package/lib/schema/wizard-config.schema.json +1 -1
- package/lib/utils/agent-trust-run-display.js +231 -0
- package/lib/utils/agent-trust-run-exit.js +59 -0
- package/lib/utils/agent-trust-wait-ui.js +94 -0
- package/lib/utils/aifabrix-test-runtime-guard.js +125 -0
- package/lib/utils/api.js +55 -31
- package/lib/utils/app-relative-path-env.js +112 -0
- package/lib/utils/cli-exit-codes.js +30 -0
- package/lib/utils/cli-secrets-error-format.js +83 -21
- package/lib/utils/cli-sync-options.js +33 -0
- package/lib/utils/cli-utils.js +16 -4
- package/lib/utils/compose-generate-docker-compose.js +4 -0
- package/lib/utils/compose-traefik-ingress-base.js +16 -1
- package/lib/utils/config-secrets-path.js +46 -0
- package/lib/utils/controller-url.js +88 -1
- package/lib/utils/credential-push-ui.js +90 -0
- package/lib/utils/credential-secrets-env.js +137 -9
- package/lib/utils/dataplane-cli-version-gate.js +202 -0
- package/lib/utils/dataplane-cli-version-help.js +74 -0
- package/lib/utils/dataplane-resolver.js +129 -2
- package/lib/utils/datasource-export-display.js +137 -0
- package/lib/utils/datasource-load-display.js +174 -0
- package/lib/utils/datasource-load-export-ui.js +120 -0
- package/lib/utils/datasource-test-run-capability-scope.js +3 -3
- package/lib/utils/datasource-test-run-display.js +3 -3
- package/lib/utils/datasource-test-run-tty-log.js +5 -1
- package/lib/utils/declarative-url-ports.js +3 -3
- package/lib/utils/deployment-auth-mode.js +41 -0
- package/lib/utils/developer-volume-id.js +33 -0
- package/lib/utils/env-copy.js +14 -9
- package/lib/utils/env-kv-orphan-lines.js +43 -0
- package/lib/utils/error-formatters/network-errors.js +13 -4
- package/lib/utils/external-env-template.js +1 -0
- package/lib/utils/external-system-readiness-core.js +10 -0
- package/lib/utils/external-system-readiness-display.js +80 -23
- package/lib/utils/health-check.js +4 -1
- package/lib/utils/help-builder.js +35 -26
- package/lib/utils/keycloak-realm-ready.js +101 -0
- package/lib/utils/load-cip-capacity-display-config.js +61 -15
- package/lib/utils/manifest-source-emit.js +24 -0
- package/lib/utils/pipeline-upload-auth-retry.js +82 -0
- package/lib/utils/platform-controller-url.js +156 -0
- package/lib/utils/postgres-platform-bootstrap.js +199 -0
- package/lib/utils/postgres-wipe.js +9 -2
- package/lib/utils/resolve-env-mode.js +47 -0
- package/lib/utils/secrets-generator.js +31 -25
- package/lib/utils/secrets-missing-error.js +164 -0
- package/lib/utils/semver-compare.js +127 -0
- package/lib/utils/service-url-label.js +81 -0
- package/lib/utils/setup-quiet-output.js +25 -0
- package/lib/utils/template-helpers.js +15 -0
- package/lib/utils/token-manager-client-credentials.js +201 -0
- package/lib/utils/token-manager-messages.js +8 -0
- package/lib/utils/token-manager-refresh.js +12 -2
- package/lib/utils/token-manager.js +86 -105
- package/lib/utils/upload-pipeline-ui.js +98 -0
- package/lib/utils/upload-sync-options.js +42 -0
- package/lib/utils/url-declarative-public-base.js +21 -3
- package/lib/utils/url-declarative-vdir-inactive-env.js +14 -4
- package/lib/utils/validation-poll-ui.js +22 -7
- package/lib/utils/validation-run-stage.js +45 -0
- package/lib/validation/wizard-config-validator.js +58 -19
- package/package.json +1 -1
- package/templates/applications/dataplane/application.yaml +2 -1
- package/templates/applications/dataplane/env.template +36 -1
- package/templates/applications/dataplane/rbac.yaml +4 -0
- package/templates/applications/miso-controller/env.template +16 -0
- package/templates/external-system/README.md.hbs +116 -11
- package/templates/python/docker-compose.hbs +19 -18
- package/templates/typescript/docker-compose.hbs +18 -17
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Upload local governance scenario YAML packs to dataplane (419.0).
|
|
3
|
+
* @author AI Fabrix Team
|
|
4
|
+
* @version 1.0.0
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
'use strict';
|
|
8
|
+
|
|
9
|
+
const path = require('path');
|
|
10
|
+
const {
|
|
11
|
+
existsSync,
|
|
12
|
+
readFileSync,
|
|
13
|
+
readdirSync,
|
|
14
|
+
mkdirSync,
|
|
15
|
+
writeFileSync
|
|
16
|
+
} = require('../internal/fs-real-sync');
|
|
17
|
+
const yaml = require('js-yaml');
|
|
18
|
+
const logger = require('../utils/logger');
|
|
19
|
+
const { getIntegrationPath } = require('../utils/paths');
|
|
20
|
+
const { upsertDatasourceGovernancePack } = require('../api/governance-scenario-pack.api');
|
|
21
|
+
const { formatSuccessLine, formatWarningLine } = require('../utils/cli-test-layout-chalk');
|
|
22
|
+
const { shortGovernanceApiError } = require('../governance/governance-cli-errors');
|
|
23
|
+
const { printGovernanceDownloadMisses } = require('../governance/governance-download-display');
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* @param {string} scenariosDir
|
|
27
|
+
* @returns {string[]}
|
|
28
|
+
*/
|
|
29
|
+
function listScenarioYamlFiles(scenariosDir) {
|
|
30
|
+
if (!existsSync(scenariosDir)) return [];
|
|
31
|
+
return readdirSync(scenariosDir)
|
|
32
|
+
.filter(name => name.endsWith('.yaml') || name.endsWith('.yml'))
|
|
33
|
+
.map(name => path.join(scenariosDir, name));
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Extract datasource keys referenced in a pack document.
|
|
38
|
+
* @param {Object} pack
|
|
39
|
+
* @returns {string[]}
|
|
40
|
+
*/
|
|
41
|
+
function datasourceKeysFromPack(pack) {
|
|
42
|
+
const keys = new Set();
|
|
43
|
+
const scenarios = pack?.spec?.scenarios || [];
|
|
44
|
+
for (const scenario of scenarios) {
|
|
45
|
+
const dsKeys = scenario?.search?.datasourceKeys || [];
|
|
46
|
+
for (const k of dsKeys) {
|
|
47
|
+
if (k && typeof k === 'string') keys.add(k.trim());
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
return [...keys];
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* @param {string} filePath
|
|
55
|
+
* @returns {Object}
|
|
56
|
+
*/
|
|
57
|
+
function loadPackFromFile(filePath) {
|
|
58
|
+
const raw = readFileSync(filePath, 'utf8');
|
|
59
|
+
const doc = yaml.load(raw);
|
|
60
|
+
if (!doc || typeof doc !== 'object') {
|
|
61
|
+
throw new Error(`Invalid scenario pack YAML: ${filePath}`);
|
|
62
|
+
}
|
|
63
|
+
return doc;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Upload scenario packs under integration/<systemKey>/scenarios/ to dataplane.
|
|
68
|
+
* @async
|
|
69
|
+
* @param {string} systemKey
|
|
70
|
+
* @param {string} dataplaneUrl
|
|
71
|
+
* @param {Object} authConfig
|
|
72
|
+
* @param {{ silent?: boolean }} [opts]
|
|
73
|
+
* @returns {Promise<number>} count of packs uploaded
|
|
74
|
+
*/
|
|
75
|
+
async function uploadGovernanceScenarioPacks(systemKey, dataplaneUrl, authConfig, opts = {}) {
|
|
76
|
+
const scenariosDir = path.join(getIntegrationPath(systemKey), 'scenarios');
|
|
77
|
+
const files = listScenarioYamlFiles(scenariosDir);
|
|
78
|
+
if (files.length === 0) return 0;
|
|
79
|
+
|
|
80
|
+
let uploaded = 0;
|
|
81
|
+
for (const filePath of files) {
|
|
82
|
+
const pack = loadPackFromFile(filePath);
|
|
83
|
+
const dsKeys = datasourceKeysFromPack(pack);
|
|
84
|
+
if (dsKeys.length === 0) {
|
|
85
|
+
if (!opts.silent) {
|
|
86
|
+
logger.log(
|
|
87
|
+
formatWarningLine(
|
|
88
|
+
`Skipping ${path.basename(filePath)} — no datasourceKeys in scenarios`
|
|
89
|
+
)
|
|
90
|
+
);
|
|
91
|
+
}
|
|
92
|
+
continue;
|
|
93
|
+
}
|
|
94
|
+
for (const dsKey of dsKeys) {
|
|
95
|
+
await upsertDatasourceGovernancePack(dataplaneUrl, authConfig, dsKey, pack);
|
|
96
|
+
uploaded += 1;
|
|
97
|
+
if (!opts.silent) {
|
|
98
|
+
logger.log(
|
|
99
|
+
formatSuccessLine(
|
|
100
|
+
`Governance scenario pack uploaded for ${dsKey} (${path.basename(filePath)})`
|
|
101
|
+
)
|
|
102
|
+
);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
return uploaded;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Write governance scenario packs from dataplane to integration/<systemKey>/scenarios/.
|
|
111
|
+
* @async
|
|
112
|
+
* @param {string} systemKey
|
|
113
|
+
* @param {string[]} datasourceKeys
|
|
114
|
+
* @param {string} dataplaneUrl
|
|
115
|
+
* @param {Object} authConfig
|
|
116
|
+
* @returns {Promise<number>}
|
|
117
|
+
*/
|
|
118
|
+
async function downloadGovernanceScenarioPacks(
|
|
119
|
+
systemKey,
|
|
120
|
+
datasourceKeys,
|
|
121
|
+
dataplaneUrl,
|
|
122
|
+
authConfig
|
|
123
|
+
) {
|
|
124
|
+
const { getDatasourceGovernancePack } = require('../api/governance-scenario-pack.api');
|
|
125
|
+
const scenariosDir = path.join(getIntegrationPath(systemKey), 'scenarios');
|
|
126
|
+
if (!existsSync(scenariosDir)) {
|
|
127
|
+
mkdirSync(scenariosDir, { recursive: true });
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
let written = 0;
|
|
131
|
+
const missing = [];
|
|
132
|
+
for (const dsKey of datasourceKeys) {
|
|
133
|
+
try {
|
|
134
|
+
const row = await getDatasourceGovernancePack(dataplaneUrl, authConfig, dsKey);
|
|
135
|
+
if (!row || !row.pack) {
|
|
136
|
+
missing.push({
|
|
137
|
+
datasourceKey: dsKey,
|
|
138
|
+
reason: 'No active pack on dataplane'
|
|
139
|
+
});
|
|
140
|
+
continue;
|
|
141
|
+
}
|
|
142
|
+
const outPath = path.join(scenariosDir, `${dsKey}.yaml`);
|
|
143
|
+
writeFileSync(outPath, yaml.dump(row.pack, { lineWidth: 120 }), 'utf8');
|
|
144
|
+
written += 1;
|
|
145
|
+
logger.log(formatSuccessLine(`Scenario pack saved: ${outPath}`));
|
|
146
|
+
} catch (err) {
|
|
147
|
+
missing.push({
|
|
148
|
+
datasourceKey: dsKey,
|
|
149
|
+
reason: shortGovernanceApiError(err)
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
printGovernanceDownloadMisses(missing);
|
|
154
|
+
return written;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Download governance packs for all datasource keys on a running manifest.
|
|
159
|
+
* @async
|
|
160
|
+
* @param {string} systemKey
|
|
161
|
+
* @param {Object} manifest
|
|
162
|
+
* @param {string} dataplaneUrl
|
|
163
|
+
* @param {Object} authConfig
|
|
164
|
+
* @returns {Promise<number>}
|
|
165
|
+
*/
|
|
166
|
+
async function downloadGovernanceScenarioPacksFromManifest(
|
|
167
|
+
systemKey,
|
|
168
|
+
manifest,
|
|
169
|
+
dataplaneUrl,
|
|
170
|
+
authConfig
|
|
171
|
+
) {
|
|
172
|
+
const dsKeys = (manifest.dataSources || []).map((ds) => ds && ds.key).filter(Boolean);
|
|
173
|
+
if (dsKeys.length === 0) {
|
|
174
|
+
return 0;
|
|
175
|
+
}
|
|
176
|
+
try {
|
|
177
|
+
return await downloadGovernanceScenarioPacks(systemKey, dsKeys, dataplaneUrl, authConfig);
|
|
178
|
+
} catch (err) {
|
|
179
|
+
logger.log(formatWarningLine(`Governance scenario download skipped: ${err.message}`));
|
|
180
|
+
return 0;
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
module.exports = {
|
|
185
|
+
listScenarioYamlFiles,
|
|
186
|
+
datasourceKeysFromPack,
|
|
187
|
+
loadPackFromFile,
|
|
188
|
+
uploadGovernanceScenarioPacks,
|
|
189
|
+
downloadGovernanceScenarioPacks,
|
|
190
|
+
downloadGovernanceScenarioPacksFromManifest
|
|
191
|
+
};
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Active progress (ora spinner or single ⏳ line) for verify-* orchestration steps.
|
|
3
|
+
*
|
|
4
|
+
* @fileoverview Verify step progress UI
|
|
5
|
+
* @author AI Fabrix Team
|
|
6
|
+
* @version 1.0.0
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
'use strict';
|
|
10
|
+
|
|
11
|
+
const chalk = require('chalk');
|
|
12
|
+
const logger = require('../utils/logger');
|
|
13
|
+
const { formatProgress } = require('../utils/cli-test-layout-chalk');
|
|
14
|
+
const { withQuietLogs } = require('./logger-quiet');
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* @param {Object} [options]
|
|
18
|
+
* @returns {boolean}
|
|
19
|
+
*/
|
|
20
|
+
function shouldShowVerifyStepProgress(options = {}) {
|
|
21
|
+
return options.json !== true;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* @param {Object} [options]
|
|
26
|
+
* @returns {boolean}
|
|
27
|
+
*/
|
|
28
|
+
function shouldUseVerifyStepSpinner(options = {}) {
|
|
29
|
+
if (!shouldShowVerifyStepProgress(options)) {
|
|
30
|
+
return false;
|
|
31
|
+
}
|
|
32
|
+
return Boolean(process && process.stdout && process.stdout.isTTY);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* @param {string} label
|
|
37
|
+
* @returns {string}
|
|
38
|
+
*/
|
|
39
|
+
function formatVerifyStepLabel(label) {
|
|
40
|
+
const text = label && String(label).trim() ? String(label).trim() : 'Processing';
|
|
41
|
+
return text.endsWith('…') ? text : `${text}…`;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Run a verify step with one active progress indicator (spinner on TTY).
|
|
46
|
+
*
|
|
47
|
+
* @template T
|
|
48
|
+
* @async
|
|
49
|
+
* @param {string} label - Plain label without trailing ellipsis
|
|
50
|
+
* @param {(progress: { setLabel: (next: string) => void }) => Promise<T>} work
|
|
51
|
+
* @param {Object} [options]
|
|
52
|
+
* @param {boolean} [options.json]
|
|
53
|
+
* @returns {Promise<T>}
|
|
54
|
+
*/
|
|
55
|
+
async function runWithVerifyStepProgress(label, work, options = {}) {
|
|
56
|
+
if (!shouldShowVerifyStepProgress(options)) {
|
|
57
|
+
return withQuietLogs(() => work({ setLabel: () => {} }));
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
if (!shouldUseVerifyStepSpinner(options)) {
|
|
61
|
+
logger.log(formatProgress(formatVerifyStepLabel(label)));
|
|
62
|
+
return withQuietLogs(() => work({ setLabel: () => {} }));
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const ora = require('ora');
|
|
66
|
+
const spinner = ora({
|
|
67
|
+
text: chalk.white(formatVerifyStepLabel(label)),
|
|
68
|
+
spinner: 'dots'
|
|
69
|
+
}).start();
|
|
70
|
+
|
|
71
|
+
const setLabel = (next) => {
|
|
72
|
+
spinner.text = chalk.white(formatVerifyStepLabel(next));
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
try {
|
|
76
|
+
return await withQuietLogs(() => work({ setLabel }));
|
|
77
|
+
} finally {
|
|
78
|
+
spinner.stop();
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
module.exports = {
|
|
83
|
+
shouldShowVerifyStepProgress,
|
|
84
|
+
shouldUseVerifyStepSpinner,
|
|
85
|
+
formatVerifyStepLabel,
|
|
86
|
+
runWithVerifyStepProgress
|
|
87
|
+
};
|
|
@@ -6,7 +6,10 @@
|
|
|
6
6
|
|
|
7
7
|
const fsRealSync = require('../internal/fs-real-sync');
|
|
8
8
|
const path = require('path');
|
|
9
|
-
const { loadRequiresDatabasesArray } = require('./database-secret-values');
|
|
9
|
+
const { loadRequiresDatabasesArray, loadShippedRequiresDatabases } = require('./database-secret-values');
|
|
10
|
+
|
|
11
|
+
/** Platform apps from shipped templates (setup/up-infra before builder/ is copied). */
|
|
12
|
+
const SHIPPED_PLATFORM_APP_KEYS = ['keycloak', 'miso-controller', 'dataplane'];
|
|
10
13
|
|
|
11
14
|
/**
|
|
12
15
|
* Extract kv:// secret key names from env template content (active lines only).
|
|
@@ -90,6 +93,25 @@ function deriveDatabaseKvKeysFromWorkspace(pathsUtil) {
|
|
|
90
93
|
return [...keys];
|
|
91
94
|
}
|
|
92
95
|
|
|
96
|
+
/**
|
|
97
|
+
* Database kv keys from shipped platform templates when builder/ apps are not copied yet
|
|
98
|
+
* (e.g. `aifabrix setup` fresh install before `up-platform`).
|
|
99
|
+
*
|
|
100
|
+
* @returns {string[]}
|
|
101
|
+
*/
|
|
102
|
+
function deriveDatabaseKvKeysFromShippedPlatformTemplates() {
|
|
103
|
+
const keys = new Set();
|
|
104
|
+
for (const appKey of SHIPPED_PLATFORM_APP_KEYS) {
|
|
105
|
+
const dbs = loadShippedRequiresDatabases(appKey);
|
|
106
|
+
if (!Array.isArray(dbs) || dbs.length === 0) continue;
|
|
107
|
+
for (let i = 0; i < dbs.length; i++) {
|
|
108
|
+
keys.add(`databases-${appKey}-${i}-urlKeyVault`);
|
|
109
|
+
keys.add(`databases-${appKey}-${i}-passwordKeyVault`);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
return [...keys];
|
|
113
|
+
}
|
|
114
|
+
|
|
93
115
|
/**
|
|
94
116
|
* Keys from env.template files whose catalog entry includes the given hook (e.g. upInfra).
|
|
95
117
|
* @param {object} pathsUtil - paths module
|
|
@@ -132,6 +154,7 @@ function getAllInfraEnsureKeys(catalog, pathsUtil) {
|
|
|
132
154
|
const set = new Set();
|
|
133
155
|
for (const k of catalog.getStandardUpInfraBootstrapKeys()) set.add(k);
|
|
134
156
|
for (const k of deriveDatabaseKvKeysFromWorkspace(pathsUtil)) set.add(k);
|
|
157
|
+
for (const k of deriveDatabaseKvKeysFromShippedPlatformTemplates()) set.add(k);
|
|
135
158
|
for (const k of discoverKvKeysFromEnvTemplatesForHook(pathsUtil, 'upInfra', catalog)) set.add(k);
|
|
136
159
|
return [...set].sort();
|
|
137
160
|
}
|
|
@@ -139,8 +162,10 @@ function getAllInfraEnsureKeys(catalog, pathsUtil) {
|
|
|
139
162
|
module.exports = {
|
|
140
163
|
extractKvKeysFromEnvContent,
|
|
141
164
|
deriveDatabaseKvKeysFromWorkspace,
|
|
165
|
+
deriveDatabaseKvKeysFromShippedPlatformTemplates,
|
|
142
166
|
discoverKvKeysFromEnvTemplatesForHook,
|
|
143
167
|
getAllInfraEnsureKeys,
|
|
144
168
|
listAppDirsForDiscovery,
|
|
145
|
-
listBuilderAppDirsForDiscovery
|
|
169
|
+
listBuilderAppDirsForDiscovery,
|
|
170
|
+
SHIPPED_PLATFORM_APP_KEYS
|
|
146
171
|
};
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Dataplane auth/url resolution for protection commands.
|
|
3
|
+
* @author AI Fabrix Team
|
|
4
|
+
* @version 1.0.0
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
'use strict';
|
|
8
|
+
|
|
9
|
+
const { resolveControllerUrl } = require('../utils/controller-url');
|
|
10
|
+
const { resolveEnvironment } = require('../core/config');
|
|
11
|
+
const { getDeviceOnlyAuth, getDeploymentAuth } = require('../utils/token-manager');
|
|
12
|
+
const { getDeploymentAuthMode } = require('../utils/deployment-auth-mode');
|
|
13
|
+
const { resolveDataplaneUrl } = require('../utils/dataplane-resolver');
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* @param {Object} [options]
|
|
17
|
+
* @param {string} [options.env]
|
|
18
|
+
* @returns {Promise<{ environment: string, controllerUrl: string, dataplaneUrl: string, authConfig: Object }>}
|
|
19
|
+
*/
|
|
20
|
+
async function resolveProtectionDataplaneContext(options = {}) {
|
|
21
|
+
const environment = options.env || (await resolveEnvironment());
|
|
22
|
+
const controllerUrl = await resolveControllerUrl();
|
|
23
|
+
if (!controllerUrl) {
|
|
24
|
+
throw new Error('Controller URL is required. Run "aifabrix login" first.');
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const authMode = getDeploymentAuthMode(options);
|
|
28
|
+
let authConfig;
|
|
29
|
+
if (authMode === 'client-credentials') {
|
|
30
|
+
authConfig = await getDeploymentAuth(controllerUrl, environment, 'dataplane', options);
|
|
31
|
+
} else {
|
|
32
|
+
try {
|
|
33
|
+
authConfig = await getDeviceOnlyAuth(controllerUrl);
|
|
34
|
+
} catch {
|
|
35
|
+
authConfig = await getDeploymentAuth(controllerUrl, environment, 'dataplane', options);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
if (!authConfig?.token) {
|
|
40
|
+
throw new Error(
|
|
41
|
+
'Authentication required. Run "aifabrix login" and try again.'
|
|
42
|
+
);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const dataplaneUrl = await resolveDataplaneUrl(controllerUrl, environment, authConfig);
|
|
46
|
+
return { environment, controllerUrl, dataplaneUrl, authConfig };
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
module.exports = {
|
|
50
|
+
resolveProtectionDataplaneContext
|
|
51
|
+
};
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Convert all protection manifests in `.protection/` between JSON and YAML.
|
|
3
|
+
* @author AI Fabrix Team
|
|
4
|
+
* @version 1.0.0
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
'use strict';
|
|
8
|
+
|
|
9
|
+
const path = require('path');
|
|
10
|
+
const { existsSync, unlinkSync } = require('../internal/fs-real-sync');
|
|
11
|
+
const { getProtectionRoot } = require('./paths');
|
|
12
|
+
const { listProtectionManifestPaths } = require('./resolve');
|
|
13
|
+
const { promptConfirm, targetFileName, convertOneFile } = require('../commands/convert');
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* @param {string} root
|
|
17
|
+
* @param {string} format
|
|
18
|
+
* @returns {string[]}
|
|
19
|
+
*/
|
|
20
|
+
function listFilesNeedingConvert(root, format) {
|
|
21
|
+
const targetExt = format === 'json' ? '.json' : '.yaml';
|
|
22
|
+
return listProtectionManifestPaths(root).filter((p) => {
|
|
23
|
+
return path.extname(p).toLowerCase() !== targetExt;
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* @param {string[]} toConvert
|
|
29
|
+
* @param {string} format
|
|
30
|
+
* @param {boolean} force
|
|
31
|
+
* @returns {Promise<void>}
|
|
32
|
+
*/
|
|
33
|
+
async function confirmProtectionConvert(toConvert, format, force) {
|
|
34
|
+
if (force) {
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
const lines = toConvert.map((p) => ` • ${path.basename(p)} → ${targetFileName(p, format)}`);
|
|
38
|
+
const confirmed = await promptConfirm(
|
|
39
|
+
`Convert all protection manifests to ${format}?\n${lines.join('\n')}\nAre you sure? (y/N) `
|
|
40
|
+
);
|
|
41
|
+
if (!confirmed) {
|
|
42
|
+
throw new Error('Convert cancelled.');
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* @param {string} root
|
|
48
|
+
* @param {string[]} toConvert
|
|
49
|
+
* @param {string} format
|
|
50
|
+
* @returns {{ converted: string[], deleted: string[] }}
|
|
51
|
+
*/
|
|
52
|
+
function convertProtectionFiles(root, toConvert, format) {
|
|
53
|
+
const converted = [];
|
|
54
|
+
const deleted = [];
|
|
55
|
+
for (const sourcePath of toConvert) {
|
|
56
|
+
const targetPath = path.join(root, targetFileName(sourcePath, format));
|
|
57
|
+
convertOneFile(sourcePath, targetPath, format);
|
|
58
|
+
converted.push(targetPath);
|
|
59
|
+
if (path.normalize(sourcePath) !== path.normalize(targetPath)) {
|
|
60
|
+
unlinkSync(sourcePath);
|
|
61
|
+
deleted.push(sourcePath);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
return { converted, deleted };
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* @param {string} format - `yaml` | `json`
|
|
69
|
+
* @param {Object} opts
|
|
70
|
+
* @returns {Promise<{ converted: string[], deleted: string[] }>}
|
|
71
|
+
*/
|
|
72
|
+
async function runConvertProtectionBatch(format, opts = {}) {
|
|
73
|
+
const root = opts.root || getProtectionRoot();
|
|
74
|
+
if (!existsSync(root)) {
|
|
75
|
+
throw new Error(`Protection folder not found: ${root}`);
|
|
76
|
+
}
|
|
77
|
+
const toConvert = listFilesNeedingConvert(root, format);
|
|
78
|
+
if (!toConvert.length) {
|
|
79
|
+
return { converted: [], deleted: [] };
|
|
80
|
+
}
|
|
81
|
+
await confirmProtectionConvert(toConvert, format, opts.force === true);
|
|
82
|
+
return convertProtectionFiles(root, toConvert, format);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
module.exports = {
|
|
86
|
+
runConvertProtectionBatch,
|
|
87
|
+
listFilesNeedingConvert,
|
|
88
|
+
confirmProtectionConvert,
|
|
89
|
+
convertProtectionFiles
|
|
90
|
+
};
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Load protection manifest YAML files (v1).
|
|
3
|
+
* @author AI Fabrix Team
|
|
4
|
+
* @version 1.0.0
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
'use strict';
|
|
8
|
+
|
|
9
|
+
const path = require('path');
|
|
10
|
+
const { existsSync, readFileSync } = require('../internal/fs-real-sync');
|
|
11
|
+
const { yamlToJson } = require('../utils/config-format');
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* @param {string} manifestPath - Absolute path to `.yaml` manifest
|
|
15
|
+
* @returns {Object} Parsed manifest object
|
|
16
|
+
*/
|
|
17
|
+
function loadProtectionManifest(manifestPath) {
|
|
18
|
+
const p = path.resolve(String(manifestPath || '').trim());
|
|
19
|
+
if (!p) {
|
|
20
|
+
throw new Error('Protection manifest path is required');
|
|
21
|
+
}
|
|
22
|
+
if (!existsSync(p)) {
|
|
23
|
+
throw new Error(`Protection manifest not found: ${p}`);
|
|
24
|
+
}
|
|
25
|
+
const ext = path.extname(p).toLowerCase();
|
|
26
|
+
if (ext !== '.yaml' && ext !== '.yml' && ext !== '.json') {
|
|
27
|
+
throw new Error(
|
|
28
|
+
`Protection manifest must be .yaml, .yml, or .json (got ${ext || '(none)'}).`
|
|
29
|
+
);
|
|
30
|
+
}
|
|
31
|
+
const content = readFileSync(p, 'utf8');
|
|
32
|
+
if (ext === '.json') {
|
|
33
|
+
const parsed = JSON.parse(content);
|
|
34
|
+
return parsed === undefined || parsed === null ? {} : parsed;
|
|
35
|
+
}
|
|
36
|
+
return yamlToJson(content);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
module.exports = {
|
|
40
|
+
loadProtectionManifest
|
|
41
|
+
};
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Protection manifest directory under repo `integration/.protection/`.
|
|
3
|
+
* @author AI Fabrix Team
|
|
4
|
+
* @version 2.0.0
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
'use strict';
|
|
8
|
+
|
|
9
|
+
const fs = require('fs');
|
|
10
|
+
const path = require('path');
|
|
11
|
+
const {
|
|
12
|
+
getAppsMaterializationParent,
|
|
13
|
+
getCwdIntegrationRoot
|
|
14
|
+
} = require('../utils/paths');
|
|
15
|
+
const { nodeFs } = require('../internal/node-fs');
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Walk upward from cwd for the nearest directory containing `integration/`.
|
|
19
|
+
*
|
|
20
|
+
* @returns {string|null} Absolute path to `integration/` or null
|
|
21
|
+
*/
|
|
22
|
+
function findNearestIntegrationRootFromCwd() {
|
|
23
|
+
const disk = nodeFs();
|
|
24
|
+
let walk = path.resolve(process.cwd());
|
|
25
|
+
for (let i = 0; i < 64; i += 1) {
|
|
26
|
+
const integrationDir = path.join(walk, 'integration');
|
|
27
|
+
try {
|
|
28
|
+
if (disk.existsSync(integrationDir)) {
|
|
29
|
+
const st = disk.statSync(integrationDir);
|
|
30
|
+
if (st && typeof st.isDirectory === 'function' && st.isDirectory()) {
|
|
31
|
+
return integrationDir;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
} catch {
|
|
35
|
+
// ignore
|
|
36
|
+
}
|
|
37
|
+
const parent = path.dirname(walk);
|
|
38
|
+
if (parent === walk) {
|
|
39
|
+
break;
|
|
40
|
+
}
|
|
41
|
+
walk = parent;
|
|
42
|
+
}
|
|
43
|
+
return null;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Canonical repo-local protection root: `integration/.protection/`.
|
|
48
|
+
*
|
|
49
|
+
* @returns {string}
|
|
50
|
+
*/
|
|
51
|
+
function getRepoProtectionRoot() {
|
|
52
|
+
const cwdInt = getCwdIntegrationRoot();
|
|
53
|
+
if (cwdInt) {
|
|
54
|
+
return path.join(cwdInt, '.protection');
|
|
55
|
+
}
|
|
56
|
+
const materializationParent = path.resolve(getAppsMaterializationParent());
|
|
57
|
+
const nearest = findNearestIntegrationRootFromCwd();
|
|
58
|
+
if (nearest) {
|
|
59
|
+
const resolvedNearest = path.resolve(nearest);
|
|
60
|
+
if (
|
|
61
|
+
resolvedNearest === materializationParent ||
|
|
62
|
+
resolvedNearest.startsWith(`${materializationParent}${path.sep}`)
|
|
63
|
+
) {
|
|
64
|
+
return path.join(resolvedNearest, '.protection');
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
return path.join(materializationParent, 'integration', '.protection');
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* @returns {string}
|
|
72
|
+
*/
|
|
73
|
+
function getLegacyProtectionRoot() {
|
|
74
|
+
return path.join(getAppsMaterializationParent(), '.protection');
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* @param {string} root
|
|
79
|
+
* @returns {boolean}
|
|
80
|
+
*/
|
|
81
|
+
function protectionRootHasManifestFiles(root) {
|
|
82
|
+
if (!root || !fs.existsSync(root)) {
|
|
83
|
+
return false;
|
|
84
|
+
}
|
|
85
|
+
try {
|
|
86
|
+
return fs.readdirSync(root).some((name) => /\.(yaml|yml|json)$/i.test(name));
|
|
87
|
+
} catch {
|
|
88
|
+
return false;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* @returns {string} Absolute path to protection manifests directory
|
|
94
|
+
*/
|
|
95
|
+
function getProtectionRoot() {
|
|
96
|
+
const explicit = process.env.AIFABRIX_PROTECTION_ROOT;
|
|
97
|
+
if (explicit && typeof explicit === 'string' && explicit.trim()) {
|
|
98
|
+
return path.resolve(explicit.trim());
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
const legacyFlag =
|
|
102
|
+
process.env.AIFABRIX_PROTECTION_LEGACY === '1' ||
|
|
103
|
+
String(process.env.AIFABRIX_PROTECTION_LEGACY || '').toLowerCase() === 'true';
|
|
104
|
+
|
|
105
|
+
const repoRoot = getRepoProtectionRoot();
|
|
106
|
+
const legacyRoot = getLegacyProtectionRoot();
|
|
107
|
+
|
|
108
|
+
if (legacyFlag) {
|
|
109
|
+
return legacyRoot;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
const repoHas = protectionRootHasManifestFiles(repoRoot);
|
|
113
|
+
const legacyHas = protectionRootHasManifestFiles(legacyRoot);
|
|
114
|
+
|
|
115
|
+
if (!repoHas && legacyHas && legacyRoot !== repoRoot) {
|
|
116
|
+
return legacyRoot;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
return repoRoot;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Human-readable folder label for batch protection TTY output.
|
|
124
|
+
*
|
|
125
|
+
* @returns {{ root: string, label: string, usingLegacy: boolean, migrationHint: string|null }}
|
|
126
|
+
*/
|
|
127
|
+
function describeProtectionRoot() {
|
|
128
|
+
const root = getProtectionRoot();
|
|
129
|
+
const legacyRoot = getLegacyProtectionRoot();
|
|
130
|
+
const repoRoot = getRepoProtectionRoot();
|
|
131
|
+
const usingLegacy = path.resolve(root) === path.resolve(legacyRoot);
|
|
132
|
+
const legacyHas = protectionRootHasManifestFiles(legacyRoot);
|
|
133
|
+
const repoHas = protectionRootHasManifestFiles(repoRoot);
|
|
134
|
+
|
|
135
|
+
let label = 'integration/.protection';
|
|
136
|
+
if (usingLegacy) {
|
|
137
|
+
label = 'work/.protection (legacy)';
|
|
138
|
+
} else if (process.env.AIFABRIX_PROTECTION_ROOT) {
|
|
139
|
+
label = 'custom';
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
let migrationHint = null;
|
|
143
|
+
if (usingLegacy && legacyHas) {
|
|
144
|
+
migrationHint = `Move manifests to ${repoRoot} (git-friendly integration/.protection/).`;
|
|
145
|
+
} else if (!usingLegacy && legacyHas && !repoHas) {
|
|
146
|
+
migrationHint = `Legacy manifests remain under ${legacyRoot}; migrate to ${repoRoot}.`;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
return { root, label, usingLegacy, migrationHint };
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
module.exports = {
|
|
153
|
+
getProtectionRoot,
|
|
154
|
+
describeProtectionRoot,
|
|
155
|
+
getRepoProtectionRoot,
|
|
156
|
+
getLegacyProtectionRoot,
|
|
157
|
+
findNearestIntegrationRootFromCwd
|
|
158
|
+
};
|