mirror of
https://github.com/flectar/mail.git
synced 2026-11-11 17:57:54 +00:00
819 lines
34 KiB
YAML
819 lines
34 KiB
YAML
name: Native platform artifacts
|
|
|
|
on:
|
|
workflow_dispatch:
|
|
inputs:
|
|
include_linux:
|
|
description: Build Linux packages
|
|
type: boolean
|
|
default: true
|
|
include_windows:
|
|
description: Build Windows packages
|
|
type: boolean
|
|
default: true
|
|
include_macos:
|
|
description: Build macOS packages
|
|
type: boolean
|
|
default: true
|
|
run_quality:
|
|
description: Run the full quality suite (disable only for manual packaging checks)
|
|
type: boolean
|
|
default: true
|
|
include_android:
|
|
description: Build the signed Android APK
|
|
type: boolean
|
|
default: true
|
|
include_ios:
|
|
description: Build the unsigned iOS app
|
|
type: boolean
|
|
default: true
|
|
pull_request:
|
|
paths:
|
|
- ".github/workflows/native-platforms.yml"
|
|
- "Cargo.lock"
|
|
- "Cargo.toml"
|
|
- "build.rs"
|
|
- "packaging/**"
|
|
- "resources/**"
|
|
- "scripts/**"
|
|
push:
|
|
tags:
|
|
- "v*.*.*"
|
|
|
|
permissions:
|
|
contents: read
|
|
|
|
concurrency:
|
|
group: native-${{ github.ref }}
|
|
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
|
|
|
|
env:
|
|
CARGO_TERM_COLOR: always
|
|
|
|
jobs:
|
|
quality:
|
|
needs: metadata
|
|
if: github.event_name == 'push' || inputs.run_quality
|
|
name: Release quality gate
|
|
uses: ./.github/workflows/quality.yml
|
|
|
|
metadata:
|
|
name: Release preflight
|
|
runs-on: ubuntu-22.04
|
|
outputs:
|
|
version: ${{ steps.version.outputs.version }}
|
|
native_version: ${{ steps.version.outputs.native_version }}
|
|
msix_version: ${{ steps.version.outputs.msix_version }}
|
|
prerelease: ${{ steps.version.outputs.prerelease }}
|
|
source_date_epoch: ${{ steps.source.outputs.source_date_epoch }}
|
|
steps:
|
|
- uses: actions/checkout@fbc6f3992d24b796d5a048ff273f7fcc4a7b6c09 # v5
|
|
- name: Use the source commit timestamp
|
|
id: source
|
|
run: |
|
|
epoch="$(git show -s --format=%ct HEAD)"
|
|
echo "SOURCE_DATE_EPOCH=$epoch" >> "$GITHUB_ENV"
|
|
echo "source_date_epoch=$epoch" >> "$GITHUB_OUTPUT"
|
|
- uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6
|
|
with:
|
|
python-version: "3.11"
|
|
- name: Install preflight tools
|
|
run: |
|
|
sudo apt-get update
|
|
sudo apt-get install -y appstream desktop-file-utils dpkg-dev gettext
|
|
- name: Check release metadata and packaging
|
|
id: version
|
|
env:
|
|
IS_TAG_PUSH: ${{ github.event_name == 'push' && github.ref_type == 'tag' }}
|
|
run: |
|
|
if [[ "$IS_TAG_PUSH" == true ]]; then
|
|
bash scripts/check-release.sh --tag "$GITHUB_REF_NAME"
|
|
else
|
|
bash scripts/check-release.sh
|
|
fi
|
|
- name: Require complete Spanish and Russian translations
|
|
if: github.event_name == 'push' && github.ref_type == 'tag'
|
|
run: bash scripts/check-translation-completeness.sh es ru
|
|
|
|
linux:
|
|
needs: metadata
|
|
if: github.event_name == 'push' || github.event_name == 'pull_request' || inputs.include_linux
|
|
name: Linux packages (${{ matrix.arch }})
|
|
strategy:
|
|
fail-fast: false
|
|
matrix:
|
|
include:
|
|
# Ubuntu 22.04 keeps the glibc 2.35 baseline on both architectures.
|
|
- arch: x64
|
|
runner: ubuntu-22.04
|
|
- arch: arm64
|
|
runner: ubuntu-22.04-arm
|
|
runs-on: ${{ matrix.runner }}
|
|
env:
|
|
SOURCE_DATE_EPOCH: ${{ needs.metadata.outputs.source_date_epoch }}
|
|
steps:
|
|
- uses: actions/checkout@fbc6f3992d24b796d5a048ff273f7fcc4a7b6c09 # v5
|
|
- uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6
|
|
with:
|
|
python-version: "3.11"
|
|
|
|
- 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:
|
|
cache-on-failure: true
|
|
key: native-linux-${{ matrix.arch }}
|
|
# Every pull request can restore caches saved on main. Caches saved by
|
|
# pull requests or tags only serve those, and they push main's out of
|
|
# the 10 GB cache limit.
|
|
save-if: ${{ github.ref == 'refs/heads/main' }}
|
|
|
|
- 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: Build release executable
|
|
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 }}
|
|
# Release files tell of newer releases; stores update the app themselves.
|
|
FLECTAR_UPDATE_CHECKS: github
|
|
run: cargo build --locked --release --no-default-features --features remote-content,gpu-renderer --bin flectar-mail
|
|
|
|
- name: Build AppImage
|
|
env:
|
|
APPIMAGE_EXTRACT_AND_RUN: "1"
|
|
FLECTAR_SKIP_BUILD: "1"
|
|
run: ./scripts/build-appimage.sh
|
|
|
|
- name: Build Debian package
|
|
env:
|
|
FLECTAR_SKIP_BUILD: "1"
|
|
run: ./scripts/build-deb.sh
|
|
|
|
- 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-${{ matrix.arch }}
|
|
path: |
|
|
target/appimage/*.AppImage
|
|
target/deb/*.deb
|
|
target/startup-benchmark/*.json
|
|
if-no-files-found: error
|
|
|
|
rpm:
|
|
needs: metadata
|
|
if: github.event_name == 'push' || github.event_name == 'pull_request' || inputs.include_linux
|
|
name: Fedora RPM build (${{ matrix.arch }}, Fedora 43 baseline)
|
|
strategy:
|
|
fail-fast: false
|
|
matrix:
|
|
include:
|
|
- arch: x64
|
|
runner: ubuntu-24.04
|
|
- arch: arm64
|
|
runner: ubuntu-24.04-arm
|
|
runs-on: ${{ matrix.runner }}
|
|
# Build against the oldest supported Fedora; test this exact artifact below.
|
|
container: fedora:43
|
|
env:
|
|
SOURCE_DATE_EPOCH: ${{ needs.metadata.outputs.source_date_epoch }}
|
|
steps:
|
|
- name: Install Fedora build dependencies
|
|
run: |
|
|
dnf install -y \
|
|
appstream \
|
|
dbus-daemon \
|
|
dbus-devel \
|
|
desktop-file-utils \
|
|
findutils \
|
|
fontconfig-devel \
|
|
gcc \
|
|
gcc-c++ \
|
|
git \
|
|
libX11-devel \
|
|
libXcursor-devel \
|
|
libxcb-devel \
|
|
libxkbcommon-devel \
|
|
libxkbcommon-x11-devel \
|
|
make \
|
|
pkgconf-pkg-config \
|
|
python3 \
|
|
rpm-build \
|
|
tar \
|
|
wayland-devel \
|
|
xorg-x11-server-Xvfb
|
|
- uses: actions/checkout@fbc6f3992d24b796d5a048ff273f7fcc4a7b6c09 # v5
|
|
- uses: dtolnay/rust-toolchain@4360b52568e2003a75bf9bc1d59f33a8e3fc893c # stable
|
|
with:
|
|
toolchain: stable
|
|
- uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2
|
|
with:
|
|
cache-on-failure: true
|
|
key: native-fedora-43-rpm-${{ matrix.arch }}
|
|
save-if: ${{ github.ref == 'refs/heads/main' }}
|
|
- name: Build Fedora release executable
|
|
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 }}
|
|
# Release files tell of newer releases; stores update the app themselves.
|
|
FLECTAR_UPDATE_CHECKS: github
|
|
run: cargo build --locked --release --no-default-features --features remote-content,gpu-renderer --bin flectar-mail
|
|
- name: Build and inspect RPM
|
|
env:
|
|
FLECTAR_SKIP_BUILD: "1"
|
|
run: |
|
|
./scripts/build-rpm.sh
|
|
rpm -qip target/rpm/flectar-mail.rpm
|
|
rpm -qlp target/rpm/flectar-mail.rpm
|
|
- uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
|
|
with:
|
|
name: flectar-mail-rpm-${{ matrix.arch }}
|
|
path: target/rpm/flectar-mail.rpm
|
|
if-no-files-found: error
|
|
|
|
rpm-test:
|
|
needs: rpm
|
|
name: RPM runtime (Fedora ${{ matrix.fedora }}, ${{ matrix.arch }})
|
|
runs-on: ${{ matrix.runner }}
|
|
strategy:
|
|
fail-fast: false
|
|
matrix:
|
|
fedora: [43, 44]
|
|
arch: [x64, arm64]
|
|
include:
|
|
- arch: x64
|
|
runner: ubuntu-24.04
|
|
- arch: arm64
|
|
runner: ubuntu-24.04-arm
|
|
steps:
|
|
- uses: actions/checkout@fbc6f3992d24b796d5a048ff273f7fcc4a7b6c09 # v5
|
|
- uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4
|
|
with:
|
|
name: flectar-mail-rpm-${{ matrix.arch }}
|
|
path: target/rpm
|
|
- name: Test published RPM in a clean Fedora container
|
|
shell: bash
|
|
env:
|
|
FEDORA_VERSION: ${{ matrix.fedora }}
|
|
run: |
|
|
mkdir -p target/rpm-test
|
|
docker run --rm \
|
|
--mount "type=bind,source=$GITHUB_WORKSPACE,target=/work,readonly" \
|
|
--mount "type=bind,source=$GITHUB_WORKSPACE/target/rpm-test,target=/results" \
|
|
"fedora:$FEDORA_VERSION" \
|
|
bash /work/scripts/test-rpm.sh /work/target/rpm/flectar-mail.rpm /results \
|
|
2>&1 | tee target/rpm-test/transaction.log
|
|
- name: Preserve RPM runtime diagnostics
|
|
if: always()
|
|
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
|
|
with:
|
|
name: rpm-test-fedora-${{ matrix.fedora }}-${{ matrix.arch }}
|
|
path: target/rpm-test
|
|
if-no-files-found: error
|
|
|
|
flatpak:
|
|
needs: metadata
|
|
if: github.event_name == 'push' || github.event_name == 'pull_request' || inputs.include_linux
|
|
name: Flatpak preview bundle (${{ matrix.arch }})
|
|
strategy:
|
|
fail-fast: false
|
|
matrix:
|
|
include:
|
|
- arch: x64
|
|
runner: ubuntu-24.04
|
|
- arch: arm64
|
|
runner: ubuntu-24.04-arm
|
|
runs-on: ${{ matrix.runner }}
|
|
env:
|
|
SOURCE_DATE_EPOCH: ${{ needs.metadata.outputs.source_date_epoch }}
|
|
steps:
|
|
- uses: actions/checkout@fbc6f3992d24b796d5a048ff273f7fcc4a7b6c09 # v5
|
|
- name: Install Flatpak build tools
|
|
run: |
|
|
sudo apt-get update
|
|
sudo apt-get install -y appstream dbus-x11 desktop-file-utils flatpak flatpak-builder xvfb
|
|
flatpak remote-add --user --if-not-exists flathub https://flathub.org/repo/flathub.flatpakrepo
|
|
flatpak install --user --noninteractive -y flathub \
|
|
org.freedesktop.Platform//25.08 \
|
|
org.freedesktop.Sdk//25.08 \
|
|
org.freedesktop.Sdk.Extension.rust-stable//25.08
|
|
- name: Build Flatpak bundle from locked sources
|
|
run: ./scripts/build-flatpak.sh
|
|
- name: Test Flatpak installation and packaged runtime
|
|
env:
|
|
EXPECTED_VERSION: ${{ needs.metadata.outputs.version }}
|
|
run: |
|
|
flatpak install --user --noninteractive -y target/flatpak/flectar-mail.flatpak
|
|
flatpak run --command=sh --env=EXPECTED_VERSION="$EXPECTED_VERSION" com.flectar.mail -c '
|
|
test -x /app/bin/flectar-mail
|
|
test -f /app/lib/flectar-mail/libpdfium.so
|
|
test -x /usr/bin/gpg
|
|
test -f /app/share/applications/com.flectar.mail.desktop
|
|
test -f /app/share/metainfo/com.flectar.mail.metainfo.xml
|
|
! grep -q X-AppImage /app/share/applications/com.flectar.mail.desktop
|
|
grep -q "version=\"$EXPECTED_VERSION\"" /app/share/metainfo/com.flectar.mail.metainfo.xml
|
|
'
|
|
flatpak info --user --show-permissions com.flectar.mail
|
|
set +e
|
|
timeout 15s dbus-run-session -- xvfb-run -a flatpak run com.flectar.mail \
|
|
> target/flatpak/launch.log 2>&1
|
|
status=$?
|
|
set -e
|
|
if [[ "$status" -ne 124 ]]; then
|
|
cat target/flatpak/launch.log
|
|
echo "Flatpak launch smoke test exited unexpectedly with status $status" >&2
|
|
exit 1
|
|
fi
|
|
flatpak uninstall --user --noninteractive -y com.flectar.mail
|
|
- uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
|
|
with:
|
|
name: flectar-mail-flatpak-${{ matrix.arch }}
|
|
path: target/flatpak/flectar-mail.flatpak
|
|
if-no-files-found: error
|
|
|
|
windows:
|
|
needs: metadata
|
|
if: github.event_name == 'push' || inputs.include_windows
|
|
name: Windows packages (${{ matrix.arch }})
|
|
strategy:
|
|
fail-fast: false
|
|
matrix:
|
|
include:
|
|
- arch: x64
|
|
runner: windows-2025
|
|
- arch: arm64
|
|
runner: windows-11-arm
|
|
runs-on: ${{ matrix.runner }}
|
|
steps:
|
|
- uses: actions/checkout@fbc6f3992d24b796d5a048ff273f7fcc4a7b6c09 # v5
|
|
- uses: dtolnay/rust-toolchain@4360b52568e2003a75bf9bc1d59f33a8e3fc893c # stable
|
|
with:
|
|
toolchain: stable
|
|
- uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2
|
|
with:
|
|
cache-on-failure: true
|
|
key: native-windows-${{ matrix.arch }}
|
|
save-if: ${{ github.ref == 'refs/heads/main' }}
|
|
|
|
- name: Put Clang on the path
|
|
if: matrix.arch == 'arm64'
|
|
shell: pwsh
|
|
run: |
|
|
# ring and aws-lc compile their Arm64 Windows sources with Clang.
|
|
if (-not (Get-Command clang -ErrorAction SilentlyContinue)) {
|
|
$llvm = Join-Path $env:ProgramFiles 'LLVM\bin'
|
|
if (-not (Test-Path (Join-Path $llvm 'clang.exe'))) { throw 'LLVM is missing from the runner' }
|
|
Add-Content $env:GITHUB_PATH $llvm
|
|
}
|
|
|
|
- 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 }}
|
|
# Release files tell of newer releases; stores update the app themselves.
|
|
FLECTAR_UPDATE_CHECKS: github
|
|
run: |
|
|
mkdir -p target
|
|
if cargo build --locked --release --no-default-features --features remote-content,gpu-renderer --bin flectar-mail 2>&1 | tee target/windows-build.log; then
|
|
exit 0
|
|
fi
|
|
# Keep the compiler's final diagnostics available in the check result,
|
|
# including when a maintainer cannot download authenticated run logs.
|
|
python - <<'PY'
|
|
from pathlib import Path
|
|
details = "\n".join(Path("target/windows-build.log").read_text(errors="replace").splitlines()[-80:])
|
|
details = details[-16000:].replace("%", "%25").replace("\r", "%0D").replace("\n", "%0A")
|
|
print(f"::error title=Windows compilation failed::{details}")
|
|
PY
|
|
exit 1
|
|
|
|
- name: Package executable
|
|
shell: pwsh
|
|
env:
|
|
ARCH: ${{ matrix.arch }}
|
|
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
|
|
python scripts/stage-pdfium.py "win-$env:ARCH" target/package/windows
|
|
if ($LASTEXITCODE -ne 0) { throw "PDFium staging failed" }
|
|
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 -Force
|
|
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/"
|
|
python scripts/test-pdf-preview.py target/package/windows/flectar-mail.exe
|
|
if ($LASTEXITCODE -ne 0) { throw "Packaged PDF preview test failed" }
|
|
Compress-Archive -Path target/package/windows/* -DestinationPath "target/flectar-mail-windows-$env:ARCH.zip" -Force
|
|
|
|
- name: Close the packaged app from its window
|
|
shell: pwsh
|
|
run: |
|
|
python scripts/test-clean-exit.py target/package/windows/flectar-mail.exe
|
|
if ($LASTEXITCODE -ne 0) { throw "The packaged app did not exit cleanly" }
|
|
|
|
- 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 }}
|
|
ARCH: ${{ matrix.arch }}
|
|
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" "/DArch=$env:ARCH" platform/windows/installer.iss
|
|
if ($LASTEXITCODE -ne 0) { throw "Inno Setup failed with exit code $LASTEXITCODE" }
|
|
|
|
- name: Verify unattended installation and uninstall
|
|
shell: pwsh
|
|
env:
|
|
ARCH: ${{ matrix.arch }}
|
|
run: |
|
|
$destination = Join-Path $env:RUNNER_TEMP 'Flectar Mail install test'
|
|
$installer = (Resolve-Path "target/flectar-mail-windows-$env:ARCH-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' }
|
|
|
|
- name: Build Microsoft Store package
|
|
shell: pwsh
|
|
env:
|
|
ARCH: ${{ matrix.arch }}
|
|
MSIX_VERSION: ${{ needs.metadata.outputs.msix_version }}
|
|
run: |
|
|
python scripts/build-msix.py pack --arch $env:ARCH --version $env:MSIX_VERSION target/package/windows "target/msix/flectar-mail-$env:ARCH.msix"
|
|
if ($LASTEXITCODE -ne 0) { throw "Store packaging failed" }
|
|
|
|
- name: Install, start and remove the Store package
|
|
shell: pwsh
|
|
env:
|
|
ARCH: ${{ matrix.arch }}
|
|
run: |
|
|
powershell -NoProfile -ExecutionPolicy Bypass -File scripts/test-msix.ps1 -Package "target/msix/flectar-mail-$env:ARCH.msix"
|
|
if ($LASTEXITCODE -ne 0) { throw "Store package test failed" }
|
|
|
|
- uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
|
|
with:
|
|
name: msix-${{ matrix.arch }}
|
|
path: target/msix/flectar-mail-${{ matrix.arch }}.msix
|
|
if-no-files-found: error
|
|
|
|
- uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
|
|
with:
|
|
name: flectar-mail-windows-${{ matrix.arch }}
|
|
path: |
|
|
target/flectar-mail-windows-${{ matrix.arch }}.zip
|
|
target/flectar-mail-windows-${{ matrix.arch }}-setup.exe
|
|
if-no-files-found: error
|
|
|
|
microsoft-store:
|
|
needs: [metadata, windows]
|
|
name: Microsoft Store bundle
|
|
runs-on: windows-2025
|
|
steps:
|
|
- uses: actions/checkout@fbc6f3992d24b796d5a048ff273f7fcc4a7b6c09 # v5
|
|
- uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4
|
|
with:
|
|
pattern: msix-*
|
|
path: target/msix
|
|
merge-multiple: true
|
|
|
|
# Partner Center takes this one file and serves each device its architecture.
|
|
- name: Bundle the Store packages
|
|
shell: pwsh
|
|
env:
|
|
APP_VERSION: ${{ needs.metadata.outputs.version }}
|
|
MSIX_VERSION: ${{ needs.metadata.outputs.msix_version }}
|
|
run: |
|
|
python scripts/build-msix.py bundle --version $env:MSIX_VERSION "target/flectar-mail-$env:APP_VERSION.msixbundle" target/msix/flectar-mail-x64.msix target/msix/flectar-mail-arm64.msix
|
|
if ($LASTEXITCODE -ne 0) { throw "Store bundling failed" }
|
|
|
|
- name: Install, start and remove the Store bundle
|
|
shell: pwsh
|
|
env:
|
|
APP_VERSION: ${{ needs.metadata.outputs.version }}
|
|
run: |
|
|
powershell -NoProfile -ExecutionPolicy Bypass -File scripts/test-msix.ps1 -Package "target/flectar-mail-$env:APP_VERSION.msixbundle"
|
|
if ($LASTEXITCODE -ne 0) { throw "Store bundle test failed" }
|
|
|
|
- uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
|
|
with:
|
|
name: microsoft-store-package
|
|
path: target/flectar-mail-${{ needs.metadata.outputs.version }}.msixbundle
|
|
if-no-files-found: error
|
|
|
|
macos:
|
|
needs: metadata
|
|
if: github.event_name == 'push' || inputs.include_macos
|
|
name: macOS application (${{ matrix.name }})
|
|
strategy:
|
|
fail-fast: false
|
|
matrix:
|
|
include:
|
|
- name: Apple silicon
|
|
runner: macos-15
|
|
architecture: arm64
|
|
- name: Intel
|
|
runner: macos-15-intel
|
|
architecture: x64
|
|
runs-on: ${{ matrix.runner }}
|
|
env:
|
|
MACOSX_DEPLOYMENT_TARGET: "14.0"
|
|
steps:
|
|
- uses: actions/checkout@fbc6f3992d24b796d5a048ff273f7fcc4a7b6c09 # v5
|
|
- uses: dtolnay/rust-toolchain@4360b52568e2003a75bf9bc1d59f33a8e3fc893c # stable
|
|
with:
|
|
toolchain: stable
|
|
- uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2
|
|
with:
|
|
cache-on-failure: true
|
|
key: native-macos-${{ matrix.architecture }}
|
|
save-if: ${{ github.ref == 'refs/heads/main' }}
|
|
|
|
- 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 }}
|
|
# Release files tell of newer releases; stores update the app themselves.
|
|
FLECTAR_UPDATE_CHECKS: github
|
|
run: cargo build --locked --release --no-default-features --features remote-content,gpu-renderer --bin flectar-mail
|
|
|
|
- name: Create ad-hoc signed app bundle
|
|
run: |
|
|
mkdir -p target/macos
|
|
if ./scripts/package-macos.sh \
|
|
target/release/flectar-mail target/macos \
|
|
"${{ matrix.architecture }}" 2>&1 | tee target/macos/package.log; then
|
|
exit 0
|
|
fi
|
|
python3 - <<'PY'
|
|
from pathlib import Path
|
|
details = "\n".join(Path("target/macos/package.log").read_text(errors="replace").splitlines()[-80:])
|
|
details = details[-16000:].replace("%", "%25").replace("\r", "%0D").replace("\n", "%0A")
|
|
print(f"::error title=macOS packaging failed::{details}")
|
|
PY
|
|
exit 1
|
|
|
|
- uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
|
|
with:
|
|
name: flectar-mail-macos-${{ matrix.architecture }}
|
|
path: |
|
|
target/macos/flectar-mail-macos-${{ matrix.architecture }}.zip
|
|
target/macos/flectar-mail-macos-${{ matrix.architecture }}.dmg
|
|
if-no-files-found: error
|
|
|
|
android:
|
|
needs: metadata
|
|
if: >-
|
|
github.event_name == 'push' ||
|
|
(github.event_name == 'workflow_dispatch' && inputs.include_android)
|
|
name: Android signed APK (arm64)
|
|
runs-on: ubuntu-24.04
|
|
environment: android-release
|
|
steps:
|
|
- uses: actions/checkout@fbc6f3992d24b796d5a048ff273f7fcc4a7b6c09 # v5
|
|
- uses: actions/setup-java@cf277c60eb25467037889841efdb72551f06f6c3 # v4
|
|
with:
|
|
distribution: temurin
|
|
java-version: "21"
|
|
|
|
- name: Restore shared app signing key
|
|
env:
|
|
FLECTAR_ANDROID_KEYSTORE_BASE64: ${{ secrets.FLECTAR_ANDROID_KEYSTORE_BASE64 }}
|
|
FLECTAR_ANDROID_KEYSTORE_PASSWORD: ${{ secrets.FLECTAR_ANDROID_KEYSTORE_PASSWORD }}
|
|
FLECTAR_ANDROID_KEY_ALIAS: ${{ secrets.FLECTAR_ANDROID_KEY_ALIAS }}
|
|
FLECTAR_ANDROID_KEY_PASSWORD: ${{ secrets.FLECTAR_ANDROID_KEY_PASSWORD }}
|
|
run: bash scripts/restore-android-keystore.sh
|
|
|
|
- uses: dtolnay/rust-toolchain@4360b52568e2003a75bf9bc1d59f33a8e3fc893c # stable
|
|
with:
|
|
toolchain: stable
|
|
targets: aarch64-linux-android
|
|
- uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2
|
|
with:
|
|
workspaces: platform/android -> ../../target
|
|
cache-on-failure: true
|
|
key: native-android-arm64
|
|
save-if: ${{ github.ref == 'refs/heads/main' }}
|
|
|
|
- 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: Build signed release APK
|
|
env:
|
|
RELEASE_PRERELEASE: ${{ needs.metadata.outputs.prerelease }}
|
|
FLECTAR_ANDROID_VERSION_NAME: ${{ needs.metadata.outputs.version }}
|
|
FLECTAR_ANDROID_KEYSTORE_PASSWORD: ${{ secrets.FLECTAR_ANDROID_KEYSTORE_PASSWORD }}
|
|
FLECTAR_ANDROID_KEY_ALIAS: ${{ secrets.FLECTAR_ANDROID_KEY_ALIAS }}
|
|
FLECTAR_ANDROID_KEY_PASSWORD: ${{ secrets.FLECTAR_ANDROID_KEY_PASSWORD }}
|
|
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 }}
|
|
# The APK on GitHub tells of newer releases.
|
|
FLECTAR_UPDATE_CHECKS: github
|
|
run: |
|
|
args=()
|
|
if [[ "$RELEASE_PRERELEASE" == true ]]; then args+=(--preview); fi
|
|
bash scripts/build-android-release.sh "${args[@]}"
|
|
|
|
- name: Verify release APK manifest, ABI, and signature
|
|
env:
|
|
FLECTAR_ANDROID_VERSION_NAME: ${{ needs.metadata.outputs.version }}
|
|
run: bash scripts/verify-android-apk.sh target/android/release/flectar-mail.apk production
|
|
|
|
- name: Record public app signing identity
|
|
run: ./scripts/android-signing-identities.sh target/android/release/flectar-mail.apk > target/android/release/signing-identities.txt
|
|
|
|
- name: Remove restored signing key
|
|
if: always()
|
|
run: |
|
|
if [[ -n "${FLECTAR_ANDROID_KEYSTORE:-}" ]]; then rm -f "$FLECTAR_ANDROID_KEYSTORE"; fi
|
|
|
|
- name: Enable Android emulator acceleration
|
|
run: sudo chmod a+rw /dev/kvm
|
|
|
|
- name: Exercise packaged JNI callbacks
|
|
uses: reactivecircus/android-emulator-runner@a421e43855164a8197daf9d8d40fe71c6996bb0d # v2
|
|
env:
|
|
FLECTAR_ANDROID_KEYSTORE: ""
|
|
with:
|
|
# This Google APIs image supports ARM64 native translation, so tests
|
|
# exercise the same stripped Rust library staged for the release APK.
|
|
api-level: 30
|
|
target: google_apis
|
|
arch: x86_64
|
|
disable-animations: true
|
|
script: ./platform/android/gradle/gradlew --no-daemon -p platform/android/gradle :app:connectedDebugAndroidTest
|
|
|
|
- name: Upload JNI test results
|
|
if: always()
|
|
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
|
|
with:
|
|
name: android-jni-test-results
|
|
path: |
|
|
target/android/gradle/app/reports/androidTests/
|
|
target/android/gradle/app/outputs/androidTest-results/
|
|
|
|
- uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
|
|
with:
|
|
name: flectar-mail-android-signed-arm64
|
|
path: |
|
|
target/android/release/flectar-mail.apk
|
|
target/android/release/signing-identities.txt
|
|
if-no-files-found: error
|
|
|
|
ios:
|
|
needs: metadata
|
|
if: github.event_name == 'push' || inputs.include_ios
|
|
name: iOS app (arm64, unsigned)
|
|
runs-on: macos-15
|
|
env:
|
|
SOURCE_DATE_EPOCH: ${{ needs.metadata.outputs.source_date_epoch }}
|
|
steps:
|
|
- uses: actions/checkout@fbc6f3992d24b796d5a048ff273f7fcc4a7b6c09 # v5
|
|
- uses: dtolnay/rust-toolchain@4360b52568e2003a75bf9bc1d59f33a8e3fc893c # stable
|
|
with:
|
|
toolchain: stable
|
|
targets: aarch64-apple-ios
|
|
- uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2
|
|
with:
|
|
cache-on-failure: true
|
|
key: native-ios-arm64
|
|
save-if: ${{ github.ref == 'refs/heads/main' }}
|
|
|
|
- name: Install Xcode project generator
|
|
run: brew install xcodegen
|
|
|
|
# Unsigned, so people can sideload it with their own Apple ID.
|
|
- name: Build the IPA
|
|
# Google gives iOS its own OAuth client, while one Microsoft
|
|
# registration serves Android and iOS (platform/ios/README.md).
|
|
env:
|
|
FLECTAR_GOOGLE_IOS_CLIENT_ID: ${{ secrets.FLECTAR_GOOGLE_IOS_CLIENT_ID }}
|
|
FLECTAR_MICROSOFT_IOS_CLIENT_ID: ${{ secrets.FLECTAR_MICROSOFT_ANDROID_CLIENT_ID }}
|
|
run: bash scripts/package-ios.sh target/ios
|
|
|
|
- uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
|
|
with:
|
|
name: flectar-mail-ios-arm64
|
|
path: target/ios/flectar-mail-ios-arm64.ipa
|
|
if-no-files-found: error
|
|
|
|
release:
|
|
name: Publish GitHub Release
|
|
# Every published channel must include an APK with the shared identity.
|
|
if: >-
|
|
!cancelled() && github.event_name == 'push' && github.ref_type == 'tag' &&
|
|
needs.metadata.result == 'success' && needs.quality.result == 'success' &&
|
|
needs.linux.result == 'success' && needs.rpm.result == 'success' &&
|
|
needs.rpm-test.result == 'success' &&
|
|
needs.flatpak.result == 'success' && needs.windows.result == 'success' &&
|
|
needs.microsoft-store.result == 'success' &&
|
|
needs.macos.result == 'success' && needs.android.result == 'success' &&
|
|
needs.ios.result == 'success'
|
|
needs: [metadata, quality, linux, rpm, rpm-test, flatpak, windows, microsoft-store, macos, android, ios]
|
|
runs-on: ubuntu-24.04
|
|
permissions:
|
|
actions: read
|
|
artifact-metadata: write
|
|
attestations: write
|
|
contents: write
|
|
id-token: write
|
|
steps:
|
|
- uses: actions/checkout@fbc6f3992d24b796d5a048ff273f7fcc4a7b6c09 # v5
|
|
|
|
# Each package artifact unpacks into release-assets/<artifact name>.
|
|
# The staging script picks the expected files from those folders.
|
|
- name: Download packages
|
|
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4
|
|
with:
|
|
pattern: flectar-mail-*
|
|
path: release-assets
|
|
|
|
- name: Prepare release downloads
|
|
env:
|
|
RELEASE_VERSION: ${{ needs.metadata.outputs.version }}
|
|
run: python3 scripts/prepare-release.py
|
|
|
|
- name: Attest release downloads
|
|
uses: actions/attest@1e69f48acb82d1966a394da916b4c1698aa569d6 # v4
|
|
with:
|
|
subject-path: dist/*
|
|
|
|
- name: Publish tagged release
|
|
env:
|
|
GH_TOKEN: ${{ github.token }}
|
|
GH_REPO: ${{ github.repository }}
|
|
run: bash scripts/publish-release.sh
|