Support building for MAS

Co-authored-by: Fedor Indutny <79877362+indutny-signal@users.noreply.github.com>
This commit is contained in:
automated-signal
2026-06-15 14:31:06 -05:00
committed by GitHub
parent 4133a3d592
commit b95c9c575b
13 changed files with 294 additions and 27 deletions
+2 -2
View File
@@ -19587,7 +19587,7 @@ For more information on this, and how to apply and follow the GNU AGPL, see
```
## libsignal-account-keys 0.1.0, libsignal-core 0.1.0, libsignal-debug 0.89.2, mrp 2.69.3, protobuf 2.69.3, ringrtc 2.69.3, regex-aot 0.1.0, partial-default-derive 0.1.0, partial-default 0.1.0
## libsignal-account-keys 0.1.0, libsignal-core 0.1.0, libsignal-debug 0.89.2, mrp 2.69.4, protobuf 2.69.4, ringrtc 2.69.4, regex-aot 0.1.0, partial-default-derive 0.1.0, partial-default 0.1.0
```
GNU AFFERO GENERAL PUBLIC LICENSE
@@ -20682,7 +20682,7 @@ THIS SOFTWARE.
```
## cubeb-core 0.34.0, cubeb-sys 0.34.0, cubeb 0.34.0
## cubeb-core 0.35.3, cubeb-sys 0.35.3, cubeb 0.35.3
```
Copyright © 2017 Mozilla Foundation
+6 -4
View File
@@ -27,13 +27,15 @@ const LOCAL_ASSETS = new Set([
'fonts/inter-v3.19/Inter-Italic.woff2',
'fonts/inter-v3.19/Inter-SemiBoldItalic.woff2',
'fonts/mono-special/MonoSpecial-Regular.woff2',
'fonts/emoji.woff2',
]);
// pathname to optional resource name
const OPTIONAL_ASSETS = new Map([
['optional-fonts/emoji-large.woff2', 'emoji-font.woff2'],
]);
const OPTIONAL_ASSETS = new Map<string, string>([]);
if (!process.mas) {
LOCAL_ASSETS.add('fonts/emoji.woff2');
OPTIONAL_ASSETS.set('optional-fonts/emoji-large.woff2', 'emoji-font.woff2');
}
export class AssetService {
readonly #resourceService: OptionalResourceService;
+4
View File
@@ -8,5 +8,9 @@
<!-- https://github.com/electron/notarize#prerequisites -->
<key>com.apple.security.cs.allow-jit</key>
<true/>
<key>com.apple.security.app-sandbox</key>
<true/>
<key>com.apple.security.inherit</key>
<true/>
</dict>
</plist>
+16 -1
View File
@@ -15,13 +15,28 @@
<true/>
<key>com.apple.security.device.camera</key>
<true/>
<key>com.apple.security.device.usb</key>
<true/>
<key>com.apple.security.files.downloads.read-write</key>
<true/>
<key>com.apple.security.files.user-selected.read-write</key>
<true/>
<key>com.apple.security.personal-information.photos-library</key>
<key>com.apple.security.files.bookmarks.app-scope</key>
<true/>
<key>com.apple.security.print</key>
<true/>
<key>com.apple.security.network.client</key>
<true/>
<key>com.apple.security.network.server</key>
<true/>
<key>com.apple.security.application-groups</key>
<array>
<string>U68MSDN6DR.org.whispersystems.signal-desktop</string>
<string>U68MSDN6DR.signal-desktop</string>
</array>
<key>com.apple.application-identifier</key>
<string>U68MSDN6DR.org.whispersystems.signal-desktop</string>
<key>com.apple.developer.team-identifier</key>
<string>U68MSDN6DR</string>
</dict>
</plist>
+13 -1
View File
@@ -42,6 +42,7 @@
"prepare-staging-build": "node scripts/prepare_staging_build.mjs",
"prepare-no-delay-release": "node scripts/prepare-no-delay-release.mjs",
"prepare-linux-build": "node scripts/prepare_linux_build.mjs",
"prepare-mas-build": "node scripts/prepare_mas_build.mjs",
"test": "run-s test-node test-electron test-lint-intl test-oxlint",
"test-electron": "node scripts/test-electron.mjs",
"test-release": "node scripts/test-release.mjs",
@@ -104,6 +105,7 @@
"build:styles:tailwind:prod": "pnpm run build:styles:tailwind --minify",
"build:electron": "cross-env NODE_OPTIONS='--import=tsx' electron-builder --config.extraMetadata.environment=$SIGNAL_ENV",
"build:release": "cross-env SIGNAL_ENV=production pnpm run build:electron --config.directories.output=release",
"build:release:mas": "pnpm run build:release --mac mas --universal",
"build:mas-dev": "bash ./scripts/build-mas-dev.sh",
"build:release-win32-all": "pnpm run build:release --arm64 --x64",
"build:preload-cache": "node scripts/generate-preload-cache.mjs",
@@ -120,7 +122,7 @@
"@indutny/simple-windows-notifications": "2.0.16",
"@signalapp/libsignal-client": "0.95.0",
"@signalapp/mute-state-change": "workspace:*",
"@signalapp/ringrtc": "2.69.3",
"@signalapp/ringrtc": "2.69.4",
"@signalapp/sqlcipher": "3.3.5",
"@signalapp/windows-ucv": "workspace:*",
"google-libphonenumber": "3.2.44"
@@ -359,6 +361,7 @@
],
"mergeASARs": true,
"sign": "scripts/sign-macos.mjs",
"signInstaller": "scripts/sign-mas-installer.mjs",
"releaseInfo": {
"vendor": {
"minOSVersion": "21.0.1"
@@ -416,6 +419,15 @@
"entitlementsInherit": "./build/entitlements.mas-dev.inherit.plist",
"preAutoEntitlements": false,
"extendInfo": {
"ITSAppUsesNonExemptEncryption": false,
"ElectronTeamID": "U68MSDN6DR",
"NSCameraUsageDescription": "Signal uses your camera for video calling.",
"NSMicrophoneUsageDescription": "Signal uses your microphone for voice and video calling."
}
},
"mas": {
"extendInfo": {
"ITSAppUsesNonExemptEncryption": false,
"ElectronTeamID": "U68MSDN6DR",
"NSCameraUsageDescription": "Signal uses your camera for video calling.",
"NSMicrophoneUsageDescription": "Signal uses your microphone for voice and video calling."
+110 -1
View File
@@ -31,6 +31,53 @@ index ffb85dea810257c8ddbed2f857c25787c9e5b694..fdcc65fb94bcf95adbb4cf97b7d4c85a
// Otherwise order by name
return a < b ? -1 : 1;
});
diff --git a/out/macPackager.js b/out/macPackager.js
index d67dc0583d1bc98d2c60f5f4ff5c22a1fcd6ff44..1364f12a2791a4319ad741bef79fc4fb81f92516 100644
--- a/out/macPackager.js
+++ b/out/macPackager.js
@@ -330,22 +330,30 @@ class MacPackager extends platformPackager_1.PlatformPackager {
await this.doSign(signOptions, customSignOptions, identity);
// https://github.com/electron-userland/electron-builder/issues/1196#issuecomment-312310209
if (masOptions != null && !isDevelopment) {
+ const customSignInstaller = await (0, resolve_1.resolveFunction)(this.appInfo.type, customSignOptions.signInstaller, "signInstaller");
+ const artifactName = this.expandArtifactNamePattern(masOptions, "pkg", arch);
+ const artifactPath = path.join(outDir, artifactName);
+
+ let masInstallerIdentity;
+ if (customSignInstaller == null) {
const certType = isDevelopment ? "Mac Developer" : "3rd Party Mac Developer Installer";
- const masInstallerIdentity = await (0, macCodeSign_1.findIdentity)(certType, masOptions.identity, keychainFile);
+ masInstallerIdentity = await (0, macCodeSign_1.findIdentity)(certType, masOptions.identity, keychainFile);
if (masInstallerIdentity == null) {
throw new builder_util_1.InvalidConfigurationError(`Cannot find valid "${certType}" identity to sign MAS installer, please see https://electron.build/code-signing`);
}
- // mas uploaded to AppStore, so, use "-" instead of space for name
- const artifactName = this.expandArtifactNamePattern(masOptions, "pkg", arch);
- const artifactPath = path.join(outDir, artifactName);
- await this.doFlat(appPath, artifactPath, masInstallerIdentity, keychainFile);
- await this.info.emitArtifactBuildCompleted({
- file: artifactPath,
- target: null,
- arch: builder_util_1.Arch.x64,
- safeArtifactName: this.computeSafeArtifactName(artifactName, "pkg", arch, true, this.platformSpecificBuildOptions.defaultArch),
- packager: this,
- });
+ }
+ // mas uploaded to AppStore, so, use "-" instead of space for name
+ await this.doFlat(appPath, artifactPath, masInstallerIdentity, keychainFile);
+ if (customSignInstaller != null) {
+ await customSignInstaller(artifactPath, this);
+ }
+ await this.info.emitArtifactBuildCompleted({
+ file: artifactPath,
+ target: null,
+ arch: builder_util_1.Arch.x64,
+ safeArtifactName: this.computeSafeArtifactName(artifactName, "pkg", arch, true, this.platformSpecificBuildOptions.defaultArch),
+ packager: this,
+ });
}
if (!isMas) {
await this.notarizeIfProvided(appPath);
diff --git a/out/targets/FpmTarget.js b/out/targets/FpmTarget.js
index f10f8148a237fae6c7af44818a127dfc881d884f..3bc2a9d1895d4f00133c82240ec2eba8c87d605a 100644
--- a/out/targets/FpmTarget.js
@@ -43,6 +90,68 @@ index f10f8148a237fae6c7af44818a127dfc881d884f..3bc2a9d1895d4f00133c82240ec2eba8
sanitizedProductName: packager.appInfo.sanitizedProductName,
productFilename: packager.appInfo.productFilename,
...packager.platformSpecificBuildOptions,
diff --git a/out/targets/nsis/NsisTarget.js b/out/targets/nsis/NsisTarget.js
index 32afb5e2f16e18a4c054d48b47b649e60fab1131..bd7c75f95d1ee15a59d705632136f66f681745e9 100644
--- a/out/targets/nsis/NsisTarget.js
+++ b/out/targets/nsis/NsisTarget.js
@@ -175,11 +175,6 @@ class NsisTarget extends core_1.Target {
if (uninstallAppKey !== guid) {
defines.UNINSTALL_REGISTRY_KEY_2 = `Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\${guid}`;
}
- const { homepage } = this.packager.info.metadata;
- (0, builder_util_1.use)(options.uninstallUrlHelp || homepage, it => (defines.UNINSTALL_URL_HELP = it));
- (0, builder_util_1.use)(options.uninstallUrlInfoAbout || homepage, it => (defines.UNINSTALL_URL_INFO_ABOUT = it));
- (0, builder_util_1.use)(options.uninstallUrlUpdateInfo || homepage, it => (defines.UNINSTALL_URL_UPDATE_INFO = it));
- (0, builder_util_1.use)(options.uninstallUrlReadme || homepage, it => (defines.UNINSTALL_URL_README = it));
const commands = {
OutFile: `"${installerPath}"`,
VIProductVersion: appInfo.getVersionInWeirdWindowsForm(),
diff --git a/scheme.json b/scheme.json
index f4ea4fbbc5f19a5b6cad17f0c8d3cc8e7776fead..4ea9b0808db815b2c3c006a7a3fbe64876b92a35 100644
--- a/scheme.json
+++ b/scheme.json
@@ -3073,6 +3073,20 @@
],
"description": "The custom function (or path to file or module id) to sign an app bundle."
},
+ "signInstaller": {
+ "anyOf": [
+ {
+ "typeof": "function"
+ },
+ {
+ "type": [
+ "null",
+ "string"
+ ]
+ }
+ ],
+ "description": "The custom function (or path to file or module id) to sign an app package."
+ },
"signIgnore": {
"anyOf": [
{
@@ -3711,6 +3725,20 @@
],
"description": "The custom function (or path to file or module id) to sign an app bundle."
},
+ "signInstaller": {
+ "anyOf": [
+ {
+ "typeof": "function"
+ },
+ {
+ "type": [
+ "null",
+ "string"
+ ]
+ }
+ ],
+ "description": "The custom function (or path to file or module id) to sign an app package."
+ },
"signIgnore": {
"anyOf": [
{
diff --git a/templates/linux/after-install.tpl b/templates/linux/after-install.tpl
index 2bb3d32d9df96a4bf0245670613f20459b32fb46..6dda2f3798ec0b13366f1b8d4adb9cd61696fd28 100644
--- a/templates/linux/after-install.tpl
@@ -87,7 +196,7 @@ index 19b3decabe18a816f9ed5440fa9124ebfd6e3907..fa1ecd2d991977c53a5082b2efb3120c
+
+POLKIT_TARGET_PATH='/usr/share/polkit-1/actions'
+if [ -d "$POLKIT_TARGET_PATH" ]; then
+ rm -f $POLKIT_TARGET_PATH/org.signalapp.${sanitizedName}.*.policy
+ rm -f $POLKIT_TARGET_PATH/org.signalapp.${sanitizedName}.*.policy
+fi
+
+# SIGNAL CHANGES END
+10 -10
View File
@@ -21,7 +21,7 @@ patchedDependencies:
'@types/chai': b1cea4e432957039e7f72a32140d7a718258944078c5d5bdb40ad62b8717cb97
'@types/fabric@4.5.3': e5f339ecf72fbab1c91505e7713e127a7184bfe8164aa3a9afe9bf45a0ad6b89
'@vitest/expect@2.0.5': e8a96f71e52bf903c9f1eadba4740489a0beb48da33db52354adca484fe1f495
app-builder-lib: 4b0b6a075db66810319028f6784bd5bb11cd9bb38570800f1fff0d80f90c44d8
app-builder-lib: 02fa41bb6c77d0ecc2b4c7a03b1d16089baaeadf948fcab1d63b2bb12404c1d6
casual@1.6.2: b88b5052437cbdc1882137778b76ca5037f71b2a030ae9ef39dc97f51670d599
dmg-builder: 75ddbdfdce4e9fad4f5e7d3e2ac0d29fc920006584534018bf560c486e0642ec
fabric@4.6.0: 259e6eff3d60c4a453f7815fca6aa0d3a81842f4efdc4051836812fc1e711e00
@@ -50,8 +50,8 @@ importers:
specifier: workspace:*
version: link:packages/mute-state-change
'@signalapp/ringrtc':
specifier: 2.69.3
version: 2.69.3
specifier: 2.69.4
version: 2.69.4
'@signalapp/sqlcipher':
specifier: 3.3.5
version: 3.3.5
@@ -4270,8 +4270,8 @@ packages:
resolution: {integrity: sha512-y2sgqdivlrG41J4Zvt/82xtH/PZjDlgItqlD2g/Cv3ZbjlR6cGhTNXbfNygCJB8nXj+C7I28pjt1Zm3k0pv2mg==, tarball: https://registry.npmjs.org/@signalapp/quill-cjs/-/quill-cjs-2.1.2.tgz}
engines: {npm: '>=8.2.3'}
'@signalapp/ringrtc@2.69.3':
resolution: {integrity: sha512-QZE9SUj0pCPAJWEjQO3W3/2BnZcX8Md7y4K4pnTQssHis72qhppZeXxZrvbnwkeQmvRvGHsRGwsspj812JXzMw==, tarball: https://registry.npmjs.org/@signalapp/ringrtc/-/ringrtc-2.69.3.tgz}
'@signalapp/ringrtc@2.69.4':
resolution: {integrity: sha512-OLavGfCzfYuqUVoAtTeHi7QrXpVNmT8LxzOI9XOPId29bn5gJNrAG31IPVw7Fw7vvWpZwZHFi5MTmcmZGibs4A==, tarball: https://registry.npmjs.org/@signalapp/ringrtc/-/ringrtc-2.69.4.tgz}
hasBin: true
'@signalapp/sqlcipher@3.3.5':
@@ -14737,7 +14737,7 @@ snapshots:
lodash: 4.18.1
quill-delta: 5.1.0
'@signalapp/ringrtc@2.69.3':
'@signalapp/ringrtc@2.69.4':
dependencies:
https-proxy-agent: 7.0.6
tar: 7.5.15
@@ -15969,7 +15969,7 @@ snapshots:
app-builder-bin@5.0.0-alpha.12: {}
app-builder-lib@26.11.1(patch_hash=4b0b6a075db66810319028f6784bd5bb11cd9bb38570800f1fff0d80f90c44d8)(dmg-builder@26.11.1)(electron-builder-squirrel-windows@26.11.1):
app-builder-lib@26.11.1(patch_hash=02fa41bb6c77d0ecc2b4c7a03b1d16089baaeadf948fcab1d63b2bb12404c1d6)(dmg-builder@26.11.1)(electron-builder-squirrel-windows@26.11.1):
dependencies:
'@develar/schema-utils': 2.6.5
'@electron/asar': 3.4.1
@@ -17042,7 +17042,7 @@ snapshots:
dmg-builder@26.11.1(patch_hash=75ddbdfdce4e9fad4f5e7d3e2ac0d29fc920006584534018bf560c486e0642ec)(electron-builder-squirrel-windows@26.11.1):
dependencies:
app-builder-lib: 26.11.1(patch_hash=4b0b6a075db66810319028f6784bd5bb11cd9bb38570800f1fff0d80f90c44d8)(dmg-builder@26.11.1)(electron-builder-squirrel-windows@26.11.1)
app-builder-lib: 26.11.1(patch_hash=02fa41bb6c77d0ecc2b4c7a03b1d16089baaeadf948fcab1d63b2bb12404c1d6)(dmg-builder@26.11.1)(electron-builder-squirrel-windows@26.11.1)
builder-util: 26.11.1
fs-extra: 10.1.0
iconv-lite: 0.6.3
@@ -17184,7 +17184,7 @@ snapshots:
electron-builder-squirrel-windows@26.11.1(dmg-builder@26.11.1):
dependencies:
app-builder-lib: 26.11.1(patch_hash=4b0b6a075db66810319028f6784bd5bb11cd9bb38570800f1fff0d80f90c44d8)(dmg-builder@26.11.1)(electron-builder-squirrel-windows@26.11.1)
app-builder-lib: 26.11.1(patch_hash=02fa41bb6c77d0ecc2b4c7a03b1d16089baaeadf948fcab1d63b2bb12404c1d6)(dmg-builder@26.11.1)(electron-builder-squirrel-windows@26.11.1)
builder-util: 26.11.1
electron-winstaller: 5.4.0
transitivePeerDependencies:
@@ -17193,7 +17193,7 @@ snapshots:
electron-builder@26.11.1(electron-builder-squirrel-windows@26.11.1):
dependencies:
app-builder-lib: 26.11.1(patch_hash=4b0b6a075db66810319028f6784bd5bb11cd9bb38570800f1fff0d80f90c44d8)(dmg-builder@26.11.1)(electron-builder-squirrel-windows@26.11.1)
app-builder-lib: 26.11.1(patch_hash=02fa41bb6c77d0ecc2b4c7a03b1d16089baaeadf948fcab1d63b2bb12404c1d6)(dmg-builder@26.11.1)(electron-builder-squirrel-windows@26.11.1)
builder-util: 26.11.1
builder-util-runtime: 9.6.1
chalk: 4.1.2
+1 -1
View File
@@ -80,7 +80,7 @@ allowBuilds:
'@parcel/watcher@2.5.6': true
'@signalapp/libsignal-client@0.92.2': true # duplicate package
'@signalapp/libsignal-client@0.95.0': true
'@signalapp/ringrtc@2.69.3': true
'@signalapp/ringrtc@2.69.4': true
'@signalapp/sqlcipher@3.3.5': true
'@signalapp/windows-ucv@1.0.1': true
'@swc/core@1.10.16': true
+2 -7
View File
@@ -2,18 +2,13 @@
// SPDX-License-Identifier: AGPL-3.0-only
// @ts-check
import { join } from 'node:path';
import { execSync } from 'node:child_process';
import { writeFileSync } from 'node:fs';
import { DAY } from './utils/durations.mjs';
import { parseVersion } from './utils/parseVersion.mjs';
import { getBuildCreationTimestamp } from './utils/getBuildCreationTimestamp.mjs';
import packageJson from '../package.json' with { type: 'json' };
const unixTimestamp = parseInt(
process.env.SOURCE_DATE_EPOCH ||
execSync('git show -s --format=%ct').toString('utf8'),
10
);
const buildCreation = unixTimestamp * 1000;
const buildCreation = getBuildCreationTimestamp();
const isNotUpdatable = !parseVersion(packageJson.version).isUpdatable;
+74
View File
@@ -0,0 +1,74 @@
// Copyright 2026 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
// @ts-check
import fs from 'node:fs';
import _ from 'lodash';
import moment from 'moment';
import { parseVersion } from './utils/parseVersion.mjs';
import { getBuildCreationTimestamp } from './utils/getBuildCreationTimestamp.mjs';
import packageJson from '../package.json' with { type: 'json' };
const version = parseVersion(packageJson.version);
if (version.channel !== 'alpha') {
console.error('Only alpha versions can be published as MAS builds');
process.exit(1);
}
console.log('prepare_mas_build: updating package.json');
// -------
const NAME_PATH = 'name';
const VERSION_PATH = 'version';
const PRODUCTION_NAME = 'signal-desktop';
const PRODUCT_NAME_PATH = 'productName';
const PRODUCTION_PRODUCT_NAME = 'Signal';
const APP_ID_PATH = 'build.appId';
const PRODUCTION_APP_ID = 'org.whispersystems.signal-desktop';
const BUNDLE_VERSION_PATH = 'build.mac.bundleVersion';
const NON_MAS_BUNDLE_VERSION = '1';
const MAS_BUNDLE_VERSION = moment(getBuildCreationTimestamp()).format('YYYY.MM.DDHHMM');
const FILE_EXCLUSION_LIST = ['!fonts/emoji.woff2'];
// -------
/**
* @param {object} object
* @param {string} objectPath
* @param {string} expected
*/
function checkValue(object, objectPath, expected) {
const actual = _.get(object, objectPath);
if (actual !== expected) {
throw new Error(`${objectPath} was ${actual}; expected ${expected}`);
}
}
// ------
checkValue(packageJson, NAME_PATH, PRODUCTION_NAME);
checkValue(packageJson, PRODUCT_NAME_PATH, PRODUCTION_PRODUCT_NAME);
checkValue(packageJson, APP_ID_PATH, PRODUCTION_APP_ID);
checkValue(packageJson, BUNDLE_VERSION_PATH, NON_MAS_BUNDLE_VERSION);
for (const file of FILE_EXCLUSION_LIST) {
if (packageJson.build.files.includes(file)) {
throw new Error(`Expected ${file} to be absent from 'files'`);
}
}
// -------
_.set(packageJson, VERSION_PATH, `${version.major}.${version.minor}.${version.patch}`);
_.set(packageJson, BUNDLE_VERSION_PATH, MAS_BUNDLE_VERSION);
for (const file of FILE_EXCLUSION_LIST) {
packageJson.build.files.push(file);
}
// -------
fs.writeFileSync('./package.json', JSON.stringify(packageJson, null, ' '));
+4
View File
@@ -34,6 +34,10 @@ export async function sign(configuration) {
// The script will update the file in-place
const returnCode = execSync(`bash "${scriptPath}" "${target}"`, {
stdio: [null, process.stdout, process.stderr],
env: {
...process.env,
ELECTRON_BUILDER_PLATFORM: configuration.platform,
},
});
if (returnCode) {
+35
View File
@@ -0,0 +1,35 @@
// Copyright 2026 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
// @ts-check
import { execSync } from 'node:child_process';
import { realpath } from 'node:fs/promises';
/**
* @param {string} installerPath
* @returns {Promise<void>}
*/
export async function signInstaller(installerPath) {
if (process.env.SKIP_SIGNING_SCRIPT === '1') {
console.log('SKIP_SIGNING_SCRIPT=1, skipping custom mas installer signing script');
return;
}
const scriptPath = process.env.SIGN_MAS_INSTALLER_SCRIPT;
if (!scriptPath) {
throw new Error(
'path to macos sign script must be provided in environment variable SIGN_MAS_INSTALLER_SCRIPT'
);
}
const target = await realpath(installerPath);
// The script will update the file in-place
const returnCode = execSync(`bash "${scriptPath}" "${target}"`, {
stdio: [null, process.stdout, process.stderr],
});
if (returnCode) {
// oxlint-disable-next-line typescript/restrict-template-expressions
throw new Error(`sign-macos: Script returned code ${returnCode}`);
}
}
@@ -0,0 +1,17 @@
// Copyright 2026 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
// @ts-check
import { execSync } from 'node:child_process';
import { SECOND } from './durations.mjs';
/**
* @returns {number}
*/
export function getBuildCreationTimestamp() {
const unixTimestamp = parseInt(
process.env.SOURCE_DATE_EPOCH ||
execSync('git show -s --format=%ct').toString('utf8'),
10
);
return unixTimestamp * SECOND;
}