sfdx-hardis 6.12.9 → 6.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -4,6 +4,16 @@
4
4
 
5
5
  Note: Can be used with `sfdx plugins:install sfdx-hardis@beta` and docker image `hardisgroupcom/sfdx-hardis@beta`
6
6
 
7
+ ## [6.13.0] 2025-11-28
8
+
9
+ - New command [hardis:org:purge:profile](https://sfdx-hardis.cloudity.com/hardis/org/purge/profile/): Removes or "mutes" Permission Sets attributes from selected Salesforce Profile metadata files and redeploys the cleaned profiles to the target org.
10
+ - New command [hardis:project:clean:profiles-extract](https://sfdx-hardis.cloudity.com/hardis/project/clean/profiles-extract/)
11
+ - Adds site/ to .gitignore only in monitoring repositories
12
+
13
+ ## [6.12.10] 2025-11-25
14
+
15
+ - Temporary downgrade isomorphic-dompurify package (jsdom dep not compliant with NodeJS < 20.19.5 and CodeBuilder / Agentforce Vibes is below)
16
+
7
17
  ## [6.12.9] 2025-11-25
8
18
 
9
19
  - Fixes compatibility issues caused by upgrading to parse5 v8.0.0.
@@ -23,7 +33,7 @@ Note: Can be used with `sfdx plugins:install sfdx-hardis@beta` and docker image
23
33
 
24
34
  ## [6.12.5] 2025-11-18
25
35
 
26
- - Improve JIRA authentication and display
36
+ - Improve JIRA authentication and display
27
37
  - [hardis:project:deploy:notify](https://sfdx-hardis.cloudity.com/hardis/project/deploy/notify/): Make default org optional
28
38
  - Improve pre/post deployment commands details display in PR comments
29
39
 
@@ -65,7 +75,7 @@ Note: Can be used with `sfdx plugins:install sfdx-hardis@beta` and docker image
65
75
 
66
76
  - [hardis:org:test:apex](https://sfdx-hardis.cloudity.com/hardis/org/test/apex/): Fix bug when there are no Apex classes in the org
67
77
  - GitHub integration: Fix way to get Pull Request number
68
- - As SF Cli now requires NodeJs >= 24, set the same requirement to sfdx-hardis default workflows
78
+ - As SF Cli now requires NodeJs >= 24, set the same requirement to sfdx-hardis default workflows
69
79
 
70
80
  ## [6.11.3] 2025-11-04
71
81
 
@@ -94,7 +104,6 @@ Note: Can be used with `sfdx plugins:install sfdx-hardis@beta` and docker image
94
104
  - Allow to override SFDX_TEST_WAIT_MINUTES and SFDX_DEPLOY_WAIT_MINUTES using CI/CD variables, and set 120 minutes as default value everywhere
95
105
  - Fix Validation Rules formula field in doc generation
96
106
 
97
-
98
107
  ## [6.9.0] 2025-10-23
99
108
 
100
109
  - Bring back Microsoft Teams notifications [using Teams Workflow](https://sfdx-hardis.cloudity.com/salesforce-ci-cd-setup-integration-ms-teams/)
@@ -0,0 +1,53 @@
1
+ import { SfCommand } from '@salesforce/sf-plugins-core';
2
+ import { AnyJson } from '@salesforce/ts-types';
3
+ export default class OrgPurgeProfile extends SfCommand<any> {
4
+ static title: string;
5
+ static description: string;
6
+ static examples: string[];
7
+ static flags: any;
8
+ protected attributesToMuteDefinition: ({
9
+ packageType: string;
10
+ nodeNameOnProfile: string;
11
+ attributesToMute: string[];
12
+ muteValue: boolean;
13
+ includedNames?: undefined;
14
+ action?: undefined;
15
+ excludedNames?: undefined;
16
+ excludedFiles?: undefined;
17
+ } | {
18
+ nodeNameOnProfile: string;
19
+ attributesToMute: never[];
20
+ includedNames: string[];
21
+ action: string;
22
+ packageType?: undefined;
23
+ muteValue?: undefined;
24
+ excludedNames?: undefined;
25
+ excludedFiles?: undefined;
26
+ } | {
27
+ nodeNameOnProfile: string;
28
+ attributesToMute: string[];
29
+ muteValue: boolean;
30
+ excludedNames: string[];
31
+ excludedFiles: string[];
32
+ packageType?: undefined;
33
+ includedNames?: undefined;
34
+ action?: undefined;
35
+ })[];
36
+ protected outputFile: any;
37
+ protected outputFilesRes: any;
38
+ protected allChanges: {
39
+ profile: string;
40
+ node: string;
41
+ name: string;
42
+ attribute: string;
43
+ oldValue: any;
44
+ newValue: any;
45
+ }[];
46
+ run(): Promise<AnyJson>;
47
+ private checkUncommittedChanges;
48
+ private loadFullOrgManifest;
49
+ private muteProfileAttributes;
50
+ filterFullOrgPackageByNamespaces(packageFullOrgPath: string, packageFilteredPackagesPath: string): Promise<void>;
51
+ filterFullOrgPackageByRelevantMetadataTypes(packageFilteredPackagesPath: string, packageFilteredProfilePath: string, selectedProfiles: string[]): Promise<void>;
52
+ deployToOrg(orgUsername: string, selectedProfiles: string[]): Promise<void>;
53
+ }
@@ -0,0 +1,384 @@
1
+ import { SfCommand, Flags, requiredOrgFlagWithDeprecations } from '@salesforce/sf-plugins-core';
2
+ import { Messages } from '@salesforce/core';
3
+ import { promptProfiles } from '../../../../common/utils/orgUtils.js';
4
+ import { getReportDirectory } from '../../../../config/index.js';
5
+ import { buildOrgManifest } from '../../../../common/utils/deployUtils.js';
6
+ import * as path from 'path';
7
+ import { execCommand, filterPackageXml, uxLog } from '../../../../common/utils/index.js';
8
+ import c from 'chalk';
9
+ import fs from 'fs';
10
+ import { parsePackageXmlFile, parseXmlFile, writePackageXmlFile, writeXmlFile } from '../../../../common/utils/xmlUtils.js';
11
+ import { prompts } from '../../../../common/utils/prompts.js';
12
+ import { MetadataUtils } from '../../../../common/metadata-utils/index.js';
13
+ import { WebSocketClient } from '../../../../common/websocketClient.js';
14
+ import { generateCsvFile, generateReportPath } from '../../../../common/utils/filesUtils.js';
15
+ Messages.importMessagesDirectoryFromMetaUrl(import.meta.url);
16
+ const messages = Messages.loadMessages('sfdx-hardis', 'org');
17
+ export default class OrgPurgeProfile extends SfCommand {
18
+ static title = 'Remove PS attributes from Profile';
19
+ static description = `
20
+ ## Command Behavior
21
+
22
+ **Removes or "mutes" Permission Sets attributes from selected Salesforce Profile metadata files and redeploys the cleaned profiles to the target org.**
23
+
24
+ This command is intended to safely remove PS attributes from Profiles after a migration from Profile-based to PS-based permission management. It:
25
+ - Builds or reuses a full org manifest to determine metadata present in the org.
26
+ - Filters the manifest to remove selected managed package namespaces and keep only relevant metadata types required for profile processing.
27
+ - Retrieves the necessary metadata (profiles, objects, fields, classes) into the local project.
28
+ - Iterates over selected profile files and mutes configured attributes (for example: classAccesses.enabled, fieldPermissions.readable/editable, objectPermissions.* and userPermissions.enabled).
29
+ - Writes the modified profile XML files back to the repository
30
+ - Deploys the updated profiles to the target org.
31
+
32
+ The command checks for uncommitted changes and will not run if the working tree has modifications, and it allows reusing a previously generated full org manifest to speed up repeated runs.
33
+
34
+ <details markdown="1">
35
+ <summary>Technical explanations</summary>
36
+
37
+ - **Manifest generation:** Uses 'buildOrgManifest' to create a full org 'package.xml'. If an existing manifest file is available the user can choose to reuse it.
38
+ - **Namespace filtering:** Queries installed packages using 'MetadataUtils.listInstalledPackages' to propose namespaces to remove from the manifest.
39
+ - **Metadata filtering:** Keeps only metadata types required to safely mute profiles (Profile plus the package types configured in the command).
40
+ - **Profile processing:** Parses profile XML files, iterates nodes ('classAccesses', 'fieldPermissions', 'objectPermissions', 'userPermissions') and sets attributes to configured mute values, skipping configured excluded names/files.
41
+ - **Retrieval & Deployment:** Uses the Salesforce CLI ('sf project retrieve' / 'sf project deploy') via 'execCommand' to retrieve metadata and deploy the updated profiles.
42
+ - **Exit behavior:** Returns an object with 'orgId' and an 'outputString'. Errors are logged to the console and do not throw uncaught exceptions within the command.
43
+ </details>
44
+ `;
45
+ static examples = [
46
+ `sf hardis:org:purge:profile`,
47
+ `sf hardis:org:purge:profile --target-org my-org@example.com`,
48
+ ];
49
+ /* jscpd:ignore-start */
50
+ static flags = {
51
+ outputfile: Flags.string({
52
+ char: 'f',
53
+ description: 'Force the path and name of output report file. Must end with .csv',
54
+ }),
55
+ debug: Flags.boolean({
56
+ char: 'd',
57
+ default: false,
58
+ description: messages.getMessage('debugMode'),
59
+ }),
60
+ websocket: Flags.string({
61
+ description: messages.getMessage('websocket'),
62
+ }),
63
+ skipauth: Flags.boolean({
64
+ description: 'Skip authentication check when a default username is required',
65
+ }),
66
+ 'target-org': requiredOrgFlagWithDeprecations,
67
+ };
68
+ /* jscpd:ignore-end */
69
+ attributesToMuteDefinition = [
70
+ {
71
+ "packageType": "ApexClass",
72
+ "nodeNameOnProfile": "classAccesses",
73
+ "attributesToMute": ["enabled"],
74
+ "muteValue": false
75
+ }, {
76
+ "packageType": "CustomField",
77
+ "nodeNameOnProfile": "fieldPermissions",
78
+ "attributesToMute": ["readable", "editable"],
79
+ "muteValue": false
80
+ }, {
81
+ "packageType": "CustomObject",
82
+ "nodeNameOnProfile": "objectPermissions",
83
+ "attributesToMute": ["allowCreate", "allowDelete", "allowEdit", "allowRead", "modifyAllRecords", "viewAllFields", "viewAllRecords"],
84
+ "muteValue": false
85
+ },
86
+ {
87
+ "nodeNameOnProfile": "tabVisibilities",
88
+ "attributesToMute": [],
89
+ "includedNames": ["standard-DelegatedAccount"],
90
+ "action": "remove"
91
+ },
92
+ {
93
+ "nodeNameOnProfile": "userPermissions",
94
+ "attributesToMute": ["enabled"],
95
+ "muteValue": false,
96
+ "excludedNames": [
97
+ "ActivitiesAccess",
98
+ "ChatterInternalUser",
99
+ "LightningConsoleAllowedForUser",
100
+ "ViewHelpLink",
101
+ ],
102
+ "excludedFiles": ["Admin.profile-meta.xml"]
103
+ }
104
+ ];
105
+ outputFile;
106
+ outputFilesRes = {};
107
+ allChanges = [];
108
+ async run() {
109
+ const { flags } = await this.parse(OrgPurgeProfile);
110
+ this.outputFile = flags.outputfile || null;
111
+ const orgUsername = flags['target-org'].getUsername();
112
+ const conn = flags['target-org'].getConnection();
113
+ const instanceUrlKey = conn.instanceUrl.replace(/https?:\/\//, '').replace(/\./g, '_').toUpperCase();
114
+ uxLog("action", this, c.cyan(`Starting profile attributes purge process on org: ${conn.instanceUrl}`));
115
+ const reportDir = await getReportDirectory();
116
+ const packageFullOrgPath = path.join(reportDir, `org-package-xml-full_${instanceUrlKey}.xml`);
117
+ const packageFilteredPackagesPath = path.join(reportDir, `org-package-xml-filtered-packages_${instanceUrlKey}.xml`);
118
+ const packageFilteredProfilePath = path.join(reportDir, `org-package-xml-filtered-profile-purge_${instanceUrlKey}.xml`);
119
+ // Check if user has uncommitted changes
120
+ if (!await this.checkUncommittedChanges()) {
121
+ const confirmPromptRes = await prompts({
122
+ type: "confirm",
123
+ message: `You have uncommitted changes in your git repository, do you want to continue anyway? This may lead to overwrite your uncommitted changes.`,
124
+ description: "It's recommended to commit, stash or discard your changes before proceeding.",
125
+ });
126
+ if (!confirmPromptRes.value === true) {
127
+ uxLog("error", this, c.blue(`Operation cancelled by user. Exiting without making changes.`));
128
+ return {};
129
+ }
130
+ }
131
+ uxLog("action", this, c.cyan(`Loading full org manifest for profile retrieval...`));
132
+ await this.loadFullOrgManifest(conn, orgUsername, packageFullOrgPath);
133
+ await this.filterFullOrgPackageByNamespaces(packageFullOrgPath, packageFilteredPackagesPath);
134
+ const selectedProfiles = await promptProfiles(flags['target-org'].getConnection(), { multiselect: true, returnApiName: true });
135
+ uxLog("action", this, c.cyan(`Filtering full org manifest to only keep relevant metadata types...`));
136
+ await this.filterFullOrgPackageByRelevantMetadataTypes(packageFilteredPackagesPath, packageFilteredProfilePath, selectedProfiles);
137
+ uxLog("action", this, c.cyan(`Retrieving metadatas required for profile purge (this will take some time)...`));
138
+ await execCommand(`sf project retrieve start --manifest ${packageFilteredProfilePath} --target-org ${orgUsername} --ignore-conflicts --json`, this, { output: false, fail: true });
139
+ uxLog("action", this, c.cyan(`Muting unwanted profile attributes...`));
140
+ const profilesDir = path.join('force-app', 'main', 'default', 'profiles');
141
+ for (const selectedProfile of selectedProfiles) {
142
+ const profileFilePath = path.join(profilesDir, `${selectedProfile}.profile-meta.xml`);
143
+ if (!fs.existsSync(profileFilePath)) {
144
+ uxLog("warning", this, c.yellow(`Profile file ${profileFilePath} does not exist. Skipping.`));
145
+ continue;
146
+ }
147
+ const profileWithMutedAttributes = await this.muteProfileAttributes(profileFilePath);
148
+ await writeXmlFile(profileFilePath, profileWithMutedAttributes);
149
+ uxLog("success", this, c.green(`Profile ${selectedProfile} processed and unwanted attributes muted.`));
150
+ WebSocketClient.sendReportFileMessage(profileFilePath, `See updated ${path.basename(profileFilePath, ".profile-meta.xml")} profile `, 'report');
151
+ }
152
+ // Generate output CSV file
153
+ this.outputFile = await generateReportPath('profile-muted-attributes', this.outputFile);
154
+ this.outputFilesRes = await generateCsvFile(this.allChanges, this.outputFile, { fileTitle: 'Profile muted attributes report' });
155
+ const promptDeployRes = await prompts({
156
+ type: "confirm",
157
+ message: `Do you want to deploy ${selectedProfiles} profiles back to the org now?`,
158
+ description: "Deploying the profiles will overwrite the existing profiles in the target org with the muted versions. Profiles: " + selectedProfiles.join(", "),
159
+ initial: true,
160
+ });
161
+ if (!promptDeployRes.value === true) {
162
+ uxLog("error", this, c.blue(`Deployment cancelled by user. Exiting without deploying profiles.`));
163
+ return { orgId: flags['target-org'].getOrgId(), outputString: "Profile purge completed without deployment." };
164
+ }
165
+ uxLog("action", this, c.cyan(`Deploying muted profiles back to the org...`));
166
+ await this.deployToOrg(orgUsername, selectedProfiles);
167
+ return {
168
+ orgId: flags['target-org'].getOrgId(),
169
+ outputString: "Successfully purged profiles.",
170
+ outputFile: this.outputFile,
171
+ outputFilesRes: this.outputFilesRes
172
+ };
173
+ }
174
+ async checkUncommittedChanges() {
175
+ const gitResult = await execCommand('git status --porcelain', this, { output: true, fail: false });
176
+ const output = typeof gitResult === 'string' ? gitResult : (gitResult && gitResult.stdout) || '';
177
+ return output.trim() === '';
178
+ }
179
+ async loadFullOrgManifest(conn, orgUsername, packageFullOrgPath) {
180
+ // Check if full org manifest already exists
181
+ let useExistingManifest = false;
182
+ if (fs.existsSync(packageFullOrgPath)) {
183
+ const promptResults = await prompts({
184
+ type: "select",
185
+ name: "useExistingManifest",
186
+ message: "Do you want to use the existing full org manifest or generate a new one?",
187
+ description: "A full org manifest file was found from a previous run. You can either use it or generate a new one to ensure it's up to date. It may take some time to generate a new one.",
188
+ choices: [
189
+ {
190
+ title: `Use the existing full org manifest`,
191
+ description: `Cache file is located at ${path.relative(process.cwd(), packageFullOrgPath)}`,
192
+ value: true
193
+ },
194
+ { title: "Generate a new full org manifest", value: false },
195
+ ],
196
+ });
197
+ useExistingManifest = promptResults.useExistingManifest;
198
+ }
199
+ if (!useExistingManifest) {
200
+ uxLog("action", this, c.cyan(`Generating full org manifest for profile retrieval...`));
201
+ await buildOrgManifest(orgUsername, packageFullOrgPath, conn);
202
+ }
203
+ }
204
+ async muteProfileAttributes(profileFilePath) {
205
+ const profileName = path.basename(profileFilePath, '.profile-meta.xml');
206
+ uxLog("action", this, c.cyan(`Processing profile: ${profileName}`));
207
+ const profileParsedXml = await parseXmlFile(profileFilePath);
208
+ const filename = path.basename(profileFilePath);
209
+ const changes = [];
210
+ for (const attributeConfig of this.attributesToMuteDefinition) {
211
+ const excludedFiles = attributeConfig.excludedFiles || [];
212
+ if (excludedFiles.includes(filename)) {
213
+ continue;
214
+ }
215
+ const nodeName = attributeConfig.nodeNameOnProfile;
216
+ if (!profileParsedXml?.Profile?.[nodeName]) {
217
+ continue;
218
+ }
219
+ // Ensure we work with an array to simplify processing
220
+ if (!Array.isArray(profileParsedXml.Profile[nodeName])) {
221
+ profileParsedXml.Profile[nodeName] = [profileParsedXml.Profile[nodeName]];
222
+ }
223
+ const muteValue = attributeConfig.muteValue || false;
224
+ const attributesToMute = attributeConfig.attributesToMute;
225
+ const includedNames = attributeConfig.includedNames || [];
226
+ const excludedNames = attributeConfig.excludedNames || [];
227
+ const action = attributeConfig.action || 'mute';
228
+ if (action === 'remove') {
229
+ // Remove nodes with names in includedNames
230
+ profileParsedXml.Profile[nodeName] = profileParsedXml.Profile[nodeName].filter((nodeObj) => {
231
+ let memberName = nodeObj.apexClass || nodeObj.field || nodeObj.object || nodeObj.apexPage || nodeObj.tab || nodeObj.recordType || nodeObj.application || nodeObj.name || 'unknown';
232
+ if (Array.isArray(memberName)) {
233
+ memberName = memberName[0];
234
+ }
235
+ if (includedNames.includes(memberName)) {
236
+ changes.push({
237
+ node: nodeName,
238
+ name: memberName,
239
+ attribute: 'entire node',
240
+ oldValue: JSON.stringify(nodeObj),
241
+ newValue: 'removed',
242
+ });
243
+ return false; // Exclude this node
244
+ }
245
+ });
246
+ continue; // Move to next attributeConfig
247
+ }
248
+ for (let i = 0; i < profileParsedXml.Profile[nodeName].length; i++) {
249
+ /* jscpd:ignore-start */
250
+ const nodeObj = profileParsedXml.Profile[nodeName][i];
251
+ let memberName = nodeObj.apexClass || nodeObj.field || nodeObj.object || nodeObj.apexPage || nodeObj.tab || nodeObj.recordType || nodeObj.application || nodeObj.name || 'unknown';
252
+ if (Array.isArray(memberName)) {
253
+ memberName = memberName[0];
254
+ }
255
+ /* jscpd:ignore-end */
256
+ for (const attr of attributesToMute) {
257
+ if (memberName && excludedNames.includes(memberName)) {
258
+ continue;
259
+ }
260
+ if (nodeObj && Object.prototype.hasOwnProperty.call(nodeObj, attr)) {
261
+ let oldVal = nodeObj[attr];
262
+ if (Array.isArray(oldVal)) {
263
+ oldVal = oldVal[0];
264
+ }
265
+ // Only record a change if the value actually differs
266
+ if (oldVal !== muteValue) {
267
+ changes.push({
268
+ node: nodeName,
269
+ name: memberName,
270
+ attribute: attr,
271
+ oldValue: oldVal,
272
+ newValue: muteValue,
273
+ });
274
+ nodeObj[attr] = muteValue;
275
+ }
276
+ }
277
+ }
278
+ }
279
+ }
280
+ // Build a single summary string and emit it with one uxLog("log") call
281
+ const summaryLines = [];
282
+ summaryLines.push(`Profile: ${profileName}`);
283
+ if (changes.length === 0) {
284
+ summaryLines.push('No attributes muted.');
285
+ }
286
+ else {
287
+ summaryLines.push(`Muted ${changes.length} attribute(s):`);
288
+ for (const ch of changes) {
289
+ summaryLines.push(`- [${ch.node}] ${ch.name} -> ${ch.attribute}: ${JSON.stringify(ch.oldValue)} => ${JSON.stringify(ch.newValue)}`);
290
+ }
291
+ }
292
+ uxLog('log', this, c.cyan(summaryLines.join('\n')));
293
+ this.allChanges.push(...changes.map(change => ({ profile: profileName, ...change })));
294
+ return profileParsedXml;
295
+ }
296
+ async filterFullOrgPackageByNamespaces(packageFullOrgPath, packageFilteredPackagesPath) {
297
+ const namespaceOptions = [];
298
+ try {
299
+ uxLog("action", this, c.cyan(`Retrieving installed packages to list namespaces...`));
300
+ const installedPackages = await MetadataUtils.listInstalledPackages(null, this);
301
+ for (const installedPackage of installedPackages) {
302
+ if (installedPackage?.SubscriberPackageNamespace !== '' && installedPackage?.SubscriberPackageNamespace != null) {
303
+ namespaceOptions.push({
304
+ title: installedPackage.SubscriberPackageNamespace, // Display title
305
+ value: installedPackage.SubscriberPackageNamespace
306
+ });
307
+ }
308
+ }
309
+ }
310
+ catch (error) {
311
+ uxLog("warning", this, c.yellow(`Could not retrieve installed packages. Listing namespaces by parsing the XML file.`));
312
+ uxLog("other", this, error?.message);
313
+ }
314
+ if (namespaceOptions.length === 0) {
315
+ uxLog("action", this, c.cyan(`No installed packages found via API. Parsing package XML to list namespaces...`));
316
+ // Fallback: parse the package XML to find namespaces
317
+ const parsedPackageXml = await parsePackageXmlFile(packageFullOrgPath);
318
+ const allTypes = Object.keys(parsedPackageXml);
319
+ const namespaceSet = new Set();
320
+ for (const typeEntry of allTypes) {
321
+ for (const member of parsedPackageXml[typeEntry]) {
322
+ const parts = member.split('__');
323
+ if (parts.length > 2 && !member.includes(".")) {
324
+ const namespace = parts[0];
325
+ namespaceSet.add(namespace);
326
+ }
327
+ else if (parts.length > 1 && !member.includes(".") && !member.includes("__c") && parts[1].length > 1) {
328
+ const namespace = parts[0];
329
+ namespaceSet.add(namespace);
330
+ }
331
+ }
332
+ }
333
+ for (const namespace of namespaceSet) {
334
+ namespaceOptions.push({
335
+ title: namespace,
336
+ value: namespace
337
+ });
338
+ }
339
+ }
340
+ const selectedNamespacesPrompt = await prompts({
341
+ type: 'multiselect',
342
+ name: "namespaces",
343
+ message: "Select the namespaces you want to ignore in the selected profiles that will be processed.",
344
+ description: "You will NOT disable access to elements related to namespaces that you will select.",
345
+ choices: namespaceOptions
346
+ });
347
+ uxLog("action", this, c.cyan(`Filtering full org manifest to remove unwanted namespaces...`));
348
+ await filterPackageXml(packageFullOrgPath, packageFilteredPackagesPath, {
349
+ removeNamespaces: selectedNamespacesPrompt.namespaces,
350
+ removeStandard: false
351
+ });
352
+ }
353
+ async filterFullOrgPackageByRelevantMetadataTypes(packageFilteredPackagesPath, packageFilteredProfilePath, selectedProfiles) {
354
+ const parsedPackage = await parsePackageXmlFile(packageFilteredPackagesPath);
355
+ const keysToKeep = Array.from(new Set([
356
+ ...this.attributesToMuteDefinition
357
+ .map((a) => a.packageType)
358
+ .filter((pkgType) => pkgType != null),
359
+ 'Profile',
360
+ ]));
361
+ for (const key of Object.keys(parsedPackage)) {
362
+ if (!keysToKeep.includes(key)) {
363
+ delete parsedPackage[key];
364
+ }
365
+ }
366
+ parsedPackage['Profile'] = selectedProfiles;
367
+ await writePackageXmlFile(packageFilteredProfilePath, parsedPackage);
368
+ }
369
+ async deployToOrg(orgUsername, selectedProfiles) {
370
+ try {
371
+ const metadataArgs = selectedProfiles
372
+ .map((p) => `--metadata "Profile:${p}"`)
373
+ .join(' ');
374
+ await execCommand(`sf project deploy start ${metadataArgs} --target-org ${orgUsername} --ignore-conflicts --json`, this, { output: true, fail: true });
375
+ uxLog("action", this, c.cyan(`Successfully deployed ${selectedProfiles.length}`));
376
+ uxLog("success", this, c.green(`Profiles deployed successfully:\n${selectedProfiles.join(', ')}`));
377
+ }
378
+ catch (error) {
379
+ uxLog("action", this, c.red(`Failed to deploy profiles.`));
380
+ uxLog("error", this, c.red(JSON.stringify(error, null, 2)));
381
+ }
382
+ }
383
+ }
384
+ //# sourceMappingURL=profile.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"profile.js","sourceRoot":"","sources":["../../../../../src/commands/hardis/org/purge/profile.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,103 @@
1
+ import { SfCommand } from '@salesforce/sf-plugins-core';
2
+ export default class ProfilesExtract extends SfCommand<void> {
3
+ static readonly description = "\n## Command Behavior\n\n**Guides administrators through extracting Salesforce profiles, personas, and related metadata into structured CSV/XLSX deliverables.**\n\nThe command inventories SObjects that contain data, lets the user pick the ones to document, and then produces persona-centric spreadsheets that cover users, personas, relationships, record types, apps, permissions, tabs, fields, and permission sets. The output consolidates everything into both CSV files and a single Excel workbook, making it easy to audit access models or prepare remediation plans.\n\nKey capabilities:\n\n- **Interactive object discovery:** Lists queryable objects with records and allows multi-selection.\n- **Persona modeling:** Lets users define the number of personas and generates cross-object matrices that leverage Excel formulas for faster updates.\n- **Comprehensive metadata export:** Captures users, record types, apps, permissions, tabs, fields, and permission sets with persona/profile visibility indicators.\n- **Profile field access coverage:** Retrieves FieldPermissions to surface read/edit status per profile and field.\n- **Consolidated reporting:** Produces standalone CSVs plus an aggregated XLSX stored in the report directory.\n\n<details markdown=\"1\">\n<summary>Technical explanations</summary>\n\n- **Salesforce connectivity:** Uses the requested target org connection from `Flags.requiredOrg` to fetch metadata and records.\n- **Bulk/REST queries:** Relies on `bulkQuery` and standard SOQL to evaluate record counts and pull FieldPermissions, Users, RecordTypes, Applications, Tabs, and PermissionSets.\n- **Describe calls:** Invokes `describeGlobal` and `describeSObject` to enumerate objects and field-level metadata, including picklists and formulas.\n- **Prompt-driven input:** Utilizes the shared `prompts` utility to collect object selections and persona counts, ensuring consistent CLI UX.\n- **Reporting pipeline:** Writes intermediate CSV files via `generateCsvFile`, stores them under the report directory from `getReportDirectory`, and finally merges them using `createXlsxFromCsvFiles`.\n- **Logging & diagnostics:** Uses `uxLog` with chalk coloring for progress, warnings, and debug output, integrating with the project-wide logging style.\n\n</details>\n";
4
+ static readonly examples: string[];
5
+ static readonly flags: {
6
+ 'target-org': import("@oclif/core/interfaces").OptionFlag<import("@salesforce/core").Org, import("@oclif/core/interfaces").CustomOptions>;
7
+ debug: import("@oclif/core/interfaces").BooleanFlag<boolean>;
8
+ websocket: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
9
+ skipauth: import("@oclif/core/interfaces").BooleanFlag<boolean>;
10
+ };
11
+ protected csvFiles: string[];
12
+ protected outputFile: string;
13
+ protected activeProfileNames: Set<string>;
14
+ /**
15
+ * Main entry point for the command. Orchestrates the extraction process:
16
+ * - Prompts user to select objects
17
+ * - Extracts users, personas, relations, record types, apps, permissions, tabs, and object fields
18
+ * - Generates CSV and XLSX reports
19
+ */
20
+ run(): Promise<void>;
21
+ /**
22
+ * Extracts field-level access for profiles using FieldPermissions object.
23
+ * Generates a CSV report of profile-field access.
24
+ * @param conn Salesforce connection
25
+ */
26
+ getProfileFieldAccessData(conn: any, selectedObjects: string[]): Promise<{
27
+ Profile: string;
28
+ SObjectType: string;
29
+ Field: string;
30
+ PermissionsRead: string;
31
+ PermissionsEdit: string;
32
+ }[]>;
33
+ /**
34
+ * Prompts the user to select Salesforce objects (SObjects) that have records in the org.
35
+ * Generates a CSV report of the selected objects.
36
+ * @param conn Salesforce connection
37
+ * @returns Array of selected object API names
38
+ */
39
+ private generateObjectsList;
40
+ /**
41
+ * Extracts active users from the org, including their username, role, and profile.
42
+ * Generates a CSV report of users.
43
+ * @param conn Salesforce connection
44
+ */
45
+ generateUsersExtract(conn: any): Promise<void>;
46
+ /**
47
+ * Prompts the user for the number of personas to create.
48
+ * Generates a CSV report listing the personas.
49
+ * @returns The number of personas
50
+ */
51
+ private generatePersonaExtract;
52
+ /**
53
+ * Generates a CSV mapping each selected object to persona permissions (Read, Create, Edit, Delete).
54
+ * @param selectedObjects Array of object API names
55
+ * @param numberOfPersonas Number of personas
56
+ */
57
+ generateRelationExtract(selectedObjects: string[], numberOfPersonas: number): Promise<void>;
58
+ /**
59
+ * Extracts record types for each selected object and maps persona access (Active, Default).
60
+ * Generates a CSV report of record types per object.
61
+ * @param conn Salesforce connection
62
+ * @param selectedObjects Array of object API names
63
+ * @param numberOfPersonas Number of personas
64
+ */
65
+ generateRTExtract(conn: any, selectedObjects: string[], numberOfPersonas: number): Promise<void>;
66
+ /**
67
+ * Extracts custom applications (AppDefinition) and maps persona access (Active, Default).
68
+ * Generates a CSV report of applications.
69
+ * @param conn Salesforce connection
70
+ * @param numberOfPersonas Number of personas
71
+ */
72
+ generateAppsExtract(conn: any, numberOfPersonas: number): Promise<void>;
73
+ /**
74
+ * Extracts all boolean permission fields from PermissionSet object.
75
+ * Generates a CSV report mapping each permission to personas.
76
+ * @param conn Salesforce connection
77
+ * @param numberOfPersonas Number of personas
78
+ */
79
+ generatePermissionsExtract(conn: any, numberOfPersonas: number): Promise<void>;
80
+ /**
81
+ * Extracts custom tabs and maps persona access.
82
+ * Generates a CSV report of tabs.
83
+ * @param conn Salesforce connection
84
+ * @param numberOfPersonas Number of personas
85
+ */
86
+ generateTabsExtract(conn: any, numberOfPersonas: number): Promise<void>;
87
+ /**
88
+ * For each selected object, extracts all fields and their metadata (label, type, picklist values, etc.).
89
+ * Maps persona visibility and read-only status for each field.
90
+ * Generates a CSV report per object.
91
+ * @param conn Salesforce connection
92
+ * @param selectedObjects Array of object API names
93
+ * @param numberOfPersonas Number of personas
94
+ */
95
+ generateObjectFieldsExtract(conn: any, selectedObjects: string[], numberOfPersonas: number, profileNames?: string[], profileFieldAccess?: any[]): Promise<void>;
96
+ /**
97
+ * Extracts all Permission Sets in the org.
98
+ * Generates a CSV report mapping each permission set to personas.
99
+ * @param conn
100
+ * @param numberOfPersonas
101
+ */
102
+ generatePermissionSetsExtract(conn: any, numberOfPersonas: number): Promise<void>;
103
+ }