Skip to content

Commit

Permalink
fix(core): asset hash is different between linux and windows (#16945)
Browse files Browse the repository at this point in the history
The hash for a specific file in a directory include its relative path.
This gives different results on Linux vs Windows because of the
different path separator. The solution is to normalize the relative path
using forward slashes.

Affects directory assets with subdirectories.

Closes #14555
Closes #16928


----

*By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license*
  • Loading branch information
jogold committed Oct 13, 2021
1 parent 2f0140e commit 59950dd
Show file tree
Hide file tree
Showing 2 changed files with 24 additions and 4 deletions.
10 changes: 6 additions & 4 deletions packages/@aws-cdk/core/lib/fs/fingerprint.ts
Expand Up @@ -53,24 +53,26 @@ export function fingerprint(fileOrDirectory: string, options: FingerprintOptions
return hash.digest('hex');

function _processFileOrDirectory(symbolicPath: string, isRootDir: boolean = false, realPath = symbolicPath) {
const relativePath = path.relative(fileOrDirectory, symbolicPath);

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

const stat = fs.lstatSync(realPath);

// Use relative path as hash component. Normalize it with forward slashes to ensure
// same hash on Windows and Linux.
const hashComponent = path.relative(fileOrDirectory, symbolicPath).replace(/\\/g, '/');

if (stat.isSymbolicLink()) {
const linkTarget = fs.readlinkSync(realPath);
const resolvedLinkTarget = path.resolve(path.dirname(realPath), linkTarget);
if (shouldFollow(follow, rootDirectory, resolvedLinkTarget)) {
_processFileOrDirectory(symbolicPath, false, resolvedLinkTarget);
} else {
_hashField(hash, `link:${relativePath}`, linkTarget);
_hashField(hash, `link:${hashComponent}`, linkTarget);
}
} else if (stat.isFile()) {
_hashField(hash, `file:${relativePath}`, contentFingerprint(realPath));
_hashField(hash, `file:${hashComponent}`, contentFingerprint(realPath));
} else if (stat.isDirectory()) {
for (const item of fs.readdirSync(realPath).sort()) {
_processFileOrDirectory(path.join(symbolicPath, item), false, path.join(realPath, item));
Expand Down
18 changes: 18 additions & 0 deletions packages/@aws-cdk/core/test/fs/fs-fingerprint.test.ts
Expand Up @@ -178,4 +178,22 @@ describe('fs fingerprint', () => {

});
});

test('normalizes relative path', () => {
// Simulate a Windows path.relative()
const originalPathRelative = path.relative;
const pathRelativeSpy = jest.spyOn(path, 'relative').mockImplementation((from: string, to: string): string => {
return originalPathRelative(from, to).replace(/\//g, '\\');
});

const hash1 = FileSystem.fingerprint(path.join(__dirname, 'fixtures', 'test1'));

// Restore Linux behavior
pathRelativeSpy.mockRestore();

const hash2 = FileSystem.fingerprint(path.join(__dirname, 'fixtures', 'test1'));

// Relative paths are normalized
expect(hash1).toEqual(hash2);
});
});

0 comments on commit 59950dd

Please sign in to comment.