@jterrats/smart-deployment 1.0.3 → 1.0.5
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/LICENSE +29 -0
- package/README.md +6 -0
- package/lib/ai/wave-validation-service.d.ts +21 -0
- package/lib/ai/wave-validation-service.js +189 -114
- package/lib/ai/wave-validation-service.js.map +1 -1
- package/lib/analysis/analysis-reporter.d.ts +12 -0
- package/lib/analysis/analysis-reporter.js +61 -0
- package/lib/analysis/analysis-reporter.js.map +1 -1
- package/lib/analysis/analyze-artifact-service.d.ts +25 -0
- package/lib/analysis/analyze-artifact-service.js +50 -0
- package/lib/analysis/analyze-artifact-service.js.map +1 -0
- package/lib/analysis/analyze-context-service.d.ts +25 -0
- package/lib/analysis/analyze-context-service.js +28 -0
- package/lib/analysis/analyze-context-service.js.map +1 -0
- package/lib/analysis/project-analysis-service.d.ts +48 -0
- package/lib/analysis/project-analysis-service.js +134 -0
- package/lib/analysis/project-analysis-service.js.map +1 -0
- package/lib/commands/analyze.d.ts +0 -2
- package/lib/commands/analyze.js +31 -162
- package/lib/commands/analyze.js.map +1 -1
- package/lib/commands/config.d.ts +0 -4
- package/lib/commands/config.js +25 -95
- package/lib/commands/config.js.map +1 -1
- package/lib/commands/resume.js +6 -16
- package/lib/commands/resume.js.map +1 -1
- package/lib/commands/start.d.ts +0 -11
- package/lib/commands/start.js +38 -550
- package/lib/commands/start.js.map +1 -1
- package/lib/commands/status.js +36 -12
- package/lib/commands/status.js.map +1 -1
- package/lib/commands/validate.d.ts +3 -0
- package/lib/commands/validate.js +5 -7
- package/lib/commands/validate.js.map +1 -1
- package/lib/config/config-mutation-service.d.ts +21 -0
- package/lib/config/config-mutation-service.js +61 -0
- package/lib/config/config-mutation-service.js.map +1 -0
- package/lib/dependencies/circular-dependency-detector.d.ts +5 -0
- package/lib/dependencies/circular-dependency-detector.js +67 -72
- package/lib/dependencies/circular-dependency-detector.js.map +1 -1
- package/lib/dependencies/dependency-depth-calculator.d.ts +28 -0
- package/lib/dependencies/dependency-depth-calculator.js +86 -39
- package/lib/dependencies/dependency-depth-calculator.js.map +1 -1
- package/lib/dependencies/dependency-graph-builder.d.ts +45 -13
- package/lib/dependencies/dependency-graph-builder.js +228 -123
- package/lib/dependencies/dependency-graph-builder.js.map +1 -1
- package/lib/dependencies/dependency-impact-analyzer.d.ts +13 -0
- package/lib/dependencies/dependency-impact-analyzer.js +146 -102
- package/lib/dependencies/dependency-impact-analyzer.js.map +1 -1
- package/lib/dependencies/dependency-merger.d.ts +10 -0
- package/lib/dependencies/dependency-merger.js +123 -61
- package/lib/dependencies/dependency-merger.js.map +1 -1
- package/lib/dependencies/dependency-resolver.d.ts +34 -9
- package/lib/dependencies/dependency-resolver.js +181 -112
- package/lib/dependencies/dependency-resolver.js.map +1 -1
- package/lib/dependencies/dependency-semantics.d.ts +20 -0
- package/lib/dependencies/dependency-semantics.js +55 -0
- package/lib/dependencies/dependency-semantics.js.map +1 -0
- package/lib/dependencies/graph-visualizer.d.ts +6 -1
- package/lib/dependencies/graph-visualizer.js +38 -4
- package/lib/dependencies/graph-visualizer.js.map +1 -1
- package/lib/dependencies/heuristic-inference.d.ts +2 -53
- package/lib/dependencies/heuristic-inference.js +8 -317
- package/lib/dependencies/heuristic-inference.js.map +1 -1
- package/lib/dependencies/heuristic-patterns.d.ts +14 -0
- package/lib/dependencies/heuristic-patterns.js +247 -0
- package/lib/dependencies/heuristic-patterns.js.map +1 -0
- package/lib/deployment/cycle-remediation-runner.d.ts +36 -0
- package/lib/deployment/cycle-remediation-runner.js +262 -0
- package/lib/deployment/cycle-remediation-runner.js.map +1 -0
- package/lib/deployment/deployment-context-service.d.ts +24 -0
- package/lib/deployment/deployment-context-service.js +17 -0
- package/lib/deployment/deployment-context-service.js.map +1 -0
- package/lib/deployment/deployment-runner.d.ts +37 -0
- package/lib/deployment/deployment-runner.js +94 -0
- package/lib/deployment/deployment-runner.js.map +1 -0
- package/lib/deployment/deployment-status-service.d.ts +5 -0
- package/lib/deployment/deployment-status-service.js +39 -18
- package/lib/deployment/deployment-status-service.js.map +1 -1
- package/lib/deployment/deployment-validation-service.d.ts +5 -2
- package/lib/deployment/deployment-validation-service.js +48 -45
- package/lib/deployment/deployment-validation-service.js.map +1 -1
- package/lib/deployment/resume-deployment-service.d.ts +14 -0
- package/lib/deployment/resume-deployment-service.js +25 -0
- package/lib/deployment/resume-deployment-service.js.map +1 -0
- package/lib/deployment/start-execution-service.d.ts +44 -0
- package/lib/deployment/start-execution-service.js +94 -0
- package/lib/deployment/start-execution-service.js.map +1 -0
- package/lib/deployment/test-executor.d.ts +60 -1
- package/lib/deployment/test-executor.js +180 -12
- package/lib/deployment/test-executor.js.map +1 -1
- package/lib/deployment/test-plan-service.d.ts +8 -0
- package/lib/deployment/test-plan-service.js +26 -0
- package/lib/deployment/test-plan-service.js.map +1 -0
- package/lib/deployment/wave-manifest-service.d.ts +11 -0
- package/lib/deployment/wave-manifest-service.js +41 -0
- package/lib/deployment/wave-manifest-service.js.map +1 -0
- package/lib/parsers/apex-class-parser.js +180 -98
- package/lib/parsers/apex-class-parser.js.map +1 -1
- package/lib/parsers/apex-trigger-parser.js +56 -7
- package/lib/parsers/apex-trigger-parser.js.map +1 -1
- package/lib/parsers/aura-parser.js +2 -2
- package/lib/parsers/aura-parser.js.map +1 -1
- package/lib/parsers/bot-parser.js +31 -4
- package/lib/parsers/bot-parser.js.map +1 -1
- package/lib/parsers/custom-metadata-parser.js +37 -3
- package/lib/parsers/custom-metadata-parser.js.map +1 -1
- package/lib/parsers/custom-object-parser.js +284 -150
- package/lib/parsers/custom-object-parser.js.map +1 -1
- package/lib/parsers/email-template-parser.js +115 -58
- package/lib/parsers/email-template-parser.js.map +1 -1
- package/lib/parsers/flexipage-parser.js +41 -16
- package/lib/parsers/flexipage-parser.js.map +1 -1
- package/lib/parsers/flow-parser.js +73 -63
- package/lib/parsers/flow-parser.js.map +1 -1
- package/lib/parsers/genai-prompt-parser.js +16 -4
- package/lib/parsers/genai-prompt-parser.js.map +1 -1
- package/lib/parsers/layout-parser.d.ts +11 -0
- package/lib/parsers/layout-parser.js +143 -68
- package/lib/parsers/layout-parser.js.map +1 -1
- package/lib/parsers/lwc-parser.d.ts +0 -9
- package/lib/parsers/lwc-parser.js +245 -100
- package/lib/parsers/lwc-parser.js.map +1 -1
- package/lib/parsers/parser-utils.d.ts +3 -0
- package/lib/parsers/parser-utils.js +16 -0
- package/lib/parsers/parser-utils.js.map +1 -0
- package/lib/parsers/permission-set-parser.d.ts +6 -0
- package/lib/parsers/permission-set-parser.js +31 -40
- package/lib/parsers/permission-set-parser.js.map +1 -1
- package/lib/parsers/profile-parser.d.ts +13 -0
- package/lib/parsers/profile-parser.js +39 -34
- package/lib/parsers/profile-parser.js.map +1 -1
- package/lib/parsers/visualforce-parser.js +2 -1
- package/lib/parsers/visualforce-parser.js.map +1 -1
- package/lib/presentation/analyze-command-presenter.d.ts +10 -0
- package/lib/presentation/analyze-command-presenter.js +30 -0
- package/lib/presentation/analyze-command-presenter.js.map +1 -0
- package/lib/presentation/config-command-presenter.d.ts +15 -0
- package/lib/presentation/config-command-presenter.js +50 -0
- package/lib/presentation/config-command-presenter.js.map +1 -0
- package/lib/presentation/project-analysis-presenter.d.ts +9 -0
- package/lib/presentation/project-analysis-presenter.js +9 -0
- package/lib/presentation/project-analysis-presenter.js.map +1 -0
- package/lib/presentation/resume-command-presenter.d.ts +7 -0
- package/lib/presentation/resume-command-presenter.js +12 -0
- package/lib/presentation/resume-command-presenter.js.map +1 -0
- package/lib/presentation/start-command-presenter.d.ts +15 -0
- package/lib/presentation/start-command-presenter.js +29 -0
- package/lib/presentation/start-command-presenter.js.map +1 -0
- package/lib/presentation/status-command-presenter.d.ts +7 -0
- package/lib/presentation/status-command-presenter.js +16 -0
- package/lib/presentation/status-command-presenter.js.map +1 -0
- package/lib/presentation/validate-command-presenter.d.ts +9 -0
- package/lib/presentation/validate-command-presenter.js +50 -0
- package/lib/presentation/validate-command-presenter.js.map +1 -0
- package/lib/services/metadata-scanner-service.d.ts +8 -12
- package/lib/services/metadata-scanner-service.js +55 -533
- package/lib/services/metadata-scanner-service.js.map +1 -1
- package/lib/services/scanners/automation-ai-metadata-scanner.d.ts +4 -0
- package/lib/services/scanners/automation-ai-metadata-scanner.js +61 -0
- package/lib/services/scanners/automation-ai-metadata-scanner.js.map +1 -0
- package/lib/services/scanners/code-metadata-scanner.d.ts +23 -0
- package/lib/services/scanners/code-metadata-scanner.js +149 -0
- package/lib/services/scanners/code-metadata-scanner.js.map +1 -0
- package/lib/services/scanners/data-metadata-scanner.d.ts +3 -0
- package/lib/services/scanners/data-metadata-scanner.js +82 -0
- package/lib/services/scanners/data-metadata-scanner.js.map +1 -0
- package/lib/services/scanners/experience-metadata-scanner.d.ts +5 -0
- package/lib/services/scanners/experience-metadata-scanner.js +123 -0
- package/lib/services/scanners/experience-metadata-scanner.js.map +1 -0
- package/lib/services/scanners/scanner-runtime.d.ts +11 -0
- package/lib/services/scanners/scanner-runtime.js +115 -0
- package/lib/services/scanners/scanner-runtime.js.map +1 -0
- package/lib/services/scanners/security-metadata-scanner.d.ts +3 -0
- package/lib/services/scanners/security-metadata-scanner.js +71 -0
- package/lib/services/scanners/security-metadata-scanner.js.map +1 -0
- package/lib/types/dependency.d.ts +11 -1
- package/lib/types/metadata.d.ts +17 -0
- package/lib/types/salesforce/flow/actions.d.ts +108 -0
- package/lib/types/salesforce/flow/actions.js +2 -0
- package/lib/types/salesforce/flow/actions.js.map +1 -0
- package/lib/types/salesforce/flow/common.d.ts +36 -0
- package/lib/types/salesforce/flow/common.js +5 -0
- package/lib/types/salesforce/flow/common.js.map +1 -0
- package/lib/types/salesforce/flow/metadata.d.ts +37 -0
- package/lib/types/salesforce/flow/metadata.js +2 -0
- package/lib/types/salesforce/flow/metadata.js.map +1 -0
- package/lib/types/salesforce/flow/records.d.ts +38 -0
- package/lib/types/salesforce/flow/records.js +2 -0
- package/lib/types/salesforce/flow/records.js.map +1 -0
- package/lib/types/salesforce/flow/resources.d.ts +44 -0
- package/lib/types/salesforce/flow/resources.js +2 -0
- package/lib/types/salesforce/flow/resources.js.map +1 -0
- package/lib/types/salesforce/flow/screens.d.ts +56 -0
- package/lib/types/salesforce/flow/screens.js +2 -0
- package/lib/types/salesforce/flow/screens.js.map +1 -0
- package/lib/types/salesforce/flow/start.d.ts +24 -0
- package/lib/types/salesforce/flow/start.js +2 -0
- package/lib/types/salesforce/flow/start.js.map +1 -0
- package/lib/types/salesforce/flow.d.ts +9 -514
- package/lib/types/salesforce/flow.js +1 -1
- package/lib/types/salesforce/object/core.d.ts +68 -0
- package/lib/types/salesforce/object/core.js +5 -0
- package/lib/types/salesforce/object/core.js.map +1 -0
- package/lib/types/salesforce/object/field.d.ts +155 -0
- package/lib/types/salesforce/object/field.js +5 -0
- package/lib/types/salesforce/object/field.js.map +1 -0
- package/lib/types/salesforce/object/filter.d.ts +16 -0
- package/lib/types/salesforce/object/filter.js +5 -0
- package/lib/types/salesforce/object/filter.js.map +1 -0
- package/lib/types/salesforce/object/policy.d.ts +27 -0
- package/lib/types/salesforce/object/policy.js +5 -0
- package/lib/types/salesforce/object/policy.js.map +1 -0
- package/lib/types/salesforce/object/record.d.ts +44 -0
- package/lib/types/salesforce/object/record.js +5 -0
- package/lib/types/salesforce/object/record.js.map +1 -0
- package/lib/types/salesforce/object/view.d.ts +71 -0
- package/lib/types/salesforce/object/view.js +5 -0
- package/lib/types/salesforce/object/view.js.map +1 -0
- package/lib/types/salesforce/object/weblink.d.ts +70 -0
- package/lib/types/salesforce/object/weblink.js +5 -0
- package/lib/types/salesforce/object/weblink.js.map +1 -0
- package/lib/types/salesforce/object.d.ts +7 -423
- package/lib/types/salesforce/object.js +7 -1
- package/lib/types/salesforce/object.js.map +1 -1
- package/lib/utils/cache-manager.d.ts +16 -15
- package/lib/utils/cache-manager.js +290 -188
- package/lib/utils/cache-manager.js.map +1 -1
- package/lib/utils/logger.d.ts +8 -0
- package/lib/utils/logger.js +82 -69
- package/lib/utils/logger.js.map +1 -1
- package/lib/validators/xml/xml-reference-rules.d.ts +3 -0
- package/lib/validators/xml/xml-reference-rules.js +173 -0
- package/lib/validators/xml/xml-reference-rules.js.map +1 -0
- package/lib/validators/xml/xml-report-formatter.d.ts +2 -0
- package/lib/validators/xml/xml-report-formatter.js +47 -0
- package/lib/validators/xml/xml-report-formatter.js.map +1 -0
- package/lib/validators/xml/xml-schema-rules.d.ts +3 -0
- package/lib/validators/xml/xml-schema-rules.js +75 -0
- package/lib/validators/xml/xml-schema-rules.js.map +1 -0
- package/lib/validators/xml/xml-validation-types.d.ts +25 -0
- package/lib/validators/xml/xml-validation-types.js +2 -0
- package/lib/validators/xml/xml-validation-types.js.map +1 -0
- package/lib/validators/xml-metadata-validator.d.ts +2 -63
- package/lib/validators/xml-metadata-validator.js +7 -338
- package/lib/validators/xml-metadata-validator.js.map +1 -1
- package/lib/waves/test-optimizer.d.ts +13 -0
- package/lib/waves/test-optimizer.js +124 -63
- package/lib/waves/test-optimizer.js.map +1 -1
- package/lib/waves/wave-builder.d.ts +17 -21
- package/lib/waves/wave-builder.js +199 -135
- package/lib/waves/wave-builder.js.map +1 -1
- package/lib/waves/wave-splitter.d.ts +12 -8
- package/lib/waves/wave-splitter.js +156 -115
- package/lib/waves/wave-splitter.js.map +1 -1
- package/npm-shrinkwrap.json +959 -422
- package/oclif.lock +294 -57
- package/oclif.manifest.json +1 -1
- package/package.json +21 -9
package/lib/commands/start.js
CHANGED
|
@@ -15,26 +15,20 @@
|
|
|
15
15
|
*
|
|
16
16
|
* @issue #46
|
|
17
17
|
*/
|
|
18
|
-
import { mkdir, readFile, writeFile } from 'node:fs/promises';
|
|
19
|
-
import * as path from 'node:path';
|
|
20
18
|
import { Messages } from '@salesforce/core';
|
|
21
19
|
import { Flags, SfCommand, optionalOrgFlagWithDeprecations } from '@salesforce/sf-plugins-core';
|
|
22
20
|
import { getLogger } from '../utils/logger.js';
|
|
23
|
-
import {
|
|
24
|
-
import {
|
|
25
|
-
import {
|
|
26
|
-
import {
|
|
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';
|
|
21
|
+
import { StartExecutionService } from '../deployment/start-execution-service.js';
|
|
22
|
+
import { DeploymentContextService } from '../deployment/deployment-context-service.js';
|
|
23
|
+
import { ProjectAnalysisPresenter } from '../presentation/project-analysis-presenter.js';
|
|
24
|
+
import { StartCommandPresenter } from '../presentation/start-command-presenter.js';
|
|
35
25
|
Messages.importMessagesDirectoryFromMetaUrl(import.meta.url);
|
|
36
26
|
const messages = Messages.loadMessages('@jterrats/smart-deployment', 'start');
|
|
37
27
|
const logger = getLogger('StartCommand');
|
|
28
|
+
const deploymentContextService = new DeploymentContextService();
|
|
29
|
+
const startExecutionService = new StartExecutionService();
|
|
30
|
+
const projectAnalysisPresenter = new ProjectAnalysisPresenter();
|
|
31
|
+
const presenter = new StartCommandPresenter();
|
|
38
32
|
export default class Start extends SfCommand {
|
|
39
33
|
static summary = messages.getMessage('summary');
|
|
40
34
|
static description = messages.getMessage('description');
|
|
@@ -97,517 +91,47 @@ export default class Start extends SfCommand {
|
|
|
97
91
|
const sourcePath = typeof flags['source-path'] === 'string' ? flags['source-path'] : undefined;
|
|
98
92
|
try {
|
|
99
93
|
logger.info('Starting smart deployment', { flags });
|
|
100
|
-
// AC-1: Analyze metadata
|
|
101
94
|
this.log('📊 Analyzing metadata...');
|
|
102
|
-
const
|
|
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,
|
|
95
|
+
const deploymentContext = await deploymentContextService.buildContext({
|
|
185
96
|
sourcePath,
|
|
186
|
-
|
|
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,
|
|
97
|
+
useAI: Boolean(flags['use-ai']),
|
|
330
98
|
orgType: typeof flags['org-type'] === 'string' ? flags['org-type'] : undefined,
|
|
331
99
|
industry: typeof flags.industry === 'string' ? flags.industry : undefined,
|
|
332
100
|
});
|
|
333
|
-
|
|
334
|
-
const
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
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),
|
|
101
|
+
projectAnalysisPresenter.reportDiagnostics(this, deploymentContext.scanResult, deploymentContext.messages);
|
|
102
|
+
const metadataCount = deploymentContext.scanResult.components.length;
|
|
103
|
+
const waves = deploymentContext.orderedWaves.length;
|
|
104
|
+
presenter.reportAnalysisSummary(this, {
|
|
105
|
+
metadataCount,
|
|
106
|
+
waves,
|
|
107
|
+
aiEnabled: Boolean(flags['use-ai']),
|
|
419
108
|
});
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
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}`);
|
|
109
|
+
const executionOptions = {
|
|
110
|
+
dryRun: flags['dry-run'] === true,
|
|
111
|
+
validateOnly: flags['validate-only'] === true,
|
|
112
|
+
allowCycleRemediation: flags['allow-cycle-remediation'] === true,
|
|
113
|
+
skipTests: flags['skip-tests'] === true,
|
|
114
|
+
targetOrg: this.getTargetOrgIdentifier(flags['target-org']),
|
|
115
|
+
sourcePath,
|
|
116
|
+
deploymentContext,
|
|
117
|
+
log: this.log.bind(this),
|
|
118
|
+
};
|
|
119
|
+
if (!executionOptions.dryRun && !executionOptions.validateOnly) {
|
|
120
|
+
presenter.reportExecutionStart(this);
|
|
450
121
|
}
|
|
451
|
-
await
|
|
452
|
-
|
|
453
|
-
|
|
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}`);
|
|
122
|
+
const executionResult = await startExecutionService.execute(executionOptions);
|
|
123
|
+
if (executionResult.kind === 'skipped') {
|
|
124
|
+
presenter.reportExecutionSkipped(this, executionResult.reason);
|
|
519
125
|
}
|
|
520
|
-
|
|
521
|
-
|
|
126
|
+
presenter.reportReportGenerationStart(this);
|
|
127
|
+
presenter.reportDeploymentReport(this, waves);
|
|
128
|
+
return { success: true, waves, ai: deploymentContext.aiContext };
|
|
522
129
|
}
|
|
523
130
|
catch (error) {
|
|
524
|
-
|
|
525
|
-
|
|
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);
|
|
131
|
+
// AC-10: Handle failures gracefully
|
|
132
|
+
logger.error('Deployment failed', { error });
|
|
133
|
+
this.error(`Deployment failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
598
134
|
}
|
|
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
135
|
}
|
|
612
136
|
getTargetOrgIdentifier(value) {
|
|
613
137
|
if (typeof value === 'string' && value.length > 0) {
|
|
@@ -619,41 +143,5 @@ export default class Start extends SfCommand {
|
|
|
619
143
|
}
|
|
620
144
|
return undefined;
|
|
621
145
|
}
|
|
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
146
|
}
|
|
659
147
|
//# sourceMappingURL=start.js.map
|