diff --git a/lib/core.js b/lib/core.js index 1bcf33af9..4e98a9076 100644 --- a/lib/core.js +++ b/lib/core.js @@ -32,6 +32,7 @@ export default class Archiver extends Transform { options = { highWaterMark: 1024 * 1024, statConcurrency: 4, + noTimestamps: false, ...options, }; super(options); @@ -301,7 +302,15 @@ export default class Archiver extends Transform { } else if (data.mode === null) { data.mode = isDir ? 493 : 420; } - if (data.stats && data.date === null) { + if (this.options && this.options.noTimestamps) { + data.date = new Date(0); + if (data.stats) { + data.stats.atime = new Date(0); + data.stats.mtime = new Date(0); + data.stats.ctime = new Date(0); + data.stats.birthtime = new Date(0); + } + } else if (data.stats && data.date === null) { data.date = data.stats.mtime; } else { data.date = dateify(data.date); @@ -799,6 +808,9 @@ export default class Archiver extends Transform { * @global * @property {Number} [statConcurrency=4] Sets the number of workers used to * process the internal fs stat queue. + * @property {Boolean} [noTimestamps=false] When enabled, forces archiver to set + * consistent epoch timestamps (new Date(0)) for reproducible builds. + * @property {Boolean} [followSymlinks=false] Sets default symlink following behavior. */ /** diff --git a/test/archiver.js b/test/archiver.js index 20de9ad19..4b970210c 100644 --- a/test/archiver.js +++ b/test/archiver.js @@ -190,6 +190,44 @@ describe("archiver", function () { assert.propertyVal(entries["directory/"], "size", 0); }); }); + describe("#options.noTimestamps", function () { + var actual; + var archive; + var entries = {}; + before(function (done) { + archive = new JsonArchive({ noTimestamps: true }); + var testStream = new WriteStream("tmp/no-timestamps.json"); + testStream.on("close", function () { + actual = readJSON("tmp/no-timestamps.json"); + actual.forEach(function (entry) { + entries[entry.name] = entry; + }); + done(); + }); + archive.pipe(testStream); + archive + .append(testBuffer, { name: "buffer.txt", date: testDate }) + .append(createReadStream("test/fixtures/test.txt"), { + name: "stream.txt", + date: testDate, + }) + .finalize(); + }); + it("should reset date to epoch zero for buffer and stream", function () { + assert.property(entries, "buffer.txt"); + assert.propertyVal( + entries["buffer.txt"], + "date", + "1970-01-01T00:00:00.000Z", + ); + assert.property(entries, "stream.txt"); + assert.propertyVal( + entries["stream.txt"], + "date", + "1970-01-01T00:00:00.000Z", + ); + }); + }); describe("#directory", function () { var actual; var archive;