Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(assets): support exceptions to exclude patterns #4473

Merged
merged 44 commits into from
Oct 30, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
44 commits
Select commit Hold shift + click to select a range
3af6b91
chore: better test
Oct 11, 2019
e0738c3
fix: folder exclusion
Oct 11, 2019
dce18c6
chore: add new test files
Oct 11, 2019
00c3852
Merge remote-tracking branch 'upstream/master' into 4450
Oct 11, 2019
5faf3ca
fix: listFilesRecursively refactor (wip)
Oct 11, 2019
d4cff4d
Merge remote-tracking branch 'upstream/master' into 4450
nmussy Oct 11, 2019
89c792f
fix: finish refactoring listFilesRecursively
nmussy Oct 11, 2019
9cc89c8
Merge remote-tracking branch 'upstream/master' into 4450
Oct 21, 2019
13602a5
fix: implement mkdirpSync
Oct 21, 2019
65a3f96
fix: symlink discovery
Oct 21, 2019
6afead3
fix: don't follow symlinks early
Oct 21, 2019
3b745bd
fix: create empty directories
Oct 21, 2019
1462566
chore: remove useless let
Oct 21, 2019
5abcf66
fix: symlink fingerprinting
Oct 21, 2019
12d896e
fix: don't throw when fingerprinting directories
Oct 21, 2019
2172793
chore: remove unneeded 'exclude' cloning
Oct 21, 2019
47bc251
chore: refactor mkdirp calls
Oct 21, 2019
e5bf485
chore: refactor AssetFile
Oct 21, 2019
4966740
chore: refactor recursion
Oct 21, 2019
6059be0
chore: prevent unneeded stats call
Oct 21, 2019
4a1d7b6
chore: createFsStructureFromTree, remove empty files
Oct 22, 2019
7f40962
chore: cleanup
Oct 22, 2019
baa4315
fix: empty-directory test
Oct 22, 2019
273d3f5
feat: shouldExcludeDeep
Oct 23, 2019
db504be
Merge remote-tracking branch 'upstream/master' into 4450
Oct 23, 2019
a26d7f2
Merge remote-tracking branch 'upstream/master' into 4450
Oct 25, 2019
10e62b5
chore: fromTree in @/assert, cleanup fn, test
Oct 25, 2019
c426803
feat: shouldExcludeDirectory
Oct 25, 2019
0c528db
chore: refactor listFiles with new methods (missing symlinks)
Oct 25, 2019
c07eb60
feat: add symlink support to fromTree
Oct 25, 2019
5de1e6d
fix: fromTree external directory symlink
Oct 25, 2019
1e9476d
fix: listFiles symlink support
Oct 25, 2019
455f912
Merge remote-tracking branch 'upstream/master' into 4450
Oct 29, 2019
b49f64f
chore: additional contridactory test
Oct 29, 2019
44f7757
chore: fix docs
Oct 29, 2019
ab92e3f
chore: ExcludeRules class refactor
Oct 29, 2019
af2098b
chore: evaluateFile refactor
Oct 29, 2019
9cf39b0
chore: further evaluateFile refactor
Oct 29, 2019
02a8d0e
chore: evaluateDirectory refactor
Oct 29, 2019
23331a3
chore: move FsUtils to assets
Oct 29, 2019
fc95caa
chore: move FsUtils to assets (unstaged files)
Oct 29, 2019
e2cee6b
Merge remote-tracking branch 'upstream/master' into 4450
Oct 29, 2019
75f7c3c
Merge branch 'master' into 4450
mergify[bot] Oct 30, 2019
d2d9f43
Merge branch 'master' into 4450
mergify[bot] Oct 30, 2019
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 17 additions & 35 deletions packages/@aws-cdk/assets/lib/fs/copy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,56 +2,38 @@ import fs = require('fs');
import path = require('path');
import { CopyOptions } from './copy-options';
import { FollowMode } from './follow-mode';
import { shouldExclude, shouldFollow } from './utils';
import { mkdirpSync } from './mkdirpSync';
import { listFilesRecursively, shouldFollow } from './utils';

