Files
plezy/.github/workflows/ci.yml
T

626 lines
21 KiB
YAML

name: CI - Sanity Checks
on:
push:
branches:
- main
# Keep untrusted code on the read-only pull_request event. Never use pull_request_target here.
pull_request:
branches:
- main
workflow_dispatch:
jobs:
analyze:
name: Code Analysis
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- name: Checkout code
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
with:
persist-credentials: false
- name: Setup Flutter
uses: subosito/flutter-action@1a449444c387b1966244ae4d4f8c696479add0b2 # v2
with:
channel: "stable"
flutter-version: "3.44.0"
cache: true
pub-cache: false
- name: Cache Pub dependencies
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6
with:
path: |
~/.pub-cache
key: ${{ runner.os }}-pub-v3-${{ hashFiles('**/pubspec.yaml', '**/pubspec.lock') }}
- name: Install dependencies
run: |
flutter pub get
- name: Install wakelock_plus development dependencies
working-directory: packages/wakelock_plus
run: flutter pub get --enforce-lockfile --no-example
- name: Verify generated files committed
run: scripts/codegen.sh --check
- name: Verify translation hygiene
run: python3 scripts/clean_translations.py --check --strict
- name: Verify workflow and script guards
run: |
python3 scripts/check_build_workflow.py
python3 scripts/test_check_build_workflow.py
python3 scripts/check_apple_spm_locks.py
python3 scripts/test_check_apple_spm_locks.py
python3 scripts/verify_runtime_inputs.py
python3 scripts/test_verify_runtime_inputs.py
python3 scripts/check_workflow_security.py
python3 scripts/test_check_workflow_security.py
python3 scripts/check_workflow_action_pins.py
python3 scripts/test_check_workflow_action_pins.py
python3 scripts/check_container_image_pins.py
python3 scripts/test_check_container_image_pins.py
python3 scripts/test_fetch_tvos_engine.py
python3 scripts/test_check_codegen.py
python3 scripts/test_generate_relay_protocol.py
python3 scripts/test_format_native.py
python3 scripts/test_run_maestro.py
python3 scripts/test_maestro_flow_contracts.py
python3 scripts/test_maestro_jellyfin_proxy.py
python3 scripts/test_maestro_real_jellyfin.py
python3 scripts/check_update_packages_workflow.py
python3 scripts/test_pubspec_version.py
python3 scripts/test_clean_translations.py
python3 scripts/test_check_icon_consistency.py
- name: Verify formatting
run: |
paths=(lib)
[ ! -d test ] || paths+=(test)
find "${paths[@]}" -name "*.dart" ! -name "*.g.dart" ! -name "*.freezed.dart" -type f -print0 |
xargs -0 -r dart format --output=none --set-exit-if-changed
- name: Verify icon consistency
run: dart run scripts/check_icon_consistency.dart
- name: Analyze code
run: dart run scripts/check_analyzer.dart
- name: Check for unused code
run: |
echo "🔍 Checking for unused code..."
dart run dart_code_linter:metrics check-unused-code lib 2>&1 | tee unused_code.txt
if grep -qi "no unused code found" unused_code.txt; then
echo "✅ No unused code found"
else
echo "❌ Found unused code:"
cat unused_code.txt
exit 1
fi
- name: Check for unused files
run: |
echo "🔍 Checking for unused files..."
dart run dart_code_linter:metrics check-unused-files lib 2>&1 | tee unused_files.txt
if grep -qi "no unused files found" unused_files.txt; then
echo "✅ No unused files found"
else
echo "❌ Found unused files:"
cat unused_files.txt
exit 1
fi
test:
name: Unit Tests
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- name: Checkout code
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
with:
persist-credentials: false
- name: Setup Flutter
uses: subosito/flutter-action@1a449444c387b1966244ae4d4f8c696479add0b2 # v2
with:
channel: "stable"
flutter-version: "3.44.0"
cache: true
pub-cache: false
- name: Cache Pub dependencies
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6
with:
path: |
~/.pub-cache
key: ${{ runner.os }}-pub-v3-${{ hashFiles('**/pubspec.yaml', '**/pubspec.lock') }}
- name: Install dependencies
run: |
flutter clean
flutter pub get
- name: Run tests
run: |
if [ -d "test" ] && [ "$(find test -name '*_test.dart' | wc -l)" -gt 0 ]; then
flutter test
else
echo "No tests found, skipping test execution"
fi
- name: Install wakelock_plus test dependencies
working-directory: packages/wakelock_plus
run: flutter pub get --enforce-lockfile
- name: Run wakelock_plus VM tests
working-directory: packages/wakelock_plus
run: flutter test test/wakelock_plus_linux_plugin_test.dart
- name: Run wakelock_plus Chrome tests
working-directory: packages/wakelock_plus
run: flutter test --platform chrome --dart-define=WEB_PLUGIN_TESTS=true test/wakelock_plus_web_plugin_test.dart
android-test:
name: Android JVM and Native Tests
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- name: Checkout code
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
with:
persist-credentials: false
- name: Setup Java
uses: actions/setup-java@03ad4de0992f5dab5e18fcb136590ce7c4a0ac95 # v5
with:
distribution: "temurin"
java-version: "21"
- name: Setup Flutter
uses: subosito/flutter-action@1a449444c387b1966244ae4d4f8c696479add0b2 # v2
with:
channel: "stable"
flutter-version: "3.44.0"
cache: true
pub-cache: false
- name: Cache Pub dependencies
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6
with:
path: |
~/.pub-cache
key: ${{ runner.os }}-pub-v3-${{ hashFiles('**/pubspec.yaml', '**/pubspec.lock') }}
- name: Cache Gradle
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6
with:
path: |
~/.gradle/caches
~/.gradle/wrapper
key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }}
restore-keys: |
${{ runner.os }}-gradle-
- name: Install dependencies
run: flutter pub get
- name: Configure Android local properties
run: printf 'flutter.sdk=%s\nsdk.dir=%s\n' "$FLUTTER_ROOT" "$ANDROID_HOME" > android/local.properties
- name: Configure Android host native tests
run: |
cmake -S android/app/src/test/cpp -B build/android-host-tests \
-DCMAKE_BUILD_TYPE=Debug
- name: Build Android host native tests
run: cmake --build build/android-host-tests --parallel 2
- name: Run Android host native tests
run: |
ctest --test-dir build/android-host-tests \
--output-on-failure --no-tests=error
- name: Run Android JVM unit tests
working-directory: android
run: ./gradlew :app:testDebugUnitTest :saf_util:testDebugUnitTest :libass:testDebugUnitTest -x :app:compileFlutterBuildDebug --continue
- name: Check Android API compatibility
working-directory: android
run: ./gradlew :app:lintDebug
native-format:
name: Native Formatting
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- name: Checkout code
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
with:
persist-credentials: false
- name: Setup Java
uses: actions/setup-java@03ad4de0992f5dab5e18fcb136590ce7c4a0ac95 # v5
with:
distribution: "temurin"
java-version: "17"
- name: Verify native formatting
run: scripts/format_native.sh --check
- name: Verify Linux native acquisition integrity
run: bash linux/packaging/build-libmpv_test.sh
linux-native-test:
name: Linux native reliability (${{ matrix.sanitizer }})
runs-on: ubuntu-latest
permissions:
contents: read
strategy:
fail-fast: false
matrix:
include:
- sanitizer: address
lifecycle_sanitizers: ON
- sanitizer: thread
lifecycle_sanitizers: OFF
steps:
- name: Checkout code
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
with:
persist-credentials: false
- name: Setup Flutter
uses: subosito/flutter-action@1a449444c387b1966244ae4d4f8c696479add0b2 # v2
with:
channel: "stable"
flutter-version: "3.44.0"
cache: true
pub-cache: false
- name: Install Linux native test dependencies
run: |
sudo apt-get update
sudo apt-get install -y --no-install-recommends \
clang cmake ninja-build pkg-config libgtk-3-dev liblzma-dev \
libstdc++-12-dev libmpv-dev libepoxy-dev libcurl4-openssl-dev libevdev-dev
- name: Prepare Flutter Linux configuration
run: |
flutter pub get --enforce-lockfile --no-example
flutter build linux --debug --config-only --no-pub
- name: Configure Linux native reliability tests
run: |
cmake -S linux -B build/linux-native-${{ matrix.sanitizer }} -G Ninja \
-DCMAKE_BUILD_TYPE=Debug \
-DPLEZY_BUILD_MPV_PLAYER_LIFECYCLE_TESTS=ON \
-DPLEZY_MPV_LIFECYCLE_SANITIZERS=${{ matrix.lifecycle_sanitizers }} \
-DPLEZY_BUILD_MPV_RELIABILITY_TESTS=ON \
-DPLEZY_MPV_RELIABILITY_SANITIZER=${{ matrix.sanitizer }}
- name: Build Linux native reliability tests
run: |
cmake --build build/linux-native-${{ matrix.sanitizer }} --parallel 2 --target \
mpv_player_lifecycle_test \
mpv_property_result_contract_test \
mpv_gpu_bootstrap_test
- name: Run Linux native reliability tests
run: |
ctest --test-dir build/linux-native-${{ matrix.sanitizer }} \
--output-on-failure --no-tests=error
apple-native-test:
name: Apple native reliability (${{ matrix.platform }})
runs-on: macos-26
permissions:
contents: read
strategy:
fail-fast: false
matrix:
include:
- platform: iOS
project_directory: ios
workspace: ios/Runner.xcworkspace
simulator_runtime: iOS
simulator_platform: iOS
static_destination: ""
- platform: macOS
project_directory: macos
workspace: macos/Runner.xcworkspace
simulator_runtime: ""
simulator_platform: ""
static_destination: platform=macOS
- platform: tvOS
project_directory: tvos
workspace: tvos/Runner.xcworkspace
simulator_runtime: tvOS
simulator_platform: tvOS
static_destination: ""
steps:
- name: Checkout code
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
with:
persist-credentials: false
- name: Setup Flutter
uses: subosito/flutter-action@1a449444c387b1966244ae4d4f8c696479add0b2 # v2
with:
channel: "stable"
flutter-version: "3.44.0"
cache: true
pub-cache: false
- name: Install locked Dart dependencies
run: flutter pub get --enforce-lockfile --no-example
- name: Record committed CocoaPods lockfile
if: matrix.platform != 'tvOS'
env:
PODFILE_LOCK: ${{ matrix.project_directory }}/Podfile.lock
run: |
test -f "$PODFILE_LOCK"
shasum -a 256 "$PODFILE_LOCK" > "$RUNNER_TEMP/plezy-podfile-lock.sha256"
- name: Prepare iOS Flutter build settings
if: matrix.platform == 'iOS'
run: flutter build ios --config-only --simulator --debug --no-pub
- name: Prepare macOS Flutter build settings
if: matrix.platform == 'macOS'
run: flutter build macos --config-only --debug --no-pub
- name: Prepare tvOS Flutter engine
if: matrix.platform == 'tvOS'
run: tvos/scripts/fetch_engine.sh
- name: Verify Flutter configuration preserved CocoaPods lockfile
if: matrix.platform != 'tvOS'
run: shasum -a 256 --check "$RUNNER_TEMP/plezy-podfile-lock.sha256"
- name: Install locked CocoaPods dependencies
if: matrix.platform != 'tvOS'
working-directory: ${{ matrix.project_directory }}
run: pod install --deployment
- name: Install tvOS CocoaPods dependencies
if: matrix.platform == 'tvOS'
run: tvos/scripts/pod_install.sh
- name: Verify tvOS project wiring
if: matrix.platform == 'tvOS'
run: ruby tvos/scripts/test_wire_mpv.rb
- name: Select Apple test destination
env:
SIMULATOR_RUNTIME: ${{ matrix.simulator_runtime }}
SIMULATOR_PLATFORM: ${{ matrix.simulator_platform }}
STATIC_DESTINATION: ${{ matrix.static_destination }}
run: |
python3 - <<'PY'
import json
import os
import subprocess
destination = os.environ["STATIC_DESTINATION"]
if not destination:
runtime_name = os.environ["SIMULATOR_RUNTIME"]
payload = json.loads(
subprocess.check_output(
["xcrun", "simctl", "list", "devices", "available", "-j"],
text=True,
)
)
devices = [
device
for runtime, candidates in payload["devices"].items()
if f".{runtime_name}-" in runtime
for device in candidates
if device.get("isAvailable", False)
]
if not devices:
raise SystemExit(f"no available {runtime_name} simulator")
destination = (
f"platform={os.environ['SIMULATOR_PLATFORM']} Simulator,"
f"id={devices[0]['udid']}"
)
with open(os.environ["GITHUB_ENV"], "a", encoding="utf-8") as output:
output.write(f"APPLE_TEST_DESTINATION={destination}\n")
PY
- name: Run Apple native reliability tests
run: |
xcodebuild test \
-workspace "${{ matrix.workspace }}" \
-scheme Runner \
-configuration Debug \
-destination "$APPLE_TEST_DESTINATION" \
-disableAutomaticPackageResolution \
CODE_SIGNING_ALLOWED=NO \
COMPILER_INDEX_STORE_ENABLE=NO
windows-native-test:
name: Windows native reliability (${{ matrix.arch }})
runs-on: ${{ matrix.runner }}
permissions:
contents: read
strategy:
fail-fast: false
matrix:
include:
- arch: x64
runner: windows-latest
flutter_setup: action
- arch: arm64
runner: windows-11-arm
flutter_setup: git
steps:
- name: Checkout code
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
with:
persist-credentials: false
- name: Install 7-Zip
if: matrix.arch == 'arm64'
shell: pwsh
run: choco install 7zip -y
- name: Setup Flutter
if: matrix.flutter_setup == 'action'
uses: subosito/flutter-action@1a449444c387b1966244ae4d4f8c696479add0b2 # v2
with:
channel: "stable"
flutter-version: "3.44.0"
cache: true
pub-cache: false
- name: Setup Flutter 3.44.0 from its immutable commit
if: matrix.flutter_setup == 'git'
shell: pwsh
run: |
$root = "$env:RUNNER_TEMP\flutter"
$expectedCommit = "559ffa3f75e7402d65a8def9c28389a9b2e6fe42"
git init $root
git -C $root remote add origin https://github.com/flutter/flutter.git
git -C $root fetch --depth 1 origin refs/tags/3.44.0:refs/tags/3.44.0
git -C $root checkout --detach refs/tags/3.44.0
$actualCommit = git -C $root rev-parse HEAD
if ($LASTEXITCODE -ne 0 -or $actualCommit -ne $expectedCommit) {
throw "Flutter 3.44.0 resolved to $actualCommit, expected $expectedCommit"
}
"$root\bin" | Out-File -FilePath $env:GITHUB_PATH -Append -Encoding utf8
& "$root\bin\flutter.bat" --version
if ($LASTEXITCODE -ne 0) {
throw "Unable to bootstrap the Flutter SDK"
}
$versionOutput = & "$root\bin\flutter.bat" --version --machine
if ($LASTEXITCODE -ne 0) {
throw "Unable to resolve the Flutter SDK version"
}
$versionJson = $versionOutput -join "`n"
if ([string]::IsNullOrWhiteSpace($versionJson)) {
throw "Flutter did not report machine-readable version JSON"
}
$version = $versionJson | ConvertFrom-Json
if ($version.frameworkVersion -ne "3.44.0") {
throw "Flutter reported version $($version.frameworkVersion), expected 3.44.0"
}
- name: Install locked Dart dependencies
shell: pwsh
run: flutter pub get --enforce-lockfile --no-example
- name: Install patched Flutter engine
shell: pwsh
run: |
flutter precache --windows
.\windows\tool\install-patched-engine.ps1
- name: Prepare Flutter Windows configuration
shell: pwsh
run: flutter build windows --debug --config-only --no-pub
- name: Configure Windows native reliability tests
shell: pwsh
run: |
$buildDir = "build/windows/${{ matrix.arch }}"
cmake -S windows -B $buildDir `
-DPLEZY_BUILD_MPV_PROPERTY_CONTRACT_TESTS=ON `
-DPLEZY_BUILD_DISPLAY_RECOVERY_TESTS=ON
- name: Build Windows native reliability tests
shell: pwsh
run: |
$buildDir = "build/windows/${{ matrix.arch }}"
cmake --build $buildDir --config Debug --parallel 2 --target `
mpv_property_result_contract_test `
mpv_player_property_contract_test `
display_mode_manager_test
- name: Run Windows native reliability tests
shell: pwsh
run: |
$buildDir = "build/windows/${{ matrix.arch }}"
ctest --test-dir "$buildDir/runner" -C Debug --output-on-failure --no-tests=error
dependency-check:
name: Dependency Validation
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- name: Checkout code
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
with:
persist-credentials: false
- name: Setup Flutter
uses: subosito/flutter-action@1a449444c387b1966244ae4d4f8c696479add0b2 # v2
with:
channel: "stable"
flutter-version: "3.44.0"
cache: true
pub-cache: false
- name: Cache Pub dependencies
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6
with:
path: |
~/.pub-cache
key: ${{ runner.os }}-pub-v3-${{ hashFiles('**/pubspec.yaml', '**/pubspec.lock') }}
- name: Verify dependencies
run: |
flutter clean
flutter pub get
flutter pub outdated
server:
name: Server checks
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- name: Checkout code
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
with:
persist-credentials: false
- name: Setup Go
uses: actions/setup-go@924ae3a1cded613372ab5595356fb5720e22ba16 # v6
with:
go-version-file: server/go.mod
cache-dependency-path: server/go.sum
- name: Run server checks
run: scripts/ci_server_checks.sh
website:
name: Website checks
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- name: Checkout code
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
with:
persist-credentials: false
- name: Setup Bun
uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2
with:
bun-version: "1.3.14"
- name: Run website checks
run: scripts/ci_website_checks.sh