Skip to content

Commit

Permalink
Pass an absolute root path to IgnoreStrategy
Browse files Browse the repository at this point in the history
IgnoreStrategy and the .ignores() method will take absolute paths.

The absolute path passed to .ignores() is relativized against the
absolute root path passed to IgnoreStrategy.
  • Loading branch information
blaenk committed Oct 28, 2020
1 parent 95cfb4e commit fe58be1
Show file tree
Hide file tree
Showing 4 changed files with 90 additions and 50 deletions.
6 changes: 3 additions & 3 deletions packages/@aws-cdk/core/lib/fs/copy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,20 +6,20 @@ import { shouldFollow } from './utils';

export function copyDirectory(srcDir: string, destDir: string, options: CopyOptions = { }, rootDir?: string) {
const follow = options.follow !== undefined ? options.follow : SymlinkFollowMode.EXTERNAL;
const ignoreStrategy = IgnoreStrategy.fromCopyOptions(options);

rootDir = rootDir || srcDir;

const ignoreStrategy = IgnoreStrategy.fromCopyOptions(options, rootDir);

if (!fs.statSync(srcDir).isDirectory()) {
throw new Error(`${srcDir} is not a directory`);
}

const files = fs.readdirSync(srcDir);
for (const file of files) {
const sourceFilePath = path.join(srcDir, file);
const relativePath = path.relative(rootDir, sourceFilePath);

if (ignoreStrategy.ignores(relativePath)) {
if (ignoreStrategy.ignores(sourceFilePath)) {
continue;
}

Expand Down
4 changes: 2 additions & 2 deletions packages/@aws-cdk/core/lib/fs/fingerprint.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ export function fingerprint(fileOrDirectory: string, options: FingerprintOptions
_hashField(hash, 'options.ignoreMode', ignoreMode);
}

const ignoreStrategy = IgnoreStrategy.fromCopyOptions(options);
const ignoreStrategy = IgnoreStrategy.fromCopyOptions(options, fileOrDirectory);
const isDir = fs.statSync(fileOrDirectory).isDirectory();
_processFileOrDirectory(fileOrDirectory, isDir);

Expand All @@ -49,7 +49,7 @@ export function fingerprint(fileOrDirectory: string, options: FingerprintOptions
function _processFileOrDirectory(symbolicPath: string, isRootDir: boolean = false, realPath = symbolicPath) {
const relativePath = path.relative(fileOrDirectory, symbolicPath);

if (!isRootDir && ignoreStrategy.ignores(relativePath)) {
if (!isRootDir && ignoreStrategy.ignores(symbolicPath)) {
return;
}

Expand Down
88 changes: 64 additions & 24 deletions packages/@aws-cdk/core/lib/fs/ignore.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import * as path from 'path';
import dockerIgnore, * as DockerIgnore from '@balena/dockerignore';
import gitIgnore, * as GitIgnore from 'ignore';
import * as minimatch from 'minimatch';
Expand All @@ -11,51 +12,55 @@ export abstract class IgnoreStrategy {
* Ignores file paths based on simple glob patterns.
*
* @returns `GlobIgnorePattern` associated with the given patterns.
* @param absoluteRootPath the absolute path to the root directory of the paths to be considered
* @param patterns
*/
public static glob(patterns: string[]): GlobIgnoreStrategy {
return new GlobIgnoreStrategy(patterns);
public static glob(absoluteRootPath: string, patterns: string[]): GlobIgnoreStrategy {
return new GlobIgnoreStrategy(absoluteRootPath, patterns);
}

/**
* Ignores file paths based on the [`.gitignore specification`](https://git-scm.com/docs/gitignore).
*
* @returns `GitIgnorePattern` associated with the given patterns.
* @param absoluteRootPath the absolute path to the root directory of the paths to be considered
* @param patterns
*/
public static git(patterns: string[]): GitIgnoreStrategy {
return new GitIgnoreStrategy(patterns);
public static git(absoluteRootPath: string, patterns: string[]): GitIgnoreStrategy {
return new GitIgnoreStrategy(absoluteRootPath, patterns);
}

/**
* Ignores file paths based on the [`.dockerignore specification`](https://docs.docker.com/engine/reference/builder/#dockerignore-file).
*
* @returns `DockerIgnorePattern` associated with the given patterns.
* @param absoluteRootPath the absolute path to the root directory of the paths to be considered
* @param patterns
*/
public static docker(patterns: string[]): DockerIgnoreStrategy {
return new DockerIgnoreStrategy(patterns);
public static docker(absoluteRootPath: string, patterns: string[]): DockerIgnoreStrategy {
return new DockerIgnoreStrategy(absoluteRootPath, patterns);
}

/**
* Creates an IgnoreStrategy based on the `ignoreMode` and `exclude` in a `CopyOptions`.
*
* @returns `IgnoreStrategy` based on the `CopyOptions`
* @param absoluteRootPath the absolute path to the root directory of the paths to be considered
* @param options the `CopyOptions` to create the `IgnoreStrategy` from
*/
public static fromCopyOptions(options: CopyOptions): IgnoreStrategy {
public static fromCopyOptions(options: CopyOptions, absoluteRootPath: string): IgnoreStrategy {
const ignoreMode = options.ignoreMode || IgnoreMode.GLOB;
const exclude = options.exclude || [];

switch (ignoreMode) {
case IgnoreMode.GLOB:
return this.glob(exclude);
return this.glob(absoluteRootPath, exclude);

case IgnoreMode.GIT:
return this.git(exclude);
return this.git(absoluteRootPath, exclude);

case IgnoreMode.DOCKER:
return this.docker(exclude);
return this.docker(absoluteRootPath, exclude);
}
}

Expand All @@ -68,21 +73,27 @@ export abstract class IgnoreStrategy {
/**
* Determines whether a given file path should be ignored or not.
*
* @param filePath file path to be assessed against the pattern
* @param absoluteFilePath absolute file path to be assessed against the pattern
* @returns `true` if the file should be ignored
*/
public abstract ignores(filePath: string): boolean;
public abstract ignores(absoluteFilePath: string): boolean;
}

/**
* Ignores file paths based on simple glob patterns.
*/
export class GlobIgnoreStrategy extends IgnoreStrategy {
private readonly absoluteRootPath: string;
private readonly patterns: string[];

constructor(patterns: string[]) {
constructor(absoluteRootPath: string, patterns: string[]) {
super();

if (!path.isAbsolute(absoluteRootPath)) {
throw new Error('GlobIgnoreStrategy expects an absolute file path');
}

this.absoluteRootPath = absoluteRootPath;
this.patterns = patterns;
}

Expand All @@ -97,15 +108,20 @@ export class GlobIgnoreStrategy extends IgnoreStrategy {
/**
* Determines whether a given file path should be ignored or not.
*
* @param filePath file path to be assessed against the pattern
* @param absoluteFilePath absolute file path to be assessed against the pattern
* @returns `true` if the file should be ignored
*/
public ignores(filePath: string): boolean {
public ignores(absoluteFilePath: string): boolean {
if (!path.isAbsolute(absoluteFilePath)) {
throw new Error('GlobIgnoreStrategy.ignores() expects an absolute path');
}

let relativePath = path.relative(this.absoluteRootPath, absoluteFilePath);
let excludeOutput = false;

for (const pattern of this.patterns) {
const negate = pattern.startsWith('!');
const match = minimatch(filePath, pattern, { matchBase: true, flipNegate: true });
const match = minimatch(relativePath, pattern, { matchBase: true, flipNegate: true });

if (!negate && match) {
excludeOutput = true;
Expand All @@ -124,11 +140,17 @@ export class GlobIgnoreStrategy extends IgnoreStrategy {
* Ignores file paths based on the [`.gitignore specification`](https://git-scm.com/docs/gitignore).
*/
export class GitIgnoreStrategy extends IgnoreStrategy {
private readonly absoluteRootPath: string;
private readonly ignore: GitIgnore.Ignore;

constructor(patterns: string[]) {
constructor(absoluteRootPath: string, patterns: string[]) {
super();

if (!path.isAbsolute(absoluteRootPath)) {
throw new Error('GitIgnoreStrategy expects an absolute file path');
}

this.absoluteRootPath = absoluteRootPath;
this.ignore = gitIgnore().add(patterns);
}

Expand All @@ -143,23 +165,35 @@ export class GitIgnoreStrategy extends IgnoreStrategy {
/**
* Determines whether a given file path should be ignored or not.
*
* @param filePath file path to be assessed against the pattern
* @param absoluteFilePath absolute file path to be assessed against the pattern
* @returns `true` if the file should be ignored
*/
public ignores(filePath: string): boolean {
return this.ignore.ignores(filePath);
public ignores(absoluteFilePath: string): boolean {
if (!path.isAbsolute(absoluteFilePath)) {
throw new Error('GitIgnoreStrategy.ignores() expects an absolute path');
}

let relativePath = path.relative(this.absoluteRootPath, absoluteFilePath);

return this.ignore.ignores(relativePath);
}
}

/**
* Ignores file paths based on the [`.dockerignore specification`](https://docs.docker.com/engine/reference/builder/#dockerignore-file).
*/
export class DockerIgnoreStrategy extends IgnoreStrategy {
private readonly absoluteRootPath: string;
private readonly ignore: DockerIgnore.Ignore;

constructor(patterns: string[]) {
constructor(absoluteRootPath: string, patterns: string[]) {
super();

if (!path.isAbsolute(absoluteRootPath)) {
throw new Error('DockerIgnoreStrategy expects an absolute file path');
}

this.absoluteRootPath = absoluteRootPath;
this.ignore = dockerIgnore().add(patterns);
}

Expand All @@ -174,10 +208,16 @@ export class DockerIgnoreStrategy extends IgnoreStrategy {
/**
* Determines whether a given file path should be ignored or not.
*
* @param filePath file path to be assessed against the pattern
* @param absoluteFilePath absolute file path to be assessed against the pattern
* @returns `true` if the file should be ignored
*/
public ignores(filePath: string): boolean {
return this.ignore.ignores(filePath);
public ignores(absoluteFilePath: string): boolean {
if (!path.isAbsolute(absoluteFilePath)) {
throw new Error('DockerIgnoreStrategy.ignores() expects an absolute path');
}

let relativePath = path.relative(this.absoluteRootPath, absoluteFilePath);

return this.ignore.ignores(relativePath);
}
}
42 changes: 21 additions & 21 deletions packages/@aws-cdk/core/test/fs/fs-ignore.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,62 +5,62 @@ import { IgnoreStrategy } from '../../lib/fs';
nodeunitShim({
globIgnorePattern: {
'excludes nothing by default'(test: Test) {
const ignore = IgnoreStrategy.glob([]);
test.ok(!ignore.ignores(path.join('some', 'file', 'path')));
const ignore = IgnoreStrategy.glob('/tmp', []);
test.ok(!ignore.ignores(path.join('/tmp', 'some', 'file', 'path')));
test.done();
},

'excludes requested files'(test: Test) {
const ignore = IgnoreStrategy.glob(['*.ignored']);
test.ok(ignore.ignores(path.join('some', 'file.ignored')));
test.ok(!ignore.ignores(path.join('some', 'important', 'file')));
const ignore = IgnoreStrategy.glob('/tmp', ['*.ignored']);
test.ok(ignore.ignores(path.join('/tmp', 'some', 'file.ignored')));
test.ok(!ignore.ignores(path.join('/tmp', 'some', 'important', 'file')));
test.done();
},

'does not exclude whitelisted files'(test: Test) {
const ignore = IgnoreStrategy.glob(['*.ignored', '!important.*']);
test.ok(!ignore.ignores(path.join('some', 'important.ignored')));
const ignore = IgnoreStrategy.glob('/tmp', ['*.ignored', '!important.*']);
test.ok(!ignore.ignores(path.join('/tmp', 'some', 'important.ignored')));
test.done();
},
},
gitIgnorePattern: {
'excludes nothing by default'(test: Test) {
const ignore = IgnoreStrategy.git([]);
test.ok(!ignore.ignores(path.join('some', 'file', 'path')));
const ignore = IgnoreStrategy.git('/tmp', []);
test.ok(!ignore.ignores(path.join('/tmp', 'some', 'file', 'path')));
test.done();
},

'excludes requested files'(test: Test) {
const ignore = IgnoreStrategy.git(['*.ignored']);
test.ok(ignore.ignores(path.join('some', 'file.ignored')));
test.ok(!ignore.ignores(path.join('some', 'important', 'file')));
const ignore = IgnoreStrategy.git('/tmp', ['*.ignored']);
test.ok(ignore.ignores(path.join('/tmp', 'some', 'file.ignored')));
test.ok(!ignore.ignores(path.join('/tmp', 'some', 'important', 'file')));
test.done();
},

'does not exclude whitelisted files'(test: Test) {
const ignore = IgnoreStrategy.git(['*.ignored', '!important.*']);
test.ok(!ignore.ignores(path.join('some', 'important.ignored')));
const ignore = IgnoreStrategy.git('/tmp', ['*.ignored', '!important.*']);
test.ok(!ignore.ignores(path.join('/tmp', 'some', 'important.ignored')));
test.done();
},
},
dockerIgnorePattern: {
'excludes nothing by default'(test: Test) {
const ignore = IgnoreStrategy.docker([]);
test.ok(!ignore.ignores(path.join('some', 'file', 'path')));
const ignore = IgnoreStrategy.docker('/tmp', []);
test.ok(!ignore.ignores(path.join('/tmp', 'some', 'file', 'path')));
test.done();
},

'excludes requested files'(test: Test) {
// In .dockerignore, * only matches files in the current directory
const ignore = IgnoreStrategy.docker(['*.ignored']);
test.ok(!ignore.ignores(path.join('some', 'file.ignored')));
test.ok(!ignore.ignores(path.join('some', 'important', 'file')));
const ignore = IgnoreStrategy.docker('/tmp', ['*.ignored']);
test.ok(!ignore.ignores(path.join('/tmp', 'some', 'file.ignored')));
test.ok(!ignore.ignores(path.join('/tmp', 'some', 'important', 'file')));
test.done();
},

'does not exclude whitelisted files'(test: Test) {
const ignore = IgnoreStrategy.docker(['*.ignored', '!important.*']);
test.ok(!ignore.ignores(path.join('some', 'important.ignored')));
const ignore = IgnoreStrategy.docker('/tmp', ['*.ignored', '!important.*']);
test.ok(!ignore.ignores(path.join('/tmp', 'some', 'important.ignored')));
test.done();
},
},
Expand Down

0 comments on commit fe58be1

Please sign in to comment.