mirror of
https://github.com/flectar/mail.git
synced 2026-11-11 17:57:54 +00:00
332 lines
14 KiB
YAML
332 lines
14 KiB
YAML
name: Quality and security
|
|
|
|
on:
|
|
push:
|
|
branches:
|
|
- main
|
|
pull_request:
|
|
schedule:
|
|
- cron: "17 4 * * *"
|
|
workflow_dispatch:
|
|
workflow_call:
|
|
|
|
permissions:
|
|
contents: read
|
|
|
|
concurrency:
|
|
group: quality-${{ github.workflow }}-${{ github.ref }}
|
|
cancel-in-progress: true
|
|
|
|
env:
|
|
CARGO_TERM_COLOR: always
|
|
# Slint's generated application is large; simultaneous lib/test rustc jobs
|
|
# can exhaust runner memory before any tests start.
|
|
CARGO_BUILD_JOBS: "1"
|
|
|
|
jobs:
|
|
quality:
|
|
if: github.event_name != 'schedule'
|
|
name: Format, lint, and test
|
|
runs-on: ubuntu-24.04
|
|
steps:
|
|
- uses: actions/checkout@fbc6f3992d24b796d5a048ff273f7fcc4a7b6c09 # v5
|
|
- name: Free runner disk space
|
|
# This job peaks at about 33 GiB of disk. Linux runners only promise
|
|
# 14 GB, and some now have a 72 GB disk instead of 145 GB, which leaves
|
|
# too little (actions/runner-images#15238). On those, delete
|
|
# preinstalled SDKs, browsers and Docker images the job never uses.
|
|
shell: bash
|
|
run: |
|
|
df -h /
|
|
if (( $(df -k --output=avail / | tail -1) < 50 * 1024 * 1024 )); then
|
|
sudo rm -rf \
|
|
/usr/local/lib/android \
|
|
/usr/share/dotnet \
|
|
/usr/local/.ghcup \
|
|
/usr/share/swift \
|
|
"${RUNNER_TOOL_CACHE:?}/CodeQL" \
|
|
/usr/local/share/powershell \
|
|
/opt/microsoft \
|
|
/usr/local/julia* \
|
|
/usr/local/share/chromium
|
|
sudo docker image prune --all --force > /dev/null ||
|
|
echo "::warning::Could not remove the preinstalled Docker images."
|
|
df -h /
|
|
fi
|
|
- name: Install native build dependencies
|
|
run: |
|
|
sudo apt-get update
|
|
sudo apt-get install -y \
|
|
build-essential \
|
|
dbus-daemon \
|
|
gettext \
|
|
gnupg \
|
|
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 \
|
|
ripgrep
|
|
- uses: dtolnay/rust-toolchain@4360b52568e2003a75bf9bc1d59f33a8e3fc893c # stable
|
|
with:
|
|
toolchain: stable
|
|
components: clippy,rustfmt
|
|
- uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2
|
|
with:
|
|
key: quality-default
|
|
# 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: Check formatting
|
|
run: cargo fmt --all -- --check
|
|
- name: Leave translation catalogs to Weblate
|
|
# Weblate's msgmerge add-on owns lang/*/LC_MESSAGES; edits from other
|
|
# branches conflict with its pending commits and lock the component.
|
|
if: >-
|
|
github.event_name == 'pull_request' &&
|
|
!(github.head_ref == 'weblate-flectar-mail-mail' &&
|
|
github.event.pull_request.head.repo.full_name == github.repository)
|
|
shell: bash
|
|
run: |
|
|
set -euo pipefail
|
|
# The checkout is the pull request's merge commit; its first parent
|
|
# is the base branch.
|
|
git fetch --quiet --deepen=1 origin "$GITHUB_SHA"
|
|
changed=$(git diff --name-only HEAD^1 HEAD -- 'lang/*/LC_MESSAGES/*.po')
|
|
if [[ -n $changed ]]; then
|
|
printf '%s\n' "$changed"
|
|
echo "::error::Only Weblate edits lang/*/LC_MESSAGES. Commit lang/flectar-mail.pot alone (scripts/update-translation-template.sh) and translate on https://translate.flectar.com/."
|
|
exit 1
|
|
fi
|
|
- name: Validate repository support files
|
|
shell: bash
|
|
run: |
|
|
set -euo pipefail
|
|
find scripts -type f -name '*.sh' -print0 | xargs -0 -n1 bash -n
|
|
python3 - <<'PY'
|
|
import ast
|
|
import json
|
|
import xml.etree.ElementTree as ET
|
|
from pathlib import Path
|
|
|
|
for path in Path("scripts").glob("*.py"):
|
|
ast.parse(path.read_text(encoding="utf-8"), filename=str(path))
|
|
for path in Path(".").glob("**/*.json"):
|
|
if "target" not in path.parts:
|
|
json.loads(path.read_text(encoding="utf-8"))
|
|
for path in Path(".").glob("**/*.xml"):
|
|
if "target" not in path.parts:
|
|
ET.parse(path)
|
|
PY
|
|
python3 scripts/test-benchmark-startup.py
|
|
python3 ui/icons/lucide/generate.py --check
|
|
python3 resources/app-icon/generate.py --check
|
|
- name: Keep operating-system names in the platform layer
|
|
run: python3 scripts/check-platform-gates.py
|
|
- name: Verify the Blitz patch queue
|
|
run: bash scripts/blitz-patches.sh verify
|
|
- name: Verify and test the Parley backport
|
|
run: |
|
|
python3 scripts/verify-parley.py
|
|
cargo test --manifest-path crates/parley/Cargo.toml --lib --locked --target-dir target/parley-tests
|
|
- name: Verify the Slint winit clipboard backport
|
|
run: python3 scripts/verify-slint-winit.py
|
|
- name: Verify and test the vendored spellbook
|
|
run: |
|
|
python3 scripts/verify-spellbook.py
|
|
cargo test --manifest-path crates/spellbook/Cargo.toml --locked --target-dir target/spellbook-tests
|
|
- uses: actions/setup-java@cf277c60eb25467037889841efdb72551f06f6c3 # v4
|
|
with:
|
|
distribution: temurin
|
|
java-version: "21"
|
|
- name: Test release validation and publication
|
|
run: |
|
|
python3 scripts/test-release.py
|
|
python3 scripts/test-pdf-packaging.py
|
|
python3 scripts/test-android-jni.py
|
|
python3 scripts/test-android-oauth.py
|
|
python3 scripts/test-android-signing.py
|
|
- name: Validate Android workspace lockfile
|
|
run: cargo metadata --manifest-path platform/android/Cargo.toml --locked --format-version 1 > /dev/null
|
|
- name: Reject unused direct dependencies
|
|
run: |
|
|
cargo install cargo-machete --locked --version 0.9.2
|
|
cargo machete --with-metadata
|
|
- name: Check every default target
|
|
run: cargo check --workspace --all-targets --locked
|
|
- name: Check the capability-minimal application
|
|
run: cargo check -p flectar-mail --no-default-features --locked
|
|
- name: Check the Flatpak application profile
|
|
run: cargo check -p flectar-mail --no-default-features --features remote-content,gpu-renderer,flatpak --locked
|
|
- name: Check the CPU-only renderer build
|
|
run: cargo check -p flectar-mail --no-default-features --features remote-content --locked
|
|
- name: Lint without warnings
|
|
run: cargo clippy --workspace --all-targets --locked -- -D warnings
|
|
- name: Validate translations
|
|
shell: bash
|
|
run: |
|
|
cargo install slint-tr-extractor --version 1.18.1 --locked
|
|
bash scripts/check-translations.sh
|
|
- name: Run the default test suite
|
|
run: cargo test --workspace --all-targets --locked
|
|
- name: Test the vendored Blitz patches
|
|
run: cargo test -p blitz-dom --lib --locked
|
|
- name: Exercise the mobile PDF renderer with bundled PDFium on the host
|
|
run: |
|
|
python3 scripts/stage-pdfium.py linux-x64 target/pdfium-mobile-test
|
|
python3 scripts/test-pdf-preview.py --fixture "$RUNNER_TEMP/mobile-preview.pdf"
|
|
FLECTAR_TEST_PDFIUM="$PWD/target/pdfium-mobile-test/libpdfium.so" \
|
|
FLECTAR_TEST_PDF="$RUNNER_TEMP/mobile-preview.pdf" \
|
|
cargo test -p flectar-mail --lib --locked mobile_native_pages_and_rejection -- --ignored
|
|
|
|
platforms:
|
|
if: github.event_name != 'schedule'
|
|
# Shared code compiles differently per target. Lint every supported
|
|
# platform so a change cannot silently break a build nobody ran locally.
|
|
name: Lint ${{ matrix.name }}
|
|
strategy:
|
|
fail-fast: false
|
|
matrix:
|
|
include:
|
|
- name: Windows
|
|
runner: windows-2025
|
|
command: cargo clippy --workspace --all-targets --locked -- -D warnings
|
|
- name: macOS
|
|
runner: macos-15
|
|
command: cargo clippy --workspace --all-targets --locked -- -D warnings
|
|
- name: iOS
|
|
runner: macos-15
|
|
target: aarch64-apple-ios
|
|
# The app's deployment target (platform/ios/project.yml).
|
|
command: >-
|
|
IPHONEOS_DEPLOYMENT_TARGET=17.0
|
|
cargo clippy --locked --target aarch64-apple-ios --bin flectar-mail
|
|
--no-default-features --features remote-content,ios -- -D warnings
|
|
- name: Android
|
|
runner: ubuntu-24.04
|
|
target: aarch64-linux-android
|
|
# The shared application is a path dependency of this separate
|
|
# workspace, so `-- -D warnings` alone would not cover it.
|
|
command: >-
|
|
RUSTFLAGS="-D warnings"
|
|
cargo clippy --manifest-path platform/android/Cargo.toml --locked
|
|
--target aarch64-linux-android -- -D warnings
|
|
runs-on: ${{ matrix.runner }}
|
|
steps:
|
|
- uses: actions/checkout@fbc6f3992d24b796d5a048ff273f7fcc4a7b6c09 # v5
|
|
- uses: dtolnay/rust-toolchain@4360b52568e2003a75bf9bc1d59f33a8e3fc893c # stable
|
|
with:
|
|
toolchain: stable
|
|
components: clippy
|
|
targets: ${{ matrix.target }}
|
|
- uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2
|
|
with:
|
|
key: quality-${{ matrix.name }}
|
|
save-if: ${{ github.ref == 'refs/heads/main' }}
|
|
# Slint's Android backend compiles its Java helper during the build.
|
|
- uses: actions/setup-java@cf277c60eb25467037889841efdb72551f06f6c3 # v4
|
|
if: matrix.name == 'Android'
|
|
with:
|
|
distribution: temurin
|
|
java-version: "21"
|
|
- name: Configure Android NDK compiler
|
|
if: matrix.name == 'Android'
|
|
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: Lint without warnings
|
|
shell: bash
|
|
run: ${{ matrix.command }}
|
|
# rustc links without compiler-rt, so an Objective-C `@available` check
|
|
# above the deployment target would only fail when the release build
|
|
# links the app.
|
|
- name: Keep the Objective-C bridges linkable
|
|
if: matrix.name == 'iOS'
|
|
run: |
|
|
archive=""
|
|
for candidate in target/aarch64-apple-ios/debug/build/flectar-mail-*/out/libflectar_documents.a; do
|
|
if [[ -z "$archive" || "$candidate" -nt "$archive" ]]; then archive="$candidate"; fi
|
|
done
|
|
undefined="$(nm -u "$archive")"
|
|
if grep -E '___is(Platform|OS)VersionAtLeast' <<<"$undefined"; then
|
|
echo "::error::Use a runtime check instead of @available in platform/ios; rustc doesn't link compiler-rt."
|
|
exit 1
|
|
fi
|
|
|
|
optional-embeddings:
|
|
if: github.event_name != 'schedule'
|
|
name: Optional embedding runtime
|
|
runs-on: ubuntu-24.04
|
|
steps:
|
|
- uses: actions/checkout@fbc6f3992d24b796d5a048ff273f7fcc4a7b6c09 # v5
|
|
- name: Install native core dependencies
|
|
run: |
|
|
sudo apt-get update
|
|
sudo apt-get install -y build-essential libdbus-1-dev pkg-config gnupg
|
|
- uses: dtolnay/rust-toolchain@4360b52568e2003a75bf9bc1d59f33a8e3fc893c # stable
|
|
with:
|
|
toolchain: stable
|
|
components: clippy
|
|
- uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2
|
|
with:
|
|
key: quality-embeddings
|
|
save-if: ${{ github.ref == 'refs/heads/main' }}
|
|
- name: Lint the opt-in model runtime
|
|
run: cargo clippy -p flectar-mail-core --all-targets --locked --features local-embeddings -- -D warnings
|
|
- name: Test the opt-in model runtime
|
|
run: cargo test -p flectar-mail-core --lib --locked --features local-embeddings
|
|
|
|
rustsec:
|
|
name: RustSec advisory scan
|
|
runs-on: ubuntu-24.04
|
|
steps:
|
|
- uses: actions/checkout@fbc6f3992d24b796d5a048ff273f7fcc4a7b6c09 # v5
|
|
- uses: dtolnay/rust-toolchain@4360b52568e2003a75bf9bc1d59f33a8e3fc893c # stable
|
|
with:
|
|
toolchain: stable
|
|
- uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2
|
|
with:
|
|
cache-targets: false
|
|
key: quality-rustsec
|
|
save-if: ${{ github.ref == 'refs/heads/main' }}
|
|
- name: Install pinned cargo-audit
|
|
run: cargo install cargo-audit --locked --version 0.22.2
|
|
- name: Audit the locked dependency graph
|
|
# The first four exceptions are unmaintained notices in optional/build
|
|
# or Slint/Blitz typography dependencies. The next three cover crates
|
|
# folded into zbus 6: remove them when Slint, ashpd, and ksni migrate
|
|
# from zbus 5. The last is pin-utils, which async-imap and Slint's
|
|
# spin_on still use: remove it when both drop it. These are maintenance
|
|
# notices, not vulnerability waivers. Deny every other warning and all
|
|
# known vulnerabilities.
|
|
run: |
|
|
lockfiles="Cargo.lock platform/android/Cargo.lock"
|
|
# Tier 3 ports are audited daily and when their own workflow runs,
|
|
# not on pull requests (platform/README.md).
|
|
case "$GITHUB_EVENT_NAME" in
|
|
schedule | workflow_dispatch) lockfiles="$lockfiles platform/ohos/Cargo.lock" ;;
|
|
esac
|
|
for lockfile in $lockfiles; do
|
|
cargo audit --file "$lockfile" --deny warnings \
|
|
--ignore RUSTSEC-2025-0141 \
|
|
--ignore RUSTSEC-2024-0436 \
|
|
--ignore RUSTSEC-2026-0206 \
|
|
--ignore RUSTSEC-2026-0192 \
|
|
--ignore RUSTSEC-2026-0370 \
|
|
--ignore RUSTSEC-2026-0371 \
|
|
--ignore RUSTSEC-2026-0372 \
|
|
--ignore RUSTSEC-2026-0441
|
|
done
|