@@ -2,6 +2,7 @@ require('./patch-electron-builder');
22
33const fs = require ( 'fs' ) ;
44const pathUtil = require ( 'path' ) ;
5+ const childProcess = require ( 'child_process' ) ;
56const builder = require ( 'electron-builder' ) ;
67const electronFuses = require ( '@electron/fuses' ) ;
78
@@ -18,6 +19,51 @@ const ELECTRON_26_FINAL = '26.6.10';
1819// Electron 32 is the last version to support macOS 10.15
1920const ELECTRON_32_FINAL = '32.3.3' ;
2021
22+ /**
23+ * @returns {Date }
24+ */
25+ const getSourceDateEpoch = ( ) => {
26+ // Used if a better date cannot be obtained for any reason.
27+ // This is from commit 35045e7c0fa4e4e14b2747e967adb4029cedb945.
28+ const ARBITRARY_FALLBACK = 1609809111000 ;
29+
30+ // If SOURCE_DATE_EPOCH is set externally, use it.
31+ if ( process . env . SOURCE_DATE_EPOCH ) {
32+ return new Date ( ( + process . env . SOURCE_DATE_EPOCH ) * 1000 ) ;
33+ }
34+
35+ // Otherwise, try to get the time of the most recent commit.
36+ const gitProcess = childProcess . spawnSync ( 'git' , [ 'log' , '-1' , '--pretty=%ct' ] ) ;
37+
38+ if ( gitProcess . error ) {
39+ if ( gitProcess . error === 'ENOENT' ) {
40+ console . warn ( 'Could not get source date epoch: git is not installed' ) ;
41+ return new Date ( ARBITRARY_FALLBACK ) ;
42+ }
43+ throw gitProcess . error ;
44+ }
45+
46+ if ( gitProcess . status !== 0 ) {
47+ console . warn ( `Could not get source date epoch: git returned status ${ gitProcess . status } ` ) ;
48+ return new Date ( ARBITRARY_FALLBACK ) ;
49+ }
50+
51+ const gitStdout = gitProcess . stdout . toString ( ) . trim ( ) ;
52+ if ( / ^ \d + $ / . test ( gitStdout ) ) {
53+ return new Date ( ( + gitStdout ) * 1000 ) ;
54+ }
55+
56+ console . warn ( `Could not get source date epoch: git did not return a date` ) ;
57+ return new Date ( ARBITRARY_FALLBACK ) ;
58+ } ;
59+
60+ const sourceDateEpoch = getSourceDateEpoch ( ) ;
61+ // Ensure that we have a SOURCE_DATE_EPOCH environment variable so that it is available
62+ // to child processes of electron-builder. This is necessary for making the Debian
63+ // packages producibile.
64+ process . env . SOURCE_DATE_EPOCH = Math . round ( sourceDateEpoch . getTime ( ) / 1000 ) . toString ( ) ;
65+ console . log ( `Source date epoch: ${ sourceDateEpoch . toISOString ( ) } (${ process . env . SOURCE_DATE_EPOCH } )` ) ;
66+
2167/**
2268 * @param {string } platformName
2369 * @returns {string } a string that indexes into Arch[...]
@@ -83,8 +129,30 @@ const flipFuses = async (context) => {
83129 await context . packager . addElectronFuses ( context , newFuses ) ;
84130} ;
85131
132+ /**
133+ * @param {string } directory
134+ * @param {Date } date
135+ */
136+ const recursivelySetFileTimes = ( directory , date ) => {
137+ const files = fs . readdirSync ( directory ) ;
138+ for ( const file of files ) {
139+ const filePath = pathUtil . join ( directory , file ) ;
140+ const stat = fs . statSync ( filePath ) ;
141+ if ( stat . isDirectory ( ) ) {
142+ recursivelySetFileTimes ( filePath , date ) ;
143+ } else {
144+ fs . utimesSync ( filePath , date , date ) ;
145+ }
146+ }
147+ fs . utimesSync ( directory , date , date ) ;
148+ } ;
149+
86150const afterPack = async ( context ) => {
87151 await flipFuses ( context ) ;
152+
153+ // When electron-builder packs the folder, modification times of the files are
154+ // preserved for some formats, so ensure that modification times are reproducible.
155+ recursivelySetFileTimes ( context . appOutDir , sourceDateEpoch ) ;
88156} ;
89157
90158const build = async ( {
@@ -123,7 +191,6 @@ const build = async ({
123191 tw_warn_legacy : isProduction ,
124192 tw_update : isProduction && manageUpdates
125193 } ,
126- afterAllArtifactBuild,
127194 afterPack,
128195 ...extraConfig ,
129196 ...await prepare ( archName )
0 commit comments