Files
mail/.github/workflows/native-platforms.yml
T

423 lines
18 KiB
YAML

name: Native platform artifacts
on:
workflow_dispatch:
inputs:
include_android:
description: Build the experimental Android test APK
type: boolean
default: true
include_ios:
description: Also build an experimental unsigned iOS archive
type: boolean
default: false
push:
tags:
- "v*.*.*"
permissions:
contents: read
concurrency:
group: native-${{ github.ref }}
cancel-in-progress: false
env:
CARGO_TERM_COLOR: always
jobs:
quality:
name: Release quality gate
uses: ./.github/workflows/quality.yml
metadata:
name: Validate release version
runs-on: ubuntu-24.04
outputs:
version: ${{ steps.version.outputs.version }}
native_version: ${{ steps.version.outputs.native_version }}
prerelease: ${{ steps.version.outputs.prerelease }}
steps:
- uses: actions/checkout@fbc6f3992d24b796d5a048ff273f7fcc4a7b6c09 # v5
- name: Read version and check pushed tag
id: version
env:
IS_TAG_PUSH: ${{ github.event_name == 'push' && github.ref_type == 'tag' }}
run: |
if [[ "$IS_TAG_PUSH" == true ]]; then
python3 scripts/release-metadata.py --tag "$GITHUB_REF_NAME"
else
python3 scripts/release-metadata.py
fi
linux:
needs: metadata
name: Linux packages (x64)
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@fbc6f3992d24b796d5a048ff273f7fcc4a7b6c09 # v5
- name: Install native build dependencies
run: |
sudo apt-get update
sudo apt-get install -y \
appstream \
build-essential \
desktop-file-utils \
file \
libdbus-1-dev \
libfontconfig1-dev \
libx11-dev \
libx11-xcb-dev \
libxcb-shape0-dev \
libxcb-xfixes0-dev \
libxcursor-dev \
libxkbcommon-dev \
libxkbcommon-x11-dev \
pkg-config \
python3 \
wget \
xinput \
xvfb
- uses: dtolnay/rust-toolchain@4360b52568e2003a75bf9bc1d59f33a8e3fc893c # stable
with:
toolchain: stable
- uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2
with:
key: native-linux-x64
- name: Build AppImage
env:
APPIMAGE_EXTRACT_AND_RUN: "1"
FLECTAR_GOOGLE_DESKTOP_CLIENT_ID: ${{ secrets.FLECTAR_GOOGLE_DESKTOP_CLIENT_ID }}
FLECTAR_GOOGLE_DESKTOP_CLIENT_SECRET: ${{ secrets.FLECTAR_GOOGLE_DESKTOP_CLIENT_SECRET }}
FLECTAR_MICROSOFT_DESKTOP_CLIENT_ID: ${{ secrets.FLECTAR_MICROSOFT_DESKTOP_CLIENT_ID }}
run: ./scripts/build-appimage.sh
- name: Build Debian package
env:
FLECTAR_GOOGLE_DESKTOP_CLIENT_ID: ${{ secrets.FLECTAR_GOOGLE_DESKTOP_CLIENT_ID }}
FLECTAR_GOOGLE_DESKTOP_CLIENT_SECRET: ${{ secrets.FLECTAR_GOOGLE_DESKTOP_CLIENT_SECRET }}
FLECTAR_MICROSOFT_DESKTOP_CLIENT_ID: ${{ secrets.FLECTAR_MICROSOFT_DESKTOP_CLIENT_ID }}
run: ./scripts/build-deb.sh
- name: Validate desktop metadata
run: |
desktop-file-validate resources/com.flectar.mail.desktop
# Network checks make private pre-publication repositories look like
# broken URLs. Flathub performs its own online validation on submit.
appstreamcli validate --no-net resources/com.flectar.mail.metainfo.xml
- name: Benchmark startup and idle resources
run: |
xvfb-run -a ./scripts/benchmark-startup.py \
--binary target/release/flectar-mail \
--profile-cache-dir resources/benchmarks/warm-cache \
--rounds 3 \
--idle-seconds 3
- uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
with:
name: flectar-mail-linux-x64
path: |
target/appimage/*.AppImage
target/deb/*.deb
target/startup-benchmark/*.json
if-no-files-found: error
windows:
needs: metadata
name: Windows installer and portable executable (x64)
runs-on: windows-2025
steps:
- uses: actions/checkout@fbc6f3992d24b796d5a048ff273f7fcc4a7b6c09 # v5
- uses: dtolnay/rust-toolchain@4360b52568e2003a75bf9bc1d59f33a8e3fc893c # stable
with:
toolchain: stable
- uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2
with:
key: native-windows-x64
- name: Build release executable
shell: bash
env:
# Keep the portable build independent of an installed VC++ runtime.
# RUSTFLAGS replaces .cargo/config.toml, so retain its stack setting.
RUSTFLAGS: "-C target-feature=+crt-static -C link-arg=/STACK:8000000"
FLECTAR_GOOGLE_DESKTOP_CLIENT_ID: ${{ secrets.FLECTAR_GOOGLE_DESKTOP_CLIENT_ID }}
FLECTAR_GOOGLE_DESKTOP_CLIENT_SECRET: ${{ secrets.FLECTAR_GOOGLE_DESKTOP_CLIENT_SECRET }}
FLECTAR_MICROSOFT_DESKTOP_CLIENT_ID: ${{ secrets.FLECTAR_MICROSOFT_DESKTOP_CLIENT_ID }}
run: cargo build --locked --release --no-default-features --features remote-content --bin flectar-mail
- name: Package executable
shell: pwsh
run: |
New-Item -ItemType Directory -Force target/package/windows | Out-Null
New-Item -ItemType Directory -Force "target/package/windows/Licenses/Google Sans Flex" | Out-Null
New-Item -ItemType Directory -Force "target/package/windows/Licenses/Noto Emoji" | Out-Null
Copy-Item target/release/flectar-mail.exe target/package/windows/
Copy-Item README.md target/package/windows/
Copy-Item LICENSE target/package/windows/
Copy-Item LICENSES target/package/windows/LICENSES -Recurse
Copy-Item THIRD_PARTY_NOTICES.md target/package/windows/
Copy-Item resources/fonts/google-sans-flex/OFL.txt "target/package/windows/Licenses/Google Sans Flex/"
Copy-Item resources/fonts/google-sans-flex/README.md "target/package/windows/Licenses/Google Sans Flex/"
Copy-Item resources/fonts/noto-emoji/OFL.txt "target/package/windows/Licenses/Noto Emoji/"
Copy-Item resources/fonts/noto-emoji/README.md "target/package/windows/Licenses/Noto Emoji/"
Compress-Archive -Path target/package/windows/* -DestinationPath target/flectar-mail-windows-x64.zip -Force
- name: Install pinned Inno Setup compiler
shell: pwsh
run: |
$installer = Join-Path $env:RUNNER_TEMP 'innosetup.exe'
Invoke-WebRequest 'https://github.com/jrsoftware/issrc/releases/download/is-6_5_4/innosetup-6.5.4.exe' -OutFile $installer
$expected = 'fa73bf47a4da250d185d07561c2bfda387e5e20db77e4570004cf6a133cc10b1'
if ((Get-FileHash $installer -Algorithm SHA256).Hash -ne $expected) { throw 'Inno Setup checksum mismatch' }
$process = Start-Process $installer -ArgumentList '/VERYSILENT', '/SUPPRESSMSGBOXES', '/NORESTART', '/SP-' -Wait -PassThru
if ($process.ExitCode -ne 0) { throw "Inno Setup install failed: $($process.ExitCode)" }
- name: Build per-user Windows installer
shell: pwsh
env:
APP_VERSION: ${{ needs.metadata.outputs.version }}
NATIVE_VERSION: ${{ needs.metadata.outputs.native_version }}
run: |
$compiler = "${env:ProgramFiles(x86)}\Inno Setup 6\ISCC.exe"
if (-not (Test-Path $compiler)) { throw "Inno Setup 6 is missing from the runner" }
& $compiler "/DAppVersion=$env:APP_VERSION" "/DNativeVersion=$env:NATIVE_VERSION" platform/windows/installer.iss
if ($LASTEXITCODE -ne 0) { throw "Inno Setup failed with exit code $LASTEXITCODE" }
- name: Verify unattended installation and uninstall
shell: pwsh
run: |
$destination = Join-Path $env:RUNNER_TEMP 'Flectar Mail install test'
$installer = (Resolve-Path target/flectar-mail-windows-x64-setup.exe).Path
$arguments = @('/VERYSILENT', '/SUPPRESSMSGBOXES', '/NORESTART', "/DIR=`"$destination`"")
$process = Start-Process $installer -ArgumentList $arguments -Wait -PassThru
if ($process.ExitCode -ne 0) { throw "Installation failed: $($process.ExitCode)" }
if (-not (Test-Path "$destination/flectar-mail.exe")) { throw 'Installed executable is missing' }
$process = Start-Process "$destination/unins000.exe" -ArgumentList '/VERYSILENT', '/SUPPRESSMSGBOXES', '/NORESTART' -Wait -PassThru
if ($process.ExitCode -ne 0) { throw "Uninstall failed: $($process.ExitCode)" }
if (Test-Path "$destination/flectar-mail.exe") { throw 'Uninstall left the executable behind' }
- uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
with:
name: flectar-mail-windows-x64
path: |
target/flectar-mail-windows-x64.zip
target/flectar-mail-windows-x64-setup.exe
if-no-files-found: error
macos:
needs: metadata
name: macOS application (Apple Silicon)
runs-on: macos-15
steps:
- uses: actions/checkout@fbc6f3992d24b796d5a048ff273f7fcc4a7b6c09 # v5
- uses: dtolnay/rust-toolchain@4360b52568e2003a75bf9bc1d59f33a8e3fc893c # stable
with:
toolchain: stable
- uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2
with:
key: native-macos-arm64
- name: Build release application
env:
FLECTAR_GOOGLE_DESKTOP_CLIENT_ID: ${{ secrets.FLECTAR_GOOGLE_DESKTOP_CLIENT_ID }}
FLECTAR_GOOGLE_DESKTOP_CLIENT_SECRET: ${{ secrets.FLECTAR_GOOGLE_DESKTOP_CLIENT_SECRET }}
FLECTAR_MICROSOFT_DESKTOP_CLIENT_ID: ${{ secrets.FLECTAR_MICROSOFT_DESKTOP_CLIENT_ID }}
run: cargo build --locked --release --no-default-features --features remote-content --bin flectar-mail
- name: Create ad-hoc signed app bundle
run: ./scripts/package-macos.sh
- uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
with:
name: flectar-mail-macos-arm64
path: |
target/macos/flectar-mail-macos-arm64.zip
target/macos/flectar-mail-macos-arm64.dmg
if-no-files-found: error
android:
needs: metadata
if: >-
(github.event_name == 'push' && needs.metadata.outputs.prerelease == 'true') ||
(github.event_name == 'workflow_dispatch' && inputs.include_android)
name: Android test-signed APK (arm64)
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@fbc6f3992d24b796d5a048ff273f7fcc4a7b6c09 # v5
- uses: actions/setup-java@cf277c60eb25467037889841efdb72551f06f6c3 # v4
with:
distribution: temurin
java-version: "21"
- uses: dtolnay/rust-toolchain@4360b52568e2003a75bf9bc1d59f33a8e3fc893c # stable
with:
toolchain: stable
targets: aarch64-linux-android
- uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2
with:
workspaces: platform/android -> ../../target
key: native-android-arm64
- uses: gradle/actions/setup-gradle@ed408507eac070d1f99cc633dbcf757c94c7933a # v4
- name: Configure Android NDK compiler
run: |
toolchain="$ANDROID_NDK_HOME/toolchains/llvm/prebuilt/linux-x86_64/bin"
test -x "$toolchain/aarch64-linux-android26-clang"
{
echo "CC_aarch64_linux_android=$toolchain/aarch64-linux-android26-clang"
echo "CXX_aarch64_linux_android=$toolchain/aarch64-linux-android26-clang++"
echo "AR_aarch64_linux_android=$toolchain/llvm-ar"
echo "CARGO_TARGET_AARCH64_LINUX_ANDROID_LINKER=$toolchain/aarch64-linux-android26-clang"
} >> "$GITHUB_ENV"
- name: Restore persistent test signing key when configured
env:
TEST_KEYSTORE_BASE64: ${{ secrets.FLECTAR_ANDROID_TEST_KEYSTORE_BASE64 }}
run: |
if [[ -n "$TEST_KEYSTORE_BASE64" ]]; then
keystore="$RUNNER_TEMP/flectar-android-test.keystore"
umask 077
printf '%s' "$TEST_KEYSTORE_BASE64" | base64 --decode > "$keystore"
echo "FLECTAR_ANDROID_TEST_KEYSTORE=$keystore" >> "$GITHUB_ENV"
else
echo '::warning::No persistent Android test key configured. This APK will not update installations signed by a previous CI run.'
fi
- name: Build test-signed release APK
env:
FLECTAR_ANDROID_VERSION_NAME: ${{ needs.metadata.outputs.version }}
FLECTAR_ANDROID_VERSION_CODE: ${{ github.run_number }}
FLECTAR_GOOGLE_ANDROID_CLIENT_ID: ${{ secrets.FLECTAR_GOOGLE_ANDROID_CLIENT_ID }}
FLECTAR_MICROSOFT_ANDROID_CLIENT_ID: ${{ secrets.FLECTAR_MICROSOFT_ANDROID_CLIENT_ID }}
FLECTAR_MICROSOFT_ANDROID_REDIRECT_URI: ${{ secrets.FLECTAR_MICROSOFT_ANDROID_REDIRECT_URI }}
run: ./scripts/build-android-test-apk.sh
- name: Verify release APK manifest, ABI, and signature
env:
FLECTAR_ANDROID_VERSION_NAME: ${{ needs.metadata.outputs.version }}
FLECTAR_ANDROID_VERSION_CODE: ${{ github.run_number }}
run: ./scripts/verify-android-apk.sh
- name: Record public test signing identity
run: ./scripts/android-signing-identities.sh target/android/release/apk/flectar-mail.apk > target/android/release/apk/signing-identities.txt
- name: Remove restored signing key
if: always()
run: rm -f "$RUNNER_TEMP/flectar-android-test.keystore"
- uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
with:
name: flectar-mail-android-test-signed-arm64
path: |
target/android/release/apk/flectar-mail.apk
target/android/release/apk/signing-identities.txt
if-no-files-found: error
ios:
if: github.event_name == 'workflow_dispatch' && inputs.include_ios
name: iOS unsigned archive (arm64)
runs-on: macos-15
steps:
- uses: actions/checkout@fbc6f3992d24b796d5a048ff273f7fcc4a7b6c09 # v5
- uses: dtolnay/rust-toolchain@4360b52568e2003a75bf9bc1d59f33a8e3fc893c # stable
with:
toolchain: stable
targets: aarch64-apple-ios,aarch64-apple-ios-sim
- uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2
with:
key: native-ios-arm64
- name: Install Xcode project generator
run: brew install xcodegen
- name: Generate Xcode project
working-directory: platform/ios
run: xcodegen generate
- name: Build unsigned device archive
env:
FLECTAR_GOOGLE_DESKTOP_CLIENT_ID: ${{ secrets.FLECTAR_GOOGLE_DESKTOP_CLIENT_ID }}
FLECTAR_GOOGLE_DESKTOP_CLIENT_SECRET: ${{ secrets.FLECTAR_GOOGLE_DESKTOP_CLIENT_SECRET }}
FLECTAR_MICROSOFT_DESKTOP_CLIENT_ID: ${{ secrets.FLECTAR_MICROSOFT_DESKTOP_CLIENT_ID }}
run: |
mkdir -p target/ios
xcodebuild \
-project platform/ios/FlectarMail.xcodeproj \
-scheme FlectarMail \
-configuration Release \
-sdk iphoneos \
-destination 'generic/platform=iOS' \
-archivePath "$PWD/target/ios/FlectarMail.xcarchive" \
CODE_SIGNING_ALLOWED=NO \
archive
ditto -c -k --sequesterRsrc --keepParent \
target/ios/FlectarMail.xcarchive \
target/ios/flectar-mail-ios-unsigned-xcarchive.zip
- uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
with:
name: flectar-mail-ios-arm64-unsigned
path: target/ios/flectar-mail-ios-unsigned-xcarchive.zip
if-no-files-found: error
release:
name: Publish GitHub Release
# Explicit results let stable releases proceed with Android skipped while
# requiring a successful Android build for every prerelease.
if: >-
!cancelled() && github.event_name == 'push' && github.ref_type == 'tag' &&
needs.metadata.result == 'success' && needs.quality.result == 'success' &&
needs.linux.result == 'success' && needs.windows.result == 'success' &&
needs.macos.result == 'success' &&
(needs.android.result == 'success' ||
(needs.metadata.outputs.prerelease == 'false' && needs.android.result == 'skipped'))
needs: [metadata, quality, linux, windows, macos, android]
runs-on: ubuntu-24.04
permissions:
actions: read
contents: write
steps:
- uses: actions/checkout@fbc6f3992d24b796d5a048ff273f7fcc4a7b6c09 # v5
- name: Download Linux packages
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4
with:
name: flectar-mail-linux-x64
path: release-assets/linux
- name: Download Windows package
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4
with:
name: flectar-mail-windows-x64
path: release-assets/windows
- name: Download macOS package
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4
with:
name: flectar-mail-macos-arm64
path: release-assets/macos
- name: Download Android test APK
if: needs.metadata.outputs.prerelease == 'true'
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4
with:
name: flectar-mail-android-test-signed-arm64
path: release-assets/android
- name: Prepare release downloads
env:
RELEASE_VERSION: ${{ needs.metadata.outputs.version }}
run: python3 scripts/prepare-release.py
- name: Publish tagged release
env:
GH_TOKEN: ${{ github.token }}
GH_REPO: ${{ github.repository }}
RELEASE_PRERELEASE: ${{ needs.metadata.outputs.prerelease }}
run: bash scripts/publish-release.sh