Skip to content

Commit a5cc02c

Browse files
Wr/retrieve nuts (#78)
* chore: retrieve NUTs
1 parent cae2e74 commit a5cc02c

File tree

17 files changed

+2340
-1433
lines changed

17 files changed

+2340
-1433
lines changed

.circleci/config.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,14 @@ workflows:
1616
name: node-12
1717
- release-management/test-nut:
1818
name: nuts-on-linux
19+
node_version: lts
1920
sfdx_version: latest
2021
requires:
2122
- node-latest
2223
- release-management/test-nut:
2324
name: nuts-on-windows
2425
sfdx_version: latest
26+
node_version: lts
2527
os: windows
2628
requires:
2729
- node-latest

CHANGELOG.md

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,26 +6,23 @@ All notable changes to this project will be documented in this file. See [standa
66

77
### [0.0.14](https://github.com/salesforcecli/plugin-source/compare/v0.0.13...v0.0.14) (2021-05-04)
88

9-
109
### Bug Fixes
1110

12-
* support hooks for deployRecentValidation and async deploys ([#75](https://github.com/salesforcecli/plugin-source/issues/75)) ([ac787f9](https://github.com/salesforcecli/plugin-source/commit/ac787f9f8238bd09d44878a6c9994384a5c567c7))
11+
- support hooks for deployRecentValidation and async deploys ([#75](https://github.com/salesforcecli/plugin-source/issues/75)) ([ac787f9](https://github.com/salesforcecli/plugin-source/commit/ac787f9f8238bd09d44878a6c9994384a5c567c7))
1312

1413
### [0.0.13](https://github.com/salesforcecli/plugin-source/compare/v0.0.12...v0.0.13) (2021-05-04)
1514

16-
1715
### Bug Fixes
1816

19-
* deploy output fixes ([#74](https://github.com/salesforcecli/plugin-source/issues/74)) ([d1bb8be](https://github.com/salesforcecli/plugin-source/commit/d1bb8be605458aea81503c1e5bc6974fe03c0ec6))
17+
- deploy output fixes ([#74](https://github.com/salesforcecli/plugin-source/issues/74)) ([d1bb8be](https://github.com/salesforcecli/plugin-source/commit/d1bb8be605458aea81503c1e5bc6974fe03c0ec6))
2018

2119
### [0.0.12](https://github.com/salesforcecli/plugin-source/compare/v0.0.11...v0.0.12) (2021-05-01)
2220

2321
### [0.0.11](https://github.com/salesforcecli/plugin-source/compare/v0.0.10...v0.0.11) (2021-04-29)
2422

25-
2623
### Bug Fixes
2724

28-
* add NUT testing LWC bug ([#70](https://github.com/salesforcecli/plugin-source/issues/70)) ([d6cb456](https://github.com/salesforcecli/plugin-source/commit/d6cb456cdf127e7e896511b8af7606fce25973cb))
25+
- add NUT testing LWC bug ([#70](https://github.com/salesforcecli/plugin-source/issues/70)) ([d6cb456](https://github.com/salesforcecli/plugin-source/commit/d6cb456cdf127e7e896511b8af7606fce25973cb))
2926

3027
### [0.0.10](https://github.com/salesforcecli/plugin-source/compare/v0.0.9...v0.0.10) (2021-04-23)
3128

package.json

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,9 @@
1616
"devDependencies": {
1717
"@oclif/dev-cli": "^1",
1818
"@oclif/plugin-command-snapshot": "^2.0.0",
19-
"@salesforce/cli-plugins-testkit": "^0.0.27",
20-
"@salesforce/dev-config": "^2.1.0",
21-
"@salesforce/dev-scripts": "^0.9.1",
22-
"@salesforce/plugin-apex": "^0.1.18",
19+
"@salesforce/cli-plugins-testkit": "^1.1.1",
20+
"@salesforce/dev-config": "^2.1.2",
21+
"@salesforce/dev-scripts": "^0.9.11",
2322
"@salesforce/plugin-command-reference": "^1.3.0",
2423
"@salesforce/plugin-config": "^1.2.6",
2524
"@salesforce/prettier-config": "^0.0.2",
@@ -34,9 +33,9 @@
3433
"debug": "^4.3.1",
3534
"eslint": "^6.8.0",
3635
"eslint-config-prettier": "^6.11.0",
37-
"eslint-config-salesforce": "^0.1.0",
38-
"eslint-config-salesforce-license": "^0.1.0",
39-
"eslint-config-salesforce-typescript": "^0.2.0",
36+
"eslint-config-salesforce": "^0.1.6",
37+
"eslint-config-salesforce-license": "^0.1.6",
38+
"eslint-config-salesforce-typescript": "^0.2.7",
4039
"eslint-plugin-header": "^3.0.0",
4140
"eslint-plugin-import": "^2.20.2",
4241
"eslint-plugin-jsdoc": "^27.0.3",
@@ -48,6 +47,7 @@
4847
"nyc": "^15.1.0",
4948
"prettier": "^2.0.5",
5049
"pretty-quick": "^2.0.1",
50+
"salesforcedx-templates": "^49.8.0",
5151
"shelljs": "^0.8.4",
5252
"shx": "0.3.3",
5353
"sinon": "^9.0.2",
@@ -83,7 +83,7 @@
8383
"@oclif/plugin-command-snapshot",
8484
"@oclif/plugin-help",
8585
"@salesforce/plugin-command-reference",
86-
"@salesforce/plugin-apex",
86+
"salesforcedx-templates",
8787
"@salesforce/plugin-config"
8888
],
8989
"topics": {
@@ -120,8 +120,10 @@
120120
"test": "sf-test",
121121
"test:command-reference": "./bin/run commandreference:generate --erroronwarnings",
122122
"test:deprecation-policy": "./bin/run snapshot:compare",
123-
"test:nuts": "ts-node ./test/nuts/generateNuts.ts && nyc mocha \"**/convert.*.nut.ts\" --slow 3000 --timeout 600000 --parallel --retries 0",
124-
"test:nuts:convert": "ts-node ./test/nuts/generateNuts.ts && nyc mocha \"**/convert.*.nut.ts\" --slow 3000 --timeout 600000 --parallel --retries 0",
123+
"test:nuts": "ts-node ./test/nuts/generateNuts.ts && nyc mocha \"**/retrieve.*.nut.ts\" --slow 3000 --timeout 600000 --retries 0",
124+
"test:nuts:convert": "PLUGIN_SOURCE_SEED_FILTER=\"convert\" ts-node ./test/nuts/generateNuts.ts && nyc mocha \"**/*.nut.ts\" --slow 3000 --timeout 600000 --parallel --retries 0",
125+
"test:nuts:retrieve": "PLUGIN_SOURCE_SEED_FILTER=\"retrieve\" ts-node ./test/nuts/generateNuts.ts && nyc mocha \"**/*.nut.ts\" --slow 3000 --timeout 600000 --parallel --retries 0",
126+
"test:nuts:deploy": "PLUGIN_SOURCE_SEED_FILTER=\"deploy\" ts-node ./test/nuts/generateNuts.ts && nyc mocha \"**/*.nut.ts\" --slow 3000 --timeout 600000 --parallel --retries 0",
125127
"version": "oclif-dev readme"
126128
},
127129
"husky": {

src/commands/force/source/deploy.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,7 @@ export class Deploy extends DeployCommand {
116116
protected async deploy(): Promise<void> {
117117
this.isAsync = this.getFlag<Duration>('wait').quantity === 0;
118118
this.isRest = await this.isRestDeploy();
119-
this.log(`*** Deploying with ${this.isRest ? 'REST' : 'SOAP'} API ***`);
119+
this.ux.log(`*** Deploying with ${this.isRest ? 'REST' : 'SOAP'} API ***`);
120120

121121
if (this.flags.validateddeployrequestid) {
122122
this.deployResult = await this.deployRecentValidation();

src/deployCommand.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ export abstract class DeployCommand extends SourceCommand {
3131

3232
const res = await this.org.getConnection().metadata.checkDeployStatus(deployId, true);
3333

34-
const deployStatus = (res as unknown) as MetadataApiDeployStatus;
34+
const deployStatus = res as unknown as MetadataApiDeployStatus;
3535
return new DeployResult(deployStatus, new ComponentSet());
3636
}
3737

@@ -98,14 +98,14 @@ export abstract class DeployCommand extends SourceCommand {
9898
const deployResult = await this.report(deployId);
9999
return {
100100
completed: getBoolean(deployResult, 'response.done'),
101-
payload: (deployResult as unknown) as AnyJson,
101+
payload: deployResult as unknown as AnyJson,
102102
};
103103
},
104104
};
105105

106106
const pollingOptions = { ...defaultOptions, ...options };
107107

108108
const pollingClient = await PollingClient.create(pollingOptions);
109-
return (pollingClient.subscribe() as unknown) as Promise<DeployResult>;
109+
return pollingClient.subscribe() as unknown as Promise<DeployResult>;
110110
}
111111
}

test/commands/source/testConsts.ts

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,8 @@ export const exampleSourceComponent = {
1818
adapter: 'matchingContentFile',
1919
},
2020
},
21-
xml:
22-
'/Users/william.ruemmele/projects/scratches/dreamhouse-lwc/force-app/main/default/classes/GeocodingService.cls-meta.xml',
23-
content:
24-
'/Users/william.ruemmele/projects/scratches/dreamhouse-lwc/force-app/main/default/classes/GeocodingService.cls',
21+
xml: '/dreamhouse-lwc/force-app/main/default/classes/GeocodingService.cls-meta.xml',
22+
content: '/dreamhouse-lwc/force-app/main/default/classes/GeocodingService.cls',
2523
};
2624

2725
export const exampleDeployResponse = {

test/nuts/assertions.ts

Lines changed: 27 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ import { MetadataResolver } from '@salesforce/source-deploy-retrieve';
1515
import { debug, Debugger } from 'debug';
1616
import { ApexClass, ApexTestResult, Context, SourceMember, SourceState, StatusResult } from './types';
1717
import { ExecutionLog } from './executionLog';
18-
import { FileTracker, countFiles } from './fileTracker';
18+
import { countFiles, FileTracker } from './fileTracker';
1919

2020
use(chaiEach);
2121

@@ -59,11 +59,16 @@ export class Assertions {
5959
/**
6060
* Expect all files found by globs to be changed according to the file history provided by FileTracker
6161
*/
62-
public async filesToBeChanged(globs: string[]): Promise<void> {
63-
const files = await this.doGlob(globs);
64-
const fileHistories = files
62+
public async filesToBeChanged(globs: string[], ignore: string[] = []): Promise<void> {
63+
const all = await this.doGlob(globs);
64+
// don't assert a result if nothing is to be ignored
65+
const toIgnore = await this.doGlob(ignore, false);
66+
const toTrack = all.filter((file) => !toIgnore.includes(file));
67+
const fileHistories = toTrack
68+
// StaticResource types are inconsistently changed
6569
.filter((f) => !f.endsWith('.resource-meta.xml'))
66-
.map((f) => this.fileTracker.getLatest(f))
70+
.filter((f) => !f.endsWith('.resource'))
71+
.map((f) => this.fileTracker.getLatest(path.normalize(f)))
6772
.filter((f) => !!f);
6873
const allChanged = fileHistories.every((f) => f.changedFromPrevious);
6974
expect(allChanged, 'all files to be changed').to.be.true;
@@ -72,9 +77,11 @@ export class Assertions {
7277
/**
7378
* Expect all files found by globs to NOT be changed according to the file history provided by FileTracker
7479
*/
75-
public async filesToNotBeChanged(globs: string[]): Promise<void> {
76-
const files = await this.doGlob(globs);
77-
const fileHistories = files
80+
public async filesToNotBeChanged(globs: string[], ignore: string[] = []): Promise<void> {
81+
const all = await this.doGlob(globs);
82+
const toIgnore = await this.doGlob(ignore, false);
83+
const toTrack = all.filter((file) => !toIgnore.includes(file));
84+
const fileHistories = toTrack
7885
.filter((f) => !f.endsWith('.resource-meta.xml'))
7986
.map((f) => this.fileTracker.getLatest(f))
8087
.filter((f) => !!f);
@@ -384,14 +391,22 @@ export class Assertions {
384391
private async retrieveApexClasses(classNames?: string[]): Promise<ApexClass[]> {
385392
const query = 'SELECT Name,Id FROM ApexClass';
386393
const result = await this.connection.tooling.query<ApexClass>(query, { autoFetch: true, maxFetch: 50000 });
387-
const apexClasses = classNames ? result.records.filter((r) => classNames.includes(r.Name)) : result.records;
388-
return apexClasses;
394+
return classNames ? result.records.filter((r) => classNames.includes(r.Name)) : result.records;
389395
}
390396

391397
private async doGlob(globs: string[], assert = true): Promise<string[]> {
392398
const files: string[] = [];
393-
for (const glob of globs) {
394-
const fullGlob = glob.startsWith(this.projectDir) ? glob : [this.projectDir, glob].join('/');
399+
const dir = this.projectDir.replace(/\\/g, '/');
400+
401+
for (let glob of globs) {
402+
let fullGlob = glob.replace(/\\/g, '/');
403+
if (glob.startsWith('!')) {
404+
glob = glob.substr(1);
405+
fullGlob = glob.startsWith(dir) ? `!${glob}` : [`!${dir}`, glob].join('/');
406+
} else {
407+
fullGlob = glob.startsWith(dir) ? glob : [dir, glob].join('/');
408+
}
409+
395410
this.debug(`Finding files using glob: ${fullGlob}`);
396411
const globResults = await fg(fullGlob);
397412
this.debug('Found: %O', globResults);

test/nuts/generateNuts.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,19 +7,19 @@
77

88
import * as path from 'path';
99
import * as os from 'os';
10+
import * as shelljs from 'shelljs';
1011
import { fs } from '@salesforce/core';
11-
import { EXECUTABLES, TEST_REPOS_MAP, RepoConfig } from './testMatrix';
12+
import { EXECUTABLES, RepoConfig, TEST_REPOS_MAP } from './testMatrix';
1213

1314
const SEED_FILTER = process.env.PLUGIN_SOURCE_SEED_FILTER || '';
1415

1516
async function getSeedFiles(): Promise<string[]> {
1617
const seedDir = path.join(__dirname, 'seeds');
1718
const files = await fs.readdir(seedDir);
18-
const seeds = files
19+
return files
1920
.filter((f) => f.endsWith('.seed.ts'))
2021
.filter((f) => f.includes(SEED_FILTER))
2122
.map((f) => path.resolve(path.join(seedDir, f)));
22-
return seeds;
2323
}
2424

2525
function parseRepoName(repo?: RepoConfig): string {
@@ -54,7 +54,7 @@ async function generateNut(
5454

5555
async function generateNuts(): Promise<void> {
5656
const generatedDir = path.resolve(__dirname, 'generated');
57-
fs.rmSync(generatedDir, { force: true, recursive: true });
57+
shelljs.rm('-rf', generatedDir);
5858
await fs.mkdirp(generatedDir);
5959
const seeds = await getSeedFiles();
6060
for (const seed of seeds) {

test/nuts/nutshell.ts

Lines changed: 37 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import { AuthInfo, Connection, fs, NamedPackageDir, SfdxProject } from '@salesfo
1818
import { AsyncCreatable } from '@salesforce/kit';
1919
import { debug, Debugger } from 'debug';
2020
import { MetadataResolver } from '@salesforce/source-deploy-retrieve';
21+
import * as shelljs from 'shelljs';
2122
import { Result, StatusResult } from './types';
2223
import { Assertions } from './assertions';
2324
import { ExecutionLog } from './executionLog';
@@ -215,7 +216,7 @@ export class Nutshell extends AsyncCreatable<Nutshell.Options> {
215216
*/
216217
public async readMaxRevision(): Promise<{ sourceMembers: JsonMap }> {
217218
const maxRevisionPath = path.join(this.session.project.dir, '.sfdx', 'orgs', this.username, 'maxRevision.json');
218-
return (fs.readJson(maxRevisionPath) as unknown) as { sourceMembers: JsonMap };
219+
return fs.readJson(maxRevisionPath) as unknown as { sourceMembers: JsonMap };
219220
}
220221

221222
/**
@@ -297,7 +298,7 @@ export class Nutshell extends AsyncCreatable<Nutshell.Options> {
297298
const allFiles = await this.doGlob(globs);
298299

299300
for (const file of allFiles) {
300-
await this.modifyLocalFile(file);
301+
await this.modifyLocalFile(path.normalize(file));
301302
}
302303
}
303304

@@ -370,9 +371,31 @@ export class Nutshell extends AsyncCreatable<Nutshell.Options> {
370371
await this.writeMaxRevision(maxRevision);
371372
}
372373

374+
public isSourcePlugin(): boolean {
375+
return this.executable.endsWith(`${path.sep}bin${path.sep}run`);
376+
}
377+
378+
// SDR does not output the package.xml in the same location as toolbelt
379+
// so we have to find it within the output dir, move it, and delete the
380+
// generated dir.
381+
public findAndMoveManifest(dir: string): void {
382+
const manifest = shelljs.find(dir).filter((file) => file.endsWith('package.xml'));
383+
if (!manifest?.length) {
384+
throw Error(`Did not find package.xml within ${dir}`);
385+
}
386+
shelljs.mv(manifest[0], path.join(process.cwd()));
387+
shelljs.rm('-rf', dir);
388+
}
389+
373390
protected async init(): Promise<void> {
374391
if (!Nutshell.Env.getString('TESTKIT_HUB_USERNAME') && !Nutshell.Env.getString('TESTKIT_AUTH_URL')) {
375-
ensureString(Nutshell.Env.getString('TESTKIT_JWT_KEY'));
392+
const jwtKey = ensureString(Nutshell.Env.getString('TESTKIT_JWT_KEY'));
393+
if (!jwtKey) {
394+
const err = Error('must set on of : TESTKIT_HUB_USERNAME, TESTKIT_AUTH_RUL, or TESTKIT_JWT_KEY');
395+
err.name = 'InvalidTestEnvironment';
396+
throw err;
397+
}
398+
ensureString(jwtKey);
376399
ensureString(Nutshell.Env.getString('TESTKIT_JWT_CLIENT_ID'));
377400
ensureString(Nutshell.Env.getString('TESTKIT_HUB_INSTANCE'));
378401
}
@@ -456,7 +479,7 @@ export class Nutshell extends AsyncCreatable<Nutshell.Options> {
456479
? []
457480
: [
458481
'sfdx config:set restDeploy=false --global',
459-
'sfdx force:org:create -d 1 -s -f config/project-scratch-def.json',
482+
`sfdx force:org:create -d 1 -s -f ${path.join('config', 'project-scratch-def.json')}`,
460483
];
461484
return await TestSession.create({
462485
project: { gitClone: this.repository },
@@ -479,9 +502,16 @@ export class Nutshell extends AsyncCreatable<Nutshell.Options> {
479502
}
480503

481504
private async doGlob(globs: string[]): Promise<string[]> {
482-
const fullGlobs = globs.map((g) =>
483-
g.startsWith(this.session.project.dir) ? g : [this.session.project.dir, g].join('/')
484-
);
505+
const dir = this.session.project.dir.replace(/\\/g, '/');
506+
const fullGlobs = globs.map((g) => {
507+
g = g.replace(/\\/g, '/');
508+
if (g.startsWith('!')) {
509+
g = g.substr(1).startsWith(dir) ? `!${g}` : [`!${dir}`, g].join('/');
510+
} else {
511+
g = g.startsWith(dir) ? g : [dir, g].join('/');
512+
}
513+
return g;
514+
});
485515
return fg(fullGlobs);
486516
}
487517
}

test/nuts/seeds/convert.seed.ts

Lines changed: 3 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -15,22 +15,6 @@ import { TEST_REPOS_MAP } from '../testMatrix';
1515
const REPO = TEST_REPOS_MAP.get('%REPO_URL%');
1616
const EXECUTABLE = '%EXECUTABLE%';
1717

18-
// SDR does not output the package.xml in the same location as toolbelt
19-
// so we have to find it within the output dir, move it, and delete the
20-
// generated dir.
21-
const mvManifest = (dir: string) => {
22-
const manifest = shelljs.find(dir).filter((file) => file.endsWith('package.xml'));
23-
if (!manifest?.length) {
24-
throw Error(`Did not find package.xml within ${dir}`);
25-
}
26-
shelljs.mv(manifest[0], path.join(process.cwd()));
27-
shelljs.rm('-rf', dir);
28-
};
29-
30-
const isSourcePlugin = (): boolean => {
31-
return EXECUTABLE.endsWith(`${path.sep}bin${path.sep}run`);
32-
};
33-
3418
context('Convert NUTs [name: %REPO_NAME%] [exec: %EXECUTABLE%]', () => {
3519
let nutshell: Nutshell;
3620

@@ -59,7 +43,7 @@ context('Convert NUTs [name: %REPO_NAME%] [exec: %EXECUTABLE%]', () => {
5943
exitCode: 0,
6044
});
6145
const outputDir = path.join(process.cwd(), 'out1');
62-
mvManifest(outputDir);
46+
nutshell.findAndMoveManifest(outputDir);
6347
const packageXml = path.join(process.cwd(), 'package.xml');
6448

6549
const res = await nutshell.convert({ args: `--manifest ${packageXml} --outputdir out2`, exitCode: 0 });
@@ -105,7 +89,7 @@ context('Convert NUTs [name: %REPO_NAME%] [exec: %EXECUTABLE%]', () => {
10589

10690
it('should throw an error if the metadata is not valid', async () => {
10791
const convert = await nutshell.convert({ args: '--metadata DOES_NOT_EXIST', exitCode: 1 });
108-
const expectedError = isSourcePlugin() ? 'RegistryError' : 'UnsupportedType';
92+
const expectedError = nutshell.isSourcePlugin() ? 'RegistryError' : 'UnsupportedType';
10993
nutshell.expect.errorToHaveName(convert, expectedError);
11094
});
11195
});
@@ -133,7 +117,7 @@ context('Convert NUTs [name: %REPO_NAME%] [exec: %EXECUTABLE%]', () => {
133117

134118
it('should throw an error if the sourcepath is not valid', async () => {
135119
const convert = await nutshell.convert({ args: '--sourcepath DOES_NOT_EXIST', exitCode: 1 });
136-
const expectedError = isSourcePlugin() ? 'SfdxError' : 'SourcePathInvalid';
120+
const expectedError = nutshell.isSourcePlugin() ? 'SfdxError' : 'SourcePathInvalid';
137121
nutshell.expect.errorToHaveName(convert, expectedError);
138122
});
139123
});

0 commit comments

Comments
 (0)