diff --git a/.github/workflows/build_linux.yaml b/.github/workflows/build_linux.yaml index d4cd5486..a478ec80 100644 --- a/.github/workflows/build_linux.yaml +++ b/.github/workflows/build_linux.yaml @@ -145,8 +145,9 @@ jobs: - name: Build App run: yarn build --publish never -l ${{ steps.config.outputs.build-args }} env: + VARIANT_PATH: variant.json # Only set for Nightly builds - ED_NIGHTLY: ${{ inputs.version }} + VERSION: ${{ inputs.version }} - name: Check native libraries run: | diff --git a/.github/workflows/build_macos.yaml b/.github/workflows/build_macos.yaml index 056505aa..892f5856 100644 --- a/.github/workflows/build_macos.yaml +++ b/.github/workflows/build_macos.yaml @@ -96,8 +96,9 @@ jobs: APPLE_APP_SPECIFIC_PASSWORD: ${{ secrets.APPLE_ID_PASSWORD }} CSC_KEY_PASSWORD: ${{ secrets.APPLE_CSC_KEY_PASSWORD }} CSC_LINK: ${{ secrets.APPLE_CSC_LINK }} + VARIANT_PATH: variant.json # Only set for Nightly builds - ED_NIGHTLY: ${{ inputs.version }} + VERSION: ${{ inputs.version }} - name: Check app was signed & notarised successfully if: inputs.sign != '' @@ -113,6 +114,7 @@ jobs: yarn build:universal --publish never env: CSC_IDENTITY_AUTO_DISCOVERY: false + VARIANT_PATH: variant.json - name: Generate releases.json if: inputs.base-url diff --git a/.github/workflows/build_prepare.yaml b/.github/workflows/build_prepare.yaml index f72a915f..02fe91c4 100644 --- a/.github/workflows/build_prepare.yaml +++ b/.github/workflows/build_prepare.yaml @@ -85,6 +85,11 @@ jobs: if: steps.branch-matching.outcome == 'failure' || steps.branch-matching.outcome == 'skipped' run: yarn run fetch --noverify -d ${{ inputs.config }} ${{ inputs.version }} + - name: Copy variant config + run: cp "$CONFIG_DIR/build.json" variant.json + env: + CONFIG_DIR: ${{ inputs.config }} + # We split this out to save the build_* scripts having to do it to make use of `hashFiles` in the cache action - name: Generate cache hash files run: | @@ -170,3 +175,4 @@ jobs: electronVersion hakHash changelog.Debian + variant.json diff --git a/.github/workflows/build_windows.yaml b/.github/workflows/build_windows.yaml index 7e7251c7..cf63bd98 100644 --- a/.github/workflows/build_windows.yaml +++ b/.github/workflows/build_windows.yaml @@ -193,8 +193,14 @@ jobs: - name: Build App run: yarn build --publish never -w ${{ steps.config.outputs.build-args }} env: + VARIANT_PATH: variant.json # Only set for Nightly builds - ED_NIGHTLY: ${{ inputs.version }} + # The windows packager relies on parsing this as semver, so we have to make it look like one. + # This will give our update packages really stupid names, but we probably can't change that either + # because squirrel windows parses them for the version too. We don't really care: nobody sees them. + # We just give the installer a static name, so you'll just see this in the 'about' dialog. + # Turns out if you use 0.0.0 here it makes Squirrel windows crash, so we use 0.0.1. + VERSION: ${{ inputs.version && format('0.0.1-nightly.{0}', inputs.version) || '' }} - name: Trust eSigner sandbox cert if: inputs.sign == '' diff --git a/electron-builder.ts b/electron-builder.ts index d2fd5370..9ddf531e 100644 --- a/electron-builder.ts +++ b/electron-builder.ts @@ -1,12 +1,12 @@ import * as os from "node:os"; import * as fs from "node:fs"; +import * as path from "node:path"; import { type Configuration as BaseConfiguration, type Protocol } from "electron-builder"; /** * This script has different outputs depending on your os platform. * * On Windows: - * Prefixes the nightly version with `0.0.1-nightly.` as it breaks if it is not semver * Passes $ED_SIGNTOOL_THUMBPRINT and $ED_SIGNTOOL_SUBJECT_NAME to * build.win.signtoolOptions.signingHashAlgorithms and build.win.signtoolOptions.certificateSubjectName respectively if specified. * @@ -16,32 +16,68 @@ import { type Configuration as BaseConfiguration, type Protocol } from "electron * Passes $ED_DEBIAN_CHANGELOG to build.deb.fpm if specified */ -const DEFAULT_APP_ID = "im.riot.app"; -const NIGHTLY_APP_ID = "im.riot.nightly"; -const NIGHTLY_DEB_NAME = "element-nightly"; - -const DEFAULT_PROTOCOL_SCHEME = "io.element.desktop"; -const NIGHTLY_PROTOCOL_SCHEME = "io.element.nightly"; - +/** + * Interface describing relevant fields of the package.json file. + */ interface Pkg { + version: string; +} + +/** + * Base metadata fields, used in both package.json and the variant configuration. + */ +interface Metadata { name: string; productName: string; description: string; - version: string; +} + +/** + * Extra metadata fields that are injected into the build to pass to the app at runtime. + */ +interface ExtraMetadata extends Metadata { + electron_appId: string; + electron_protocol: string; +} + +/** + * Interface describing the variant configuration format. + */ +interface Variant extends Metadata { + "appId": string; + "linux.executableName"?: string; + "linux.deb.name"?: string; + "protocols": string[]; } type Writable = NonNullable< T extends Function ? T : T extends object ? { -readonly [K in keyof T]: Writable } : T >; -const pkg: Pkg = JSON.parse(fs.readFileSync("package.json", "utf8")); +// Load the default variant as a base configuration +const DEFAULT_VARIANT = path.join("element.io", "release", "build.json"); +let variant: Variant = JSON.parse(fs.readFileSync(DEFAULT_VARIANT, "utf8")); + +/** + * If a variant is specified, we will use it to override the build-specific values. + * This allows us to have different builds for different purposes (e.g. stable, nightly). + */ +if (process.env.VARIANT_PATH) { + console.log(`Using variant configuration from '${process.env.VARIANT_PATH}':`); + variant = { + ...variant, + ...JSON.parse(fs.readFileSync(`${process.env.VARIANT_PATH}`, "utf8")), + }; +} else { + console.warn(`No VARIANT_PATH specified, using default variant configuration '${DEFAULT_VARIANT}':`); +} + +for (const key in variant) { + console.log(`${key}: ${variant[key]}`); +} interface Configuration extends BaseConfiguration { - extraMetadata: Partial> & - Omit & { - electron_appId: string; - electron_protocol: string; - }; + extraMetadata: Partial> & ExtraMetadata; linux: BaseConfiguration["linux"]; win: BaseConfiguration["win"]; mac: BaseConfiguration["mac"]; @@ -58,7 +94,7 @@ const config: Omit, "electronFuses"> & { // Make all fuses required to ensure they are all explicitly specified electronFuses: Required; } = { - appId: DEFAULT_APP_ID, + appId: variant.appId, asarUnpack: "**/*.node", electronFuses: { enableCookieEncryption: true, @@ -84,17 +120,17 @@ const config: Omit, "electronFuses"> & { ], extraResources: ["build/icon.*", "webapp.asar"], extraMetadata: { - name: pkg.name, - productName: pkg.productName, - description: pkg.description, - electron_appId: DEFAULT_APP_ID, - electron_protocol: DEFAULT_PROTOCOL_SCHEME, + name: variant.name, + productName: variant.productName, + description: variant.description, + electron_appId: variant.appId, + electron_protocol: variant.protocols[0], }, linux: { target: ["tar.gz", "deb"], category: "Network;InstantMessaging;Chat", icon: "build/icon.png", - executableName: pkg.name, // element-desktop or element-desktop-nightly + executableName: variant.name, // element-desktop or element-desktop-nightly }, deb: { packageCategory: "net", @@ -140,14 +176,27 @@ const config: Omit, "electronFuses"> & { output: "dist", }, protocols: { - name: "element", - schemes: [DEFAULT_PROTOCOL_SCHEME, "element"], + name: variant.productName, + schemes: variant.protocols, }, nativeRebuilder: "sequential", nodeGypRebuild: false, npmRebuild: true, }; +/** + * Allow specifying the version via env var. + * If unspecified, it will default to the version in package.json. + * @param {string} process.env.VERSION + */ +if (process.env.VERSION) { + config.extraMetadata.version = process.env.VERSION; +} + +if (variant["linux.deb.name"]) { + config.deb.fpm.push("--name", variant["linux.deb.name"]); +} + /** * Allow specifying windows signing cert via env vars * @param {string} process.env.ED_SIGNTOOL_SUBJECT_NAME @@ -158,33 +207,6 @@ if (process.env.ED_SIGNTOOL_SUBJECT_NAME && process.env.ED_SIGNTOOL_THUMBPRINT) config.win.signtoolOptions!.certificateSha1 = process.env.ED_SIGNTOOL_THUMBPRINT; } -/** - * Allow specifying nightly version via env var - * @param {string} process.env.ED_NIGHTLY - */ -if (process.env.ED_NIGHTLY) { - config.deb.fpm = []; // Clear the fpm as the breaks deb fields don't apply to nightly - - config.appId = config.extraMetadata.electron_appId = NIGHTLY_APP_ID; - config.extraMetadata.productName += " Nightly"; - config.extraMetadata.name += "-nightly"; - config.extraMetadata.description += " (nightly unstable build)"; - config.linux.executableName += "-nightly"; - config.deb.fpm.push("--name", NIGHTLY_DEB_NAME); - (config.protocols as Protocol).schemes[0] = config.extraMetadata.electron_protocol = NIGHTLY_PROTOCOL_SCHEME; - - let version = process.env.ED_NIGHTLY; - if (os.platform() === "win32") { - // The windows packager relies on parsing this as semver, so we have to make it look like one. - // This will give our update packages really stupid names, but we probably can't change that either - // because squirrel windows parses them for the version too. We don't really care: nobody sees them. - // We just give the installer a static name, so you'll just see this in the 'about' dialog. - // Turns out if you use 0.0.0 here it makes Squirrel windows crash, so we use 0.0.1. - version = "0.0.1-nightly." + version; - } - config.extraMetadata.version = version; -} - if (os.platform() === "linux") { // Electron crashes on debian if there's a space in the path. // https://github.com/vector-im/element-web/issues/13171 diff --git a/element.io/nightly/build.json b/element.io/nightly/build.json new file mode 100644 index 00000000..1e028291 --- /dev/null +++ b/element.io/nightly/build.json @@ -0,0 +1,9 @@ +{ + "appId": "im.riot.nightly", + "name": "element-desktop-nightly", + "productName": "Element Nightly", + "description": "Element: the future of secure communication (nightly unstable build)", + "protocols": ["io.element.nightly", "element"], + "linux.executableName": "element-desktop-nightly", + "linux.deb.name": "element-nightly" +} diff --git a/element.io/release/build.json b/element.io/release/build.json new file mode 100644 index 00000000..33b85f7e --- /dev/null +++ b/element.io/release/build.json @@ -0,0 +1,7 @@ +{ + "appId": "im.riot.app", + "name": "element-desktop", + "productName": "Element", + "description": "Element: the future of secure communication", + "protocols": ["io.element.desktop", "element"] +}