Skip to content

Commit

Permalink
fix(core): asset hash is different between linux and windows (aws#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 aws#14555
Closes aws#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 authored and TikiTDO committed Feb 21, 2022
1 parent 1ce4a37 commit d8011f8
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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 d8011f8

Please sign in to comment.