@jterrats/smart-deployment 1.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +193 -0
- package/lib/ai/agentforce-error-handler.d.ts +81 -0
- package/lib/ai/agentforce-error-handler.js +196 -0
- package/lib/ai/agentforce-error-handler.js.map +1 -0
- package/lib/ai/agentforce-priority-service.d.ts +82 -0
- package/lib/ai/agentforce-priority-service.js +257 -0
- package/lib/ai/agentforce-priority-service.js.map +1 -0
- package/lib/ai/agentforce-service.d.ts +99 -0
- package/lib/ai/agentforce-service.js +300 -0
- package/lib/ai/agentforce-service.js.map +1 -0
- package/lib/ai/circuit-breaker.d.ts +115 -0
- package/lib/ai/circuit-breaker.js +277 -0
- package/lib/ai/circuit-breaker.js.map +1 -0
- package/lib/ai/dependency-inference-service.d.ts +76 -0
- package/lib/ai/dependency-inference-service.js +220 -0
- package/lib/ai/dependency-inference-service.js.map +1 -0
- package/lib/ai/llm-provider-factory.d.ts +15 -0
- package/lib/ai/llm-provider-factory.js +36 -0
- package/lib/ai/llm-provider-factory.js.map +1 -0
- package/lib/ai/llm-provider.d.ts +27 -0
- package/lib/ai/llm-provider.js +2 -0
- package/lib/ai/llm-provider.js.map +1 -0
- package/lib/ai/openai-service.d.ts +20 -0
- package/lib/ai/openai-service.js +89 -0
- package/lib/ai/openai-service.js.map +1 -0
- package/lib/ai/prompt-builder.d.ts +79 -0
- package/lib/ai/prompt-builder.js +180 -0
- package/lib/ai/prompt-builder.js.map +1 -0
- package/lib/ai/response-parser.d.ts +67 -0
- package/lib/ai/response-parser.js +234 -0
- package/lib/ai/response-parser.js.map +1 -0
- package/lib/ai/wave-validation-service.d.ts +111 -0
- package/lib/ai/wave-validation-service.js +381 -0
- package/lib/ai/wave-validation-service.js.map +1 -0
- package/lib/analysis/analysis-reporter.d.ts +56 -0
- package/lib/analysis/analysis-reporter.js +170 -0
- package/lib/analysis/analysis-reporter.js.map +1 -0
- package/lib/analytics/error-analytics.d.ts +80 -0
- package/lib/analytics/error-analytics.js +162 -0
- package/lib/analytics/error-analytics.js.map +1 -0
- package/lib/commands/analyze.d.ts +49 -0
- package/lib/commands/analyze.js +232 -0
- package/lib/commands/analyze.js.map +1 -0
- package/lib/commands/config.d.ts +42 -0
- package/lib/commands/config.js +219 -0
- package/lib/commands/config.js.map +1 -0
- package/lib/commands/resume.d.ts +26 -0
- package/lib/commands/resume.js +69 -0
- package/lib/commands/resume.js.map +1 -0
- package/lib/commands/start.d.ts +70 -0
- package/lib/commands/start.js +659 -0
- package/lib/commands/start.js.map +1 -0
- package/lib/commands/status.d.ts +37 -0
- package/lib/commands/status.js +69 -0
- package/lib/commands/status.js.map +1 -0
- package/lib/commands/validate.d.ts +33 -0
- package/lib/commands/validate.js +66 -0
- package/lib/commands/validate.js.map +1 -0
- package/lib/config/repo-config.d.ts +22 -0
- package/lib/config/repo-config.js +31 -0
- package/lib/config/repo-config.js.map +1 -0
- package/lib/constants/agentforce-limits.d.ts +174 -0
- package/lib/constants/agentforce-limits.js +262 -0
- package/lib/constants/agentforce-limits.js.map +1 -0
- package/lib/constants/api-version.d.ts +70 -0
- package/lib/constants/api-version.js +122 -0
- package/lib/constants/api-version.js.map +1 -0
- package/lib/constants/deployment-order.d.ts +68 -0
- package/lib/constants/deployment-order.js +162 -0
- package/lib/constants/deployment-order.js.map +1 -0
- package/lib/constants/salesforce-limits.d.ts +107 -0
- package/lib/constants/salesforce-limits.js +104 -0
- package/lib/constants/salesforce-limits.js.map +1 -0
- package/lib/dependencies/circular-dependency-detector.d.ts +137 -0
- package/lib/dependencies/circular-dependency-detector.js +329 -0
- package/lib/dependencies/circular-dependency-detector.js.map +1 -0
- package/lib/dependencies/cycle-remediation-planner.d.ts +50 -0
- package/lib/dependencies/cycle-remediation-planner.js +192 -0
- package/lib/dependencies/cycle-remediation-planner.js.map +1 -0
- package/lib/dependencies/dependency-cache.d.ts +134 -0
- package/lib/dependencies/dependency-cache.js +303 -0
- package/lib/dependencies/dependency-cache.js.map +1 -0
- package/lib/dependencies/dependency-depth-calculator.d.ts +145 -0
- package/lib/dependencies/dependency-depth-calculator.js +368 -0
- package/lib/dependencies/dependency-depth-calculator.js.map +1 -0
- package/lib/dependencies/dependency-graph-builder.d.ts +151 -0
- package/lib/dependencies/dependency-graph-builder.js +411 -0
- package/lib/dependencies/dependency-graph-builder.js.map +1 -0
- package/lib/dependencies/dependency-impact-analyzer.d.ts +145 -0
- package/lib/dependencies/dependency-impact-analyzer.js +330 -0
- package/lib/dependencies/dependency-impact-analyzer.js.map +1 -0
- package/lib/dependencies/dependency-merger.d.ts +122 -0
- package/lib/dependencies/dependency-merger.js +245 -0
- package/lib/dependencies/dependency-merger.js.map +1 -0
- package/lib/dependencies/dependency-resolver.d.ts +157 -0
- package/lib/dependencies/dependency-resolver.js +298 -0
- package/lib/dependencies/dependency-resolver.js.map +1 -0
- package/lib/dependencies/dependency-validator.d.ts +123 -0
- package/lib/dependencies/dependency-validator.js +291 -0
- package/lib/dependencies/dependency-validator.js.map +1 -0
- package/lib/dependencies/graph-visualizer.d.ts +110 -0
- package/lib/dependencies/graph-visualizer.js +262 -0
- package/lib/dependencies/graph-visualizer.js.map +1 -0
- package/lib/dependencies/heuristic-inference.d.ts +136 -0
- package/lib/dependencies/heuristic-inference.js +430 -0
- package/lib/dependencies/heuristic-inference.js.map +1 -0
- package/lib/deployment/cycle-source-editor.d.ts +34 -0
- package/lib/deployment/cycle-source-editor.js +121 -0
- package/lib/deployment/cycle-source-editor.js.map +1 -0
- package/lib/deployment/deployment-error-handler.d.ts +38 -0
- package/lib/deployment/deployment-error-handler.js +79 -0
- package/lib/deployment/deployment-error-handler.js.map +1 -0
- package/lib/deployment/deployment-reporter.d.ts +63 -0
- package/lib/deployment/deployment-reporter.js +150 -0
- package/lib/deployment/deployment-reporter.js.map +1 -0
- package/lib/deployment/deployment-state-summary.d.ts +38 -0
- package/lib/deployment/deployment-state-summary.js +209 -0
- package/lib/deployment/deployment-state-summary.js.map +1 -0
- package/lib/deployment/deployment-status-service.d.ts +36 -0
- package/lib/deployment/deployment-status-service.js +128 -0
- package/lib/deployment/deployment-status-service.js.map +1 -0
- package/lib/deployment/deployment-tracker.d.ts +42 -0
- package/lib/deployment/deployment-tracker.js +79 -0
- package/lib/deployment/deployment-tracker.js.map +1 -0
- package/lib/deployment/deployment-validation-service.d.ts +28 -0
- package/lib/deployment/deployment-validation-service.js +161 -0
- package/lib/deployment/deployment-validation-service.js.map +1 -0
- package/lib/deployment/retry-handler.d.ts +37 -0
- package/lib/deployment/retry-handler.js +86 -0
- package/lib/deployment/retry-handler.js.map +1 -0
- package/lib/deployment/sf-cli-integration.d.ts +42 -0
- package/lib/deployment/sf-cli-integration.js +105 -0
- package/lib/deployment/sf-cli-integration.js.map +1 -0
- package/lib/deployment/state-manager.d.ts +61 -0
- package/lib/deployment/state-manager.js +83 -0
- package/lib/deployment/state-manager.js.map +1 -0
- package/lib/deployment/test-executor.d.ts +41 -0
- package/lib/deployment/test-executor.js +87 -0
- package/lib/deployment/test-executor.js.map +1 -0
- package/lib/errors/base-error.d.ts +24 -0
- package/lib/errors/base-error.js +66 -0
- package/lib/errors/base-error.js.map +1 -0
- package/lib/errors/dependency-error.d.ts +37 -0
- package/lib/errors/dependency-error.js +76 -0
- package/lib/errors/dependency-error.js.map +1 -0
- package/lib/errors/deployment-error.d.ts +55 -0
- package/lib/errors/deployment-error.js +132 -0
- package/lib/errors/deployment-error.js.map +1 -0
- package/lib/errors/index.d.ts +45 -0
- package/lib/errors/index.js +71 -0
- package/lib/errors/index.js.map +1 -0
- package/lib/errors/network-error.d.ts +53 -0
- package/lib/errors/network-error.js +111 -0
- package/lib/errors/network-error.js.map +1 -0
- package/lib/errors/parsing-error.d.ts +41 -0
- package/lib/errors/parsing-error.js +69 -0
- package/lib/errors/parsing-error.js.map +1 -0
- package/lib/errors/validation-error-reporter.d.ts +34 -0
- package/lib/errors/validation-error-reporter.js +99 -0
- package/lib/errors/validation-error-reporter.js.map +1 -0
- package/lib/errors/validation-error.d.ts +58 -0
- package/lib/errors/validation-error.js +131 -0
- package/lib/errors/validation-error.js.map +1 -0
- package/lib/index.d.ts +2 -0
- package/lib/index.js +2 -0
- package/lib/index.js.map +1 -0
- package/lib/monitoring/performance-monitor.d.ts +98 -0
- package/lib/monitoring/performance-monitor.js +260 -0
- package/lib/monitoring/performance-monitor.js.map +1 -0
- package/lib/parsers/apex-class-parser.d.ts +47 -0
- package/lib/parsers/apex-class-parser.js +368 -0
- package/lib/parsers/apex-class-parser.js.map +1 -0
- package/lib/parsers/apex-trigger-parser.d.ts +48 -0
- package/lib/parsers/apex-trigger-parser.js +229 -0
- package/lib/parsers/apex-trigger-parser.js.map +1 -0
- package/lib/parsers/aura-parser.d.ts +55 -0
- package/lib/parsers/aura-parser.js +229 -0
- package/lib/parsers/aura-parser.js.map +1 -0
- package/lib/parsers/bot-parser.d.ts +65 -0
- package/lib/parsers/bot-parser.js +225 -0
- package/lib/parsers/bot-parser.js.map +1 -0
- package/lib/parsers/custom-metadata-parser.d.ts +94 -0
- package/lib/parsers/custom-metadata-parser.js +199 -0
- package/lib/parsers/custom-metadata-parser.js.map +1 -0
- package/lib/parsers/custom-object-parser.d.ts +62 -0
- package/lib/parsers/custom-object-parser.js +297 -0
- package/lib/parsers/custom-object-parser.js.map +1 -0
- package/lib/parsers/email-template-parser.d.ts +64 -0
- package/lib/parsers/email-template-parser.js +238 -0
- package/lib/parsers/email-template-parser.js.map +1 -0
- package/lib/parsers/error-resilient-parser.d.ts +110 -0
- package/lib/parsers/error-resilient-parser.js +277 -0
- package/lib/parsers/error-resilient-parser.js.map +1 -0
- package/lib/parsers/flexipage-parser.d.ts +64 -0
- package/lib/parsers/flexipage-parser.js +196 -0
- package/lib/parsers/flexipage-parser.js.map +1 -0
- package/lib/parsers/flow-parser.d.ts +54 -0
- package/lib/parsers/flow-parser.js +287 -0
- package/lib/parsers/flow-parser.js.map +1 -0
- package/lib/parsers/genai-prompt-parser.d.ts +67 -0
- package/lib/parsers/genai-prompt-parser.js +160 -0
- package/lib/parsers/genai-prompt-parser.js.map +1 -0
- package/lib/parsers/layout-parser.d.ts +64 -0
- package/lib/parsers/layout-parser.js +267 -0
- package/lib/parsers/layout-parser.js.map +1 -0
- package/lib/parsers/lwc-parser.d.ts +60 -0
- package/lib/parsers/lwc-parser.js +264 -0
- package/lib/parsers/lwc-parser.js.map +1 -0
- package/lib/parsers/permission-set-parser.d.ts +86 -0
- package/lib/parsers/permission-set-parser.js +152 -0
- package/lib/parsers/permission-set-parser.js.map +1 -0
- package/lib/parsers/profile-parser.d.ts +81 -0
- package/lib/parsers/profile-parser.js +141 -0
- package/lib/parsers/profile-parser.js.map +1 -0
- package/lib/parsers/visualforce-parser.d.ts +47 -0
- package/lib/parsers/visualforce-parser.js +180 -0
- package/lib/parsers/visualforce-parser.js.map +1 -0
- package/lib/provisioning/data-provisioner.d.ts +88 -0
- package/lib/provisioning/data-provisioner.js +257 -0
- package/lib/provisioning/data-provisioner.js.map +1 -0
- package/lib/scanner/custom-structure-scanner.d.ts +66 -0
- package/lib/scanner/custom-structure-scanner.js +229 -0
- package/lib/scanner/custom-structure-scanner.js.map +1 -0
- package/lib/scanner/forceignore-parser.d.ts +69 -0
- package/lib/scanner/forceignore-parser.js +195 -0
- package/lib/scanner/forceignore-parser.js.map +1 -0
- package/lib/scanner/metadata-format-scanner.d.ts +77 -0
- package/lib/scanner/metadata-format-scanner.js +282 -0
- package/lib/scanner/metadata-format-scanner.js.map +1 -0
- package/lib/scanner/monorepo-scanner.d.ts +71 -0
- package/lib/scanner/monorepo-scanner.js +225 -0
- package/lib/scanner/monorepo-scanner.js.map +1 -0
- package/lib/scanner/project-validator.d.ts +55 -0
- package/lib/scanner/project-validator.js +235 -0
- package/lib/scanner/project-validator.js.map +1 -0
- package/lib/scanner/sfdx-project-detector.d.ts +86 -0
- package/lib/scanner/sfdx-project-detector.js +240 -0
- package/lib/scanner/sfdx-project-detector.js.map +1 -0
- package/lib/scanner/structure-validator.d.ts +64 -0
- package/lib/scanner/structure-validator.js +296 -0
- package/lib/scanner/structure-validator.js.map +1 -0
- package/lib/services/metadata-scanner-service.d.ts +64 -0
- package/lib/services/metadata-scanner-service.js +651 -0
- package/lib/services/metadata-scanner-service.js.map +1 -0
- package/lib/types/agentforce.d.ts +157 -0
- package/lib/types/agentforce.js +2 -0
- package/lib/types/agentforce.js.map +1 -0
- package/lib/types/dependency.d.ts +98 -0
- package/lib/types/dependency.js +5 -0
- package/lib/types/dependency.js.map +1 -0
- package/lib/types/deployment-plan.d.ts +81 -0
- package/lib/types/deployment-plan.js +6 -0
- package/lib/types/deployment-plan.js.map +1 -0
- package/lib/types/deployment.d.ts +88 -0
- package/lib/types/deployment.js +5 -0
- package/lib/types/deployment.js.map +1 -0
- package/lib/types/graph.d.ts +35 -0
- package/lib/types/graph.js +5 -0
- package/lib/types/graph.js.map +1 -0
- package/lib/types/index.d.ts +12 -0
- package/lib/types/index.js +17 -0
- package/lib/types/index.js.map +1 -0
- package/lib/types/metadata.d.ts +101 -0
- package/lib/types/metadata.js +13 -0
- package/lib/types/metadata.js.map +1 -0
- package/lib/types/project.d.ts +156 -0
- package/lib/types/project.js +56 -0
- package/lib/types/project.js.map +1 -0
- package/lib/types/salesforce/apex.d.ts +94 -0
- package/lib/types/salesforce/apex.js +6 -0
- package/lib/types/salesforce/apex.js.map +1 -0
- package/lib/types/salesforce/aura.d.ts +150 -0
- package/lib/types/salesforce/aura.js +6 -0
- package/lib/types/salesforce/aura.js.map +1 -0
- package/lib/types/salesforce/bot.d.ts +293 -0
- package/lib/types/salesforce/bot.js +6 -0
- package/lib/types/salesforce/bot.js.map +1 -0
- package/lib/types/salesforce/common.d.ts +15 -0
- package/lib/types/salesforce/common.js +5 -0
- package/lib/types/salesforce/common.js.map +1 -0
- package/lib/types/salesforce/custom-metadata.d.ts +92 -0
- package/lib/types/salesforce/custom-metadata.js +6 -0
- package/lib/types/salesforce/custom-metadata.js.map +1 -0
- package/lib/types/salesforce/email.d.ts +56 -0
- package/lib/types/salesforce/email.js +6 -0
- package/lib/types/salesforce/email.js.map +1 -0
- package/lib/types/salesforce/flexipage.d.ts +149 -0
- package/lib/types/salesforce/flexipage.js +6 -0
- package/lib/types/salesforce/flexipage.js.map +1 -0
- package/lib/types/salesforce/flow.d.ts +516 -0
- package/lib/types/salesforce/flow.js +6 -0
- package/lib/types/salesforce/flow.js.map +1 -0
- package/lib/types/salesforce/genai.d.ts +67 -0
- package/lib/types/salesforce/genai.js +6 -0
- package/lib/types/salesforce/genai.js.map +1 -0
- package/lib/types/salesforce/index.d.ts +27 -0
- package/lib/types/salesforce/index.js +43 -0
- package/lib/types/salesforce/index.js.map +1 -0
- package/lib/types/salesforce/layout.d.ts +236 -0
- package/lib/types/salesforce/layout.js +6 -0
- package/lib/types/salesforce/layout.js.map +1 -0
- package/lib/types/salesforce/lwc.d.ts +123 -0
- package/lib/types/salesforce/lwc.js +6 -0
- package/lib/types/salesforce/lwc.js.map +1 -0
- package/lib/types/salesforce/object.d.ts +427 -0
- package/lib/types/salesforce/object.js +6 -0
- package/lib/types/salesforce/object.js.map +1 -0
- package/lib/types/salesforce/parser-types.d.ts +79 -0
- package/lib/types/salesforce/parser-types.js +80 -0
- package/lib/types/salesforce/parser-types.js.map +1 -0
- package/lib/types/salesforce/permission.d.ts +289 -0
- package/lib/types/salesforce/permission.js +6 -0
- package/lib/types/salesforce/permission.js.map +1 -0
- package/lib/types/salesforce/resource.d.ts +93 -0
- package/lib/types/salesforce/resource.js +6 -0
- package/lib/types/salesforce/resource.js.map +1 -0
- package/lib/types/salesforce/visualforce.d.ts +70 -0
- package/lib/types/salesforce/visualforce.js +6 -0
- package/lib/types/salesforce/visualforce.js.map +1 -0
- package/lib/utils/cache-manager.d.ts +158 -0
- package/lib/utils/cache-manager.js +429 -0
- package/lib/utils/cache-manager.js.map +1 -0
- package/lib/utils/deployment-plan-manager.d.ts +40 -0
- package/lib/utils/deployment-plan-manager.js +183 -0
- package/lib/utils/deployment-plan-manager.js.map +1 -0
- package/lib/utils/error-aggregator.d.ts +117 -0
- package/lib/utils/error-aggregator.js +268 -0
- package/lib/utils/error-aggregator.js.map +1 -0
- package/lib/utils/file-system.d.ts +62 -0
- package/lib/utils/file-system.js +167 -0
- package/lib/utils/file-system.js.map +1 -0
- package/lib/utils/functional.d.ts +52 -0
- package/lib/utils/functional.js +61 -0
- package/lib/utils/functional.js.map +1 -0
- package/lib/utils/graph-algorithms.d.ts +53 -0
- package/lib/utils/graph-algorithms.js +177 -0
- package/lib/utils/graph-algorithms.js.map +1 -0
- package/lib/utils/logger.d.ts +154 -0
- package/lib/utils/logger.js +327 -0
- package/lib/utils/logger.js.map +1 -0
- package/lib/utils/network-handler.d.ts +64 -0
- package/lib/utils/network-handler.js +147 -0
- package/lib/utils/network-handler.js.map +1 -0
- package/lib/utils/performance.d.ts +148 -0
- package/lib/utils/performance.js +294 -0
- package/lib/utils/performance.js.map +1 -0
- package/lib/utils/string.d.ts +197 -0
- package/lib/utils/string.js +331 -0
- package/lib/utils/string.js.map +1 -0
- package/lib/utils/xml.d.ts +97 -0
- package/lib/utils/xml.js +227 -0
- package/lib/utils/xml.js.map +1 -0
- package/lib/validators/xml-metadata-validator.d.ts +106 -0
- package/lib/validators/xml-metadata-validator.js +509 -0
- package/lib/validators/xml-metadata-validator.js.map +1 -0
- package/lib/waves/priority-wave-generator-ai.d.ts +85 -0
- package/lib/waves/priority-wave-generator-ai.js +191 -0
- package/lib/waves/priority-wave-generator-ai.js.map +1 -0
- package/lib/waves/priority-wave-generator.d.ts +47 -0
- package/lib/waves/priority-wave-generator.js +88 -0
- package/lib/waves/priority-wave-generator.js.map +1 -0
- package/lib/waves/test-optimizer.d.ts +155 -0
- package/lib/waves/test-optimizer.js +290 -0
- package/lib/waves/test-optimizer.js.map +1 -0
- package/lib/waves/wave-builder.d.ts +147 -0
- package/lib/waves/wave-builder.js +286 -0
- package/lib/waves/wave-builder.js.map +1 -0
- package/lib/waves/wave-diff-generator.d.ts +17 -0
- package/lib/waves/wave-diff-generator.js +6 -0
- package/lib/waves/wave-diff-generator.js.map +1 -0
- package/lib/waves/wave-executor.d.ts +33 -0
- package/lib/waves/wave-executor.js +50 -0
- package/lib/waves/wave-executor.js.map +1 -0
- package/lib/waves/wave-merger.d.ts +96 -0
- package/lib/waves/wave-merger.js +181 -0
- package/lib/waves/wave-merger.js.map +1 -0
- package/lib/waves/wave-metadata-generator.d.ts +13 -0
- package/lib/waves/wave-metadata-generator.js +12 -0
- package/lib/waves/wave-metadata-generator.js.map +1 -0
- package/lib/waves/wave-splitter.d.ts +154 -0
- package/lib/waves/wave-splitter.js +307 -0
- package/lib/waves/wave-splitter.js.map +1 -0
- package/lib/waves/wave-validator.d.ts +17 -0
- package/lib/waves/wave-validator.js +15 -0
- package/lib/waves/wave-validator.js.map +1 -0
- package/messages/analyze.json +18 -0
- package/messages/config.json +29 -0
- package/messages/resume.json +9 -0
- package/messages/start.json +24 -0
- package/messages/status.json +8 -0
- package/messages/validate.json +9 -0
- package/npm-shrinkwrap.json +25676 -0
- package/oclif.lock +11988 -0
- package/oclif.manifest.json +589 -0
- package/package.json +224 -0
|
@@ -0,0 +1,659 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* smart-deployment:start command
|
|
3
|
+
* Main deployment command that orchestrates the entire workflow
|
|
4
|
+
*
|
|
5
|
+
* @ac US-046-AC-1: Analyzes metadata automatically
|
|
6
|
+
* @ac US-046-AC-2: Generates deployment waves
|
|
7
|
+
* @ac US-046-AC-3: Executes deployment sequentially
|
|
8
|
+
* @ac US-046-AC-4: Supports --target-org flag
|
|
9
|
+
* @ac US-046-AC-5: Supports --dry-run flag
|
|
10
|
+
* @ac US-046-AC-6: Supports --validate-only flag
|
|
11
|
+
* @ac US-046-AC-7: Supports --skip-tests flag
|
|
12
|
+
* @ac US-046-AC-8: Shows progress bar
|
|
13
|
+
* @ac US-046-AC-9: Generates deployment report
|
|
14
|
+
* @ac US-046-AC-10: Handles failures gracefully
|
|
15
|
+
*
|
|
16
|
+
* @issue #46
|
|
17
|
+
*/
|
|
18
|
+
import { mkdir, readFile, writeFile } from 'node:fs/promises';
|
|
19
|
+
import * as path from 'node:path';
|
|
20
|
+
import { Messages } from '@salesforce/core';
|
|
21
|
+
import { Flags, SfCommand, optionalOrgFlagWithDeprecations } from '@salesforce/sf-plugins-core';
|
|
22
|
+
import { getLogger } from '../utils/logger.js';
|
|
23
|
+
import { CycleRemediationPlanner } from '../dependencies/cycle-remediation-planner.js';
|
|
24
|
+
import { CycleSourceEditor, } from '../deployment/cycle-source-editor.js';
|
|
25
|
+
import { SfCliIntegration } from '../deployment/sf-cli-integration.js';
|
|
26
|
+
import { MetadataScannerService } from '../services/metadata-scanner-service.js';
|
|
27
|
+
import { WaveBuilder } from '../waves/wave-builder.js';
|
|
28
|
+
import { AIEnhancedPriorityWaveGenerator } from '../waves/priority-wave-generator-ai.js';
|
|
29
|
+
import { getWavesInExecutionOrder } from '../waves/wave-executor.js';
|
|
30
|
+
import { StateManager } from '../deployment/state-manager.js';
|
|
31
|
+
import { DeploymentTracker } from '../deployment/deployment-tracker.js';
|
|
32
|
+
import { AgentforcePriorityService } from '../ai/agentforce-priority-service.js';
|
|
33
|
+
import { DependencyInferenceService } from '../ai/dependency-inference-service.js';
|
|
34
|
+
import { DependencyGraphBuilder } from '../dependencies/dependency-graph-builder.js';
|
|
35
|
+
Messages.importMessagesDirectoryFromMetaUrl(import.meta.url);
|
|
36
|
+
const messages = Messages.loadMessages('@jterrats/smart-deployment', 'start');
|
|
37
|
+
const logger = getLogger('StartCommand');
|
|
38
|
+
export default class Start extends SfCommand {
|
|
39
|
+
static summary = messages.getMessage('summary');
|
|
40
|
+
static description = messages.getMessage('description');
|
|
41
|
+
static examples = messages.getMessages('examples');
|
|
42
|
+
/**
|
|
43
|
+
* @ac US-046-AC-4: Supports --target-org flag
|
|
44
|
+
* @ac US-046-AC-5: Supports --dry-run flag
|
|
45
|
+
* @ac US-046-AC-6: Supports --validate-only flag
|
|
46
|
+
* @ac US-046-AC-7: Supports --skip-tests flag
|
|
47
|
+
* @ac US-057-AC-1: Send component list to Agentforce
|
|
48
|
+
*/
|
|
49
|
+
static flags = {
|
|
50
|
+
'target-org': optionalOrgFlagWithDeprecations,
|
|
51
|
+
'dry-run': Flags.boolean({
|
|
52
|
+
summary: messages.getMessage('flags.dry-run.summary'),
|
|
53
|
+
char: 'd',
|
|
54
|
+
default: false,
|
|
55
|
+
}),
|
|
56
|
+
'validate-only': Flags.boolean({
|
|
57
|
+
summary: messages.getMessage('flags.validate-only.summary'),
|
|
58
|
+
char: 'v',
|
|
59
|
+
default: false,
|
|
60
|
+
}),
|
|
61
|
+
'skip-tests': Flags.boolean({
|
|
62
|
+
summary: messages.getMessage('flags.skip-tests.summary'),
|
|
63
|
+
char: 's',
|
|
64
|
+
default: false,
|
|
65
|
+
}),
|
|
66
|
+
'source-path': Flags.string({
|
|
67
|
+
summary: messages.getMessage('flags.source-path.summary'),
|
|
68
|
+
description: messages.getMessage('flags.source-path.description'),
|
|
69
|
+
}),
|
|
70
|
+
'allow-cycle-remediation': Flags.boolean({
|
|
71
|
+
summary: messages.getMessage('flags.allow-cycle-remediation.summary'),
|
|
72
|
+
description: messages.getMessage('flags.allow-cycle-remediation.description'),
|
|
73
|
+
default: false,
|
|
74
|
+
}),
|
|
75
|
+
'use-ai': Flags.boolean({
|
|
76
|
+
summary: messages.getMessage('flags.use-ai.summary'),
|
|
77
|
+
description: messages.getMessage('flags.use-ai.description'),
|
|
78
|
+
default: false,
|
|
79
|
+
}),
|
|
80
|
+
'org-type': Flags.string({
|
|
81
|
+
summary: messages.getMessage('flags.org-type.summary'),
|
|
82
|
+
description: messages.getMessage('flags.org-type.description'),
|
|
83
|
+
options: ['Production', 'Sandbox', 'Developer'],
|
|
84
|
+
}),
|
|
85
|
+
industry: Flags.string({
|
|
86
|
+
summary: messages.getMessage('flags.industry.summary'),
|
|
87
|
+
description: messages.getMessage('flags.industry.description'),
|
|
88
|
+
}),
|
|
89
|
+
};
|
|
90
|
+
/**
|
|
91
|
+
* @ac US-046-AC-8: Shows progress bar
|
|
92
|
+
* @ac US-046-AC-9: Generates deployment report
|
|
93
|
+
* @ac US-046-AC-10: Handles failures gracefully
|
|
94
|
+
*/
|
|
95
|
+
async run() {
|
|
96
|
+
const { flags } = await this.parse(Start);
|
|
97
|
+
const sourcePath = typeof flags['source-path'] === 'string' ? flags['source-path'] : undefined;
|
|
98
|
+
try {
|
|
99
|
+
logger.info('Starting smart deployment', { flags });
|
|
100
|
+
// AC-1: Analyze metadata
|
|
101
|
+
this.log('📊 Analyzing metadata...');
|
|
102
|
+
const metadataCount = await this.analyzeMetadata(sourcePath);
|
|
103
|
+
this.log(`✅ Found ${metadataCount} metadata components`);
|
|
104
|
+
// AC-2: Generate waves
|
|
105
|
+
this.log('🌊 Generating deployment waves...');
|
|
106
|
+
const deploymentContext = await this.buildDeploymentContext(flags, sourcePath);
|
|
107
|
+
const waves = deploymentContext.orderedWaves.length;
|
|
108
|
+
this.log(`✅ Generated ${waves} waves`);
|
|
109
|
+
// AC US-057-AC-6: Report AI decisions
|
|
110
|
+
if (flags['use-ai']) {
|
|
111
|
+
this.log('🤖 AI-enhanced prioritization enabled');
|
|
112
|
+
}
|
|
113
|
+
// AC-3: Execute deployment
|
|
114
|
+
if (!flags['dry-run']) {
|
|
115
|
+
this.log('🚀 Executing deployment...');
|
|
116
|
+
await this.executeDeployment(flags, sourcePath);
|
|
117
|
+
}
|
|
118
|
+
else {
|
|
119
|
+
this.log('🔍 Dry-run mode: skipping actual deployment');
|
|
120
|
+
}
|
|
121
|
+
// AC-9: Generate report
|
|
122
|
+
this.log('📄 Generating deployment report...');
|
|
123
|
+
this.generateReport(waves);
|
|
124
|
+
return { success: true, waves, ai: deploymentContext.aiContext };
|
|
125
|
+
}
|
|
126
|
+
catch (error) {
|
|
127
|
+
// AC-10: Handle failures gracefully
|
|
128
|
+
logger.error('Deployment failed', { error });
|
|
129
|
+
this.error(`Deployment failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
async analyzeMetadata(sourcePath) {
|
|
133
|
+
const scanner = new MetadataScannerService();
|
|
134
|
+
const result = await scanner.scan({ sourcePath });
|
|
135
|
+
if (result.errors.length > 0) {
|
|
136
|
+
logger.error('Metadata scanning completed with errors', { errors: result.errors });
|
|
137
|
+
result.errors.forEach((err) => this.warn(err));
|
|
138
|
+
}
|
|
139
|
+
if (result.warnings.length > 0) {
|
|
140
|
+
logger.warn('Metadata scanning completed with warnings', { warnings: result.warnings });
|
|
141
|
+
result.warnings.forEach((warn) => this.warn(warn));
|
|
142
|
+
}
|
|
143
|
+
return result.components.length;
|
|
144
|
+
}
|
|
145
|
+
async executeDeployment(flags, sourcePath) {
|
|
146
|
+
const dryRun = flags['dry-run'];
|
|
147
|
+
const validateOnly = flags['validate-only'];
|
|
148
|
+
const allowCycleRemediation = flags['allow-cycle-remediation'];
|
|
149
|
+
const skipTests = flags['skip-tests'];
|
|
150
|
+
const targetOrg = this.getTargetOrgIdentifier(flags['target-org']);
|
|
151
|
+
if (dryRun || validateOnly) {
|
|
152
|
+
this.log('🔍 Dry-run/Validate mode: skipping actual deployment');
|
|
153
|
+
return;
|
|
154
|
+
}
|
|
155
|
+
const deploymentContext = await this.buildDeploymentContext(flags, sourcePath);
|
|
156
|
+
const { scanResult, orderedWaves } = deploymentContext;
|
|
157
|
+
const planner = new CycleRemediationPlanner(scanResult.dependencyResult.graph, {
|
|
158
|
+
components: scanResult.dependencyResult.components,
|
|
159
|
+
});
|
|
160
|
+
const remediationPlan = planner.createPlan();
|
|
161
|
+
if (remediationPlan.cycles.length > 0) {
|
|
162
|
+
this.log(`♻️ Detected ${remediationPlan.cycles.length} circular dependency cycle(s).`);
|
|
163
|
+
if (!allowCycleRemediation) {
|
|
164
|
+
this.error('Circular dependencies detected. Re-run with --allow-cycle-remediation for supported ApexClass cycles or resolve them manually.');
|
|
165
|
+
}
|
|
166
|
+
if (!remediationPlan.supported) {
|
|
167
|
+
this.error([
|
|
168
|
+
'Cycle remediation was requested, but one or more cycles are not safely supported.',
|
|
169
|
+
...remediationPlan.warnings,
|
|
170
|
+
].join('\n'));
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
// Initialize deployment services
|
|
174
|
+
const sfCli = new SfCliIntegration();
|
|
175
|
+
const stateManager = new StateManager({ baseDir: sourcePath ?? process.cwd() });
|
|
176
|
+
const tracker = new DeploymentTracker();
|
|
177
|
+
const deploymentId = `deployment-${Date.now()}`;
|
|
178
|
+
if (remediationPlan.cycles.length > 0) {
|
|
179
|
+
if (!targetOrg) {
|
|
180
|
+
this.error('The --target-org flag is required for cycle remediation deployments.');
|
|
181
|
+
}
|
|
182
|
+
await this.executeCycleRemediationDeployment({
|
|
183
|
+
deploymentId,
|
|
184
|
+
targetOrg,
|
|
185
|
+
sourcePath,
|
|
186
|
+
stateManager,
|
|
187
|
+
tracker,
|
|
188
|
+
plan: remediationPlan,
|
|
189
|
+
sfCli,
|
|
190
|
+
skipTests,
|
|
191
|
+
componentMap: scanResult.dependencyResult.components,
|
|
192
|
+
});
|
|
193
|
+
return;
|
|
194
|
+
}
|
|
195
|
+
if (!targetOrg) {
|
|
196
|
+
this.error('The --target-org flag is required for real deployments.');
|
|
197
|
+
}
|
|
198
|
+
// Execute waves sequentially
|
|
199
|
+
await this.forEachSequentially(orderedWaves, async (wave) => {
|
|
200
|
+
this.log(`\n🌊 Deploying Wave ${wave.number}/${orderedWaves.length} (${wave.components.length} components)...`);
|
|
201
|
+
try {
|
|
202
|
+
// Generate manifest for this wave
|
|
203
|
+
const manifestPath = await this.generateWaveManifest({
|
|
204
|
+
baseDir: sourcePath ?? process.cwd(),
|
|
205
|
+
waveNumber: wave.number,
|
|
206
|
+
components: wave.components,
|
|
207
|
+
componentMap: scanResult.dependencyResult.components,
|
|
208
|
+
});
|
|
209
|
+
// Execute deployment
|
|
210
|
+
tracker.startTracking(deploymentId, wave.number, orderedWaves.length);
|
|
211
|
+
const result = await sfCli.deploy({
|
|
212
|
+
manifestPath,
|
|
213
|
+
targetOrg,
|
|
214
|
+
testLevel: this.resolveTestLevel(skipTests),
|
|
215
|
+
});
|
|
216
|
+
tracker.updateProgress(deploymentId, result);
|
|
217
|
+
if (!result.success) {
|
|
218
|
+
const aiMetadata = deploymentContext.aiContext === undefined
|
|
219
|
+
? {}
|
|
220
|
+
: {
|
|
221
|
+
aiProvider: deploymentContext.aiContext.provider,
|
|
222
|
+
aiModel: deploymentContext.aiContext.model,
|
|
223
|
+
aiFallback: deploymentContext.aiContext.fallback,
|
|
224
|
+
aiAdjustments: deploymentContext.aiContext.aiAdjustments,
|
|
225
|
+
aiUnknownTypes: deploymentContext.aiContext.unknownTypes,
|
|
226
|
+
aiInferenceFallback: deploymentContext.aiContext.inferenceFallback,
|
|
227
|
+
aiInferredDependencies: deploymentContext.aiContext.inferredDependencies,
|
|
228
|
+
};
|
|
229
|
+
await stateManager.saveState({
|
|
230
|
+
deploymentId,
|
|
231
|
+
targetOrg,
|
|
232
|
+
timestamp: new Date().toISOString(),
|
|
233
|
+
totalWaves: orderedWaves.length,
|
|
234
|
+
completedWaves: Array.from({ length: Math.max(0, wave.number - 1) }, (_, i) => i + 1),
|
|
235
|
+
currentWave: wave.number,
|
|
236
|
+
failedWave: {
|
|
237
|
+
waveNumber: wave.number,
|
|
238
|
+
error: result.output,
|
|
239
|
+
timestamp: new Date().toISOString(),
|
|
240
|
+
},
|
|
241
|
+
metadata: {
|
|
242
|
+
lastKnownStatus: result.status,
|
|
243
|
+
testsRun: result.testsRun,
|
|
244
|
+
testFailures: result.testFailures,
|
|
245
|
+
testLevel: this.resolveTestLevel(skipTests),
|
|
246
|
+
...aiMetadata,
|
|
247
|
+
},
|
|
248
|
+
});
|
|
249
|
+
this.error(`Wave ${wave.number} failed: ${result.output}`);
|
|
250
|
+
}
|
|
251
|
+
// Save state after each wave
|
|
252
|
+
const aiMetadata = deploymentContext.aiContext === undefined
|
|
253
|
+
? {}
|
|
254
|
+
: {
|
|
255
|
+
aiProvider: deploymentContext.aiContext.provider,
|
|
256
|
+
aiModel: deploymentContext.aiContext.model,
|
|
257
|
+
aiFallback: deploymentContext.aiContext.fallback,
|
|
258
|
+
aiAdjustments: deploymentContext.aiContext.aiAdjustments,
|
|
259
|
+
aiUnknownTypes: deploymentContext.aiContext.unknownTypes,
|
|
260
|
+
aiInferenceFallback: deploymentContext.aiContext.inferenceFallback,
|
|
261
|
+
aiInferredDependencies: deploymentContext.aiContext.inferredDependencies,
|
|
262
|
+
};
|
|
263
|
+
await stateManager.saveState({
|
|
264
|
+
deploymentId,
|
|
265
|
+
targetOrg,
|
|
266
|
+
timestamp: new Date().toISOString(),
|
|
267
|
+
totalWaves: orderedWaves.length,
|
|
268
|
+
completedWaves: Array.from({ length: wave.number }, (_, i) => i + 1),
|
|
269
|
+
currentWave: wave.number,
|
|
270
|
+
metadata: {
|
|
271
|
+
lastKnownStatus: result.status,
|
|
272
|
+
testsRun: result.testsRun,
|
|
273
|
+
testFailures: result.testFailures,
|
|
274
|
+
testLevel: this.resolveTestLevel(skipTests),
|
|
275
|
+
...aiMetadata,
|
|
276
|
+
},
|
|
277
|
+
});
|
|
278
|
+
this.log(`✅ Wave ${wave.number} deployed successfully`);
|
|
279
|
+
}
|
|
280
|
+
catch (error) {
|
|
281
|
+
logger.error('Wave deployment failed', { wave: wave.number, error });
|
|
282
|
+
this.error(`Wave ${wave.number} failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
283
|
+
}
|
|
284
|
+
});
|
|
285
|
+
// Clear state on success
|
|
286
|
+
await stateManager.clearState();
|
|
287
|
+
this.log('\n✅ All waves deployed successfully!');
|
|
288
|
+
}
|
|
289
|
+
async buildDeploymentContext(flags, sourcePath) {
|
|
290
|
+
const scanner = new MetadataScannerService();
|
|
291
|
+
const scanResult = await scanner.scan({ sourcePath });
|
|
292
|
+
if (scanResult.errors.length > 0) {
|
|
293
|
+
logger.error('Metadata scanning completed with errors', { errors: scanResult.errors });
|
|
294
|
+
scanResult.errors.forEach((err) => this.warn(err));
|
|
295
|
+
}
|
|
296
|
+
if (scanResult.warnings.length > 0) {
|
|
297
|
+
logger.warn('Metadata scanning completed with warnings', { warnings: scanResult.warnings });
|
|
298
|
+
scanResult.warnings.forEach((warn) => this.warn(warn));
|
|
299
|
+
}
|
|
300
|
+
let dependencyResult = scanResult.dependencyResult;
|
|
301
|
+
let inferredDependencies = 0;
|
|
302
|
+
let inferenceFallback = false;
|
|
303
|
+
if (flags['use-ai']) {
|
|
304
|
+
const inferenceService = new DependencyInferenceService({
|
|
305
|
+
baseDir: sourcePath ?? process.cwd(),
|
|
306
|
+
});
|
|
307
|
+
const inferenceResult = await inferenceService.inferDependencies(scanResult.components, scanResult.dependencyResult.graph);
|
|
308
|
+
inferredDependencies = inferenceResult.highConfidenceCount;
|
|
309
|
+
inferenceFallback = inferenceResult.fallbackToStatic;
|
|
310
|
+
if (inferenceResult.dependencies.length > 0) {
|
|
311
|
+
const enrichedComponents = this.applyInferredDependencies(scanResult.components, inferenceResult.dependencies);
|
|
312
|
+
const graphBuilder = new DependencyGraphBuilder();
|
|
313
|
+
graphBuilder.addComponents(enrichedComponents);
|
|
314
|
+
scanResult.components = enrichedComponents;
|
|
315
|
+
dependencyResult = graphBuilder.build();
|
|
316
|
+
scanResult.dependencyResult = dependencyResult;
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
const waveBuilder = new WaveBuilder();
|
|
320
|
+
const waveResult = waveBuilder.generateWaves(dependencyResult.graph);
|
|
321
|
+
let orderedWaves = getWavesInExecutionOrder(waveResult);
|
|
322
|
+
let aiContext;
|
|
323
|
+
if (flags['use-ai']) {
|
|
324
|
+
this.log(' 🤖 Using AI priority weighting for wave ordering...');
|
|
325
|
+
const priorityService = new AgentforcePriorityService({
|
|
326
|
+
baseDir: sourcePath ?? process.cwd(),
|
|
327
|
+
});
|
|
328
|
+
const aiWaveGenerator = new AIEnhancedPriorityWaveGenerator({
|
|
329
|
+
agentforceService: priorityService,
|
|
330
|
+
orgType: typeof flags['org-type'] === 'string' ? flags['org-type'] : undefined,
|
|
331
|
+
industry: typeof flags.industry === 'string' ? flags.industry : undefined,
|
|
332
|
+
});
|
|
333
|
+
orderedWaves = await aiWaveGenerator.applyPriorityWavesAsync(orderedWaves, scanResult.dependencyResult.components);
|
|
334
|
+
const unknownTypesWarning = aiWaveGenerator.getUnknownTypesWarning();
|
|
335
|
+
if (unknownTypesWarning) {
|
|
336
|
+
this.warn(unknownTypesWarning);
|
|
337
|
+
}
|
|
338
|
+
const aiReport = aiWaveGenerator.getAIReport();
|
|
339
|
+
if (aiReport) {
|
|
340
|
+
this.log(aiReport);
|
|
341
|
+
}
|
|
342
|
+
const aiStats = aiWaveGenerator.getAIStats();
|
|
343
|
+
const providerConfig = priorityService.getProviderConfig();
|
|
344
|
+
aiContext = {
|
|
345
|
+
enabled: true,
|
|
346
|
+
provider: providerConfig.provider,
|
|
347
|
+
model: providerConfig.model,
|
|
348
|
+
aiAdjustments: aiStats?.aiAdjustments ?? 0,
|
|
349
|
+
unknownTypes: aiStats?.unknownTypes ?? [],
|
|
350
|
+
fallback: aiStats?.usedFallback ?? false,
|
|
351
|
+
inferredDependencies,
|
|
352
|
+
inferenceFallback,
|
|
353
|
+
};
|
|
354
|
+
}
|
|
355
|
+
return { scanResult, orderedWaves, aiContext };
|
|
356
|
+
}
|
|
357
|
+
generateReport(waves) {
|
|
358
|
+
this.log('\n📊 Deployment Report:');
|
|
359
|
+
this.log(` - Waves: ${waves}`);
|
|
360
|
+
this.log(' - Status: Success');
|
|
361
|
+
}
|
|
362
|
+
async forEachSequentially(items, callback) {
|
|
363
|
+
let chain = Promise.resolve();
|
|
364
|
+
items.forEach((item, index) => {
|
|
365
|
+
chain = chain.then(async () => callback(item, index));
|
|
366
|
+
});
|
|
367
|
+
await chain;
|
|
368
|
+
}
|
|
369
|
+
async executeCycleRemediationDeployment(params) {
|
|
370
|
+
const { deploymentId, targetOrg, sourcePath, stateManager, tracker, plan, sfCli, skipTests, componentMap } = params;
|
|
371
|
+
const editor = new CycleSourceEditor();
|
|
372
|
+
const startedAt = new Date().toISOString();
|
|
373
|
+
const editRecords = [];
|
|
374
|
+
const cycleId = plan.cycles.map((cycle) => cycle.id).join('||');
|
|
375
|
+
const phaseOneComponents = [
|
|
376
|
+
...new Set(plan.cycles.flatMap((cycle) => cycle.deployPhases.find((phase) => phase.phase === 1)?.components ?? [])),
|
|
377
|
+
];
|
|
378
|
+
const phaseTwoComponents = [
|
|
379
|
+
...new Set(plan.cycles.flatMap((cycle) => cycle.deployPhases.find((phase) => phase.phase === 2)?.components ?? [])),
|
|
380
|
+
];
|
|
381
|
+
let editsRestored = false;
|
|
382
|
+
let failureStateSaved = false;
|
|
383
|
+
try {
|
|
384
|
+
this.log('🩹 Applying conservative cycle remediation edits...');
|
|
385
|
+
await this.forEachSequentially(plan.cycles, async (cycle) => {
|
|
386
|
+
await this.forEachSequentially(cycle.edits, async (edit) => {
|
|
387
|
+
const request = await this.createCycleEditRequest(edit);
|
|
388
|
+
editRecords.push(await editor.applyEdit(request));
|
|
389
|
+
});
|
|
390
|
+
});
|
|
391
|
+
await stateManager.saveState({
|
|
392
|
+
deploymentId,
|
|
393
|
+
targetOrg,
|
|
394
|
+
timestamp: startedAt,
|
|
395
|
+
totalWaves: 2,
|
|
396
|
+
completedWaves: [],
|
|
397
|
+
currentWave: 1,
|
|
398
|
+
cycleRemediation: {
|
|
399
|
+
cycleId,
|
|
400
|
+
strategy: 'comment-reference',
|
|
401
|
+
activePhase: 1,
|
|
402
|
+
startedAt,
|
|
403
|
+
completedPhases: [],
|
|
404
|
+
editRecords,
|
|
405
|
+
},
|
|
406
|
+
});
|
|
407
|
+
tracker.startTracking(deploymentId, 1, 2);
|
|
408
|
+
this.log('♻️ Phase 1/2: deploying temporarily cycle-broken metadata...');
|
|
409
|
+
const phaseOneManifestPath = await this.generateWaveManifest({
|
|
410
|
+
baseDir: sourcePath ?? process.cwd(),
|
|
411
|
+
waveNumber: 1,
|
|
412
|
+
components: phaseOneComponents,
|
|
413
|
+
componentMap,
|
|
414
|
+
});
|
|
415
|
+
const phaseOneResult = await sfCli.deploy({
|
|
416
|
+
manifestPath: phaseOneManifestPath,
|
|
417
|
+
targetOrg,
|
|
418
|
+
testLevel: this.resolveTestLevel(skipTests),
|
|
419
|
+
});
|
|
420
|
+
if (!phaseOneResult.success) {
|
|
421
|
+
await stateManager.saveState({
|
|
422
|
+
deploymentId,
|
|
423
|
+
targetOrg,
|
|
424
|
+
timestamp: new Date().toISOString(),
|
|
425
|
+
totalWaves: 2,
|
|
426
|
+
completedWaves: [],
|
|
427
|
+
currentWave: 1,
|
|
428
|
+
failedWave: {
|
|
429
|
+
waveNumber: 1,
|
|
430
|
+
error: phaseOneResult.output,
|
|
431
|
+
timestamp: new Date().toISOString(),
|
|
432
|
+
},
|
|
433
|
+
cycleRemediation: {
|
|
434
|
+
cycleId,
|
|
435
|
+
strategy: 'comment-reference',
|
|
436
|
+
activePhase: 1,
|
|
437
|
+
startedAt,
|
|
438
|
+
completedPhases: [],
|
|
439
|
+
editRecords,
|
|
440
|
+
},
|
|
441
|
+
metadata: {
|
|
442
|
+
lastKnownStatus: phaseOneResult.status,
|
|
443
|
+
testsRun: phaseOneResult.testsRun,
|
|
444
|
+
testFailures: phaseOneResult.testFailures,
|
|
445
|
+
testLevel: this.resolveTestLevel(skipTests),
|
|
446
|
+
},
|
|
447
|
+
});
|
|
448
|
+
failureStateSaved = true;
|
|
449
|
+
throw new Error(`Cycle remediation phase 1 failed: ${phaseOneResult.output}`);
|
|
450
|
+
}
|
|
451
|
+
await stateManager.saveState({
|
|
452
|
+
deploymentId,
|
|
453
|
+
targetOrg,
|
|
454
|
+
timestamp: new Date().toISOString(),
|
|
455
|
+
totalWaves: 2,
|
|
456
|
+
completedWaves: [1],
|
|
457
|
+
currentWave: 2,
|
|
458
|
+
cycleRemediation: {
|
|
459
|
+
cycleId,
|
|
460
|
+
strategy: 'comment-reference',
|
|
461
|
+
activePhase: 2,
|
|
462
|
+
startedAt,
|
|
463
|
+
completedPhases: [1],
|
|
464
|
+
editRecords,
|
|
465
|
+
},
|
|
466
|
+
metadata: {
|
|
467
|
+
lastKnownStatus: phaseOneResult.status,
|
|
468
|
+
testsRun: phaseOneResult.testsRun,
|
|
469
|
+
testFailures: phaseOneResult.testFailures,
|
|
470
|
+
testLevel: this.resolveTestLevel(skipTests),
|
|
471
|
+
},
|
|
472
|
+
});
|
|
473
|
+
this.log('♻️ Restoring original references before phase 2...');
|
|
474
|
+
await this.restoreCycleEdits(editor, editRecords);
|
|
475
|
+
editsRestored = true;
|
|
476
|
+
tracker.startTracking(deploymentId, 2, 2);
|
|
477
|
+
this.log('♻️ Phase 2/2: redeploying restored metadata...');
|
|
478
|
+
const phaseTwoManifestPath = await this.generateWaveManifest({
|
|
479
|
+
baseDir: sourcePath ?? process.cwd(),
|
|
480
|
+
waveNumber: 2,
|
|
481
|
+
components: phaseTwoComponents,
|
|
482
|
+
componentMap,
|
|
483
|
+
});
|
|
484
|
+
const phaseTwoResult = await sfCli.deploy({
|
|
485
|
+
manifestPath: phaseTwoManifestPath,
|
|
486
|
+
targetOrg,
|
|
487
|
+
testLevel: this.resolveTestLevel(skipTests),
|
|
488
|
+
});
|
|
489
|
+
if (!phaseTwoResult.success) {
|
|
490
|
+
await stateManager.saveState({
|
|
491
|
+
deploymentId,
|
|
492
|
+
targetOrg,
|
|
493
|
+
timestamp: new Date().toISOString(),
|
|
494
|
+
totalWaves: 2,
|
|
495
|
+
completedWaves: [1],
|
|
496
|
+
currentWave: 2,
|
|
497
|
+
failedWave: {
|
|
498
|
+
waveNumber: 2,
|
|
499
|
+
error: phaseTwoResult.output,
|
|
500
|
+
timestamp: new Date().toISOString(),
|
|
501
|
+
},
|
|
502
|
+
cycleRemediation: {
|
|
503
|
+
cycleId,
|
|
504
|
+
strategy: 'comment-reference',
|
|
505
|
+
activePhase: 2,
|
|
506
|
+
startedAt,
|
|
507
|
+
completedPhases: [1],
|
|
508
|
+
editRecords,
|
|
509
|
+
},
|
|
510
|
+
metadata: {
|
|
511
|
+
lastKnownStatus: phaseTwoResult.status,
|
|
512
|
+
testsRun: phaseTwoResult.testsRun,
|
|
513
|
+
testFailures: phaseTwoResult.testFailures,
|
|
514
|
+
testLevel: this.resolveTestLevel(skipTests),
|
|
515
|
+
},
|
|
516
|
+
});
|
|
517
|
+
failureStateSaved = true;
|
|
518
|
+
throw new Error(`Cycle remediation phase 2 failed: ${phaseTwoResult.output}`);
|
|
519
|
+
}
|
|
520
|
+
await stateManager.clearState();
|
|
521
|
+
this.log('\n✅ Cycle remediation deployment completed successfully!');
|
|
522
|
+
}
|
|
523
|
+
catch (error) {
|
|
524
|
+
if (!editsRestored) {
|
|
525
|
+
await this.restoreCycleEdits(editor, editRecords, true);
|
|
526
|
+
}
|
|
527
|
+
if (!failureStateSaved) {
|
|
528
|
+
await stateManager.saveState({
|
|
529
|
+
deploymentId,
|
|
530
|
+
targetOrg,
|
|
531
|
+
timestamp: new Date().toISOString(),
|
|
532
|
+
totalWaves: 2,
|
|
533
|
+
completedWaves: [],
|
|
534
|
+
currentWave: 1,
|
|
535
|
+
failedWave: {
|
|
536
|
+
waveNumber: 1,
|
|
537
|
+
error: error instanceof Error ? error.message : String(error),
|
|
538
|
+
timestamp: new Date().toISOString(),
|
|
539
|
+
},
|
|
540
|
+
cycleRemediation: {
|
|
541
|
+
cycleId,
|
|
542
|
+
strategy: 'comment-reference',
|
|
543
|
+
activePhase: 1,
|
|
544
|
+
startedAt,
|
|
545
|
+
completedPhases: [],
|
|
546
|
+
editRecords,
|
|
547
|
+
},
|
|
548
|
+
});
|
|
549
|
+
}
|
|
550
|
+
throw error;
|
|
551
|
+
}
|
|
552
|
+
}
|
|
553
|
+
async createCycleEditRequest(edit) {
|
|
554
|
+
if (edit.filePath === undefined) {
|
|
555
|
+
throw new Error(`Cycle remediation edit for ${edit.nodeId} is missing a file path.`);
|
|
556
|
+
}
|
|
557
|
+
const content = await readFile(edit.filePath, 'utf8');
|
|
558
|
+
const dependencyName = edit.targetDependency.includes(':')
|
|
559
|
+
? edit.targetDependency.split(':').pop() ?? edit.targetDependency
|
|
560
|
+
: edit.targetDependency;
|
|
561
|
+
const candidateLines = content.split('\n').filter((line) => {
|
|
562
|
+
const trimmed = line.trim();
|
|
563
|
+
return trimmed.length > 0 && !trimmed.startsWith('//') && line.includes(dependencyName);
|
|
564
|
+
});
|
|
565
|
+
if (candidateLines.length !== 1) {
|
|
566
|
+
throw new Error(`Cycle remediation for ${edit.nodeId} requires exactly one candidate source line containing ${dependencyName}; found ${candidateLines.length}.`);
|
|
567
|
+
}
|
|
568
|
+
return {
|
|
569
|
+
filePath: edit.filePath,
|
|
570
|
+
targetDescription: edit.targetDescription,
|
|
571
|
+
targetDependency: edit.targetDependency,
|
|
572
|
+
sourceSnippet: candidateLines[0],
|
|
573
|
+
};
|
|
574
|
+
}
|
|
575
|
+
applyInferredDependencies(components, inferredDependencies) {
|
|
576
|
+
const clonedComponents = components.map((component) => ({
|
|
577
|
+
...component,
|
|
578
|
+
dependencies: new Set(component.dependencies),
|
|
579
|
+
dependents: new Set(component.dependents),
|
|
580
|
+
}));
|
|
581
|
+
const nodeIdsByName = new Map();
|
|
582
|
+
for (const component of clonedComponents) {
|
|
583
|
+
nodeIdsByName.set(component.name, `${component.type}:${component.name}`);
|
|
584
|
+
}
|
|
585
|
+
const componentsByNodeId = new Map();
|
|
586
|
+
for (const component of clonedComponents) {
|
|
587
|
+
const nodeId = `${component.type}:${component.name}`;
|
|
588
|
+
componentsByNodeId.set(nodeId, component);
|
|
589
|
+
}
|
|
590
|
+
for (const inferred of inferredDependencies) {
|
|
591
|
+
const fromNodeId = nodeIdsByName.get(inferred.from);
|
|
592
|
+
const toNodeId = nodeIdsByName.get(inferred.to);
|
|
593
|
+
if (!fromNodeId || !toNodeId) {
|
|
594
|
+
continue;
|
|
595
|
+
}
|
|
596
|
+
componentsByNodeId.get(fromNodeId)?.dependencies.add(toNodeId);
|
|
597
|
+
componentsByNodeId.get(toNodeId)?.dependents.add(fromNodeId);
|
|
598
|
+
}
|
|
599
|
+
return clonedComponents;
|
|
600
|
+
}
|
|
601
|
+
async restoreCycleEdits(editor, editRecords, bestEffort = false) {
|
|
602
|
+
await this.forEachSequentially([...editRecords].reverse(), async (record) => {
|
|
603
|
+
const result = await editor.restoreEdit(record);
|
|
604
|
+
if (!result.restored && result.reason !== 'backup-missing' && !bestEffort) {
|
|
605
|
+
throw new Error(`Failed to restore cycle remediation edit for ${record.filePath}: ${result.reason ?? 'unknown'}.`);
|
|
606
|
+
}
|
|
607
|
+
});
|
|
608
|
+
}
|
|
609
|
+
resolveTestLevel(skipTests) {
|
|
610
|
+
return skipTests ? 'NoTestRun' : 'RunLocalTests';
|
|
611
|
+
}
|
|
612
|
+
getTargetOrgIdentifier(value) {
|
|
613
|
+
if (typeof value === 'string' && value.length > 0) {
|
|
614
|
+
return value;
|
|
615
|
+
}
|
|
616
|
+
if (typeof value === 'object' && value !== null && 'getUsername' in value) {
|
|
617
|
+
const getUsername = value.getUsername;
|
|
618
|
+
return typeof getUsername === 'function' ? getUsername.call(value) : undefined;
|
|
619
|
+
}
|
|
620
|
+
return undefined;
|
|
621
|
+
}
|
|
622
|
+
async generateWaveManifest(params) {
|
|
623
|
+
const manifestDir = path.join(params.baseDir, '.smart-deployment', 'manifests');
|
|
624
|
+
await mkdir(manifestDir, { recursive: true });
|
|
625
|
+
const grouped = new Map();
|
|
626
|
+
for (const nodeId of params.components) {
|
|
627
|
+
const component = params.componentMap.get(nodeId);
|
|
628
|
+
if (!component) {
|
|
629
|
+
continue;
|
|
630
|
+
}
|
|
631
|
+
if (!grouped.has(component.type)) {
|
|
632
|
+
grouped.set(component.type, new Set());
|
|
633
|
+
}
|
|
634
|
+
grouped.get(component.type).add(component.name);
|
|
635
|
+
}
|
|
636
|
+
const typeBlocks = [...grouped.entries()]
|
|
637
|
+
.sort((a, b) => a[0].localeCompare(b[0]))
|
|
638
|
+
.map(([type, members]) => {
|
|
639
|
+
const memberLines = [...members]
|
|
640
|
+
.sort((left, right) => left.localeCompare(right))
|
|
641
|
+
.map((member) => ` <members>${member}</members>`)
|
|
642
|
+
.join('\n');
|
|
643
|
+
return [' <types>', memberLines, ` <name>${type}</name>`, ' </types>'].join('\n');
|
|
644
|
+
})
|
|
645
|
+
.join('\n');
|
|
646
|
+
const content = [
|
|
647
|
+
'<?xml version="1.0" encoding="UTF-8"?>',
|
|
648
|
+
'<Package xmlns="http://soap.sforce.com/2006/04/metadata">',
|
|
649
|
+
typeBlocks,
|
|
650
|
+
' <version>61.0</version>',
|
|
651
|
+
'</Package>',
|
|
652
|
+
'',
|
|
653
|
+
].join('\n');
|
|
654
|
+
const manifestPath = path.join(manifestDir, `wave-${String(params.waveNumber).padStart(3, '0')}.xml`);
|
|
655
|
+
await writeFile(manifestPath, content, 'utf8');
|
|
656
|
+
return manifestPath;
|
|
657
|
+
}
|
|
658
|
+
}
|
|
659
|
+
//# sourceMappingURL=start.js.map
|