export function copyDirectory(srcDir: string, destDir: string, options: CopyOptions = { }, rootDir?: string) {
const follow = options.follow !== undefined ? options.follow : FollowMode.EXTERNAL;
const exclude = options.exclude || [];

rootDir = rootDir || srcDir;

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);
for (const assetFile of listFilesRecursively(srcDir, {...options, follow}, rootDir)) {
const filePath = assetFile.relativePath;
const destFilePath = path.join(destDir, filePath);

if (shouldExclude(exclude, path.relative(rootDir, sourceFilePath))) {
continue;
}

const destFilePath = path.join(destDir, file);

let stat: fs.Stats | undefined = follow === FollowMode.ALWAYS
? fs.statSync(sourceFilePath)
: fs.lstatSync(sourceFilePath);

if (stat && stat.isSymbolicLink()) {
const target = fs.readlinkSync(sourceFilePath);
if (follow !== FollowMode.ALWAYS) {
if (assetFile.isSymbolicLink) {
const targetPath = path.normalize(path.resolve(srcDir, assetFile.symlinkTarget));
if (!shouldFollow(follow, rootDir, targetPath)) {
fs.symlinkSync(assetFile.symlinkTarget, destFilePath);

// determine if this is an external link (i.e. the target's absolute path
// is outside of the root directory).
const targetPath = path.normalize(path.resolve(srcDir, target));

if (shouldFollow(follow, rootDir, targetPath)) {
stat = fs.statSync(sourceFilePath);
} else {
fs.symlinkSync(target, destFilePath);
stat = undefined;
continue;
}
}
}

if (stat && stat.isDirectory()) {
fs.mkdirSync(destFilePath);
copyDirectory(sourceFilePath, destFilePath, options, rootDir);
stat = undefined;
}

if (stat && stat.isFile()) {
fs.copyFileSync(sourceFilePath, destFilePath);
stat = undefined;
if (!assetFile.isDirectory) {
mkdirpSync(path.dirname(destFilePath));
fs.copyFileSync(assetFile.absolutePath, destFilePath);
} else {
mkdirpSync(destFilePath);
}
}
}
44 changes: 16 additions & 28 deletions packages/@aws-cdk/assets/lib/fs/fingerprint.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import fs = require('fs');
import path = require('path');
import { CopyOptions } from './copy-options';
import { FollowMode } from './follow-mode';
import { shouldExclude, shouldFollow } from './utils';
import { listFilesRecursively, shouldFollow } from './utils';

const BUFFER_SIZE = 8 * 1024;
const CTRL_SOH = '\x01';
Expand Down Expand Up @@ -38,40 +38,28 @@ export function fingerprint(fileOrDirectory: string, options: FingerprintOptions
const rootDirectory = fs.statSync(fileOrDirectory).isDirectory()
? fileOrDirectory
: path.dirname(fileOrDirectory);
const exclude = options.exclude || [];
_processFileOrDirectory(fileOrDirectory);

return hash.digest('hex');

function _processFileOrDirectory(symbolicPath: string, realPath = symbolicPath) {
if (shouldExclude(exclude, symbolicPath)) {
return;
}

const stat = fs.lstatSync(realPath);
const relativePath = path.relative(fileOrDirectory, symbolicPath);
for (const assetFile of listFilesRecursively(fileOrDirectory, {...options, follow}, rootDirectory)) {
const relativePath = path.relative(fileOrDirectory, assetFile.absolutePath);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you not use relativePath from the AssetFile here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is relative to fileOrDirectory instead of rootDirectory


if (stat.isSymbolicLink()) {
const linkTarget = fs.readlinkSync(realPath);
const resolvedLinkTarget = path.resolve(path.dirname(realPath), linkTarget);
if (shouldFollow(follow, rootDirectory, resolvedLinkTarget)) {
_processFileOrDirectory(symbolicPath, resolvedLinkTarget);
if (assetFile.isSymbolicLink) {
const resolvedLinkTarget = path.resolve(path.dirname(assetFile.absolutePath), assetFile.symlinkTarget);
if (!shouldFollow(follow, rootDirectory, resolvedLinkTarget)) {
_hashField(hash, `link:${relativePath}`, assetFile.symlinkTarget);
} else {
_hashField(hash, `link:${relativePath}`, linkTarget);
_hashField(hash, `file:${relativePath}`, _contentFingerprint(assetFile.absolutePath, assetFile.size));
}
} else if (stat.isFile()) {
_hashField(hash, `file:${relativePath}`, _contentFingerprint(realPath, stat));
} else if (stat.isDirectory()) {
for (const item of fs.readdirSync(realPath).sort()) {
_processFileOrDirectory(path.join(symbolicPath, item), path.join(realPath, item));
}
} else {
throw new Error(`Unable to hash ${symbolicPath}: it is neither a file nor a directory`);
} else if (assetFile.isFile) {
_hashField(hash, `file:${relativePath}`, _contentFingerprint(assetFile.absolutePath, assetFile.size));
} else if (!assetFile.isDirectory) {
throw new Error(`Unable to hash ${assetFile.absolutePath}: it is neither a file nor a directory`);
}
}

return hash.digest('hex');
}

function _contentFingerprint(file: string, stat: fs.Stats): string {
function _contentFingerprint(file: string, size: number): string {
const hash = crypto.createHash('sha256');
const buffer = Buffer.alloc(BUFFER_SIZE);
// tslint:disable-next-line: no-bitwise
Expand All @@ -85,7 +73,7 @@ function _contentFingerprint(file: string, stat: fs.Stats): string {
} finally {
fs.closeSync(fd);
}
return `${stat.size}:${hash.digest('hex')}`;
return `${size}:${hash.digest('hex')}`;
}

function _hashField(hash: crypto.Hash, header: string, value: string | Buffer | DataView) {
Expand Down
67 changes: 67 additions & 0 deletions packages/@aws-cdk/assets/lib/fs/mkdirpSync.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
// Slightly refactored version of fs-extra mkdirpSync
// https://github.com/jprichardson/node-fs-extra/blob/d1a01e735e81688e08688557d7a254fa8297d98e/lib/mkdirs/mkdirs.js

import fs = require('fs');
import path = require('path');

const INVALID_PATH_CHARS = /[<>:"|?*]/;
const o777 = parseInt('0777', 8);

function getRootPath(p: string) {
const paths = path.normalize(path.resolve(p)).split(path.sep);
if (paths.length > 0) { return paths[0]; }
return null;
}

function invalidWin32Path(p: string) {
const rp = getRootPath(p);
p = p.replace(rp || '', '');
return INVALID_PATH_CHARS.test(p);
}

export function mkdirpSync(p: string, opts?: any, made?: any) {
if (!opts || typeof opts !== 'object') {
opts = { mode: opts };
}

let mode = opts.mode;
const xfs = opts.fs || fs;

if (process.platform === 'win32' && invalidWin32Path(p)) {
const errInval = new Error(p + ' contains invalid WIN32 path characters.');
// @ts-ignore
errInval.code = 'EINVAL';
throw errInval;
}

if (mode === undefined) {
// tslint:disable-next-line: no-bitwise
mode = o777 & (~process.umask());
}
if (!made) { made = null; }

p = path.resolve(p);

try {
xfs.mkdirSync(p, mode);
made = made || p;
} catch (err0) {
if (err0.code === 'ENOENT') {
if (path.dirname(p) === p) { throw err0; }
made = mkdirpSync(path.dirname(p), opts, made);
mkdirpSync(p, opts, made);
} else {
// In the case of any other error, just see if there's a dir there
// already. If so, then hooray! If not, then something is borked.
let stat;
try {
stat = xfs.statSync(p);
} catch (err1) {
throw err0;
}
if (!stat.isDirectory()) { throw err0; }
}
}

return made;
}
Loading