From 2a37b0f74d60a615102878fd66e37ac106e98047 Mon Sep 17 00:00:00 2001 From: mshanemc Date: Mon, 17 Jan 2022 18:05:37 -0600 Subject: [PATCH 01/12] feat: ignored list --- messages/ignored_list.json | 8 ++ src/commands/force/source/ignored/list.ts | 92 ++++++++++++++ test/commands/source/ignored_list.test.ts | 139 ++++++++++++++++++++++ 3 files changed, 239 insertions(+) create mode 100644 messages/ignored_list.json create mode 100644 src/commands/force/source/ignored/list.ts create mode 100644 test/commands/source/ignored_list.test.ts diff --git a/messages/ignored_list.json b/messages/ignored_list.json new file mode 100644 index 000000000..0420461ec --- /dev/null +++ b/messages/ignored_list.json @@ -0,0 +1,8 @@ +{ + "description": "check your local project package directories for forceignored files", + "examples": ["$ sfdx force:source:ignored", "$ sfdx force:source:ignored --sourcepath force-app"], + "flags": { + "sourcepath": "file or directory of files that the command checks for foreceignored files" + }, + "invalidSourcePath": "File or directory '%s' doesn't exist in your project. Specify one that exists and rerun the command." +} diff --git a/src/commands/force/source/ignored/list.ts b/src/commands/force/source/ignored/list.ts new file mode 100644 index 000000000..ecdc25a47 --- /dev/null +++ b/src/commands/force/source/ignored/list.ts @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2020, salesforce.com, inc. + * All rights reserved. + * 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 * as path from 'path'; +import { flags, FlagsConfig, SfdxCommand } from '@salesforce/command'; +import { fs as fsCore, Messages, SfdxError } from '@salesforce/core'; +import { ForceIgnore } from '@salesforce/source-deploy-retrieve'; + +Messages.importMessagesDirectory(__dirname); +const messages = Messages.loadMessages('@salesforce/plugin-source', 'ignored_list'); + +export type SourceIgnoredResults = { + ignoredFiles: string[]; +}; + +export class SourceIgnoredCommand extends SfdxCommand { + public static readonly description = messages.getMessage('description'); + public static readonly requiresProject = true; + + public static readonly flagsConfig: FlagsConfig = { + sourcepath: flags.filepath({ + char: 'p', + description: messages.getMessage('flags.sourcepath'), + }), + }; + + private forceIgnore: ForceIgnore; + /** + * Outputs all forceignored files from package directories of a project, + * or based on a sourcepath param that points to a specific file or directory. + */ + // eslint-disable-next-line @typescript-eslint/require-await + public async run(): Promise { + try { + this.forceIgnore = ForceIgnore.findAndCreate(this.project.getPath()); + const sourcepaths = this.flags.sourcepath + ? [this.flags.sourcepath as string] + : this.project.getUniquePackageDirectories().map((pDir) => pDir.path); + + const ignoredFiles = (await Promise.all(sourcepaths.map((sp) => this.statIgnored(sp.trim())))).flat(); + + // Command output + if (ignoredFiles.length) { + this.ux.log('Found the following ignored files:'); + ignoredFiles.forEach((filepath) => this.ux.log(filepath)); + } else { + this.ux.log('No ignored files found in paths:'); + sourcepaths.forEach((sp) => this.ux.log(sp)); + } + + return { ignoredFiles }; + } catch (err) { + // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access + if (err.code === 'ENOENT') { + throw SfdxError.create('@salesforce/plugin-source', 'ignored_list', 'invalidSourcePath', [ + this.flags.sourcepath, + ]); + } + throw SfdxError.wrap(err); + } + } + + // Stat the filepath. Test if a file, recurse if a directory. + private async statIgnored(filepath: string): Promise { + const stats = await fsCore.stat(filepath); + if (stats.isDirectory()) { + return (await Promise.all(await this.findIgnored(filepath))).flat(); + } else { + return this.isIgnored(filepath) ? [filepath] : []; + } + } + + // Recursively search a directory for source files to test. + private async findIgnored(dir: string): Promise>> { + this.logger.debug(`Searching dir: ${dir}`); + return (await fsCore.readdir(dir)).map((filename) => this.statIgnored(path.join(dir, filename))); + } + + // Test if a source file is denied, adding any ignored files to + // the ignoredFiles array for output. + private isIgnored(filepath: string): boolean { + if (this.forceIgnore.denies(filepath)) { + this.logger.debug(`[DENIED]: ${filepath}`); + return true; + } + this.logger.debug(`[ACCEPTED]: ${filepath}`); + return false; + } +} diff --git a/test/commands/source/ignored_list.test.ts b/test/commands/source/ignored_list.test.ts new file mode 100644 index 000000000..f0766210c --- /dev/null +++ b/test/commands/source/ignored_list.test.ts @@ -0,0 +1,139 @@ +/* + * Copyright (c) 2020, salesforce.com, inc. + * All rights reserved. + * 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 { join as pathJoin } from 'path'; +import { fs as fsCore, SfdxError, SfdxProject } from '@salesforce/core'; +import { spyMethod, stubMethod } from '@salesforce/ts-sinon'; +import { ForceIgnore } from '@salesforce/source-deploy-retrieve'; +import * as sinon from 'sinon'; +import { expect } from 'chai'; +import { Dictionary } from '@salesforce/ts-types'; +import { SourceIgnoredCommand, SourceIgnoredResults } from '../../../src/commands/force/source/ignored/list'; + +describe('source:ignored:list command', () => { + const packageDirPath = pathJoin('foo', 'bar'); + const packageDirPath2 = pathJoin('main', 'default'); + const packageDirs = [{ path: packageDirPath, default: true }]; + const packageDirsMpd = [{ path: packageDirPath, default: true }, { path: packageDirPath2 }]; + + let getUniquePackageDirectoriesStub: sinon.SinonStub; + let findAndCreateStub: sinon.SinonStub; + let readdirStub: sinon.SinonStub; + let statStub: sinon.SinonStub; + + const sandbox = sinon.createSandbox(); + + beforeEach(() => { + getUniquePackageDirectoriesStub = stubMethod(sandbox, SfdxProject.prototype, 'getUniquePackageDirectories'); + findAndCreateStub = stubMethod(sandbox, ForceIgnore, 'findAndCreate'); + readdirStub = stubMethod(sandbox, fsCore, 'readdir'); + statStub = stubMethod(sandbox, fsCore, 'stat'); + }); + + afterEach(() => { + sandbox.restore(); + }); + + // eslint-disable-next-line @typescript-eslint/require-await + const run = async (flags: Dictionary = {}): Promise => + // Run the command + // eslint-disable-next-line @typescript-eslint/no-unsafe-return + SourceIgnoredCommand.prototype.run.call({ + flags: Object.assign({}, flags), + // eslint-disable-next-line @typescript-eslint/no-empty-function + ux: { log: () => {} }, + logger: { + // eslint-disable-next-line @typescript-eslint/no-empty-function + debug: () => {}, + }, + ignoredFiles: [], + project: { + getPath: () => '', + getUniquePackageDirectories: getUniquePackageDirectoriesStub, + }, + statIgnored: spyMethod(sandbox, SourceIgnoredCommand.prototype, 'statIgnored'), + findIgnored: spyMethod(sandbox, SourceIgnoredCommand.prototype, 'findIgnored'), + isIgnored: spyMethod(sandbox, SourceIgnoredCommand.prototype, 'isIgnored'), + }); + it('should list no ignored files by sourcepath', async () => { + findAndCreateStub.returns({ denies: () => false }); + readdirStub.returns(['something.js', 'nothing.js']); + statStub + .onFirstCall() + .returns({ isDirectory: () => true }) + .onSecondCall() + .returns({ isDirectory: () => false }) + .onThirdCall() + .returns({ isDirectory: () => false }); + const result = await run({ sourcepath: packageDirPath }); + expect(result).to.deep.equal({ ignoredFiles: [] }); + }); + + it('should list an ignored file by sourcepath', async () => { + findAndCreateStub.returns({ denies: () => true }); + readdirStub.returns(['something.js']); + statStub + .onFirstCall() + .returns({ isDirectory: () => true }) + .onSecondCall() + .returns({ isDirectory: () => false }); + const result = await run({ sourcepath: packageDirPath }); + expect(result).to.deep.equal({ + ignoredFiles: [pathJoin(packageDirPath, 'something.js')], + }); + }); + + it('should throw an error with correct message if sourcepath is not valid', async () => { + try { + const err = new SfdxError('test', 'test', null, 1); + err.code = 'ENOENT'; + statStub.onFirstCall().throws(err); + await run({ sourcepath: 'notValidPath' }); + } catch (err) { + // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access + expect(err.message).to.equal( + "File or directory 'notValidPath' doesn't exist in your project. Specify one that exists and rerun the command." + ); + } + }); + + it('should list all ignored files by default', async () => { + getUniquePackageDirectoriesStub.returns(packageDirs); + findAndCreateStub.returns({ denies: () => true }); + readdirStub.returns(['something.js', 'nothing.js']); + statStub + .onFirstCall() + .returns({ isDirectory: () => true }) + .onSecondCall() + .returns({ isDirectory: () => false }) + .onThirdCall() + .returns({ isDirectory: () => false }); + const result = await run(); + expect(result).to.deep.equal({ + ignoredFiles: [pathJoin(packageDirPath, 'something.js'), pathJoin(packageDirPath, 'nothing.js')], + }); + }); + + it('should list all ignored files by default and multiple package dirs', async () => { + getUniquePackageDirectoriesStub.returns(packageDirsMpd); + findAndCreateStub.returns({ denies: () => true }); + readdirStub.returns(['something.js']); + statStub + .withArgs(packageDirPath) + .returns({ isDirectory: () => true }) + .withArgs(packageDirPath2) + .returns({ isDirectory: () => true }) + .withArgs(pathJoin(packageDirPath, 'something.js')) + .returns({ isDirectory: () => false }) + .withArgs(pathJoin(packageDirPath2, 'something.js')) + .returns({ isDirectory: () => false }); + const result = await run(); + expect(result).to.deep.equal({ + ignoredFiles: [pathJoin(packageDirPath, 'something.js'), pathJoin(packageDirPath2, 'something.js')], + }); + }); +}); From 393764ed1efa5289c616f400fe934411c4f8a3ad Mon Sep 17 00:00:00 2001 From: mshanemc Date: Tue, 18 Jan 2022 07:43:25 -0600 Subject: [PATCH 02/12] chore: snapshot update --- command-snapshot.json | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/command-snapshot.json b/command-snapshot.json index 9e4622016..b8721f288 100644 --- a/command-snapshot.json +++ b/command-snapshot.json @@ -31,11 +31,6 @@ "plugin": "@salesforce/plugin-source", "flags": ["apiversion", "jobid", "json", "loglevel", "targetusername", "verbose", "wait"] }, - { - "command": "force:mdapi:deploy:cancel", - "plugin": "@salesforce/plugin-source", - "flags": ["apiversion", "jobid", "json", "loglevel", "targetusername", "wait"] - }, { "command": "force:mdapi:beta:retrieve", "plugin": "@salesforce/plugin-source", @@ -71,6 +66,11 @@ "zipfilename" ] }, + { + "command": "force:mdapi:deploy:cancel", + "plugin": "@salesforce/plugin-source", + "flags": ["apiversion", "jobid", "json", "loglevel", "targetusername", "wait"] + }, { "command": "force:mdapi:describemetadata", "plugin": "@salesforce/plugin-source", @@ -162,6 +162,11 @@ "plugin": "@salesforce/plugin-source", "flags": ["apiversion", "jobid", "json", "loglevel", "targetusername", "verbose", "wait"] }, + { + "command": "force:source:ignored:list", + "plugin": "@salesforce/plugin-source", + "flags": ["json", "loglevel", "sourcepath"] + }, { "command": "force:source:manifest:create", "plugin": "@salesforce/plugin-source", From ba6d26646df1eb8c1aa60096982218a3f0d33edc Mon Sep 17 00:00:00 2001 From: mshanemc Date: Tue, 18 Jan 2022 09:21:27 -0600 Subject: [PATCH 03/12] refactor: use SDR compSet --- src/commands/force/source/ignored/list.ts | 69 +++-------- test/commands/source/ignored_list.test.ts | 139 ---------------------- 2 files changed, 14 insertions(+), 194 deletions(-) delete mode 100644 test/commands/source/ignored_list.test.ts diff --git a/src/commands/force/source/ignored/list.ts b/src/commands/force/source/ignored/list.ts index ecdc25a47..3f6b94781 100644 --- a/src/commands/force/source/ignored/list.ts +++ b/src/commands/force/source/ignored/list.ts @@ -4,10 +4,9 @@ * 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 * as path from 'path'; import { flags, FlagsConfig, SfdxCommand } from '@salesforce/command'; -import { fs as fsCore, Messages, SfdxError } from '@salesforce/core'; -import { ForceIgnore } from '@salesforce/source-deploy-retrieve'; +import { Messages } from '@salesforce/core'; +import { ComponentSet } from '@salesforce/source-deploy-retrieve'; Messages.importMessagesDirectory(__dirname); const messages = Messages.loadMessages('@salesforce/plugin-source', 'ignored_list'); @@ -27,66 +26,26 @@ export class SourceIgnoredCommand extends SfdxCommand { }), }; - private forceIgnore: ForceIgnore; /** * Outputs all forceignored files from package directories of a project, * or based on a sourcepath param that points to a specific file or directory. */ // eslint-disable-next-line @typescript-eslint/require-await public async run(): Promise { - try { - this.forceIgnore = ForceIgnore.findAndCreate(this.project.getPath()); - const sourcepaths = this.flags.sourcepath - ? [this.flags.sourcepath as string] - : this.project.getUniquePackageDirectories().map((pDir) => pDir.path); - - const ignoredFiles = (await Promise.all(sourcepaths.map((sp) => this.statIgnored(sp.trim())))).flat(); - - // Command output - if (ignoredFiles.length) { - this.ux.log('Found the following ignored files:'); - ignoredFiles.forEach((filepath) => this.ux.log(filepath)); - } else { - this.ux.log('No ignored files found in paths:'); - sourcepaths.forEach((sp) => this.ux.log(sp)); - } - - return { ignoredFiles }; - } catch (err) { - // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access - if (err.code === 'ENOENT') { - throw SfdxError.create('@salesforce/plugin-source', 'ignored_list', 'invalidSourcePath', [ - this.flags.sourcepath, - ]); - } - throw SfdxError.wrap(err); - } - } - - // Stat the filepath. Test if a file, recurse if a directory. - private async statIgnored(filepath: string): Promise { - const stats = await fsCore.stat(filepath); - if (stats.isDirectory()) { - return (await Promise.all(await this.findIgnored(filepath))).flat(); + const sourcepaths = this.flags.sourcepath + ? [this.flags.sourcepath as string] + : this.project.getUniquePackageDirectories().map((pDir) => pDir.path); + const compSet = ComponentSet.fromSource(sourcepaths); + const ignoredFiles = Array.from(compSet.forceIgnoredPaths); + // Command output + if (ignoredFiles.length) { + this.ux.log('Found the following ignored files:'); + ignoredFiles.forEach((filepath) => this.ux.log(filepath)); } else { - return this.isIgnored(filepath) ? [filepath] : []; + this.ux.log('No ignored files found in paths:'); + sourcepaths.forEach((sp) => this.ux.log(sp)); } - } - // Recursively search a directory for source files to test. - private async findIgnored(dir: string): Promise>> { - this.logger.debug(`Searching dir: ${dir}`); - return (await fsCore.readdir(dir)).map((filename) => this.statIgnored(path.join(dir, filename))); - } - - // Test if a source file is denied, adding any ignored files to - // the ignoredFiles array for output. - private isIgnored(filepath: string): boolean { - if (this.forceIgnore.denies(filepath)) { - this.logger.debug(`[DENIED]: ${filepath}`); - return true; - } - this.logger.debug(`[ACCEPTED]: ${filepath}`); - return false; + return { ignoredFiles }; } } diff --git a/test/commands/source/ignored_list.test.ts b/test/commands/source/ignored_list.test.ts deleted file mode 100644 index f0766210c..000000000 --- a/test/commands/source/ignored_list.test.ts +++ /dev/null @@ -1,139 +0,0 @@ -/* - * Copyright (c) 2020, salesforce.com, inc. - * All rights reserved. - * 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 { join as pathJoin } from 'path'; -import { fs as fsCore, SfdxError, SfdxProject } from '@salesforce/core'; -import { spyMethod, stubMethod } from '@salesforce/ts-sinon'; -import { ForceIgnore } from '@salesforce/source-deploy-retrieve'; -import * as sinon from 'sinon'; -import { expect } from 'chai'; -import { Dictionary } from '@salesforce/ts-types'; -import { SourceIgnoredCommand, SourceIgnoredResults } from '../../../src/commands/force/source/ignored/list'; - -describe('source:ignored:list command', () => { - const packageDirPath = pathJoin('foo', 'bar'); - const packageDirPath2 = pathJoin('main', 'default'); - const packageDirs = [{ path: packageDirPath, default: true }]; - const packageDirsMpd = [{ path: packageDirPath, default: true }, { path: packageDirPath2 }]; - - let getUniquePackageDirectoriesStub: sinon.SinonStub; - let findAndCreateStub: sinon.SinonStub; - let readdirStub: sinon.SinonStub; - let statStub: sinon.SinonStub; - - const sandbox = sinon.createSandbox(); - - beforeEach(() => { - getUniquePackageDirectoriesStub = stubMethod(sandbox, SfdxProject.prototype, 'getUniquePackageDirectories'); - findAndCreateStub = stubMethod(sandbox, ForceIgnore, 'findAndCreate'); - readdirStub = stubMethod(sandbox, fsCore, 'readdir'); - statStub = stubMethod(sandbox, fsCore, 'stat'); - }); - - afterEach(() => { - sandbox.restore(); - }); - - // eslint-disable-next-line @typescript-eslint/require-await - const run = async (flags: Dictionary = {}): Promise => - // Run the command - // eslint-disable-next-line @typescript-eslint/no-unsafe-return - SourceIgnoredCommand.prototype.run.call({ - flags: Object.assign({}, flags), - // eslint-disable-next-line @typescript-eslint/no-empty-function - ux: { log: () => {} }, - logger: { - // eslint-disable-next-line @typescript-eslint/no-empty-function - debug: () => {}, - }, - ignoredFiles: [], - project: { - getPath: () => '', - getUniquePackageDirectories: getUniquePackageDirectoriesStub, - }, - statIgnored: spyMethod(sandbox, SourceIgnoredCommand.prototype, 'statIgnored'), - findIgnored: spyMethod(sandbox, SourceIgnoredCommand.prototype, 'findIgnored'), - isIgnored: spyMethod(sandbox, SourceIgnoredCommand.prototype, 'isIgnored'), - }); - it('should list no ignored files by sourcepath', async () => { - findAndCreateStub.returns({ denies: () => false }); - readdirStub.returns(['something.js', 'nothing.js']); - statStub - .onFirstCall() - .returns({ isDirectory: () => true }) - .onSecondCall() - .returns({ isDirectory: () => false }) - .onThirdCall() - .returns({ isDirectory: () => false }); - const result = await run({ sourcepath: packageDirPath }); - expect(result).to.deep.equal({ ignoredFiles: [] }); - }); - - it('should list an ignored file by sourcepath', async () => { - findAndCreateStub.returns({ denies: () => true }); - readdirStub.returns(['something.js']); - statStub - .onFirstCall() - .returns({ isDirectory: () => true }) - .onSecondCall() - .returns({ isDirectory: () => false }); - const result = await run({ sourcepath: packageDirPath }); - expect(result).to.deep.equal({ - ignoredFiles: [pathJoin(packageDirPath, 'something.js')], - }); - }); - - it('should throw an error with correct message if sourcepath is not valid', async () => { - try { - const err = new SfdxError('test', 'test', null, 1); - err.code = 'ENOENT'; - statStub.onFirstCall().throws(err); - await run({ sourcepath: 'notValidPath' }); - } catch (err) { - // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access - expect(err.message).to.equal( - "File or directory 'notValidPath' doesn't exist in your project. Specify one that exists and rerun the command." - ); - } - }); - - it('should list all ignored files by default', async () => { - getUniquePackageDirectoriesStub.returns(packageDirs); - findAndCreateStub.returns({ denies: () => true }); - readdirStub.returns(['something.js', 'nothing.js']); - statStub - .onFirstCall() - .returns({ isDirectory: () => true }) - .onSecondCall() - .returns({ isDirectory: () => false }) - .onThirdCall() - .returns({ isDirectory: () => false }); - const result = await run(); - expect(result).to.deep.equal({ - ignoredFiles: [pathJoin(packageDirPath, 'something.js'), pathJoin(packageDirPath, 'nothing.js')], - }); - }); - - it('should list all ignored files by default and multiple package dirs', async () => { - getUniquePackageDirectoriesStub.returns(packageDirsMpd); - findAndCreateStub.returns({ denies: () => true }); - readdirStub.returns(['something.js']); - statStub - .withArgs(packageDirPath) - .returns({ isDirectory: () => true }) - .withArgs(packageDirPath2) - .returns({ isDirectory: () => true }) - .withArgs(pathJoin(packageDirPath, 'something.js')) - .returns({ isDirectory: () => false }) - .withArgs(pathJoin(packageDirPath2, 'something.js')) - .returns({ isDirectory: () => false }); - const result = await run(); - expect(result).to.deep.equal({ - ignoredFiles: [pathJoin(packageDirPath, 'something.js'), pathJoin(packageDirPath2, 'something.js')], - }); - }); -}); From b9b445e3cc3cede547fb5e057de59ea18b6be2a0 Mon Sep 17 00:00:00 2001 From: mshanemc Date: Tue, 18 Jan 2022 09:59:12 -0600 Subject: [PATCH 04/12] test: nut for ignored:list --- test/nuts/ignored_list.nut.ts | 108 ++++++++++++++++++++++++++++++++++ 1 file changed, 108 insertions(+) create mode 100644 test/nuts/ignored_list.nut.ts diff --git a/test/nuts/ignored_list.nut.ts b/test/nuts/ignored_list.nut.ts new file mode 100644 index 000000000..df23c15f7 --- /dev/null +++ b/test/nuts/ignored_list.nut.ts @@ -0,0 +1,108 @@ +/* + * Copyright (c) 2020, salesforce.com, inc. + * All rights reserved. + * 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 * as fs from 'fs'; +import * as os from 'os'; +import * as path from 'path'; +import { execCmd, TestSession } from '@salesforce/cli-plugins-testkit'; +import { expect } from 'chai'; +import { AuthStrategy } from '@salesforce/cli-plugins-testkit/lib/hubAuth'; +import { SourceIgnoredResults } from '../../src/commands/force/source/ignored/list'; + +describe('force:source:ignored:list', () => { + let session: TestSession; + let forceIgnorePath: string; + let originalForceIgnore; + + const pathToIgnoredFile1 = path.join('foo-bar', 'app', 'classes', 'FooBar.cls'); + const pathToIgnoredFile2 = path.join('foo-bar', 'app', 'classes', 'FooBar.cls-meta.xml'); + + before(async () => { + session = await TestSession.create({ + project: { + gitClone: 'https://github.com/salesforcecli/sample-project-multiple-packages', + }, + authStrategy: AuthStrategy.NONE, + }); + forceIgnorePath = path.join(session.project.dir, '.forceignore'); + originalForceIgnore = await fs.promises.readFile(forceIgnorePath, 'utf8'); + }); + + after(async () => { + await session?.clean(); + }); + + describe('returns an ignored class using specified path in forceignore', () => { + before(async () => { + await fs.promises.appendFile(forceIgnorePath, `${pathToIgnoredFile1}${os.EOL}`); + await fs.promises.appendFile(forceIgnorePath, `${pathToIgnoredFile2}${os.EOL}`); + }); + after(async () => { + await fs.promises.writeFile(forceIgnorePath, originalForceIgnore); + }); + it('default PkgDir', () => { + const result = execCmd('force:source:ignored:list --json', { ensureExitCode: 0 }).jsonOutput + .result; + expect(result.ignoredFiles).to.include(pathToIgnoredFile1); + expect(result.ignoredFiles).to.include(pathToIgnoredFile2); + }); + it('specified sourcePath', () => { + const result2 = execCmd('force:source:ignored:list --json -p foo-bar', { + ensureExitCode: 0, + }).jsonOutput.result; + expect(result2.ignoredFiles).to.include(pathToIgnoredFile1); + expect(result2.ignoredFiles).to.include(pathToIgnoredFile2); + }); + }); + + describe('returns an ignored class using wildcards', () => { + before(async () => { + await fs.promises.appendFile(forceIgnorePath, '**/FooBar.*'); + }); + after(async () => { + await fs.promises.writeFile(forceIgnorePath, originalForceIgnore); + }); + + it('default PkgDir', () => { + const result = execCmd('force:source:ignored:list --json', { ensureExitCode: 0 }).jsonOutput + .result; + expect(result.ignoredFiles).to.include(pathToIgnoredFile1); + expect(result.ignoredFiles).to.include(pathToIgnoredFile2); + }); + it('specified sourcePath', () => { + const result2 = execCmd('force:source:ignored:list --json -p foo-bar', { + ensureExitCode: 0, + }).jsonOutput.result; + expect(result2.ignoredFiles).to.include(pathToIgnoredFile1); + expect(result2.ignoredFiles).to.include(pathToIgnoredFile2); + }); + }); + + describe('returns an ignored non-metadata component', () => { + const lwcDir = path.join('foo-bar', 'app', 'lwc'); + const lwcConfigPath = path.join(lwcDir, 'jsconfig.json'); + + before(async () => { + await fs.promises.mkdir(path.join(session.project.dir, lwcDir), { recursive: true }); + await fs.promises.writeFile(path.join(session.project.dir, lwcConfigPath), '{}'); + }); + after(async () => { + await fs.promises.writeFile(forceIgnorePath, originalForceIgnore); + }); + + it('default PkgDir', () => { + const result = execCmd('force:source:ignored:list --json', { ensureExitCode: 0 }).jsonOutput + .result; + expect(result.ignoredFiles).to.include(lwcConfigPath); + }); + it('specified sourcePath', () => { + const result2 = execCmd('force:source:ignored:list --json -p foo-bar', { + ensureExitCode: 0, + }).jsonOutput.result; + expect(result2.ignoredFiles).to.include(lwcConfigPath); + }); + }); +}); From ec242176036414c9dbc3e30948ce21853a6889b1 Mon Sep 17 00:00:00 2001 From: mshanemc Date: Tue, 18 Jan 2022 15:53:11 -0600 Subject: [PATCH 05/12] test: ci for unstable org signup destinations --- .circleci/config.yml | 6 ++++++ package.json | 12 ++++++------ 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 5c793a277..57667f136 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -79,6 +79,12 @@ workflows: - 'yarn test:nuts:specialTypes' - 'yarn test:nuts:deploy:destructive' - 'yarn test:nuts:tracking' + # these 5 use e-bikes, which currently has some scratch org api version issues + - 'yarn test:nuts:tracking:basics' + - 'yarn test:nuts:tracking:conflicts' + - 'yarn test:nuts:tracking:lwc' + - 'yarn test:nuts:tracking:resetClear' + - 'yarn test:nuts:tracking:remote' - release-management/release-package: sign: true github-release: true diff --git a/package.json b/package.json index 3133ffe1c..042d1765e 100644 --- a/package.json +++ b/package.json @@ -174,13 +174,13 @@ "test:nuts:retrieve": "cross-env PLUGIN_SOURCE_SEED_FILTER=retrieve ts-node ./test/nuts/generateNuts.ts && mocha \"test/nuts/generated/*.nut.ts\" --slow 4500 --timeout 600000 --parallel --retries 0", "test:nuts:specialTypes": "mocha \"test/nuts/territory2.nut.ts\" \"test/nuts/folderTypes.nut.ts\" --slow 4500 --timeout 600000 --retries 0 --parallel", "test:nuts:territory2": "mocha \"test/nuts/territory2.nut.ts\" --slow 4500 --timeout 600000 --retries 0", - "test:nuts:tracking": "mocha \"test/nuts/trackingCommands/*.nut.ts\" --slow 3000 --timeout 600000 --parallel --retries 0", - "test:nuts:tracking:basics": "mocha \"test/nuts/trackingCommands/basics.nut.ts\" --slow 3000 --timeout 600000 --retries 0", - "test:nuts:tracking:conflicts": "mocha \"test/nuts/trackingCommands/conflicts.nut.ts\" --slow 3000 --timeout 600000 --retries 0", + "test:nuts:tracking": "mocha \"test/nuts/trackingCommands/forceIgnore.nut.ts\" \"test/nuts/trackingCommands/mpd*.nut.ts\" --slow 3000 --timeout 600000 --parallel --retries 0", + "test:nuts:tracking:basics": "mocha \"test/nuts/trackingCommands/basics.nut.ts\" --slow 3000 --timeout 600000 --retries 3", + "test:nuts:tracking:conflicts": "mocha \"test/nuts/trackingCommands/conflicts.nut.ts\" --slow 3000 --timeout 600000 --retries 3", "test:nuts:tracking:forceignore": "mocha \"test/nuts/trackingCommands/forceIgnore.nut.ts\" --slow 3000 --timeout 600000 --retries 0", - "test:nuts:tracking:remote": "mocha \"test/nuts/trackingCommands/remoteChanges.nut.ts\" --slow 3000 --timeout 600000 --retries 0", - "test:nuts:tracking:resetClear": "mocha \"test/nuts/trackingCommands/resetClear.nut.ts\" --slow 3000 --timeout 600000 --retries 0", - "test:nuts:tracking:lwc": "mocha \"test/nuts/trackingCommands/lwc.nut.ts\" --slow 3000 --timeout 600000 --retries 0", + "test:nuts:tracking:remote": "mocha \"test/nuts/trackingCommands/remoteChanges.nut.ts\" --slow 3000 --timeout 600000 --retries 3", + "test:nuts:tracking:resetClear": "mocha \"test/nuts/trackingCommands/resetClear.nut.ts\" --slow 3000 --timeout 600000 --retries 3", + "test:nuts:tracking:lwc": "mocha \"test/nuts/trackingCommands/lwc.nut.ts\" --slow 3000 --timeout 600000 --retries 3", "version": "oclif-dev readme" }, "publishConfig": { From d25bd7da570d97c9670c5a06cb81f99f3f3af0ab Mon Sep 17 00:00:00 2001 From: mshanemc Date: Wed, 19 Jan 2022 08:14:51 -0600 Subject: [PATCH 06/12] chore: bump stl/sdr --- package.json | 4 ++-- yarn.lock | 49 ++++++++++++++++++++++++++++++++++++++----------- 2 files changed, 40 insertions(+), 13 deletions(-) diff --git a/package.json b/package.json index 042d1765e..b94c5b0e3 100644 --- a/package.json +++ b/package.json @@ -9,7 +9,7 @@ "@salesforce/command": "^4.2.0", "@salesforce/core": "^2.31.0", "@salesforce/source-deploy-retrieve": "^5.9.1", - "@salesforce/source-tracking": "^0.5.1", + "@salesforce/source-tracking": "^0.5.2", "chalk": "^4.1.2", "cli-ux": "^5.6.3", "open": "^8.2.1", @@ -175,7 +175,7 @@ "test:nuts:specialTypes": "mocha \"test/nuts/territory2.nut.ts\" \"test/nuts/folderTypes.nut.ts\" --slow 4500 --timeout 600000 --retries 0 --parallel", "test:nuts:territory2": "mocha \"test/nuts/territory2.nut.ts\" --slow 4500 --timeout 600000 --retries 0", "test:nuts:tracking": "mocha \"test/nuts/trackingCommands/forceIgnore.nut.ts\" \"test/nuts/trackingCommands/mpd*.nut.ts\" --slow 3000 --timeout 600000 --parallel --retries 0", - "test:nuts:tracking:basics": "mocha \"test/nuts/trackingCommands/basics.nut.ts\" --slow 3000 --timeout 600000 --retries 3", + "test:nuts:tracking:basics": "mocha \"test/nuts/trackingCommands/basics.nut.ts\" --slow 3000 --timeout 600000 --retries 0", "test:nuts:tracking:conflicts": "mocha \"test/nuts/trackingCommands/conflicts.nut.ts\" --slow 3000 --timeout 600000 --retries 3", "test:nuts:tracking:forceignore": "mocha \"test/nuts/trackingCommands/forceIgnore.nut.ts\" --slow 3000 --timeout 600000 --retries 0", "test:nuts:tracking:remote": "mocha \"test/nuts/trackingCommands/remoteChanges.nut.ts\" --slow 3000 --timeout 600000 --retries 3", diff --git a/yarn.lock b/yarn.lock index 7093c7684..87974544a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -835,6 +835,28 @@ sfdx-faye "^1.0.9" ts-retry-promise "^0.6.0" +"@salesforce/core@^2.33.1": + version "2.33.1" + resolved "https://registry.yarnpkg.com/@salesforce/core/-/core-2.33.1.tgz#23c22953174ad0e809b2f1c7b2881b5ba8a89d9c" + integrity sha512-jKVFYEvlV+loBoau5heBOVXmzsPO+RbYh6SPybJK6xF7khQmzu7+WAQbikY2eY8VaXcded2kka8L/FKuD/LKBg== + dependencies: + "@salesforce/bunyan" "^2.0.0" + "@salesforce/kit" "^1.5.0" + "@salesforce/schemas" "^1.0.1" + "@salesforce/ts-types" "^1.5.13" + "@types/graceful-fs" "^4.1.5" + "@types/jsforce" "^1.9.35" + "@types/mkdirp" "^1.0.1" + debug "^3.1.0" + faye "^1.4.0" + graceful-fs "^4.2.4" + jsen "0.6.6" + jsforce "^1.11.0" + jsonwebtoken "8.5.0" + mkdirp "1.0.4" + semver "^7.3.5" + ts-retry-promise "^0.6.0" + "@salesforce/dev-config@^2.1.2": version "2.1.2" resolved "https://registry.npmjs.org/@salesforce/dev-config/-/dev-config-2.1.2.tgz#b4e206f860e87065d068bf8ba3994a032389ad81" @@ -957,10 +979,10 @@ unzipper "0.10.11" xmldom-sfdx-encoding "^0.1.29" -"@salesforce/source-deploy-retrieve@^5.8.0": - version "5.8.0" - resolved "https://registry.yarnpkg.com/@salesforce/source-deploy-retrieve/-/source-deploy-retrieve-5.8.0.tgz#f9358874d282d6cdd27506819964c037ad1dfee7" - integrity sha512-Z2Wh0guPL0ZEjD2HqNCPT8Gih6FMN85v9xGB+oss5yrrsx8QwD9UBNFivLojeCNduuNRF0BMOwp9mMt7QlpdJA== +"@salesforce/source-deploy-retrieve@^5.8.2": + version "5.9.2" + resolved "https://registry.yarnpkg.com/@salesforce/source-deploy-retrieve/-/source-deploy-retrieve-5.9.2.tgz#7e6fd87572a3a5fb41e2a2d0b3bb75f231835d6b" + integrity sha512-Q1FamL7pQ5wj1ZlwqaqcJBHe+jZ1JNNMeyFct/uygohOypy0nJpS+AhKKpIerD9OPPW9KYa2fmqBf0QpmBEIbQ== dependencies: "@salesforce/core" "2.31.0" "@salesforce/kit" "^1.5.0" @@ -969,7 +991,7 @@ fast-xml-parser "^3.17.4" graceful-fs "^4.2.8" ignore "^5.1.8" - mime "2.4.6" + mime "2.6.0" unzipper "0.10.11" xmldom-sfdx-encoding "^0.1.29" @@ -1006,14 +1028,14 @@ sinon "^10.0.0" strip-ansi "^6.0.0" -"@salesforce/source-tracking@^0.5.1": - version "0.5.1" - resolved "https://registry.yarnpkg.com/@salesforce/source-tracking/-/source-tracking-0.5.1.tgz#1be69d4cd5ec51e6a09a863d0a1b15ac8f4af7be" - integrity sha512-unbwKIwCg/ZwHC5cNjm65oxhkpQBT3Pc5XEwTtRNpgyGhHMuNS74zcZSmABjXrGbQDsYf4TbwC+NMKwvErY3Qg== +"@salesforce/source-tracking@^0.5.2": + version "0.5.2" + resolved "https://registry.yarnpkg.com/@salesforce/source-tracking/-/source-tracking-0.5.2.tgz#c0f36b31887bd2505cdfef7db2da00f2757288e6" + integrity sha512-vWA8hJxCv7ov/6Rh0865/cOlnZ7GgQtu3+tUKGMhZBmtubCuYIKSIwSifh6tunbnmVeqUa4FntgVvGUT2nYIqw== dependencies: - "@salesforce/core" "^2.31.0" + "@salesforce/core" "^2.33.1" "@salesforce/kit" "^1.5.17" - "@salesforce/source-deploy-retrieve" "^5.8.0" + "@salesforce/source-deploy-retrieve" "^5.8.2" isomorphic-git "^1.9.2" ts-retry-promise "^0.6.0" @@ -5527,6 +5549,11 @@ mime@2.4.6: resolved "https://registry.npmjs.org/mime/-/mime-2.4.6.tgz#e5b407c90db442f2beb5b162373d07b69affa4d1" integrity sha512-RZKhC3EmpBchfTGBVb8fb+RL2cWyw/32lshnsETttkBAyAUXSGHxbEJWWRXc751DrIxG1q04b8QwMbAwkRPpUA== +mime@2.6.0: + version "2.6.0" + resolved "https://registry.yarnpkg.com/mime/-/mime-2.6.0.tgz#a2a682a95cd4d0cb1d6257e28f83da7e35800367" + integrity sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg== + mimic-fn@^1.0.0: version "1.2.0" resolved "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz#820c86a39334640e99516928bd03fca88057d022" From 5c4f07ea2a0e64c7c49e3cc2c3c935ab7d933146 Mon Sep 17 00:00:00 2001 From: mshanemc Date: Wed, 19 Jan 2022 08:16:00 -0600 Subject: [PATCH 07/12] Revert "test: ci for unstable org signup destinations" This reverts commit ec242176036414c9dbc3e30948ce21853a6889b1. --- .circleci/config.yml | 6 ------ package.json | 10 +++++----- 2 files changed, 5 insertions(+), 11 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 57667f136..5c793a277 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -79,12 +79,6 @@ workflows: - 'yarn test:nuts:specialTypes' - 'yarn test:nuts:deploy:destructive' - 'yarn test:nuts:tracking' - # these 5 use e-bikes, which currently has some scratch org api version issues - - 'yarn test:nuts:tracking:basics' - - 'yarn test:nuts:tracking:conflicts' - - 'yarn test:nuts:tracking:lwc' - - 'yarn test:nuts:tracking:resetClear' - - 'yarn test:nuts:tracking:remote' - release-management/release-package: sign: true github-release: true diff --git a/package.json b/package.json index b94c5b0e3..5572b2505 100644 --- a/package.json +++ b/package.json @@ -174,13 +174,13 @@ "test:nuts:retrieve": "cross-env PLUGIN_SOURCE_SEED_FILTER=retrieve ts-node ./test/nuts/generateNuts.ts && mocha \"test/nuts/generated/*.nut.ts\" --slow 4500 --timeout 600000 --parallel --retries 0", "test:nuts:specialTypes": "mocha \"test/nuts/territory2.nut.ts\" \"test/nuts/folderTypes.nut.ts\" --slow 4500 --timeout 600000 --retries 0 --parallel", "test:nuts:territory2": "mocha \"test/nuts/territory2.nut.ts\" --slow 4500 --timeout 600000 --retries 0", - "test:nuts:tracking": "mocha \"test/nuts/trackingCommands/forceIgnore.nut.ts\" \"test/nuts/trackingCommands/mpd*.nut.ts\" --slow 3000 --timeout 600000 --parallel --retries 0", + "test:nuts:tracking": "mocha \"test/nuts/trackingCommands/*.nut.ts\" --slow 3000 --timeout 600000 --parallel --retries 0", "test:nuts:tracking:basics": "mocha \"test/nuts/trackingCommands/basics.nut.ts\" --slow 3000 --timeout 600000 --retries 0", - "test:nuts:tracking:conflicts": "mocha \"test/nuts/trackingCommands/conflicts.nut.ts\" --slow 3000 --timeout 600000 --retries 3", + "test:nuts:tracking:conflicts": "mocha \"test/nuts/trackingCommands/conflicts.nut.ts\" --slow 3000 --timeout 600000 --retries 0", "test:nuts:tracking:forceignore": "mocha \"test/nuts/trackingCommands/forceIgnore.nut.ts\" --slow 3000 --timeout 600000 --retries 0", - "test:nuts:tracking:remote": "mocha \"test/nuts/trackingCommands/remoteChanges.nut.ts\" --slow 3000 --timeout 600000 --retries 3", - "test:nuts:tracking:resetClear": "mocha \"test/nuts/trackingCommands/resetClear.nut.ts\" --slow 3000 --timeout 600000 --retries 3", - "test:nuts:tracking:lwc": "mocha \"test/nuts/trackingCommands/lwc.nut.ts\" --slow 3000 --timeout 600000 --retries 3", + "test:nuts:tracking:remote": "mocha \"test/nuts/trackingCommands/remoteChanges.nut.ts\" --slow 3000 --timeout 600000 --retries 0", + "test:nuts:tracking:resetClear": "mocha \"test/nuts/trackingCommands/resetClear.nut.ts\" --slow 3000 --timeout 600000 --retries 0", + "test:nuts:tracking:lwc": "mocha \"test/nuts/trackingCommands/lwc.nut.ts\" --slow 3000 --timeout 600000 --retries 0", "version": "oclif-dev readme" }, "publishConfig": { From b213b04350179657700d8298d9489b9ea716205d Mon Sep 17 00:00:00 2001 From: mshanemc Date: Wed, 19 Jan 2022 08:25:44 -0600 Subject: [PATCH 08/12] test: pr feedback test ideas --- messages/ignored_list.json | 3 +-- test/nuts/ignored_list.nut.ts | 40 +++++++++++++++++++++++++++++++++++ 2 files changed, 41 insertions(+), 2 deletions(-) diff --git a/messages/ignored_list.json b/messages/ignored_list.json index 0420461ec..8899217e3 100644 --- a/messages/ignored_list.json +++ b/messages/ignored_list.json @@ -3,6 +3,5 @@ "examples": ["$ sfdx force:source:ignored", "$ sfdx force:source:ignored --sourcepath force-app"], "flags": { "sourcepath": "file or directory of files that the command checks for foreceignored files" - }, - "invalidSourcePath": "File or directory '%s' doesn't exist in your project. Specify one that exists and rerun the command." + } } diff --git a/test/nuts/ignored_list.nut.ts b/test/nuts/ignored_list.nut.ts index df23c15f7..094fe4633 100644 --- a/test/nuts/ignored_list.nut.ts +++ b/test/nuts/ignored_list.nut.ts @@ -35,6 +35,46 @@ describe('force:source:ignored:list', () => { await session?.clean(); }); + describe('no forceignore', () => { + before(async () => { + await fs.promises.rm(forceIgnorePath); + }); + after(async () => { + await fs.promises.writeFile(forceIgnorePath, originalForceIgnore); + }); + it('default PkgDir', () => { + const result = execCmd('force:source:ignored:list --json', { ensureExitCode: 0 }).jsonOutput + .result; + expect(result.ignoredFiles).to.deep.equal([]); + }); + it('specified sourcePath', () => { + const result2 = execCmd('force:source:ignored:list --json -p foo-bar', { + ensureExitCode: 0, + }).jsonOutput.result; + expect(result2.ignoredFiles).to.deep.equal([]); + }); + }); + + describe('no files are ignored (empty forceignore)', () => { + before(async () => { + await fs.promises.writeFile(forceIgnorePath, ''); + }); + after(async () => { + await fs.promises.writeFile(forceIgnorePath, originalForceIgnore); + }); + it('default PkgDir', () => { + const result = execCmd('force:source:ignored:list --json', { ensureExitCode: 0 }).jsonOutput + .result; + expect(result.ignoredFiles).to.deep.equal([]); + }); + it('specified sourcePath', () => { + const result2 = execCmd('force:source:ignored:list --json -p foo-bar', { + ensureExitCode: 0, + }).jsonOutput.result; + expect(result2.ignoredFiles).to.deep.equal([]); + }); + }); + describe('returns an ignored class using specified path in forceignore', () => { before(async () => { await fs.promises.appendFile(forceIgnorePath, `${pathToIgnoredFile1}${os.EOL}`); From b2837ef8fb9d058d53fe67e8c44fb9aaa077faa5 Mon Sep 17 00:00:00 2001 From: mshanemc Date: Wed, 19 Jan 2022 09:44:35 -0600 Subject: [PATCH 09/12] ci: run the misc command NUTs --- .circleci/config.yml | 1 + package.json | 1 + 2 files changed, 2 insertions(+) diff --git a/.circleci/config.yml b/.circleci/config.yml index 5c793a277..cd7933607 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -71,6 +71,7 @@ workflows: - windows command: - 'yarn test:nuts:convert' + - 'yarn test:nuts:commands:other' - 'yarn test:nuts:delete' - 'yarn test:nuts:deploy' - 'yarn test:nuts:deploy:async' diff --git a/package.json b/package.json index 5572b2505..6be48320c 100644 --- a/package.json +++ b/package.json @@ -158,6 +158,7 @@ "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 4500 --timeout 600000 --parallel --retries 0", + "test:nuts:commands:other": "mocha \"test/nuts/open.nut.ts\" \"test/nuts/ignore_list.nut.ts\" --slow 4500 --timeout 600000 --retries 0 --parallel", "test:nuts:convert": "cross-env PLUGIN_SOURCE_SEED_FILTER=convert ts-node ./test/nuts/generateNuts.ts && mocha \"test/nuts/generated/*.nut.ts\" --slow 4500 --timeout 600000 --parallel --retries 0", "test:nuts:delete": "mocha \"test/nuts/delete.nut.ts\" --slow 4500 --timeout 600000 --retries 0", "test:nuts:deploy": "cross-env PLUGIN_SOURCE_SEED_FILTER=deploy PLUGIN_SOURCE_SEED_EXCLUDE=async ts-node ./test/nuts/generateNuts.ts && mocha \"test/nuts/generated/*.nut.ts\" --slow 4500 --timeout 600000 --parallel --retries 0", From 2302bf1cacd3d0e83b6dad1f2d200e40de9c2bc4 Mon Sep 17 00:00:00 2001 From: mshanemc Date: Thu, 20 Jan 2022 07:56:57 -0600 Subject: [PATCH 10/12] refactor: revert to walking all files for ignored --- messages/ignored_list.json | 3 +- src/commands/force/source/ignored/list.ts | 71 ++++++++++++++++++----- 2 files changed, 58 insertions(+), 16 deletions(-) diff --git a/messages/ignored_list.json b/messages/ignored_list.json index 8899217e3..0420461ec 100644 --- a/messages/ignored_list.json +++ b/messages/ignored_list.json @@ -3,5 +3,6 @@ "examples": ["$ sfdx force:source:ignored", "$ sfdx force:source:ignored --sourcepath force-app"], "flags": { "sourcepath": "file or directory of files that the command checks for foreceignored files" - } + }, + "invalidSourcePath": "File or directory '%s' doesn't exist in your project. Specify one that exists and rerun the command." } diff --git a/src/commands/force/source/ignored/list.ts b/src/commands/force/source/ignored/list.ts index 3f6b94781..07425f5b8 100644 --- a/src/commands/force/source/ignored/list.ts +++ b/src/commands/force/source/ignored/list.ts @@ -1,12 +1,13 @@ /* - * Copyright (c) 2020, salesforce.com, inc. + * Copyright (c) 2022, salesforce.com, inc. * All rights reserved. * 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 * as path from 'path'; import { flags, FlagsConfig, SfdxCommand } from '@salesforce/command'; -import { Messages } from '@salesforce/core'; -import { ComponentSet } from '@salesforce/source-deploy-retrieve'; +import { fs as fsCore, Messages, SfdxError } from '@salesforce/core'; +import { ForceIgnore } from '@salesforce/source-deploy-retrieve'; Messages.importMessagesDirectory(__dirname); const messages = Messages.loadMessages('@salesforce/plugin-source', 'ignored_list'); @@ -26,26 +27,66 @@ export class SourceIgnoredCommand extends SfdxCommand { }), }; + private forceIgnore: ForceIgnore; /** * Outputs all forceignored files from package directories of a project, * or based on a sourcepath param that points to a specific file or directory. */ // eslint-disable-next-line @typescript-eslint/require-await public async run(): Promise { - const sourcepaths = this.flags.sourcepath - ? [this.flags.sourcepath as string] - : this.project.getUniquePackageDirectories().map((pDir) => pDir.path); - const compSet = ComponentSet.fromSource(sourcepaths); - const ignoredFiles = Array.from(compSet.forceIgnoredPaths); - // Command output - if (ignoredFiles.length) { - this.ux.log('Found the following ignored files:'); - ignoredFiles.forEach((filepath) => this.ux.log(filepath)); + try { + this.forceIgnore = ForceIgnore.findAndCreate(this.project.getPath()); + const sourcepaths = this.flags.sourcepath + ? [this.flags.sourcepath as string] + : this.project.getUniquePackageDirectories().map((pDir) => pDir.path); + + const ignoredFiles = (await Promise.all(sourcepaths.map((sp) => this.statIgnored(sp.trim())))).flat(); + + // Command output + if (ignoredFiles.length) { + this.ux.log('Found the following ignored files:'); + ignoredFiles.forEach((filepath) => this.ux.log(filepath)); + } else { + this.ux.log('No ignored files found in paths:'); + sourcepaths.forEach((sp) => this.ux.log(sp)); + } + + return { ignoredFiles }; + } catch (err) { + // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access + if (err.code === 'ENOENT') { + throw SfdxError.create('@salesforce/plugin-source', 'ignored_list', 'invalidSourcePath', [ + this.flags.sourcepath, + ]); + } + throw SfdxError.wrap(err); + } + } + + // Stat the filepath. Test if a file, recurse if a directory. + private async statIgnored(filepath: string): Promise { + const stats = await fsCore.stat(filepath); + if (stats.isDirectory()) { + return (await Promise.all(await this.findIgnored(filepath))).flat(); } else { - this.ux.log('No ignored files found in paths:'); - sourcepaths.forEach((sp) => this.ux.log(sp)); + return this.isIgnored(filepath) ? [filepath] : []; } + } - return { ignoredFiles }; + // Recursively search a directory for source files to test. + private async findIgnored(dir: string): Promise>> { + this.logger.debug(`Searching dir: ${dir}`); + return (await fsCore.readdir(dir)).map((filename) => this.statIgnored(path.join(dir, filename))); + } + + // Test if a source file is denied, adding any ignored files to + // the ignoredFiles array for output. + private isIgnored(filepath: string): boolean { + if (this.forceIgnore.denies(filepath)) { + this.logger.debug(`[DENIED]: ${filepath}`); + return true; + } + this.logger.debug(`[ACCEPTED]: ${filepath}`); + return false; } } From e2c9a5df71ed0af1d58cb315779b0259b0bacdcc Mon Sep 17 00:00:00 2001 From: mshanemc Date: Thu, 20 Jan 2022 10:10:38 -0600 Subject: [PATCH 11/12] test: spell nut path correctly --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 89faf6b1d..030cc42b0 100644 --- a/package.json +++ b/package.json @@ -158,7 +158,7 @@ "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 4500 --timeout 600000 --parallel --retries 0", - "test:nuts:commands:other": "mocha \"test/nuts/open.nut.ts\" \"test/nuts/ignore_list.nut.ts\" --slow 4500 --timeout 600000 --retries 0 --parallel", + "test:nuts:commands:other": "mocha \"test/nuts/open.nut.ts\" \"test/nuts/ignored_list.nut.ts\" --slow 4500 --timeout 600000 --retries 0 --parallel", "test:nuts:convert": "cross-env PLUGIN_SOURCE_SEED_FILTER=convert ts-node ./test/nuts/generateNuts.ts && mocha \"test/nuts/generated/*.nut.ts\" --slow 4500 --timeout 600000 --parallel --retries 0", "test:nuts:delete": "mocha \"test/nuts/delete.nut.ts\" --slow 4500 --timeout 600000 --retries 0", "test:nuts:deploy": "cross-env PLUGIN_SOURCE_SEED_FILTER=deploy PLUGIN_SOURCE_SEED_EXCLUDE=async ts-node ./test/nuts/generateNuts.ts && mocha \"test/nuts/generated/*.nut.ts\" --slow 4500 --timeout 600000 --parallel --retries 0", From 20603579c53326d5a369925a8d8503318528a7e5 Mon Sep 17 00:00:00 2001 From: mshanemc Date: Thu, 20 Jan 2022 11:09:28 -0600 Subject: [PATCH 12/12] test: forceignore doesn't use windows paths --- test/nuts/ignored_list.nut.ts | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/test/nuts/ignored_list.nut.ts b/test/nuts/ignored_list.nut.ts index 094fe4633..0f52d0988 100644 --- a/test/nuts/ignored_list.nut.ts +++ b/test/nuts/ignored_list.nut.ts @@ -77,8 +77,15 @@ describe('force:source:ignored:list', () => { describe('returns an ignored class using specified path in forceignore', () => { before(async () => { - await fs.promises.appendFile(forceIgnorePath, `${pathToIgnoredFile1}${os.EOL}`); - await fs.promises.appendFile(forceIgnorePath, `${pathToIgnoredFile2}${os.EOL}`); + // forceignore uses a library that wants ignore rules in posix format. + await fs.promises.appendFile( + forceIgnorePath, + `${path.normalize(pathToIgnoredFile1).split(path.sep).join(path.posix.sep)}${os.EOL}` + ); + await fs.promises.appendFile( + forceIgnorePath, + `${path.normalize(pathToIgnoredFile2).split(path.sep).join(path.posix.sep)}${os.EOL}` + ); }); after(async () => { await fs.promises.writeFile(forceIgnorePath, originalForceIgnore);