Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/main' into sm/preserve-comments-…
Browse files Browse the repository at this point in the history
…alternative
  • Loading branch information
mshanemc committed Apr 30, 2024
2 parents c8d73a6 + daeb4c1 commit cd5ce83
Show file tree
Hide file tree
Showing 82 changed files with 5,597 additions and 612 deletions.
1,922 changes: 1,512 additions & 410 deletions CHANGELOG.md

Large diffs are not rendered by default.

6 changes: 4 additions & 2 deletions METADATA_SUPPORT.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ This list compares metadata types found in Salesforce v60 with the [metadata reg

This repository is used by both the Salesforce CLIs and Salesforce's VSCode Extensions.

Currently, there are 564/596 supported metadata types.
Currently, there are 565/598 supported metadata types.
For status on any existing gaps, please search or file an issue in the [Salesforce CLI issues only repo](https://github.com/forcedotcom/cli/issues).
To contribute a new metadata type, please see the [Contributing Metadata Types to the Registry](./contributing/metadata.md)

Expand Down Expand Up @@ -214,6 +214,7 @@ To contribute a new metadata type, please see the [Contributing Metadata Types t
|EinsteinAISettings|||
|EinsteinAgentSettings|||
|EinsteinAssistantSettings|||
|EinsteinCopilotSettings|||
|EinsteinDealInsightsSettings|||
|EinsteinDocumentCaptureSettings|||
|EinsteinGptSettings|||
Expand Down Expand Up @@ -302,6 +303,7 @@ To contribute a new metadata type, please see the [Contributing Metadata Types t
|GenAiFunction||Not supported, but support could be added|
|GenAiPlanner||Not supported, but support could be added|
|GenAiPlugin||Not supported, but support could be added|
|GenAiPluginInstructionDef||Not supported, but support could be added|
|GlobalValueSet|||
|GlobalValueSetTranslation|||
|GoogleAppsSettings|||
Expand Down Expand Up @@ -626,7 +628,7 @@ v61 introduces the following new types. Here's their current level of support
|LargeQuotesandOrdersForRlmSettings|||
|RetrievalSummaryDefinition||Not supported, but support could be added|
|SearchCustomization||Not supported, but support could be added (but not for tracking)|
|SearchOrgWideObjectConfig||Not supported, but support could be added|
|SearchOrgWideObjectConfig||Not supported, but support could be added (but not for tracking)|
|WaveAnalyticAssetCollection||Not supported, but support could be added|

## Additional Types
Expand Down
2 changes: 1 addition & 1 deletion contributing/metadata.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ Got questions?

- If you work for Salesforce,
- For general questions, post in [#platform-cli](https://salesforce-internal.slack.com/archives/C01LKDT1P6J)
- For PR reviews, post in [#sfdx-cli-plugin-providers](https://salesforce.enterprise.slack.com/archives/C0298EE05PU)
- For PR reviews, post in [#platform-cli-collaboration](https://salesforce.enterprise.slack.com/archives/C06V045BZD0)
- If not, [open an issue](https://github.com/forcedotcom/cli/issues)

## Adding new types to the registry via a script
Expand Down
15 changes: 7 additions & 8 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@salesforce/source-deploy-retrieve",
"version": "11.0.1",
"version": "11.1.2",
"description": "JavaScript library to run Salesforce metadata deploys and retrieves",
"main": "lib/src/index.js",
"author": "Salesforce",
Expand All @@ -25,8 +25,8 @@
"node": ">=18.0.0"
},
"dependencies": {
"@salesforce/core": "^7.2.0",
"@salesforce/kit": "^3.1.0",
"@salesforce/core": "^7.3.1",
"@salesforce/kit": "^3.1.1",
"@salesforce/ts-types": "^2.0.9",
"fast-levenshtein": "^3.0.0",
"fast-xml-parser": "^4.3.6",
Expand All @@ -36,12 +36,11 @@
"jszip": "^3.10.1",
"mime": "2.6.0",
"minimatch": "^5.1.6",
"proxy-agent": "^6.4.0",
"ts-retry-promise": "^0.7.1"
"proxy-agent": "^6.4.0"
},
"devDependencies": {
"@jsforce/jsforce-node": "^3.1.0",
"@salesforce/cli-plugins-testkit": "^5.2.1",
"@jsforce/jsforce-node": "^3.2.0",
"@salesforce/cli-plugins-testkit": "^5.3.1",
"@salesforce/dev-scripts": "^9.0.0",
"@types/deep-equal-in-any-order": "^1.0.1",
"@types/fast-levenshtein": "^0.0.4",
Expand All @@ -50,7 +49,7 @@
"@types/minimatch": "^5.1.2",
"deep-equal-in-any-order": "^1.1.19",
"deepmerge": "^4.3.1",
"eslint-plugin-sf-plugin": "^1.18.0",
"eslint-plugin-sf-plugin": "^1.18.2",
"mocha-junit-reporter": "^1.23.3",
"mocha-snap": "^5.0.0",
"ts-node": "^10.9.2",
Expand Down
2 changes: 1 addition & 1 deletion src/client/metadataApiRetrieve.ts
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ export class MetadataApiRetrieve extends MetadataTransfer<
MetadataApiRetrieveOptions
> {
public static DEFAULT_OPTIONS: Partial<MetadataApiRetrieveOptions> = { merge: false };
private options: MetadataApiRetrieveOptions;
private readonly options: MetadataApiRetrieveOptions;
private orgId?: string;

public constructor(options: MetadataApiRetrieveOptions) {
Expand Down
66 changes: 59 additions & 7 deletions src/collections/componentSetBuilder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,17 @@
*/

import * as path from 'node:path';
import { StateAggregator, Logger, SfError, Messages } from '@salesforce/core';
import { Logger, Messages, SfError, StateAggregator } from '@salesforce/core';
import fs from 'graceful-fs';
import minimatch from 'minimatch';
import { MetadataComponent } from '../resolve/types';
import { SourceComponent } from '../resolve/sourceComponent';
import { ComponentSet } from '../collections/componentSet';
import { RegistryAccess } from '../registry/registryAccess';
import type { FileProperties } from '../client/types';
import { MetadataType } from '../registry/types';
import { FromConnectionOptions } from './types';
import { MetadataResolver } from '../resolve';
import { DestructiveChangesType, FromConnectionOptions } from './types';

Messages.importMessagesDirectory(__dirname);
const messages = Messages.loadMessages('@salesforce/source-deploy-retrieve', 'sdr');
Expand All @@ -29,6 +31,14 @@ export type ManifestOption = {
type MetadataOption = {
metadataEntries: string[];
directoryPaths: string[];
/**
* Array of metadata type:name pairs to delete before the deploy. Use of wildcards is not allowed.
*/
destructiveEntriesPre?: string[];
/**
* Array of metadata type:name pairs to delete after the deploy. Use of wildcards is not allowed.
*/
destructiveEntriesPost?: string[];
};

type OrgOption = {
Expand Down Expand Up @@ -60,6 +70,7 @@ export class ComponentSetBuilder {
* @param options: options for creating a ComponentSet
*/

// eslint-disable-next-line complexity
public static async build(options: ComponentSetOptions): Promise<ComponentSet> {
const logger = Logger.childFromRoot('componentSetBuilder');
let componentSet: ComponentSet | undefined;
Expand Down Expand Up @@ -114,12 +125,46 @@ export class ComponentSetBuilder {
.map(addToComponentSet(componentSetFilter));

logger.debug(`Searching for matching metadata in directories: ${directoryPaths.join(', ')}`);

// add destructive changes if defined. Because these are deletes, all entries
// are resolved to SourceComponents
if (metadata.destructiveEntriesPre) {
metadata.destructiveEntriesPre
.map(entryToTypeAndName(registryAccess))
.map(assertNoWildcardInDestructiveEntries)
.flatMap(typeAndNameToMetadataComponents({ directoryPaths, registry: registryAccess }))
.map((mdComponent) => new SourceComponent({ type: mdComponent.type, name: mdComponent.fullName }))
.map(addToComponentSet(componentSet, DestructiveChangesType.PRE));
}
if (metadata.destructiveEntriesPost) {
metadata.destructiveEntriesPost
.map(entryToTypeAndName(registryAccess))
.map(assertNoWildcardInDestructiveEntries)
.flatMap(typeAndNameToMetadataComponents({ directoryPaths, registry: registryAccess }))
.map((mdComponent) => new SourceComponent({ type: mdComponent.type, name: mdComponent.fullName }))
.map(addToComponentSet(componentSet, DestructiveChangesType.POST));
}

const resolvedComponents = ComponentSet.fromSource({
fsPaths: directoryPaths,
include: componentSetFilter,
registry: registryAccess,
});
componentSet.forceIgnoredPaths = resolvedComponents.forceIgnoredPaths;

if (resolvedComponents.forceIgnoredPaths) {
// if useFsForceIgnore = true, then we won't be able to resolve a forceignored path,
// which we need to do to get the ignored source component
const resolver = new MetadataResolver(registryAccess, undefined, false);

for (const ignoredPath of resolvedComponents.forceIgnoredPaths ?? []) {
resolver.getComponentsFromPath(ignoredPath).map((ignored) => {
componentSet = componentSet?.filter(
(resolved) => !(resolved.fullName === ignored.name && resolved.type === ignored.type)
);
});
}
}

resolvedComponents.toArray().map(addToComponentSet(componentSet));
}

Expand All @@ -133,7 +178,7 @@ export class ComponentSetBuilder {
}`
);

const mdMap: MetadataMap = metadata
const mdMap = metadata
? buildMapFromComponents(metadata.metadataEntries.map(entryToTypeAndName(registryAccess)))
: (new Map() as MetadataMap);

Expand Down Expand Up @@ -162,9 +207,9 @@ export class ComponentSetBuilder {
}

const addToComponentSet =
(cs: ComponentSet) =>
(cs: ComponentSet, deletionType?: DestructiveChangesType) =>
(cmp: MetadataComponent): MetadataComponent => {
cs.add(cmp);
cs.add(cmp, deletionType);
return cmp;
};

Expand Down Expand Up @@ -195,6 +240,13 @@ const assertComponentSetIsNotUndefined = (componentSet: ComponentSet | undefined
return componentSet;
};

const assertNoWildcardInDestructiveEntries = (mdEntry: MetadataTypeAndMetadataName): MetadataTypeAndMetadataName => {
if (mdEntry.metadataName.includes('*')) {
throw SfError.create({ message: 'Wildcards are not supported when providing destructive metadata entries' });
}
return mdEntry;
};

/** This is only for debug output of matched files based on the command flags.
* It will log up to 20 file matches. */
const logComponents = (logger: Logger, componentSet: ComponentSet): void => {
Expand Down Expand Up @@ -281,7 +333,7 @@ const typeAndNameToMetadataComponents =

// TODO: use Map.groupBy when it's available
const buildMapFromComponents = (components: MetadataTypeAndMetadataName[]): MetadataMap => {
const mdMap: MetadataMap = new Map();
const mdMap: MetadataMap = new Map<string, string[]>();
components.map((cmp) => {
mdMap.set(cmp.type.name, [...(mdMap.get(cmp.type.name) ?? []), cmp.metadataName]);
});
Expand Down
4 changes: 1 addition & 3 deletions src/convert/metadataConverter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -183,9 +183,7 @@ function getMergeConfigOutputs(
mergeSet.add(component.parent ?? component);
}
const writer = new StandardWriter(output.defaultDirectory);
if (output.forceIgnoredPaths) {
writer.forceIgnoredPaths = output.forceIgnoredPaths;
}

return {
writer,
mergeSet,
Expand Down
19 changes: 7 additions & 12 deletions src/convert/streams.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
* Licensed under the BSD 3-Clause license.
* For full license text, see LICENSE.txt file in the repo root or https://opensource.org/licenses/BSD-3-Clause
*/
import { basename, dirname, isAbsolute, join } from 'node:path';
import { isAbsolute, join } from 'node:path';
import { pipeline as cbPipeline, Readable, Stream, Transform, Writable } from 'node:stream';
import { promisify } from 'node:util';
import { Messages, SfError } from '@salesforce/core';
Expand All @@ -20,6 +20,7 @@ import { ComponentSet } from '../collections/componentSet';
import { RegistryAccess } from '../registry/registryAccess';
import { ensureFileExists } from '../utils/fileSystemHandler';
import { ComponentStatus, FileResponseSuccess } from '../client/types';
import { ForceIgnore } from '../resolve';
import { MetadataTransformerFactory } from './transformers/metadataTransformerFactory';
import { ConvertContext } from './convertContext/convertContext';
import { SfdxFileFormat, WriteInfo, WriterFormat } from './types';
Expand All @@ -35,7 +36,6 @@ export const stream2buffer = async (stream: Stream): Promise<Buffer> =>
const buf = Array<any>();
stream.on('data', (chunk) => buf.push(chunk));
stream.on('end', () => resolve(Buffer.concat(buf)));
// eslint-disable-next-line @typescript-eslint/restrict-template-expressions
stream.on('error', (err) => reject(`error converting stream - ${err}`));
});

Expand Down Expand Up @@ -113,7 +113,6 @@ export class ComponentConverter extends Transform {
}

export abstract class ComponentWriter extends Writable {
public forceIgnoredPaths: Set<string> = new Set<string>();
protected rootDestination?: SourcePath;
protected logger: Logger;

Expand All @@ -128,9 +127,11 @@ export class StandardWriter extends ComponentWriter {
/** filepaths that converted files were written to */
public readonly converted: string[] = [];
public readonly deleted: FileResponseSuccess[] = [];
public readonly forceignore: ForceIgnore;

public constructor(rootDestination: SourcePath) {
super(rootDestination);
this.forceignore = ForceIgnore.findAndCreate(rootDestination);
}

public async _write(chunk: WriterFormat, encoding: string, callback: (err?: Error) => void): Promise<void> {
Expand All @@ -144,8 +145,7 @@ export class StandardWriter extends ComponentWriter {
await Promise.all(
chunk.writeInfos
.map(makeWriteInfoAbsolute(this.rootDestination))
.filter(existsOrDoesntMatchIgnored(this.forceIgnoredPaths))
.filter((info) => !this.forceIgnoredPaths.has(info.output))
.filter(existsOrDoesntMatchIgnored(this.forceignore))
.map((info) => {
if (info.shouldDelete) {
this.deleted.push({
Expand Down Expand Up @@ -289,11 +289,6 @@ const makeWriteInfoAbsolute =
});

const existsOrDoesntMatchIgnored =
(ignoredPaths: Set<string>) =>
(forceignore: ForceIgnore) =>
(writeInfo: WriteInfo): boolean =>
existsSync(writeInfo.output) ||
[...ignoredPaths].every(
(ignoredPath) =>
!dirname(ignoredPath).includes(dirname(writeInfo.output)) &&
!basename(ignoredPath).includes(basename(writeInfo.output))
);
existsSync(writeInfo.output) || forceignore.accepts(writeInfo.output);
3 changes: 2 additions & 1 deletion src/registry/presets/decomposeCustomLabelsBeta.json
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,8 @@
"decomposition": "topLevel",
"transformer": "decomposed"
},
"suffix": "labels"
"suffix": "labels",
"supportsPartialDelete": true
}
}
}
3 changes: 2 additions & 1 deletion src/registry/presets/decomposePermissionSetBeta.json
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,8 @@
"transformer": "decomposed"
},
"strictDirectoryName": true,
"suffix": "permissionset"
"suffix": "permissionset",
"supportsPartialDelete": true
}
}
}
3 changes: 2 additions & 1 deletion src/registry/presets/decomposeSharingRulesBeta.json
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,8 @@
"transformer": "decomposed"
},
"strictDirectoryName": true,
"suffix": "sharingRules"
"suffix": "sharingRules",
"supportsPartialDelete": true
}
}
}
4 changes: 2 additions & 2 deletions src/resolve/adapters/baseSourceAdapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,8 @@ export abstract class BaseSourceAdapter implements SourceAdapter {
public constructor(
type: MetadataType,
registry = new RegistryAccess(),
forceIgnore: ForceIgnore = new ForceIgnore(),
tree: TreeContainer = new NodeFSTreeContainer()
forceIgnore = new ForceIgnore(),
tree = new NodeFSTreeContainer()
) {
this.type = type;
this.registry = registry;
Expand Down

2 comments on commit cd5ce83

@svc-cli-bot
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Benchmark

Benchmark suite Current: cd5ce83 Previous: 5a6e5fa Ratio
eda-componentSetCreate-linux 183 ms 195 ms 0.94
eda-sourceToMdapi-linux 2025 ms 1957 ms 1.03
eda-sourceToZip-linux 1497 ms 1406 ms 1.06
eda-mdapiToSource-linux 2831 ms 3225 ms 0.88
lotsOfClasses-componentSetCreate-linux 473 ms 393 ms 1.20
lotsOfClasses-sourceToMdapi-linux 3712 ms 3586 ms 1.04
lotsOfClasses-sourceToZip-linux 3146 ms 3060 ms 1.03
lotsOfClasses-mdapiToSource-linux 3559 ms 3515 ms 1.01
lotsOfClassesOneDir-componentSetCreate-linux 658 ms 622 ms 1.06
lotsOfClassesOneDir-sourceToMdapi-linux 6610 ms 6581 ms 1.00
lotsOfClassesOneDir-sourceToZip-linux 5855 ms 5691 ms 1.03
lotsOfClassesOneDir-mdapiToSource-linux 6424 ms 6280 ms 1.02

This comment was automatically generated by workflow using github-action-benchmark.

@svc-cli-bot
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Benchmark

Benchmark suite Current: cd5ce83 Previous: 5a6e5fa Ratio
eda-componentSetCreate-win32 378 ms 416 ms 0.91
eda-sourceToMdapi-win32 3484 ms 3811 ms 0.91
eda-sourceToZip-win32 2096 ms 2293 ms 0.91
eda-mdapiToSource-win32 5633 ms 6382 ms 0.88
lotsOfClasses-componentSetCreate-win32 883 ms 927 ms 0.95
lotsOfClasses-sourceToMdapi-win32 7480 ms 8281 ms 0.90
lotsOfClasses-sourceToZip-win32 4649 ms 4993 ms 0.93
lotsOfClasses-mdapiToSource-win32 7487 ms 7524 ms 1.00
lotsOfClassesOneDir-componentSetCreate-win32 1470 ms 1507 ms 0.98
lotsOfClassesOneDir-sourceToMdapi-win32 13747 ms 13856 ms 0.99
lotsOfClassesOneDir-sourceToZip-win32 8610 ms 8883 ms 0.97
lotsOfClassesOneDir-mdapiToSource-win32 13520 ms 13673 ms 0.99

This comment was automatically generated by workflow using github-action-benchmark.

Please sign in to comment.