# This workflow relies on actions/cache to store the hak dependency artifacts as they take a long time to build # Due to this extra care must be taken to only ever run all build_* scripts against the same branch to ensure # the correct cache scoping, and additional care must be taken to not run untrusted actions on the develop branch. on: workflow_call: secrets: APPLE_ID: required: false APPLE_ID_PASSWORD: required: false APPLE_CSC_KEY_PASSWORD: required: false APPLE_CSC_LINK: required: false inputs: ref: type: string required: false description: "The git ref to checkout, defaults to the default branch" version: type: string required: false description: "Version string to override the one in package.json, used for non-release builds" sign: type: string required: false description: "Whether to sign & notarise the build, requires 'Desktop Apple' environment" base-url: type: string required: false description: "The URL to which the output will be deployed." blob_report: type: boolean required: false description: "Whether to run the blob report" prepare-artifact-name: type: string required: false description: | The name of the prepare artifact to use, defaults to 'webapp'. The artifact must contain the following: + webapp.asar - the asar archive of the webapp to embed in the desktop app + electronVersion - the version of electron to use for cache keying + hakHash - the hash of the .hak directory to use for cache keying + variant.json - the variant configuration to use for the build The artifact can also contain any additional files which will be applied as overrides to the checkout root before building, for example icons in the `build/` directory to override the app icons. default: "webapp" test: type: boolean required: false default: true description: "Whether to run the test stage after building" test-args: type: string required: false description: "Additional arguments to pass to playwright" artifact-prefix: type: string required: false description: "An optional prefix to add to the artifact name, useful for distinguishing builds in private repos." default: "" targets: type: string required: false description: "List of targets to build" default: "dmg zip" permissions: {} # No permissions required jobs: build: name: Build macOS Universal runs-on: macos-15 # M1 environment: ${{ inputs.sign && 'Desktop Apple' || '' }} steps: - uses: maxim-lobanov/setup-xcode@ed7a3b1fda3918c0306d1b724322adc0b8cc0a90 # v1 with: xcode-version: latest-stable - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 with: repository: element-hq/element-web ref: ${{ inputs.ref }} persist-credentials: false - uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8 with: name: ${{ inputs.prepare-artifact-name }} path: apps/desktop - name: Cache .hak id: cache uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5 with: key: ${{ runner.os }}-${{ hashFiles('hakHash', 'electronVersion') }} path: | apps/desktop/.hak - name: Install Rust if: steps.cache.outputs.cache-hit != 'true' run: | rustup toolchain install stable --profile minimal --no-self-update rustup default stable rustup target add aarch64-apple-darwin rustup target add x86_64-apple-darwin # M1 macos-14 comes without Python preinstalled - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6 with: python-version: "3.13" # Install Quartz for DMG badges # https://github.com/electron-userland/electron-builder/issues/9511#issuecomment-3774092888 - run: sudo pip3 install pyobjc-framework-Quartz - uses: pnpm/action-setup@fc06bc1257f339d1d5d8b3a19a8cae5388b55320 # v5 - uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6 with: node-version-file: apps/desktop/.node-version cache: "pnpm" - name: Install Deps working-directory: apps/desktop run: "pnpm install --frozen-lockfile" - name: Build Natives if: steps.cache.outputs.cache-hit != 'true' working-directory: apps/desktop run: pnpm run build:native:universal - name: "Build App" working-directory: apps/desktop run: pnpm run build:universal --publish never -m ${TARGETS} env: # Code signing parameters CSC_IDENTITY_AUTO_DISCOVERY: ${{ inputs.sign != '' }} APPLE_TEAM_ID: ${{ case(inputs.sign != '', vars.APPLE_TEAM_ID, '') }} APPLE_ID: ${{ case(inputs.sign != '', secrets.APPLE_ID, '') }} APPLE_APP_SPECIFIC_PASSWORD: ${{ case(inputs.sign != '', secrets.APPLE_ID_PASSWORD, '') }} CSC_KEY_PASSWORD: ${{ case(inputs.sign != '', secrets.APPLE_CSC_KEY_PASSWORD, '') }} CSC_LINK: ${{ case(inputs.sign != '', secrets.APPLE_CSC_LINK, '') }} VARIANT_PATH: variant.json TARGETS: ${{ inputs.targets }} # Only set for Nightly builds VERSION: ${{ inputs.version }} - name: Check app was signed & notarised successfully if: inputs.sign != '' working-directory: apps/desktop run: | hdiutil attach dist/*.dmg -mountpoint /Volumes/Element codesign -dv --verbose=4 /Volumes/Element/*.app spctl -a -vvv -t install /Volumes/Element/*.app hdiutil detach /Volumes/Element - name: Generate releases.json if: inputs.base-url working-directory: apps/desktop run: | PKG_JSON_VERSION=$(cat package.json | jq -r .version) LATEST=$(find dist -type f -iname "*-mac.zip" | xargs -0 -n1 -- basename) # Encode spaces in the URL as Squirrel.Mac complains about bad JSON otherwise URL="${BASE_URL}/update/macos/${LATEST// /%20}" jq -n --arg version "${VERSION:-$PKG_JSON_VERSION}" --arg url "$URL" ' { currentRelease: $version, releases: [{ version: $version, updateTo: { version: $version, url: $url, }, }], } ' > dist/releases.json jq -n --arg url "$URL" ' { url: $url } ' > dist/releases-legacy.json env: VERSION: ${{ inputs.version }} BASE_URL: ${{ inputs.base-url }} # We exclude mac-universal as the unpacked app takes forever to upload and zip and dmg already contains it - name: Upload Artifacts uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7 with: name: ${{ inputs.artifact-prefix }}macos path: | apps/desktop/dist !apps/desktop/dist/mac-universal/** retention-days: 1 - name: Assert zip is present if: contains(inputs.targets, 'zip') working-directory: apps/desktop run: | test -f ./dist/Element*-mac.zip - name: Assert dmg is present if: contains(inputs.targets, 'dmg') working-directory: apps/desktop run: | test -f ./dist/Element*.dmg test: name: Test macOS Universal needs: build if: inputs.test && contains(inputs.targets, 'dmg') uses: ./.github/workflows/build_desktop_test.yaml with: project: macos artifact: ${{ inputs.artifact-prefix }}macos runs-on: macos-14 executable: /Users/runner/Applications/Element*.app/Contents/MacOS/Element* # We need to mount the DMG and copy the app to the Applications folder as a mounted DMG is # read-only and thus would not allow us to override the fuses as is required for Playwright. prepare_cmd: | hdiutil attach ./dist/*.dmg -mountpoint /Volumes/Element && rsync -a /Volumes/Element/Element*.app ~/Applications/ && hdiutil detach /Volumes/Element blob_report: ${{ inputs.blob_report }} args: ${{ inputs.test-args }}