Skip to content

Commit a6d238e

Browse files
authored
Merge pull request #358 from snehaljha-sf/auto-deploy
feat: auto deploy
2 parents f792ecb + f181dc7 commit a6d238e

File tree

18 files changed

+901
-137
lines changed

18 files changed

+901
-137
lines changed

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,4 +47,5 @@ omnistudio_migration
4747

4848
assessment_reports/
4949
migration_report/
50-
.sfdx/
50+
.sfdx/
51+
package.xml

README.md

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ The Omnistudio Migration Assistant is a command-line interface (CLI) plugin that
88

99
- Install Salesforce CLI on your computer. See : [Install Salesforce CLI](https://developer.salesforce.com/docs/atlas.en-us.sfdx_setup.meta/sfdx_setup/sfdx_setup_install_cli.htm).
1010

11+
- To deploy LWC as part of auto-deployment process, environment variable 'OMA_AUTH_KEY' should be set with requested NPM repository access key from Salesforce Customer Support.
12+
1113
## Install and Run the Omnistudio Migration Assistant
1214

1315
1. Install SF cli using the official documentation located [here](https://developer.salesforce.com/docs/atlas.en-us.sfdx_setup.meta/sfdx_setup/sfdx_setup_install_cli.htm).
@@ -168,12 +170,14 @@ OPTIONS
168170
os (OmniScripts), fc (FlexCards)
169171
170172
--relatedobjects=relatedobjects specify related objects to assess:
171-
'apex' for Apex classes
172-
```
173+
'apex' for Apex classes
174+
'lwc' for LWC (Lightning Web Components)
175+
'expsites' for Experience Sites
176+
'flexipage' for Lightning record pages
173177
174-
> **Note:** LWC (Lightning Web Components) assessment/migration functionality is temporarily disabled in the current version. LWC features will be re-enabled in a future releases.
178+
```
175179

176-
> Apex migration functionality remains fully available. The `--relatedobjects` flag only accepts values ('apex')
180+
> **Note:** LWC migration auto-deployment needs minimum node version of 18.17.1
177181
178182
Terms:
179183
Notwithstanding anything stated in the terms and conditions agreed between Salesforce (‘SFDC’) and you (‘Customer’), the use of the OmniStudio Migration Assistant (‘Assistant’) is designed to facilitate the migration and it’s going to modify your custom code and by deploying and using the Assistant you hereby provide your consent to automate the migration process and enable a smooth transition. Customer shall access and use the Assistant only as permitted to the Customer and shall not compromise, break or circumvent any technical processes or security measures associated with the services provided by SFDC.

messages/assess.json

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -124,8 +124,7 @@
124124
"errorGeneratingHTML": "Error while generating HTML from template for %s with properties %s",
125125
"unexpectedError": "Unexpected Assessment error",
126126
"errorEvaluatingExpression": "Error evaluating expression: %s, %s",
127-
"retrievingFlexiPages": "Retrieving FlexiPages",
128-
"successfullyRetrievedFlexiPages": "Successfully retrieved %s FlexiPages",
127+
"foundFlexiPages": "Found %s FlexiPages to assess",
129128
"assessingFlexiPages": "Assessing FlexiPages",
130129
"completedProcessingAllFlexiPages": "Completed processing all flexipage files. Total processed: %s",
131130
"completedProcessingFlexiPage": "Completed processing %s - Errors: %s",

messages/migrate.json

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -119,8 +119,7 @@
119119
"labelStatusComplete": "Complete",
120120
"migrationConsentNotGiven": "Couldn't confirm whether assessment errors are resolved",
121121
"migrationConsentMessage": "Ensure that all items in the assessment report are marked as Green before proceeding with the migration. Do you want to proceed?",
122-
"retrievingFlexiPages": "Retrieving FlexiPages",
123-
"successfullyRetrievedFlexiPages": "Successfully retrieved %s FlexiPages",
122+
"foundFlexiPages": "Found %s FlexiPages to migrate",
124123
"migratingFlexiPages": "Migrating FlexiPages",
125124
"completedProcessingAllFlexiPages": "Completed processing all flexipage files. Total processed: %s",
126125
"completedProcessingFlexiPage": "Completed processing %s - Errors: %s",
@@ -196,16 +195,28 @@
196195
"missingInfo": "Info is missing",
197196
"errorCheckingGlobalAutoNumber": "We couldn’t check whether the Global Auto Number setting is enabled: %s. Try again later.",
198197
"errorMigrationMessage": "Error migrating object: %s",
198+
"nameMappingUndefined": "Name Mapping is undefined",
199+
"autoDeployConsentMessage": "Do you want to auto deploy the related objects? [y/n]",
200+
"errorDeployingComponents": "Error deploying related objects post migration",
201+
"installingDependency": "Installing node dependency %s",
202+
"dependencyInstalled": "Node dependency %s installed",
203+
"deployingFromManifest": "Deploying from metadata packages",
204+
"manifestDeployFailed": "Metadata packages deployment failed, check logs or status for more details",
205+
"installingRequiredDependencies": "Installing required node dependencies",
206+
"creatingNPMConfigFile": "Creating npm config file",
207+
"npmConfigFileCreated": "Npm config file created",
208+
"authKeyEnvVarNotSet": "OMA_AUTH_KEY environment variable is not set, LWCs will not be deployed",
199209
"experienceSiteException": "Exception occurred while processing experience sites",
200210
"reservedKeysFoundInPropertySet": "Reserved keys found in any of output response transformation fields: %s.",
201211
"incompleteMigrationDetected": "We couldn't complete the migration process",
202-
"nameMappingUndefined": "Name Mapping is undefined",
203212
"errorComponentMapping": "Error during component pre-processing: %s",
204213
"startingComponentPreProcessing": "Pre-processing components for name mapping",
205214
"completeComponentMappingMessage": "Registered name mappings for %s components",
206215
"componentMappingNotFound": "No registry mapping found for %s component: %s, using fallback cleaning",
207216
"flexCardWithAngularOmniScriptWarning": "FlexCard has dependencies on Angular OmniScript(s) which are not migrated. Please convert OmniScript(s) to LWC before migrating this FlexCard.",
208217
"angularOmniScriptDependencyWarning": "Element '%s' references Angular OmniScript '%s' which will not be migrated. Consider converting the referenced OmniScript to LWC",
209218
"skipFlexcardAngularOmniScriptDependencyWarning": "Skipping FlexCard %s due to Angular OmniScript dependencies",
210-
"flexCardMigrationProcessingMessage": "Processing %s FlexCards for migration (%s skipped due to Angular dependencies)"
211-
}
219+
"flexCardMigrationProcessingMessage": "Processing %s FlexCards for migration (%s skipped due to Angular dependencies)",
220+
"noMetadataToDeploy": "No metadata to deploy",
221+
"manifestDeployementStarted": "Manifest deployment started with id: %s"
222+
}

src/commands/omnistudio/migration/migrate.ts

Lines changed: 68 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -130,11 +130,28 @@ export default class Migrate extends OmniStudioBaseCommand {
130130
const namespace = orgs.packageDetails.namespace;
131131
// Let's time every step
132132
DebugTimer.getInstance().start();
133+
let projectPath: string;
134+
let objectsToProcess: string[] = [];
135+
let targetApexNamespace: string;
136+
const preMigrate: PreMigrate = new PreMigrate(namespace, conn, this.logger, messages, this.ux);
137+
const isExperienceBundleMetadataAPIProgramaticallyEnabled: { value: boolean } = { value: false };
133138

134-
// Handle related objects processing
135-
const relatedObjectsResult = await this.processRelatedObjects(relatedObjects, conn, namespace);
136-
const { projectPath, objectsToProcess, targetApexNamespace, isExperienceBundleMetadataAPIProgramaticallyEnabled } =
137-
relatedObjectsResult;
139+
let actionItems = [];
140+
141+
let deploymentConfig = { autoDeploy: false, authKey: undefined };
142+
if (relatedObjects) {
143+
const relatedObjectProcessResult = await this.processRelatedObjects(
144+
relatedObjects,
145+
preMigrate,
146+
conn,
147+
isExperienceBundleMetadataAPIProgramaticallyEnabled,
148+
actionItems
149+
);
150+
objectsToProcess = relatedObjectProcessResult.objectsToProcess;
151+
projectPath = relatedObjectProcessResult.projectPath;
152+
targetApexNamespace = relatedObjectProcessResult.targetApexNamespace;
153+
deploymentConfig = relatedObjectProcessResult.deploymentConfig;
154+
}
138155

139156
Logger.log(messages.getMessage('migrationInitialization', [String(namespace)]));
140157
Logger.log(messages.getMessage('apiVersionInfo', [apiVersion]));
@@ -181,15 +198,17 @@ export default class Migrate extends OmniStudioBaseCommand {
181198
const relatedObjectMigrationResult = omnistudioRelatedObjectsMigration.migrateAll(objectsToProcess);
182199

183200
// POST MIGRATION
184-
let actionItems = [];
201+
185202
const postMigrate: PostMigrate = new PostMigrate(
186203
this.org,
187204
namespace,
188205
conn,
189206
this.logger,
190207
messages,
191208
this.ux,
192-
objectsToProcess
209+
objectsToProcess,
210+
deploymentConfig,
211+
projectPath
193212
);
194213

195214
if (!migrateOnly) {
@@ -206,11 +225,19 @@ export default class Migrate extends OmniStudioBaseCommand {
206225

207226
generatePackageXml.createChangeList(
208227
relatedObjectMigrationResult.apexAssessmentInfos,
209-
relatedObjectMigrationResult.lwcAssessmentInfos,
228+
deploymentConfig.autoDeploy && deploymentConfig.authKey ? relatedObjectMigrationResult.lwcAssessmentInfos : [],
210229
relatedObjectMigrationResult.experienceSiteAssessmentInfos,
211-
relatedObjectMigrationResult.flexipageAssessmentInfos
230+
relatedObjectMigrationResult.flexipageAssessmentInfos,
231+
this.org.getConnection().version,
232+
messages
212233
);
213234

235+
try {
236+
postMigrate.deploy();
237+
} catch (error) {
238+
Logger.error(messages.getMessage('errorDeployingComponents'), error);
239+
}
240+
214241
await ResultsBuilder.generateReport(
215242
objectMigrationResults,
216243
relatedObjectMigrationResult,
@@ -222,52 +249,52 @@ export default class Migrate extends OmniStudioBaseCommand {
222249
);
223250

224251
// Return results needed for --json flag
225-
return { objectMigrationResults };
252+
return { objectMigrationResults: [] };
226253
}
227254

228255
private async processRelatedObjects(
229256
relatedObjects: string,
257+
preMigrate: PreMigrate,
230258
conn: Connection,
231-
namespace: string
259+
isExperienceBundleMetadataAPIProgramaticallyEnabled: { value: boolean },
260+
actionItems: string[]
232261
): Promise<{
233-
projectPath: string;
234262
objectsToProcess: string[];
263+
projectPath: string;
235264
targetApexNamespace: string;
236-
isExperienceBundleMetadataAPIProgramaticallyEnabled: { value: boolean };
265+
deploymentConfig: { autoDeploy: boolean; authKey: string | undefined };
237266
}> {
267+
const validOptions = [Constants.Apex, Constants.ExpSites, Constants.FlexiPage, Constants.LWC];
268+
const objectsToProcess = relatedObjects.split(',').map((obj) => obj.trim());
269+
// Validate input
270+
for (const obj of objectsToProcess) {
271+
if (!validOptions.includes(obj)) {
272+
Logger.error(messages.getMessage('invalidRelatedObjectsOption', [obj]));
273+
process.exit(1);
274+
}
275+
}
276+
277+
let deploymentConfig = { autoDeploy: false, authKey: undefined };
238278
let projectPath: string;
239-
let objectsToProcess: string[] = [];
240279
let targetApexNamespace: string;
241-
const isExperienceBundleMetadataAPIProgramaticallyEnabled: { value: boolean } = { value: false };
242-
if (relatedObjects) {
243-
const validOptions = [Constants.Apex, Constants.ExpSites, Constants.FlexiPage, Constants.LWC];
244-
objectsToProcess = relatedObjects.split(',').map((obj) => obj.trim());
245-
// Validate input
246-
for (const obj of objectsToProcess) {
247-
if (!validOptions.includes(obj)) {
248-
Logger.error(messages.getMessage('invalidRelatedObjectsOption', [obj]));
249-
process.exit(1);
250-
}
251-
}
252-
// Check for general consent to make modifications with OMT
253-
const generalConsent = await this.getGeneralConsent();
254-
if (generalConsent) {
255-
// Use ProjectPathUtil for APEX project folder selection (matches assess.ts logic)
256-
projectPath = await ProjectPathUtil.getProjectPath(messages, true);
257-
targetApexNamespace = await this.getTargetApexNamespace(objectsToProcess, targetApexNamespace);
258-
const preMigrate: PreMigrate = new PreMigrate(namespace, conn, this.logger, messages, this.ux);
259-
await preMigrate.handleExperienceSitePrerequisites(
260-
objectsToProcess,
261-
conn,
262-
isExperienceBundleMetadataAPIProgramaticallyEnabled
263-
);
264-
Logger.logVerbose(
265-
'The objects to process after handleExpSitePrerequisite are ' + JSON.stringify(objectsToProcess)
266-
);
267-
} // TODO - What if general consent is no
280+
// Check for general consent to make modifications with OMT
281+
const generalConsent = await this.getGeneralConsent();
282+
if (generalConsent) {
283+
// Use ProjectPathUtil for APEX project folder selection (matches assess.ts logic)
284+
projectPath = await ProjectPathUtil.getProjectPath(messages, true);
285+
targetApexNamespace = await this.getTargetApexNamespace(objectsToProcess, targetApexNamespace);
286+
await preMigrate.handleExperienceSitePrerequisites(
287+
objectsToProcess,
288+
conn,
289+
isExperienceBundleMetadataAPIProgramaticallyEnabled
290+
);
291+
deploymentConfig = await preMigrate.getAutoDeployConsent(objectsToProcess.includes(Constants.LWC), actionItems);
292+
Logger.logVerbose(
293+
'The objects to process after handleExpSitePrerequisite are ' + JSON.stringify(objectsToProcess)
294+
);
268295
}
269296

270-
return { projectPath, objectsToProcess, targetApexNamespace, isExperienceBundleMetadataAPIProgramaticallyEnabled };
297+
return { objectsToProcess, projectPath, targetApexNamespace, deploymentConfig };
271298
}
272299

273300
private async getMigrationConsent(): Promise<boolean> {

src/migration/deployer.ts

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
import * as path from 'path';
2+
import { Messages } from '@salesforce/core';
3+
import * as shell from 'shelljs';
4+
import { sfProject } from '../utils/sfcli/project/sfProject';
5+
import { Logger } from '../utils/logger';
6+
7+
export class Deployer {
8+
private readonly projectPath: string;
9+
private readonly authKey: string;
10+
private readonly omniscriptCustomizationPackage = '@omnistudio/omniscript_customization';
11+
private readonly omniscriptCustomizationPackageVersion = '250.0.0';
12+
private readonly username: string;
13+
private readonly messages: Messages;
14+
15+
public constructor(projectPath: string, messages: Messages, username: string, authKey: string) {
16+
this.projectPath = projectPath;
17+
this.username = username;
18+
this.messages = messages;
19+
this.authKey = authKey;
20+
}
21+
22+
public deploy(): void {
23+
const pwd = shell.pwd();
24+
shell.cd(this.projectPath);
25+
try {
26+
if (this.authKey) {
27+
sfProject.createNPMConfigFile(this.authKey);
28+
Logger.logVerbose(this.messages.getMessage('installingRequiredDependencies'));
29+
sfProject.installDependency();
30+
sfProject.installDependency(
31+
`${this.omniscriptCustomizationPackage}@${this.omniscriptCustomizationPackageVersion}`
32+
);
33+
}
34+
Logger.log(path.join(pwd.toString(), 'package.xml'));
35+
sfProject.deployFromManifest(path.join(pwd.toString(), 'package.xml'), this.username);
36+
} finally {
37+
shell.cd(pwd);
38+
}
39+
}
40+
}

src/migration/postMigrate.ts

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,15 @@ import { AnonymousApexRunner } from '../utils/apex/executor/AnonymousApexRunner'
88
import { Constants } from '../utils/constants/stringContants';
99
import { OrgPreferences } from '../utils/orgPreferences';
1010
import { BaseMigrationTool } from './base';
11+
import { Deployer } from './deployer';
12+
import * as fs from 'fs';
13+
import * as path from 'path';
1114

1215
export class PostMigrate extends BaseMigrationTool {
1316
private readonly org: Org;
1417
private readonly relatedObjectsToProcess: string[];
18+
private readonly projectPath: string;
19+
private readonly deploymentConfig: { autoDeploy: boolean; authKey: string | undefined };
1520

1621
// Source Custom Object Names
1722
constructor(
@@ -21,11 +26,15 @@ export class PostMigrate extends BaseMigrationTool {
2126
logger: Logger,
2227
messages: Messages,
2328
ux: UX,
24-
relatedObjectsToProcess: string[]
29+
relatedObjectsToProcess: string[],
30+
deploymentConfig?: { autoDeploy: boolean; authKey: string | undefined },
31+
projectPath?: string
2532
) {
2633
super(namespace, connection, logger, messages, ux);
2734
this.org = org;
2835
this.relatedObjectsToProcess = relatedObjectsToProcess;
36+
this.deploymentConfig = deploymentConfig;
37+
this.projectPath = projectPath;
2938
}
3039

3140
public async setDesignersToUseStandardDataModel(
@@ -77,4 +86,21 @@ export class PostMigrate extends BaseMigrationTool {
7786
}
7887
}
7988
}
89+
90+
public deploy(): void {
91+
if (!this.deploymentConfig.autoDeploy || !fs.existsSync(path.join(process.cwd(), 'package.xml'))) {
92+
return;
93+
}
94+
try {
95+
const deployer = new Deployer(
96+
this.projectPath,
97+
this.messages,
98+
this.org.getUsername(),
99+
this.deploymentConfig.authKey
100+
);
101+
deployer.deploy();
102+
} catch (error) {
103+
Logger.error(this.messages.getMessage('errorDeployingComponents'), error);
104+
}
105+
}
80106
}

0 commit comments

Comments
 (0)