Skip to content

Commit 9101161

Browse files
jogoldrix0rrr
authored andcommitted
fix(toolkit): ensure asset zips are consistently produced (#2931)
Zip files were not consistent across deploys resulting in unnecessary S3 uploads and stack updates. Ensure consistency by appending files in series (guarantees file ordering in the zip) and reseting dates (guarantees same hash for same content). Closes #1997, Closes #2759
1 parent db981bf commit 9101161

File tree

5 files changed

+80
-8
lines changed

5 files changed

+80
-8
lines changed

packages/aws-cdk/lib/archive.ts

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,39 @@
11
import archiver = require('archiver');
22
import crypto = require('crypto');
33
import fs = require('fs-extra');
4+
import glob = require('glob');
5+
import path = require('path');
46

57
export function zipDirectory(directory: string, outputFile: string): Promise<void> {
68
return new Promise((ok, fail) => {
7-
const output = fs.createWriteStream(outputFile);
8-
const archive = archiver('zip');
99
// The below options are needed to support following symlinks when building zip files:
10-
// - nodir: This will prevent symlinks themselves from being copied into the zip.
10+
// - nodir: This will prevent symlinks themselves from being copied into the zip.
1111
// - follow: This will follow symlinks and copy the files within.
1212
const globOptions = {
1313
dot: true,
1414
nodir: true,
1515
follow: true,
16-
cwd: directory
16+
cwd: directory,
1717
};
18-
archive.glob('**', globOptions);
19-
archive.pipe(output);
20-
archive.finalize();
18+
const files = glob.sync('**', globOptions); // The output here is already sorted
19+
20+
const output = fs.createWriteStream(outputFile);
2121

22+
const archive = archiver('zip');
2223
archive.on('warning', fail);
2324
archive.on('error', fail);
25+
archive.pipe(output);
26+
27+
files.forEach(file => { // Append files serially to ensure file order
28+
archive.append(fs.createReadStream(path.join(directory, file)), {
29+
name: file,
30+
date: new Date('1980-01-01T00:00:00.000Z'), // reset dates to get the same hash for the same content
31+
});
32+
});
33+
34+
archive.finalize();
35+
36+
// archive has been finalized and the output file descriptor has closed, resolve promise
2437
output.once('close', () => ok());
2538
});
2639
}

packages/aws-cdk/package-lock.json

Lines changed: 48 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/aws-cdk/package.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
"devDependencies": {
3737
"@types/archiver": "^3.0.0",
3838
"@types/fs-extra": "^7.0.0",
39+
"@types/jszip": "^3.1.6",
3940
"@types/minimatch": "^3.0.3",
4041
"@types/mockery": "^1.4.29",
4142
"@types/request": "^2.48.1",
@@ -46,6 +47,7 @@
4647
"@types/yaml": "^1.0.2",
4748
"@types/yargs": "^13.0.0",
4849
"cdk-build-tools": "^0.35.0",
50+
"jszip": "^3.2.1",
4951
"mockery": "^2.1.0",
5052
"pkglint": "^0.35.0",
5153
"sinon": "^7.3.2"
@@ -60,6 +62,7 @@
6062
"colors": "^1.3.3",
6163
"decamelize": "^3.2.0",
6264
"fs-extra": "^8.0.1",
65+
"glob": "^7.1.4",
6366
"json-diff": "^0.5.4",
6467
"minimatch": ">=3.0",
6568
"promptly": "^3.0.3",

packages/aws-cdk/test/test.archive.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { exec as _exec } from 'child_process';
22
import fs = require('fs-extra');
3+
import jszip = require('jszip');
34
import { Test } from 'nodeunit';
45
import os = require('os');
56
import path = require('path');
@@ -24,6 +25,13 @@ export = {
2425
test.ok(false, `extracted directory ${extractDir} differs from original ${originalDir}`);
2526
}
2627

28+
// inspect the zile file to check that dates are reset
29+
const zip = await fs.readFile(zipFile);
30+
const zipData = await jszip.loadAsync(zip);
31+
const dates = Object.values(zipData.files).map(file => file.date.toISOString());
32+
test.equal(dates[0], '1980-01-01T00:00:00.000Z', 'Dates are not reset');
33+
test.equal(new Set(dates).size, 1, 'Dates are not equal');
34+
2735
await fs.remove(stagingDir);
2836
await fs.remove(extractDir);
2937
test.done();

packages/aws-cdk/tsconfig.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"compilerOptions": {
33
"target":"ES2018",
44
"module": "commonjs",
5-
"lib": ["es2016", "es2017.object", "es2017.string"],
5+
"lib": ["es2016", "es2017.object", "es2017.string", "dom"],
66
"declaration": true,
77
"strict": true,
88
"noImplicitAny": true,

0 commit comments

Comments
 (0)