Skip to content

Instantly share code, notes, and snippets.

@maggie44
Last active January 23, 2023 21:31
Show Gist options
  • Save maggie44/a689fc01737f6a5fd72868f0f07e3d3e to your computer and use it in GitHub Desktop.
Save maggie44/a689fc01737f6a5fd72868f0f07e3d3e to your computer and use it in GitHub Desktop.
Apple and Windows Electron signing and notarising via Electron Builder and GitHub Actions.

Electron Builder Signing and Notarising Process via GitHub Actions (Apple and Windows)

This gist provides details of how to build, sign and notarize Electron apps for macOS and Windows using Electron Builder via a GitHub Action.

Step 1: Add Electron Builder aftersign process

Electron Builder takes care of a lot of the signing process. But part of the process to notarize the app with Apple involves submitting the app to Apple for review. This is done through the electron-notarize package. Add the following code to your repository as afterSignHook.js.

"use strict";

const { notarize } = require("@electron/notarize");
const {
  ELECTRON_SKIP_NOTARIZATION,
  XCODE_APP_LOADER_EMAIL,
  XCODE_APP_LOADER_PASSWORD,
} = process.env;

async function main(context) {
  const { electronPlatformName, appOutDir } = context;

  if (
    electronPlatformName !== "darwin" ||
    ELECTRON_SKIP_NOTARIZATION === "true" ||
    !XCODE_APP_LOADER_EMAIL ||
    !XCODE_APP_LOADER_PASSWORD
  ) {
    console.log("Skipping Apple notarization.");
    return;
  }

  console.log("Starting Apple notarization.");

  const appName = context.packager.appInfo.productFilename;

  await notarize({
    appBundleId: "io.balena.starterinterface",
    appPath: `${appOutDir}/${appName}.app`,
    appleId: XCODE_APP_LOADER_EMAIL,
    appleIdPassword: XCODE_APP_LOADER_PASSWORD,
  });
}

exports.default = main;

You will then need to add the following configuration to your Electron Builder config file:

"build": {
  "afterSign": "scripts/notarize.js"
}

Step 2: Configure your GitHub secrets

The following GitHub secrets need to be added for Apple signing. GitHub's own docs are helpful on generating these:

BUILD_CERTIFICATE_BASE64
P12_PASSWORD
BUILD_PROVISION_PROFILE_BASE64
KEYCHAIN_PASSWORD

Your Apple developer user account details also need adding into the following secrets. You can generate an App Specific password through your developer account and your username needs to be set the same as your developer account username (i.e. the email address you signed in with):

XCODE_APP_LOADER_EMAIL
XCODE_APP_LOADER_PASSWORD

Windows signing key secrets are required for Windows builds. Details on generating a Code Signing Certificate can be found on the Electron Builder site and Windows pages. Once generated, add the following secrets:

CSC_LINK # The base64-encoded data of the *.p12 or *.pfx certificate file.
CSC_KEY_PASSWORD # The password to decrypt the certificate given in CSC_LINK.

Step 3: Add your GitHub Action

The following GitHub action will build and deploy the built app as GitHub releases.

But first configure your Electron Builder config file as follows:

artifactName: 'Your App Name.${ext}',
mac: {
    target: 'dmg',
    artifactName: 'Your App Name ${arch}.${ext}'
}

Electron Builder will replace ${arch}.${ext} for you with the architecture and file extension of each build. Without this the Apple amd64 and aarch64 builds will have the same name and one will overwrite the other. Your installed application will still be set as your normal artifact name, it is only the .dmg installed file that is changed.

You will also need to replace the ELECTRON_OUTPUT_PATH env variable in the below with the output path of Electron Builder. This can vary depending on your setup.

Then insert the file in ./github/workflows/deploy.yml of your GitHub repository.

# deploy.yml
name: Deploy to production

on:
  push:
    # This limits the run of the workflow to when a tag is pushed
    tags:
      - "*"

env:
  ELECTRON_OUTPUT_PATH: "your/output/path"

jobs:
  build-electron-app:
      matrix:
        job_name: ["linux", "mac_amd64", "mac_arm64", "windows"]

        include:
          - job_name: linux
            os: ubuntu-latest

          - job_name: mac_amd64
            os: macos-latest
            apple_signing: true

          - job_name: mac_arm64
            os: macos-latest
            build_arch: --arch arm64
            apple_signing: true

          - job_name: windows
            os: windows-latest

    name: ${{ matrix.job_name }}

    runs-on: ${{ matrix.os }}

    steps:
      - uses: actions/checkout@v3

      - uses: actions/setup-node@v3
        with:
          node-version: "18.12.1"
          cache: "yarn"

      - name: Install the Apple certificates
        if: ${{ matrix.apple_signing }}
        env:
          BUILD_CERTIFICATE_BASE64: ${{ secrets.BUILD_CERTIFICATE_BASE64 }}
          P12_PASSWORD: ${{ secrets.P12_PASSWORD }}
          BUILD_PROVISION_PROFILE_BASE64: ${{ secrets.BUILD_PROVISION_PROFILE_BASE64 }}
          KEYCHAIN_PASSWORD: ${{ secrets.KEYCHAIN_PASSWORD }}

        run: |
          # create variables
          CERTIFICATE_PATH=$RUNNER_TEMP/build_certificate.p12
          PP_PATH=$RUNNER_TEMP/build_pp.mobileprovision
          KEYCHAIN_PATH=$RUNNER_TEMP/app-signing.keychain-db

          # import certificate and provisioning profile from secrets
          echo -n "$BUILD_CERTIFICATE_BASE64" | base64 --decode -o $CERTIFICATE_PATH
          echo -n "$BUILD_PROVISION_PROFILE_BASE64" | base64 --decode -o $PP_PATH

          # create temporary keychain
          security create-keychain -p "$KEYCHAIN_PASSWORD" $KEYCHAIN_PATH
          security set-keychain-settings -lut 21600 $KEYCHAIN_PATH
          security unlock-keychain -p "$KEYCHAIN_PASSWORD" $KEYCHAIN_PATH

          # import certificate to keychain
          security import $CERTIFICATE_PATH -P "$P12_PASSWORD" -A -t cert -f pkcs12 -k $KEYCHAIN_PATH
          security list-keychain -d user -s $KEYCHAIN_PATH

          # apply provisioning profile
          mkdir -p ~/Library/MobileDevice/Provisioning\ Profiles
          cp $PP_PATH ~/Library/MobileDevice/Provisioning\ Profiles

      - name: Install packages
        run: |
          yarn install --immutable

      - name: Build Electron App
        env:
          # Windows signing keys
          CSC_LINK: ${{ secrets.CSC_LINK }}
          CSC_KEY_PASSWORD: ${{ secrets.CSC_KEY_PASSWORD }}
          # Apple signing keys
          XCODE_APP_LOADER_EMAIL: ${{ secrets.XCODE_APP_LOADER_EMAIL }}
          XCODE_APP_LOADER_PASSWORD: ${{ secrets.XCODE_APP_LOADER_PASSWORD }}
        run: yarn build ${{ matrix.build_arch }}

      - name: Upload artifacts to GitHub temporary storage
        uses: actions/upload-artifact@v3
        with:
          name: ${{ matrix.job_name }}
          path: |
            ${{ env.ELECTRON_OUTPUT_PATH }}/*.exe
            ${{ env.ELECTRON_OUTPUT_PATH }}/*.dmg
            ${{ env.ELECTRON_OUTPUT_PATH }}/*.AppImage

  # An example deploy action. You can replace this with your own deployment actions or steps as required.
  create-release:
    needs: [build-electron-app]
    runs-on: ubuntu-22.04
    steps:
      - name: Download all workflow artifacts
        uses: actions/download-artifact@v3

      - name: Create production changeLog and release with the artifacts
        uses: "marvinpinto/action-automatic-releases@d68defdd11f9dcc7f52f35c1b7c236ee7513bcc1"
        with:
          repo_token: "${{ secrets.GITHUB_TOKEN }}"
          prerelease: false
          files: |
            mac_amd64/*
            mac_arm64/*
            linux/*
            windows/*
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment