Skip to content

Commit d9655f0

Browse files
authored
fix: forceignore relative paths and 1 exception for extensions W-20965680 (#1676)
* feat: add one exception to default forceignore "no dot files" rules for IDE use * fix: relative paths for searchUp/forceignore * fix: relative path handling * test: forceignore assertions for remoteMetadata * test: more assertions * chore: even more specific ignore path * test: more assertions * test: remove missing case
1 parent 9d00fab commit d9655f0

4 files changed

Lines changed: 82 additions & 20 deletions

File tree

src/resolve/forceIgnore.ts

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,23 @@ export class ForceIgnore {
2828

2929
private readonly parser?: Ignore;
3030
private readonly forceIgnoreDirectory?: string;
31-
private DEFAULT_IGNORE = ['**/*.dup', '**/.*', '**/package2-descriptor.json', '**/package2-manifest.json'];
31+
private DEFAULT_IGNORE = [
32+
'**/*.dup',
33+
// I know it's ugly. But I want to be able to retrieve metadata to a local dir, segregated by org.
34+
// and `.sf` is already ignored in projects, and we already have orgIds for STL
35+
// so this nastiness is "ignore all dot files except this one directory"
36+
// once you ignore a parent ex `**/.*` you can't unignore something inside that path, at least with the curent ignore library
37+
'**/.*',
38+
'!.sf',
39+
'**/.sf/**',
40+
'!**/.sf/orgs',
41+
'!**/.sf/orgs/**',
42+
'**/.sf/orgs/*/**',
43+
'!**/.sf/orgs/*/remoteMetadata',
44+
'!**/.sf/orgs/*/remoteMetadata/**',
45+
'**/package2-descriptor.json',
46+
'**/package2-manifest.json',
47+
];
3248

3349
public constructor(forceIgnorePath = '') {
3450
try {

src/utils/fileSystemHandler.ts

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -23,22 +23,23 @@ export const ensureFileExists = async (filePath: string): Promise<void> => {
2323
};
2424

2525
/**
26-
* Traverse up a file path and search for the given file name.
26+
* Traverse up a file path and search for the given file name. Always returns an absolute path.
2727
*
2828
* @param start File or folder path to start searching from
2929
* @param fileName File name to search for
3030
*/
3131
export function searchUp(start: SourcePath, fileName: string): string | undefined {
32-
const filePath = path.join(start, fileName);
32+
const absoluteStart = path.isAbsolute(start) ? start : path.join(process.cwd(), start);
33+
const filePath = path.join(absoluteStart, fileName);
3334
if (fs.existsSync(filePath)) {
3435
return filePath;
3536
}
3637

37-
const normalizedStart = path.normalize(start);
38-
const parent = path.dirname(normalizedStart);
38+
const normalizedAbsoluteStart = path.normalize(absoluteStart);
39+
const parent = path.dirname(normalizedAbsoluteStart);
3940

4041
// If we're at root, stop (don't try to go up with ..)
41-
if (parent === normalizedStart || normalizedStart === path.parse(normalizedStart).root) {
42+
if (parent === normalizedAbsoluteStart || normalizedAbsoluteStart === path.parse(normalizedAbsoluteStart).root) {
4243
return;
4344
}
4445

test/nuts/local/searchUp/searchUp.nut.ts

Lines changed: 35 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -60,41 +60,63 @@ describe('searchUp nut test', () => {
6060

6161
describe('relative paths', () => {
6262
it('finds file in parent directory', () => {
63-
const startPath = path.join(session.project.dir, 'level1', 'level2', 'startDir');
64-
const result = searchUp(startPath, 'target.txt');
63+
const relativeStartPath = path.relative(
64+
session.project.dir,
65+
path.join(session.project.dir, 'level1', 'level2', 'startDir')
66+
);
67+
expect(path.isAbsolute(relativeStartPath)).to.be.false;
68+
const result = searchUp(relativeStartPath, 'target.txt');
6569
const expected = path.join(session.project.dir, 'level1', 'level2', 'target.txt');
6670

6771
expect(result).to.equal(expected);
6872
});
6973

7074
it('finds file multiple levels up', () => {
71-
const startPath = path.join(session.project.dir, 'level1', 'level2', 'startDir');
72-
const result = searchUp(startPath, '.gitignore');
75+
const relativeStartPath = path.relative(
76+
session.project.dir,
77+
path.join(session.project.dir, 'level1', 'level2', 'startDir')
78+
);
79+
expect(path.isAbsolute(relativeStartPath)).to.be.false;
80+
const result = searchUp(relativeStartPath, '.gitignore');
7381
const expected = path.join(session.project.dir, '.gitignore');
7482

7583
expect(result).to.equal(expected);
7684
});
7785

7886
it('finds file in current directory', () => {
79-
const startPath = path.join(session.project.dir, 'level1', 'level2', 'startDir');
80-
fs.writeFileSync(path.join(startPath, 'localFile.txt'), 'local content');
81-
const result = searchUp(startPath, 'localFile.txt');
82-
const expected = path.join(startPath, 'localFile.txt');
87+
const relativeStartPath = path.relative(
88+
session.project.dir,
89+
path.join(session.project.dir, 'level1', 'level2', 'startDir')
90+
);
91+
expect(path.isAbsolute(relativeStartPath)).to.be.false;
92+
const absoluteStartPath = path.resolve(session.project.dir, relativeStartPath);
93+
fs.writeFileSync(path.join(absoluteStartPath, 'localFile.txt'), 'local content');
94+
const result = searchUp(relativeStartPath, 'localFile.txt');
95+
const expected = path.join(absoluteStartPath, 'localFile.txt');
8396

8497
expect(result).to.equal(expected);
8598
});
8699

87100
it('returns undefined when file not found', () => {
88-
const startPath = path.join(session.project.dir, 'level1', 'level2', 'startDir');
89-
const result = searchUp(startPath, 'nonexistent.txt');
101+
const relativeStartPath = path.relative(
102+
session.project.dir,
103+
path.join(session.project.dir, 'level1', 'level2', 'startDir')
104+
);
105+
expect(path.isAbsolute(relativeStartPath)).to.be.false;
106+
const result = searchUp(relativeStartPath, 'nonexistent.txt');
90107

91108
expect(result).to.be.undefined;
92109
});
93110

94111
it('works when starting from file path', () => {
95-
const filePath = path.join(session.project.dir, 'level1', 'level2', 'startDir', 'someFile.txt');
96-
fs.writeFileSync(filePath, 'content');
97-
const result = searchUp(filePath, 'target.txt');
112+
const relativeFilePath = path.relative(
113+
session.project.dir,
114+
path.join(session.project.dir, 'level1', 'level2', 'startDir', 'someFile.txt')
115+
);
116+
expect(path.isAbsolute(relativeFilePath)).to.be.false;
117+
const absoluteFilePath = path.resolve(session.project.dir, relativeFilePath);
118+
fs.writeFileSync(absoluteFilePath, 'content');
119+
const result = searchUp(relativeFilePath, 'target.txt');
98120
const expected = path.join(session.project.dir, 'level1', 'level2', 'target.txt');
99121

100122
expect(result).to.equal(expected);

test/resolve/forceIgnore.test.ts

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -116,11 +116,34 @@ describe('ForceIgnore', () => {
116116

117117
it('Should ignore files starting with a dot', () => {
118118
const dotPath = join(root, '.xyz');
119-
120119
expect(forceIgnore.accepts(dotPath)).to.be.false;
121120
expect(forceIgnore.denies(dotPath)).to.be.true;
122121
});
123122

123+
it('Should NOT ignore .sf/orgs/<orgId>/remoteMetadata', () => {
124+
const remoteMetadataPath = join(root, '.sf', 'orgs', '00D000000000000', 'remoteMetadata');
125+
expect(forceIgnore.accepts(remoteMetadataPath)).to.be.true;
126+
expect(forceIgnore.denies(remoteMetadataPath)).to.be.false;
127+
});
128+
129+
it('Should NOT ignore stuff inside .sf/orgs/<orgId>/remoteMetadata', () => {
130+
const remoteMetadataPath = join(root, '.sf', 'orgs', '00D000000000000', 'remoteMetadata', 'foo', 'bar');
131+
expect(forceIgnore.accepts(remoteMetadataPath)).to.be.true;
132+
expect(forceIgnore.denies(remoteMetadataPath)).to.be.false;
133+
});
134+
135+
it('Should ignore .sf/orgs/<orgId>/anythingElse', () => {
136+
const dotSfNotInRemoteMetadata = join(root, '.sf', 'orgs', '00D000000000000', 'foo');
137+
expect(forceIgnore.accepts(dotSfNotInRemoteMetadata)).to.be.false;
138+
expect(forceIgnore.denies(dotSfNotInRemoteMetadata)).to.be.true;
139+
});
140+
141+
it('Should ignore .sf/anythingElse', () => {
142+
const dotSfNotInRemoteMetadata = join(root, '.sf', 'foo');
143+
expect(forceIgnore.accepts(dotSfNotInRemoteMetadata)).to.be.false;
144+
expect(forceIgnore.denies(dotSfNotInRemoteMetadata)).to.be.true;
145+
});
146+
124147
it('Should ignore files ending in .dup', () => {
125148
const dupPath = join(root, 'abc.dup');
126149

0 commit comments

Comments
 (0)