Skip to content

Build and Release App #23

Build and Release App

Build and Release App #23

name: Build and Release App
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
on:
workflow_dispatch:
inputs:
release_type:
description: "Release type"
required: true
default: "release"
type: choice
options:
- release
- prerelease
workflow_run:
workflows: ["Bump Version & Release"]
types:
- completed
jobs:
get-version:
runs-on: ubuntu-latest
outputs:
version: ${{ steps.get_version.outputs.version }}
prerelease: ${{ steps.get_version.outputs.prerelease }}
steps:
- uses: actions/checkout@v5
with:
fetch-depth: 0
- name: Get version
id: get_version
run: |
VERSION=$(cat pubspec.yaml | grep version: | awk '{print $2}')
VERSION="v$VERSION"
if [[ "${{ github.event_name }}" == "workflow_dispatch" && "${{ github.event.inputs.release_type }}" == "prerelease" ]]; then
PRERELEASE=true
else
PRERELEASE=false
fi
echo "version=$VERSION" >> $GITHUB_OUTPUT
echo "prerelease=$PRERELEASE" >> $GITHUB_OUTPUT
build:
needs: get-version
runs-on: ${{ matrix.os }}
strategy:
# One platform failing should not hide whether the others work.
fail-fast: false
matrix:
include:
- platform: android
os: ubuntu-latest
artifact: android
- platform: windows
os: windows-latest
artifact: windows
- platform: macos
os: macos-latest
artifact: macos
- platform: linux
os: ubuntu-latest
artifact: linux-amd64
steps:
- uses: actions/checkout@v5
with:
fetch-depth: 0
- name: Set up Flutter
uses: subosito/flutter-action@v2
with:
flutter-version: 3.47.0
# Flutter's Linux build needs the GTK toolchain, which the runner image
# does not carry by default.
- name: Install Linux build dependencies
if: matrix.platform == 'linux'
run: |
sudo apt-get update
sudo apt-get install -y clang cmake ninja-build pkg-config libgtk-3-dev liblzma-dev libstdc++-12-dev libsecret-1-dev libjsoncpp-dev
- name: Install dependencies
run: flutter pub get
# A single timestamp shared by every dart-define below, so all platforms
# in one release report the same build time.
- name: Stamp build time
shell: bash
run: echo "BUILD_TIME=$(date -u +%Y-%m-%dT%H:%M:%SZ)" >> "$GITHUB_ENV"
# Without a stable key the APK is signed with a debug key that runners
# regenerate on every run, so each release refuses to install over the last.
- name: Prepare Android signing
if: matrix.platform == 'android'
shell: bash
env:
KEYSTORE_BASE64: ${{ secrets.ANDROID_KEYSTORE_BASE64 }}
STORE_PASSWORD: ${{ secrets.ANDROID_KEYSTORE_PASSWORD }}
KEY_PASSWORD: ${{ secrets.ANDROID_KEY_PASSWORD }}
KEY_ALIAS: ${{ secrets.ANDROID_KEY_ALIAS }}
run: |
if [ -z "$KEYSTORE_BASE64" ]; then
echo "::warning::ANDROID_KEYSTORE_BASE64 is not set. The APK will be debug-signed and will not install over an existing Admincraft."
exit 0
fi
echo "$KEYSTORE_BASE64" | base64 -d > android/app/release.jks
{
echo "storeFile=release.jks"
echo "storePassword=$STORE_PASSWORD"
echo "keyAlias=$KEY_ALIAS"
echo "keyPassword=$KEY_PASSWORD"
} > android/key.properties
echo "Release signing configured."
- name: Build Android
if: matrix.platform == 'android'
run: >-
flutter build apk --release
--dart-define=ADMINCRAFT_BUILD_TIME=${{ env.BUILD_TIME }}
--dart-define=ADMINCRAFT_GOOGLE_WEB_CLIENT_ID=${{ secrets.ADMINCRAFT_GOOGLE_WEB_CLIENT_ID }}
- name: Build Windows
if: matrix.platform == 'windows'
run: >-
flutter build windows --release
--dart-define=ADMINCRAFT_BUILD_TIME=${{ env.BUILD_TIME }}
--dart-define=ADMINCRAFT_GOOGLE_DESKTOP_CLIENT_ID=${{ secrets.ADMINCRAFT_GOOGLE_DESKTOP_CLIENT_ID }}
--dart-define=ADMINCRAFT_GOOGLE_DESKTOP_CLIENT_SECRET=${{ secrets.ADMINCRAFT_GOOGLE_DESKTOP_CLIENT_SECRET }}
- name: Build macOS
if: matrix.platform == 'macos'
run: >-
flutter build macos --release
--dart-define=ADMINCRAFT_BUILD_TIME=${{ env.BUILD_TIME }}
--dart-define=ADMINCRAFT_GOOGLE_DESKTOP_CLIENT_ID=${{ secrets.ADMINCRAFT_GOOGLE_DESKTOP_CLIENT_ID }}
--dart-define=ADMINCRAFT_GOOGLE_DESKTOP_CLIENT_SECRET=${{ secrets.ADMINCRAFT_GOOGLE_DESKTOP_CLIENT_SECRET }}
- name: Build Linux
if: matrix.platform == 'linux'
run: >-
flutter build linux --release
--dart-define=ADMINCRAFT_BUILD_TIME=${{ env.BUILD_TIME }}
--dart-define=ADMINCRAFT_GOOGLE_DESKTOP_CLIENT_ID=${{ secrets.ADMINCRAFT_GOOGLE_DESKTOP_CLIENT_ID }}
--dart-define=ADMINCRAFT_GOOGLE_DESKTOP_CLIENT_SECRET=${{ secrets.ADMINCRAFT_GOOGLE_DESKTOP_CLIENT_SECRET }}
- name: Package Android
if: matrix.platform == 'android'
shell: bash
run: |
mkdir -p dist
mv build/app/outputs/flutter-apk/app-release.apk "dist/admincraft-${VERSION}-android.apk"
env:
VERSION: ${{ needs.get-version.outputs.version }}
# Portable zip plus an Inno Setup installer, so the download page offers
# the same pair as every other desktop platform.
# PowerShell rather than bash: Git bash rewrites arguments that look like
# paths, so ISCC received "//DSourceDir=/d/a/..." and rejected it.
- name: Package Windows
if: matrix.platform == 'windows'
shell: pwsh
run: |
$version = "${{ needs.get-version.outputs.version }}"
$dist = Join-Path $PWD 'dist'
$releaseDir = Join-Path $PWD 'build\windows\x64\runner\Release'
New-Item -ItemType Directory -Force $dist | Out-Null
Compress-Archive -Path (Join-Path $releaseDir '*') `
-DestinationPath (Join-Path $dist "admincraft-$version-windows-portable.zip")
# Inno Setup ships on the runner image, but the path has moved between
# image versions, so look for it before falling back to installing it.
$iscc = @(
"${env:ProgramFiles(x86)}\Inno Setup 6\ISCC.exe",
"$env:ProgramFiles\Inno Setup 6\ISCC.exe"
) | Where-Object { Test-Path $_ } | Select-Object -First 1
if (-not $iscc) {
choco install innosetup -y --no-progress
$iscc = "${env:ProgramFiles(x86)}\Inno Setup 6\ISCC.exe"
}
Write-Host "Using $iscc"
& $iscc "/DAppVersion=$($version.TrimStart('v'))" "/DSourceDir=$releaseDir" "/DOutputDir=$dist" 'windows\packaging\admincraft.iss'
if ($LASTEXITCODE -ne 0) { throw "ISCC failed with exit code $LASTEXITCODE" }
Move-Item (Join-Path $dist 'admincraft-setup.exe') `
(Join-Path $dist "admincraft-$version-windows-installer.exe")
# Unsigned: without an Apple Developer certificate Gatekeeper asks the user
# to allow the app on first launch. Signing would need a MAC_CSC_LINK
# secret and an Apple Developer account.
- name: Package macOS
if: matrix.platform == 'macos'
shell: bash
run: |
mkdir -p dist
APP=build/macos/Build/Products/Release/admincraft.app
BASE="dist/admincraft-${VERSION}-${{ matrix.artifact }}"
echo "Architectures in the built binary:"
lipo -archs "$APP/Contents/MacOS/admincraft" || true
ditto -c -k --keepParent "$APP" "${BASE}-portable.zip"
hdiutil create -volname Admincraft -srcfolder "$APP" -ov -format UDZO "${BASE}-installer.dmg"
env:
VERSION: ${{ needs.get-version.outputs.version }}
- name: Package Linux
if: matrix.platform == 'linux'
shell: bash
run: |
bash linux/packaging/build-packages.sh "$VERSION" build/linux/x64/release/bundle dist
env:
VERSION: ${{ needs.get-version.outputs.version }}
- name: Upload artifact
uses: actions/upload-artifact@v4
with:
name: admincraft-${{ needs.get-version.outputs.version }}-${{ matrix.artifact }}
path: dist/*
if-no-files-found: error
release:
needs: [get-version, build]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
with:
fetch-depth: 0
# Collected by pattern rather than one step per platform, so adding a
# platform to the matrix above needs no change here.
- name: Download all artifacts
uses: actions/download-artifact@v4
with:
pattern: admincraft-${{ needs.get-version.outputs.version }}-*
path: dist
merge-multiple: true
- name: List collected files
run: ls -lh dist
- name: Create GitHub Release
uses: softprops/action-gh-release@v1
with:
tag_name: ${{ needs.get-version.outputs.version }}
target_commitish: ${{ github.event_name == 'workflow_run' && github.event.workflow_run.head_sha || github.sha }}
name: Release ${{ needs.get-version.outputs.version }}
prerelease: ${{ needs.get-version.outputs.prerelease }}
generate_release_notes: true
files: dist/*
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Delete build artifacts from GitHub
uses: geekyeggo/delete-artifact@v5
with:
name: admincraft-${{ needs.get-version.outputs.version }}-*