Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: confirmation and warnings for convert behavior #1130

Merged
merged 12 commits into from
Aug 16, 2024
10 changes: 4 additions & 6 deletions messages/convert.source-behavior.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,15 +51,13 @@ Your project has a default org (target-org) that uses source tracking. This oper
- Run this command again.
- Create a new org ("sf org create scratch" or "sf org create sandbox") and deploy the modified source.

# error.packageDirectoryNeedsMainDefault
# mainDefaultConfirmation

The package directory %s doesn't have a main/default structure.
This command moves metadata into a main/default structure, but your package directories aren't ready for it.
- This command will put components in a newly created `main/default` folder in each package directory. You might need to re-organize them into your preferred structure.
mshanemc marked this conversation as resolved.
Show resolved Hide resolved

# error.packageDirectoryNeedsMainDefault.actions
# basicConfirmation

- Update %s to have all its metadata inside a main/default directory structure.
- Run the command again.
- This command will make changes to your project. Be sure you've committed any source changes before continuing so you can easily revert if necessary.
mshanemc marked this conversation as resolved.
Show resolved Hide resolved

# success.dryRun

Expand Down
14 changes: 7 additions & 7 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,14 @@
"author": "Salesforce",
"bugs": "https://github.com/forcedotcom/cli/issues",
"dependencies": {
"@oclif/core": "^4.0.12",
"@salesforce/apex-node": "^8.1.1",
"@salesforce/core": "^8.2.8",
"@salesforce/kit": "^3.1.6",
"@oclif/core": "^4.0.17",
"@salesforce/apex-node": "^8.1.3",
"@salesforce/core": "^8.3.0",
"@salesforce/kit": "^3.2.1",
"@salesforce/plugin-info": "^3.3.24",
"@salesforce/sf-plugins-core": "^11.3.0",
"@salesforce/source-deploy-retrieve": "^12.1.11",
"@salesforce/source-tracking": "^7.1.2",
"@salesforce/sf-plugins-core": "^11.3.2",
"@salesforce/source-deploy-retrieve": "^12.2.1",
"@salesforce/source-tracking": "^7.1.5",
"@salesforce/ts-types": "^2.0.12",
"ansis": "^3.3.2"
},
Expand Down
23 changes: 22 additions & 1 deletion src/commands/project/convert/source-behavior.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,10 @@
*/

import { rm, readFile, writeFile } from 'node:fs/promises';
import { join } from 'node:path';
import { existsSync } from 'node:fs';
import { SfCommand, Flags } from '@salesforce/sf-plugins-core';
import { Messages } from '@salesforce/core';
import { Messages, SfProject } from '@salesforce/core';
import {
getValidatedProjectJson,
TMP_DIR,
Expand All @@ -17,6 +19,7 @@ import {
PRESET_CHOICES,
getPackageDirectoriesForPreset,
convertBackToSource,
ComponentSetAndPackageDirPath,
} from '../../../utils/convertBehavior.js';

Messages.importMessagesDirectoryFromMetaUrl(import.meta.url);
Expand Down Expand Up @@ -61,6 +64,13 @@ export default class ConvertSourceBehavior extends SfCommand<SourceBehaviorResul
flags['dry-run'] ? readFile(projectJson.getPath()) : '',
getPackageDirectoriesForPreset(this.project!, flags.behavior),
]);

this.warn(messages.getMessage('basicConfirmation'));
if (!packageDirsWithDecomposable.every(hasMainDefault(this.project!.getPath()))) {
this.warn(messages.getMessage('mainDefaultConfirmation'));
}
await this.confirm({ message: 'Proceed' });

const filesToDelete = await convertToMdapi(packageDirsWithDecomposable);

// flip the preset in the sfdx-project.json, even for dry-run, since the registry will need for conversions
Expand All @@ -71,6 +81,10 @@ export default class ConvertSourceBehavior extends SfCommand<SourceBehaviorResul
// delete the “original” files that no longer work because of project update
await Promise.all(flags['dry-run'] ? [] : filesToDelete.map((f) => rm(f)));

// @ts-expect-error there's publicly accessible way to clear a project's instance
// eslint-disable-next-line @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-member-access
SfProject.instances.clear();

const createdFiles = await convertBackToSource({
packageDirsWithPreset: packageDirsWithDecomposable,
projectDir: this.project!.getPath(),
Expand Down Expand Up @@ -103,3 +117,10 @@ export default class ConvertSourceBehavior extends SfCommand<SourceBehaviorResul
};
}
}

/** convert will put things in /main/default. If the packageDirs aren't configured that way, we'll need to warn the user
* See https://salesforce.quip.com/va5IAgXmTMWF for details on that issue */
const hasMainDefault =
(projectDir: string) =>
(i: ComponentSetAndPackageDirPath): boolean =>
existsSync(join(projectDir, i.packageDirPath, 'main', 'default'));
29 changes: 6 additions & 23 deletions src/utils/convertBehavior.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 { existsSync, readdirSync } from 'node:fs';
import { readdirSync } from 'node:fs';
import { readFile } from 'node:fs/promises';
import { join, resolve } from 'node:path';
import { fileURLToPath } from 'node:url';
Expand All @@ -15,7 +15,6 @@ import {
ConvertResult,
MetadataConverter,
MetadataRegistry,
RegistryAccess,
SourceComponent,
presetMap,
} from '@salesforce/source-deploy-retrieve';
Expand All @@ -38,20 +37,19 @@ export const getPackageDirectoriesForPreset = async (
preset: string
): Promise<ComponentSetAndPackageDirPath[]> => {
const projectDir = project.getPath();
const messages = loadMessages();
const output = (
await Promise.all(
project
.getPackageDirectories()
.map((pd) => pd.path)
.map(componentSetFromPackageDirectory(projectDir)(await getTypesFromPreset(preset)))
)
)
.filter(componentSetIsNonEmpty)
// we do this after filtering componentSets to reduce false positives (ex: dir does not have main/default but also has nothing to decompose)
.map(validateMainDefault(projectDir));
).filter(componentSetIsNonEmpty);
if (output.length === 0) {
loadMessages().createError('error.noTargetTypes', [preset]);
messages.createError('error.noTargetTypes', [preset]);
}

return output;
};

Expand Down Expand Up @@ -144,7 +142,7 @@ const convertToSource = async ({
}): Promise<ConvertResult[]> => {
// mdapi=>source convert the target dir back to the project
// it's a new converter because the project has changed and it should reload the project's registry.
const converter = new MetadataConverter(new RegistryAccess(undefined, projectDir));
const converter = new MetadataConverter();
return Promise.all(
packageDirsWithPreset.map(async (pd) =>
converter.convert(
Expand Down Expand Up @@ -179,21 +177,6 @@ export const getTypesFromPreset = async (preset: string): Promise<string[]> =>
(JSON.parse(await readFile(join(PRESET_DIR, `${preset}.json`), 'utf-8')) as MetadataRegistry).types
).map((t) => t.name);

/** convert will put things in /main/default. If the packageDirs aren't configured that way, we don't want to make a mess.
* See https://salesforce.quip.com/va5IAgXmTMWF for details on that issue */
const validateMainDefault =
(projectDir: string) =>
(i: ComponentSetAndPackageDirPath): ComponentSetAndPackageDirPath => {
if (!existsSync(join(projectDir, i.packageDirPath, 'main', 'default'))) {
throw loadMessages().createError(
'error.packageDirectoryNeedsMainDefault',
[i.packageDirPath],
[i.packageDirPath]
);
}
return i;
};

const getComponentSetFiles = (cs: ComponentSet): string[] =>
cs
.getSourceComponents()
Expand Down
Loading
Loading