diff --git a/packages/eas-cli/src/vcs/__tests__/local-test.ts b/packages/eas-cli/src/vcs/__tests__/local-test.ts index abb9796e8b..cea85fbe09 100644 --- a/packages/eas-cli/src/vcs/__tests__/local-test.ts +++ b/packages/eas-cli/src/vcs/__tests__/local-test.ts @@ -25,6 +25,18 @@ describe(Ignore, () => { expect(ignore.ignores('dir/bbb')).toBe(true); }); + it('matches nested .gitignore prefixes against Windows-style relative paths', async () => { + vol.fromJSON( + { + 'packages/app/.gitignore': 'ignored.txt', + }, + '/root' + ); + + const ignore = await Ignore.createForCopyingAsync('/root'); + expect(ignore.ignores(String.raw`packages\app\ignored.txt`)).toBe(true); + }); + it('ignores .gitignore files if .easignore is present', async () => { vol.fromJSON( { @@ -111,4 +123,17 @@ describe(Ignore, () => { const ignore = await Ignore.createForCopyingAsync('/root'); expect(() => ignore.ignores('dir/test')).not.toThrowError(); }); + + it('treats directory rules as directories when checking copy filters', async () => { + vol.fromJSON( + { + '.gitignore': 'dist/\n', + }, + '/root' + ); + + const ignore = await Ignore.createForCopyingAsync('/root'); + expect(ignore.ignores('dist', { isDirectory: true })).toBe(true); + expect(ignore.ignores('dist/index.js')).toBe(true); + }); }); diff --git a/packages/eas-cli/src/vcs/local.ts b/packages/eas-cli/src/vcs/local.ts index 2e8abf47c2..ddf866c8b2 100644 --- a/packages/eas-cli/src/vcs/local.ts +++ b/packages/eas-cli/src/vcs/local.ts @@ -83,9 +83,14 @@ node_modules }); } - public ignores(relativePath: string): boolean { + public ignores(relativePath: string, options: { isDirectory?: boolean } = {}): boolean { + const normalizedPath = normalizeIgnorePath(relativePath, options); for (const [prefix, ignore] of this.ignoreMapping) { - if (relativePath.startsWith(prefix) && ignore.ignores(relativePath.slice(prefix.length))) { + const normalizedPrefix = normalizeIgnorePath(prefix); + if ( + normalizedPath.startsWith(normalizedPrefix) && + ignore.ignores(normalizedPath.slice(normalizedPrefix.length)) + ) { return true; } } @@ -93,6 +98,14 @@ node_modules } } +function normalizeIgnorePath(relativePath: string, options: { isDirectory?: boolean } = {}): string { + const normalizedPath = relativePath.replace(/\\/g, '/'); + if (options.isDirectory && normalizedPath && !normalizedPath.endsWith('/')) { + return `${normalizedPath}/`; + } + return normalizedPath; +} + export async function makeShallowCopyAsync(_src: string, dst: string): Promise { // `node:fs` on Windows adds a namespace prefix (e.g. `\\?\`) to the path provided // to the `filter` function in `fs.cp`. We need to ensure that we compare the right paths @@ -114,8 +127,11 @@ export async function makeShallowCopyAsync(_src: string, dst: string): Promise