Skip to content

Commit

Permalink
Sh/enable convert nu ts (#76)
Browse files Browse the repository at this point in the history
* feat: enable convert NUTs

* feat: enable NUTs in circle ci

enable NUTs in circle ci.  Change the NUTs defaults to run both sfdx and bin/run executables.

* fix: ignore errors when trying to delete the NUT generated dir

* fix: changes from code review

* fix: change nutshell to not set jwt when TESTKIT_AUTH_URL is set

* fix: use fs.rmdir since fs.rmSync does not work on windows

* fix: trying shelljs to remove the dir

* chore: debugging windows

* chore: disabling no-console for debugging in circle

* chore: debugging in circle

* chore: relax regex a bit for windows

* chore: modify logging to inspect sfdx executables

* chore: ensure proper windows paths

* chore: changes some paths in the hopes for better windows NUT results

* chore: more debugging

* chore: check for exit code and use the location from the response

* chore: debugging

* chore: change back testMatrix files and only change sourcepath on convert command

* chore: glob wants forward slashes

* chore: debugging

* chore: debugging

* chore: path changes

* chore: use relative paths for verification

* chore: more path conversions

* fix: fixes paths for NUTs on windows
  • Loading branch information
shetzel authored May 7, 2021
1 parent e92f7c0 commit b142fb6
Show file tree
Hide file tree
Showing 11 changed files with 104 additions and 38 deletions.
11 changes: 11 additions & 0 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,17 @@ workflows:
node_version: latest
- release-management/test-package:
name: node-12
- release-management/test-nut:
name: nuts-on-linux
sfdx_version: latest
requires:
- node-latest
- release-management/test-nut:
name: nuts-on-windows
sfdx_version: latest
os: windows
requires:
- node-latest
- release-management/release-package:
sign: true
github-release: true
Expand Down
13 changes: 1 addition & 12 deletions command-snapshot.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,7 @@
{
"command": "force:source:convert",
"plugin": "@salesforce/plugin-source",
"flags": [
"apiversion",
"json",
"loglevel",
"manifest",
"metadata",
"outputdir",
"packagename",
"rootdir",
"sourcepath",
"targetusername"
]
"flags": ["json", "loglevel", "manifest", "metadata", "outputdir", "packagename", "rootdir", "sourcepath"]
},
{
"command": "force:source:deploy",
Expand Down
3 changes: 2 additions & 1 deletion messages/convert.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,6 @@
"sourcepath": "comma-separated list of paths to the local source files to convert",
"metadata": "comma-separated list of metadata component names to convert"
},
"success": "Source was successfully converted to Metadata API format and written to the location: %s"
"success": "Source was successfully converted to Metadata API format and written to the location: %s",
"convertFailed": "Failed to convert source"
}
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,8 @@
"test": "sf-test",
"test:command-reference": "./bin/run commandreference:generate --erroronwarnings",
"test:deprecation-policy": "./bin/run snapshot:compare",
"test:nuts": "ts-node ./test/nuts/generateNuts.ts && nyc mocha \"**/*.nut.ts\" --slow 3000 --timeout 600000 --parallel --retries 0",
"test:nuts": "ts-node ./test/nuts/generateNuts.ts && nyc mocha \"**/convert.*.nut.ts\" --slow 3000 --timeout 600000 --parallel --retries 0",
"test:nuts:convert": "ts-node ./test/nuts/generateNuts.ts && nyc mocha \"**/convert.*.nut.ts\" --slow 3000 --timeout 600000 --parallel --retries 0",
"version": "oclif-dev readme"
},
"husky": {
Expand Down
1 change: 0 additions & 1 deletion src/commands/force/source/convert.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ export class Convert extends SourceCommand {
public static readonly description = messages.getMessage('description');
public static readonly examples = messages.getMessage('examples').split(os.EOL);
public static readonly requiresProject = true;
public static readonly requiresUsername = true;
public static readonly flagsConfig: FlagsConfig = {
rootdir: flags.directory({
char: 'r',
Expand Down
5 changes: 2 additions & 3 deletions src/formatters/convertResultFormatter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

import { resolve } from 'path';
import { UX } from '@salesforce/command';
import { Logger, Messages } from '@salesforce/core';
import { Logger, Messages, SfdxError } from '@salesforce/core';
import { ConvertResult } from '@salesforce/source-deploy-retrieve';
import { ResultFormatter } from './resultFormatter';

Expand Down Expand Up @@ -36,8 +36,7 @@ export class ConvertResultFormatter extends ResultFormatter {
if (this.isSuccess()) {
this.ux.log(messages.getMessage('success', [this.result.packagePath]));
} else {
// TODO: make this better
this.ux.log('Failed to convert source');
throw new SfdxError(messages.getMessage('convertFailed'), 'ConvertFailed');
}
}
}
1 change: 1 addition & 0 deletions test/nuts/assertions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,7 @@ export class Assertions {
* Expects files to exist in convert output directory
*/
public async filesToBeConverted(directory: string, globs: string[]): Promise<void> {
directory = directory.split(path.sep).join('/');
const fullGlobs = globs.map((glob) => [directory, glob].join('/'));
const convertedFiles = await fg(fullGlobs);
expect(convertedFiles.length, 'files to be converted').to.be.greaterThan(0);
Expand Down
11 changes: 9 additions & 2 deletions test/nuts/generateNuts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
*/

import * as path from 'path';
import * as os from 'os';
import { fs } from '@salesforce/core';
import { EXECUTABLES, TEST_REPOS_MAP, RepoConfig } from './testMatrix';

Expand Down Expand Up @@ -38,6 +39,12 @@ async function generateNut(
? `${seedName}.${repoName}.${executableName}.nut.ts`
: `${seedName}.${executableName}.nut.ts`;
const nutFilePath = path.join(generatedDir, nutFileName);

// On windows the executable path is being changed to
// single backslashes so ensure proper path.sep.
if (os.platform() === 'win32') {
executable = executable.replace(/\\/g, '\\\\');
}
const contents = seedContents
.replace(/%REPO_URL%/g, repo?.gitUrl)
.replace(/%EXECUTABLE%/g, executable)
Expand All @@ -47,14 +54,14 @@ async function generateNut(

async function generateNuts(): Promise<void> {
const generatedDir = path.resolve(__dirname, 'generated');
await fs.rmdir(generatedDir, { recursive: true });
fs.rmSync(generatedDir, { force: true, recursive: true });
await fs.mkdirp(generatedDir);
const seeds = await getSeedFiles();
for (const seed of seeds) {
const seedName = path.basename(seed).replace('.seed.ts', '');
const seedContents = await fs.readFile(seed, 'UTF-8');
for (const executable of EXECUTABLES.filter((e) => !e.skip)) {
const hasRepo = /const\sREPO\s=\s(.*?)\n/.test(seedContents);
const hasRepo = /const\sREPO\s=\s/.test(seedContents);
if (hasRepo) {
for (const repo of [...TEST_REPOS_MAP.values()].filter((r) => !r.skip)) {
await generateNut(generatedDir, seedName, seedContents, executable.path, repo);
Expand Down
2 changes: 1 addition & 1 deletion test/nuts/nutshell.ts
Original file line number Diff line number Diff line change
Expand Up @@ -371,7 +371,7 @@ export class Nutshell extends AsyncCreatable<Nutshell.Options> {
}

protected async init(): Promise<void> {
if (!Nutshell.Env.getString('TESTKIT_HUB_USERNAME')) {
if (!Nutshell.Env.getString('TESTKIT_HUB_USERNAME') && !Nutshell.Env.getString('TESTKIT_AUTH_URL')) {
ensureString(Nutshell.Env.getString('TESTKIT_JWT_KEY'));
ensureString(Nutshell.Env.getString('TESTKIT_JWT_CLIENT_ID'));
ensureString(Nutshell.Env.getString('TESTKIT_HUB_INSTANCE'));
Expand Down
90 changes: 74 additions & 16 deletions test/nuts/seeds/convert.seed.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,31 @@
*/

import * as path from 'path';
import * as shelljs from 'shelljs';
import { asString } from '@salesforce/ts-types';
import { Nutshell } from '../nutshell';
import { TEST_REPOS_MAP } from '../testMatrix';

// DO NOT TOUCH. generateNuts.ts will insert these values
const REPO = TEST_REPOS_MAP.get('%REPO_URL%');
const EXECUTABLE = '%EXECUTABLE%';

// SDR does not output the package.xml in the same location as toolbelt
// so we have to find it within the output dir, move it, and delete the
// generated dir.
const mvManifest = (dir: string) => {
const manifest = shelljs.find(dir).filter((file) => file.endsWith('package.xml'));
if (!manifest?.length) {
throw Error(`Did not find package.xml within ${dir}`);
}
shelljs.mv(manifest[0], path.join(process.cwd()));
shelljs.rm('-rf', dir);
};

const isSourcePlugin = (): boolean => {
return EXECUTABLE.endsWith(`${path.sep}bin${path.sep}run`);
};

context('Convert NUTs [name: %REPO_NAME%] [exec: %EXECUTABLE%]', () => {
let nutshell: Nutshell;

Expand All @@ -30,15 +48,32 @@ context('Convert NUTs [name: %REPO_NAME%] [exec: %EXECUTABLE%]', () => {
});

describe('--manifest flag', () => {
let convertDir: string;

for (const testCase of REPO.convert.manifest) {
it(`should convert ${testCase.toConvert}`, async () => {
await nutshell.convert({ args: `--sourcepath ${testCase.toConvert} --outputdir out1` });
const packageXml = path.join('out1', 'package.xml');
// Generate a package.xml by converting via sourcepath
const toConvert = path.normalize(testCase.toConvert);
await nutshell.convert({
args: `--sourcepath ${toConvert} --outputdir out1`,
exitCode: 0,
});
const outputDir = path.join(process.cwd(), 'out1');
mvManifest(outputDir);
const packageXml = path.join(process.cwd(), 'package.xml');

const res = await nutshell.convert({ args: `--manifest ${packageXml} --outputdir out2`, exitCode: 0 });

await nutshell.convert({ args: `--manifest ${packageXml} --outputdir out2` });
await nutshell.expect.directoryToHaveSomeFiles('out2');
await nutshell.expect.fileToExist(path.join('out2', 'package.xml'));
await nutshell.expect.filesToBeConverted('out2', testCase.toVerify);
convertDir = path.relative(process.cwd(), asString(res.result?.location));
await nutshell.expect.directoryToHaveSomeFiles(convertDir);
await nutshell.expect.fileToExist(path.join(convertDir, 'package.xml'));
await nutshell.expect.filesToBeConverted(convertDir, testCase.toVerify);
});

afterEach(() => {
if (convertDir) {
shelljs.rm('-rf', convertDir);
}
});
}

Expand All @@ -49,34 +84,57 @@ context('Convert NUTs [name: %REPO_NAME%] [exec: %EXECUTABLE%]', () => {
});

describe('--metadata flag', () => {
let convertDir: string;

for (const testCase of REPO.convert.metadata) {
it(`should convert ${testCase.toConvert}`, async () => {
await nutshell.convert({ args: `--metadata ${testCase.toConvert} --outputdir out` });
await nutshell.expect.directoryToHaveSomeFiles('out');
await nutshell.expect.fileToExist(path.join('out', 'package.xml'));
await nutshell.expect.filesToBeConverted('out', testCase.toVerify);
const res = await nutshell.convert({ args: `--metadata ${testCase.toConvert} --outputdir out`, exitCode: 0 });

convertDir = path.relative(process.cwd(), asString(res.result?.location));
await nutshell.expect.directoryToHaveSomeFiles(convertDir);
await nutshell.expect.fileToExist(path.join(convertDir, 'package.xml'));
await nutshell.expect.filesToBeConverted(convertDir, testCase.toVerify);
});
}

afterEach(() => {
if (convertDir) {
shelljs.rm('-rf', convertDir);
}
});

it('should throw an error if the metadata is not valid', async () => {
const convert = await nutshell.convert({ args: '--metadata DOES_NOT_EXIST', exitCode: 1 });
nutshell.expect.errorToHaveName(convert, 'UnsupportedType');
const expectedError = isSourcePlugin() ? 'RegistryError' : 'UnsupportedType';
nutshell.expect.errorToHaveName(convert, expectedError);
});
});

describe('--sourcepath flag', () => {
let convertDir: string;

for (const testCase of REPO.convert.sourcepath) {
it(`should convert ${testCase.toConvert}`, async () => {
await nutshell.convert({ args: `--sourcepath ${testCase.toConvert} --outputdir out` });
await nutshell.expect.directoryToHaveSomeFiles('out');
await nutshell.expect.fileToExist(path.join('out', 'package.xml'));
await nutshell.expect.filesToBeConverted('out', testCase.toVerify);
const toConvert = path.normalize(testCase.toConvert);
const res = await nutshell.convert({ args: `--sourcepath ${toConvert} --outputdir out`, exitCode: 0 });

convertDir = path.relative(process.cwd(), asString(res.result?.location));
await nutshell.expect.directoryToHaveSomeFiles(convertDir);
await nutshell.expect.fileToExist(path.join(convertDir, 'package.xml'));
await nutshell.expect.filesToBeConverted(convertDir, testCase.toVerify);
});
}

afterEach(() => {
if (convertDir) {
shelljs.rm('-rf', convertDir);
}
});

it('should throw an error if the sourcepath is not valid', async () => {
const convert = await nutshell.convert({ args: '--sourcepath DOES_NOT_EXIST', exitCode: 1 });
nutshell.expect.errorToHaveName(convert, 'SourcePathInvalid');
const expectedError = isSourcePlugin() ? 'SfdxError' : 'SourcePathInvalid';
nutshell.expect.errorToHaveName(convert, expectedError);
});
});
});
2 changes: 1 addition & 1 deletion test/nuts/testMatrix.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ export const EXECUTABLES = [
},
{
path: path.join(process.cwd(), 'bin', 'run'), // path to the plugin's bin/run executable
skip: !env.getBoolean('PLUGIN_SOURCE_TEST_BIN_RUN', false),
skip: !env.getBoolean('PLUGIN_SOURCE_TEST_BIN_RUN', true),
},
];

Expand Down

0 comments on commit b142fb6

Please sign in to comment.