A power on lightbulbA power off lightbulb

Sign and notarize MacOS electron app on Github Actions (part 2)

Sign and notarize MacOS electron app on Github Actions (part 2)

Sign and notarize MacOS electron app on Github Actions (part 2)

Sign and notarize MacOS electron app on Github Actions (part 2)

published on Ramiel's Creations on Feb 22, 2025

published on Ramiel's Creations on Feb 22, 2025

Sign and notarize MacOS electron app on Github Actions (part 2)

In the second part of this guide, we will focus on automating the process of signing an Electron app using GitHub Actions. This guide builds on the steps described in part one.

The Goal

Now that we can sign an Electron app on our device, it's time to automate the process using a GitHub Action. By the end of this guide, every time your build runs on GitHub, a new artifact will be created and posted in the "Release" section of your repository.

How To

To sign the app, the signing and notarizing tool needs access to a keychain containing your signing certificate. While this is straightforward on your machine using the Keychain app, it's different on GitHub Actions due to the different environment. Since the action runs on a macOS machine, we will use the CLI to copy all relevant certificates to the keychain location and create a keychain to read them.

The relevant part of our GitHub Action file is as follows:

- name: Install the Apple certificate and provisioning profile
  env:
    BUILD_CERTIFICATE_BASE64: ${{ secrets.BUILD_CERTIFICATE_BASE64 }}
    BUILD_CERTIFICATE_PASSWORD: ${{ secrets.BUILD_CERTIFICATE_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.provisionprofile
    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 "$BUILD_CERTIFICATE_PASSWORD" -A -t cert -f pkcs12 -k $KEYCHAIN_PATH
    security set-key-partition-list -S apple-tool:,apple: -k "$KEYCHAIN_PASSWORD" $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

Getting the Certificates

We need some information (the env section) to proceed. Specifically, we need the certificate itself, the certificate password, the profile, and the keychain password. These should be stored as secrets in the GitHub repository in base64 format. Let's retrieve all the necessary information.

  1. The Build Certificate and Its Password:

The team owner must export the certificate from their machine. Open your Keychain, right-click on the certificate (created in the first part of this guide), and select Export.

Export the certificate

You should get a .p12 file.

We need the base64 encoded version of it to store in our repository secret. Use the following command:

base64 -i Certificate.p12 -o -

This will output the content to stdout. Copy it and create a secret for your repository.

On GitHub, go to your repository, then Settings -> Secrets and Variables -> Actions.
Add a new Repository Secret, call it BUILD_CERTIFICATE_BASE64, and save the base64 version of your file.

Create another secret for the password of your certificate. Use the application password you created in the first part of this tutorial. Create another secret called BUILD_CERTIFICATE_PASSWORD and paste the plain password as the value.

  1. Build Provision Profile:

Visit your Apple Developer page and download your profile from the Profile page. If you don't have one, create a new profile of type Developer ID (as shown below), and then download it.

Create a provisioning profile

Export this file to its base64 version:

base64 -i My_profile.provisionprofile -o -

Save a new secret in your GitHub repository called BUILD_PROVISION_PROFILE_BASE64.

  1. Choose a Keychain Password:

Our keychain must be protected by a password. Choose a password and save it in a new secret variable called KEYCHAIN_PASSWORD.

Last Environment Variables

We need some more variables: APPLE_ID, APPLE_ID_PASSWORD, and APPLE_TEAM_ID. Refer to this section of the first part of the guide to find their values.

Complete GitHub Action File

Here is the complete GitHub Action file:

# Choose the name you prefer for the action
name: My app - build, sign, release

# Adjust this part to your requirements
on:
  push:
    branches:
      - main

jobs:
  release:
    runs-on: macos-latest

    env:
      APPLE_ID: ${{ secrets.APPLE_ID }}
      APPLE_ID_PASSWORD: ${{ secrets.APPLE_ID_PASSWORD }}
      APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}

    steps:
      - name: Check out Git repository
        uses: actions/checkout@v4

      - name: Install Node.js
        uses: actions/setup-node@v4
        with:
          node-version: 20
          cache: "npm"

      - name: Setup Python
        uses: actions/setup-python@v5
        with:
          python-version: "3.11"

      - name: Install Python dependencies
        run: python3 -m pip install setuptools --break-system-packages

      - name: Install the Apple certificate and provisioning profile
        env:
          BUILD_CERTIFICATE_BASE64: ${{ secrets.BUILD_CERTIFICATE_BASE64 }}
          BUILD_CERTIFICATE_PASSWORD: ${{ secrets.BUILD_CERTIFICATE_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.provisionprofile
          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 "$BUILD_CERTIFICATE_PASSWORD" -A -t cert -f pkcs12 -k $KEYCHAIN_PATH
          security set-key-partition-list -S apple-tool:,apple: -k "$KEYCHAIN_PASSWORD" $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 Dependencies
        run: pnpm install --shamefully-hoist

      - name: Build for macOS
        run: npm run build:mac

      - name: Release
        uses: softprops/action-gh-release@v2
        with:
          draft: true
          files: |
            dist/*.exe
            dist/@my-app/*.exe
            dist/*.dmg
            dist/@my-app/*.dmg
            dist/*.AppImage
            dist/@my-app/*.AppImage
            dist/*.deb
            dist/@my-app/*.deb

      - name: Clean up keychain and provisioning profile
        run: |
          security delete-keychain $RUNNER_TEMP/app-signing.keychain-db
          rm ~/Library/MobileDevice/Provisioning\ Profiles/build_pp.provisionprofile

You can adjust the GitHub Action to fit your needs, but the part about creating the keychain and the build step should be very close to what you need.


Notes

1 I may be wrong about the password to use for this step. I no longer have access to the original data, so please, if you find anything wrong here or in any other section, leave a comment. back to text

Share on: