diff --git a/src/resolve/forceIgnore.ts b/src/resolve/forceIgnore.ts index f47c3ecf78..dcbe01fb17 100644 --- a/src/resolve/forceIgnore.ts +++ b/src/resolve/forceIgnore.ts @@ -28,7 +28,23 @@ export class ForceIgnore { private readonly parser?: Ignore; private readonly forceIgnoreDirectory?: string; - private DEFAULT_IGNORE = ['**/*.dup', '**/.*', '**/package2-descriptor.json', '**/package2-manifest.json']; + private DEFAULT_IGNORE = [ + '**/*.dup', + // I know it's ugly. But I want to be able to retrieve metadata to a local dir, segregated by org. + // and `.sf` is already ignored in projects, and we already have orgIds for STL + // so this nastiness is "ignore all dot files except this one directory" + // once you ignore a parent ex `**/.*` you can't unignore something inside that path, at least with the curent ignore library + '**/.*', + '!.sf', + '**/.sf/**', + '!**/.sf/orgs', + '!**/.sf/orgs/**', + '**/.sf/orgs/*/**', + '!**/.sf/orgs/*/remoteMetadata', + '!**/.sf/orgs/*/remoteMetadata/**', + '**/package2-descriptor.json', + '**/package2-manifest.json', + ]; public constructor(forceIgnorePath = '') { try { diff --git a/src/utils/fileSystemHandler.ts b/src/utils/fileSystemHandler.ts index cad382d519..3fcca58566 100644 --- a/src/utils/fileSystemHandler.ts +++ b/src/utils/fileSystemHandler.ts @@ -23,22 +23,23 @@ export const ensureFileExists = async (filePath: string): Promise => { }; /** - * Traverse up a file path and search for the given file name. + * Traverse up a file path and search for the given file name. Always returns an absolute path. * * @param start File or folder path to start searching from * @param fileName File name to search for */ export function searchUp(start: SourcePath, fileName: string): string | undefined { - const filePath = path.join(start, fileName); + const absoluteStart = path.isAbsolute(start) ? start : path.join(process.cwd(), start); + const filePath = path.join(absoluteStart, fileName); if (fs.existsSync(filePath)) { return filePath; } - const normalizedStart = path.normalize(start); - const parent = path.dirname(normalizedStart); + const normalizedAbsoluteStart = path.normalize(absoluteStart); + const parent = path.dirname(normalizedAbsoluteStart); // If we're at root, stop (don't try to go up with ..) - if (parent === normalizedStart || normalizedStart === path.parse(normalizedStart).root) { + if (parent === normalizedAbsoluteStart || normalizedAbsoluteStart === path.parse(normalizedAbsoluteStart).root) { return; } diff --git a/test/nuts/local/searchUp/searchUp.nut.ts b/test/nuts/local/searchUp/searchUp.nut.ts index e07fe1a7e8..bbfa229eb9 100644 --- a/test/nuts/local/searchUp/searchUp.nut.ts +++ b/test/nuts/local/searchUp/searchUp.nut.ts @@ -60,41 +60,63 @@ describe('searchUp nut test', () => { describe('relative paths', () => { it('finds file in parent directory', () => { - const startPath = path.join(session.project.dir, 'level1', 'level2', 'startDir'); - const result = searchUp(startPath, 'target.txt'); + const relativeStartPath = path.relative( + session.project.dir, + path.join(session.project.dir, 'level1', 'level2', 'startDir') + ); + expect(path.isAbsolute(relativeStartPath)).to.be.false; + const result = searchUp(relativeStartPath, 'target.txt'); const expected = path.join(session.project.dir, 'level1', 'level2', 'target.txt'); expect(result).to.equal(expected); }); it('finds file multiple levels up', () => { - const startPath = path.join(session.project.dir, 'level1', 'level2', 'startDir'); - const result = searchUp(startPath, '.gitignore'); + const relativeStartPath = path.relative( + session.project.dir, + path.join(session.project.dir, 'level1', 'level2', 'startDir') + ); + expect(path.isAbsolute(relativeStartPath)).to.be.false; + const result = searchUp(relativeStartPath, '.gitignore'); const expected = path.join(session.project.dir, '.gitignore'); expect(result).to.equal(expected); }); it('finds file in current directory', () => { - const startPath = path.join(session.project.dir, 'level1', 'level2', 'startDir'); - fs.writeFileSync(path.join(startPath, 'localFile.txt'), 'local content'); - const result = searchUp(startPath, 'localFile.txt'); - const expected = path.join(startPath, 'localFile.txt'); + const relativeStartPath = path.relative( + session.project.dir, + path.join(session.project.dir, 'level1', 'level2', 'startDir') + ); + expect(path.isAbsolute(relativeStartPath)).to.be.false; + const absoluteStartPath = path.resolve(session.project.dir, relativeStartPath); + fs.writeFileSync(path.join(absoluteStartPath, 'localFile.txt'), 'local content'); + const result = searchUp(relativeStartPath, 'localFile.txt'); + const expected = path.join(absoluteStartPath, 'localFile.txt'); expect(result).to.equal(expected); }); it('returns undefined when file not found', () => { - const startPath = path.join(session.project.dir, 'level1', 'level2', 'startDir'); - const result = searchUp(startPath, 'nonexistent.txt'); + const relativeStartPath = path.relative( + session.project.dir, + path.join(session.project.dir, 'level1', 'level2', 'startDir') + ); + expect(path.isAbsolute(relativeStartPath)).to.be.false; + const result = searchUp(relativeStartPath, 'nonexistent.txt'); expect(result).to.be.undefined; }); it('works when starting from file path', () => { - const filePath = path.join(session.project.dir, 'level1', 'level2', 'startDir', 'someFile.txt'); - fs.writeFileSync(filePath, 'content'); - const result = searchUp(filePath, 'target.txt'); + const relativeFilePath = path.relative( + session.project.dir, + path.join(session.project.dir, 'level1', 'level2', 'startDir', 'someFile.txt') + ); + expect(path.isAbsolute(relativeFilePath)).to.be.false; + const absoluteFilePath = path.resolve(session.project.dir, relativeFilePath); + fs.writeFileSync(absoluteFilePath, 'content'); + const result = searchUp(relativeFilePath, 'target.txt'); const expected = path.join(session.project.dir, 'level1', 'level2', 'target.txt'); expect(result).to.equal(expected); diff --git a/test/resolve/forceIgnore.test.ts b/test/resolve/forceIgnore.test.ts index ea82a8a0b3..c160be2172 100644 --- a/test/resolve/forceIgnore.test.ts +++ b/test/resolve/forceIgnore.test.ts @@ -116,11 +116,34 @@ describe('ForceIgnore', () => { it('Should ignore files starting with a dot', () => { const dotPath = join(root, '.xyz'); - expect(forceIgnore.accepts(dotPath)).to.be.false; expect(forceIgnore.denies(dotPath)).to.be.true; }); + it('Should NOT ignore .sf/orgs//remoteMetadata', () => { + const remoteMetadataPath = join(root, '.sf', 'orgs', '00D000000000000', 'remoteMetadata'); + expect(forceIgnore.accepts(remoteMetadataPath)).to.be.true; + expect(forceIgnore.denies(remoteMetadataPath)).to.be.false; + }); + + it('Should NOT ignore stuff inside .sf/orgs//remoteMetadata', () => { + const remoteMetadataPath = join(root, '.sf', 'orgs', '00D000000000000', 'remoteMetadata', 'foo', 'bar'); + expect(forceIgnore.accepts(remoteMetadataPath)).to.be.true; + expect(forceIgnore.denies(remoteMetadataPath)).to.be.false; + }); + + it('Should ignore .sf/orgs//anythingElse', () => { + const dotSfNotInRemoteMetadata = join(root, '.sf', 'orgs', '00D000000000000', 'foo'); + expect(forceIgnore.accepts(dotSfNotInRemoteMetadata)).to.be.false; + expect(forceIgnore.denies(dotSfNotInRemoteMetadata)).to.be.true; + }); + + it('Should ignore .sf/anythingElse', () => { + const dotSfNotInRemoteMetadata = join(root, '.sf', 'foo'); + expect(forceIgnore.accepts(dotSfNotInRemoteMetadata)).to.be.false; + expect(forceIgnore.denies(dotSfNotInRemoteMetadata)).to.be.true; + }); + it('Should ignore files ending in .dup', () => { const dupPath = join(root, 'abc.dup');