Flutter Community
Published in

Flutter Community

Build, Sign and Deliver Flutter MacOS Desktop Applications on GitHub Actions

https://unsplash.com/photos/SRjZtxsK3Os
My research on the matter :)

Creating the Certificate for Signing

Adding the Certificate to Your Local Machine

Creating the GitHub Actions Steps

#.github/workflows/on_macos_deploy.yml
---
name: On MacOS Deploy
'on':
push:
branches:
- "main"

Adding Flutter and Configuring GitHub Actions for Flutter Desktop

#.github/workflows/on_macos_deploy.yml

jobs:
on-push-main:
runs-on: macos-latest
env:
MACOS_APP_RELEASE_PATH: build/macos/Build/Products/Release
#.github/workflows/on_macos_deploy.yml
steps:
- uses: actions/checkout@v2
with:
fetch-depth: 0
- uses: subosito/flutter-action@v1
with:
channel: dev
#.github/workflows/on_macos_deploy.yml
- name: Enable Macos
run: flutter config --enable-macos-desktop
- name: Build macOS app
run: flutter build macos --release

Signing the macOS Desktop Application

  1. Select your project in the project navigator.
  2. Select your app in the list of targets.
  3. Click “Build Settings”.
  4. Click “All”.
  5. Click “Levels”.
  6. Type “identity” into the search field.
base64 Certificates.p12 | pbcopy
- name: Codesign executable
env:
MACOS_CERTIFICATE: ${{ secrets.MACOS_CERTIFICATE }}
MACOS_CERTIFICATE_PWD: ${{ secrets.MACOS_CERTIFICATE_PWD }}
echo $MACOS_CERTIFICATE | base64 --decode > certificate.p12
#1
security create-keychain -p <YOUR-PASSWORD> build.keychain
#2
security default-keychain -s build.keychain
#3
security unlock-keychain -p <YOUR-PASSWORD> build.keychain
#4
security import certificate.p12 -k build.keychain -P $MACOS_CERTIFICATE_PWD -T /usr/bin/codesign
#5
security set-key-partition-list -S apple-tool:,apple:,codesign: -s -k <YOUR-PASSWORD> build.keychain
#6
security find-identity
#7
/usr/bin/codesign --force --deep -s <YOUR-IDENTITY> ./$MACOS_APP_RELEASE_PATH/<YOUR-APP-NAME>.app

Creating a .dmg file

- name: Create a dmg
run: |
echo "Install create-dmg"
brew install create-dmg
cd $MACOS_APP_RELEASE_PATH

create-dmg \
--volname "<YOUR-APP-NAME>" \
--window-pos 200 120 \
--window-size 800 529 \
--icon-size 130 \
--text-size 14 \
--icon "<YOUR-APP-NAME>.app" 260 250 \
--hide-extension "<YOUR-APP-NAME>.app" \
--app-drop-link 540 250 \
--hdiutil-quiet \
"<YOUR-APP-NAME>.dmg" \
"<YOUR-APP-NAME>.app"

Creating a GitHub Release, Uploading Artifacts, and BONUS: Generating a version number

- name: Create Release
id: create_release
uses: actions/create-release@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
tag_name: <YOUR-TAG-NAME>
release_name: Release <YOUR-RELEASE-NUMBER>
body: |
Whatever you want as release note.
draft: false
prerelease: false
- name: Upload Release Asset
id: upload-release-asset
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_path: <Asset-Path-to-dmg>
asset_name: <Name-of-the-output>.dmg
asset_content_type: application/octet-stream
- name: Create Version Number
id: versions #1
run: |
git fetch #2
VERSION_WITHOUT_SUFFIX="$(grep 'version:' pubspec.yaml | awk '{ print $2 }' | cut -d'+' -f 1)" #3
function parse_git_hash() {
git rev-list --count origin/main
} #4
MAIN_COUNT=$(parse_git_hash) #5
APP_VERSION="$VERSION_WITHOUT_SUFFIX+$MAIN_COUNT" #6
echo "::set-output name=version::$(echo $APP_VERSION)" #7
  1. When you create a GitHub Actions which outputs some value, you are required to add an id to the action. That is why you are adding this here as well.
  2. For getting the latest merged version of the branch, you need to fetch the latest updates.
  3. This step is necessary only if you have a version with x.x.x+x format.
    Versioning in Flutter happens over the pubspec.yaml file with a version number that is using semantic versioning. In your case, you will get the version and split the metadata part, and add your metadata.
  4. This is a simple function to get the number of commits to the main branch.
  5. Assigning the number of commits to a variable
  6. Creating the app version with the metadata you decided to use.
  7. Outputs the data defined in step 6 with the name version.
tag_name: ${{ steps.versions.outputs.version }}
release_name: Release ${{ steps.versions.outputs.version }}

--

--

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store