5984c161c5
Changelog: Large profiles no longer crash; downloads trust core CAs
587 lines
18 KiB
YAML
587 lines
18 KiB
YAML
name: build
|
|
|
|
on:
|
|
push:
|
|
branches:
|
|
- '**'
|
|
tags:
|
|
- 'v*'
|
|
workflow_dispatch:
|
|
|
|
concurrency:
|
|
group: ${{ github.workflow }}-${{ github.ref }}
|
|
cancel-in-progress: ${{ !startsWith(github.ref, 'refs/tags/v') }}
|
|
|
|
env:
|
|
IS_STABLE: ${{ !contains(github.ref, '-') }}
|
|
# See CI Caches in .agents/commands.md.
|
|
BRANCH_CACHE: ${{ !startsWith(github.ref, 'refs/tags/') }}
|
|
FLUTTER_VERSION: '3.47.6'
|
|
GO_VERSION: '1.27.1'
|
|
NDK_VERSION: r28c
|
|
|
|
jobs:
|
|
dart:
|
|
name: Dart
|
|
# A private copy pays for its Actions minutes, so it runs the checks only when
|
|
# started by hand.
|
|
if: &checks github.event_name == 'workflow_dispatch' || !github.event.repository.private
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
- name: Checkout
|
|
uses: actions/checkout@v4
|
|
with:
|
|
fetch-depth: 0
|
|
submodules: true
|
|
|
|
- name: Test git hooks
|
|
run: bash tool/hooks/test.sh
|
|
|
|
- name: Setup Rust cache
|
|
uses: Swatinem/rust-cache@v2
|
|
with:
|
|
workspaces: plugins/rust_api/rust
|
|
cache-directories: .dart_tool/hooks_runner
|
|
save-if: ${{ env.BRANCH_CACHE }}
|
|
|
|
- name: Setup Flutter
|
|
uses: subosito/flutter-action@v2
|
|
with:
|
|
channel: stable
|
|
flutter-version: ${{ env.FLUTTER_VERSION }}
|
|
cache: ${{ env.BRANCH_CACHE }}
|
|
|
|
- name: Install dependencies
|
|
run: flutter pub get
|
|
|
|
- name: Check format
|
|
run: dart format --output=none --set-exit-if-changed lib test tool plugins setup.dart
|
|
|
|
- name: Analyze
|
|
run: flutter analyze --no-fatal-infos
|
|
|
|
- name: Skip the Go core build hook
|
|
run: |
|
|
yq -i '.hooks.user_defines.setup.build_assets = false' pubspec.yaml
|
|
|
|
- name: Verify changelog
|
|
if: github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/')
|
|
run: dart run tool/changelog.dart verify
|
|
|
|
- name: Run tests
|
|
run: dart run tool/test.dart -- --reporter expanded --coverage
|
|
|
|
- name: Check coverage
|
|
run: dart run tool/check_coverage.dart coverage/lcov.info 75
|
|
|
|
plugins:
|
|
name: Plugins
|
|
if: *checks
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
- name: Checkout
|
|
uses: actions/checkout@v4
|
|
|
|
- name: Setup Flutter
|
|
uses: subosito/flutter-action@v2
|
|
with:
|
|
channel: stable
|
|
flutter-version: ${{ env.FLUTTER_VERSION }}
|
|
cache: ${{ env.BRANCH_CACHE }}
|
|
|
|
- name: Validate local Flutter packages
|
|
run: bash tool/check_plugins.sh
|
|
|
|
- name: Validate setup hooks package
|
|
working-directory: plugins/setup/setup_hooks
|
|
run: |
|
|
dart pub get
|
|
dart analyze
|
|
dart test
|
|
|
|
desktop:
|
|
name: Desktop ${{ matrix.platform }}
|
|
if: (github.event_name == 'workflow_dispatch' || !github.event.repository.private) && !(github.event_name == 'push' && startsWith(github.ref, 'refs/tags/v'))
|
|
runs-on: ${{ matrix.os }}
|
|
strategy:
|
|
fail-fast: false
|
|
matrix:
|
|
include:
|
|
- platform: linux
|
|
os: ubuntu-latest
|
|
- platform: windows
|
|
os: windows-2022
|
|
- platform: macos
|
|
os: macos-latest
|
|
steps:
|
|
- name: Checkout
|
|
uses: actions/checkout@v4
|
|
with:
|
|
submodules: recursive
|
|
|
|
- name: Setup Go
|
|
uses: actions/setup-go@v5
|
|
with:
|
|
go-version: ${{ env.GO_VERSION }}
|
|
cache-dependency-path: |
|
|
core/go.sum
|
|
plugins/setup/setup_hooks/lib/src/go_builder.dart
|
|
|
|
- name: Setup Rust cache
|
|
uses: Swatinem/rust-cache@v2
|
|
with:
|
|
key: desktop-${{ matrix.platform }}
|
|
workspaces: |
|
|
services/helper
|
|
plugins/rust_api/rust
|
|
cache-directories: .dart_tool/hooks_runner
|
|
|
|
- name: Setup Flutter
|
|
uses: subosito/flutter-action@v2
|
|
with:
|
|
channel: stable
|
|
flutter-version: ${{ env.FLUTTER_VERSION }}
|
|
cache: true
|
|
|
|
- name: Install Linux build dependencies
|
|
if: matrix.platform == 'linux'
|
|
run: |
|
|
sudo apt-get update
|
|
sudo apt-get install -y ninja-build libgtk-3-dev libayatana-appindicator3-dev libsecret-1-dev
|
|
|
|
- name: Setup Xcode
|
|
if: matrix.platform == 'macos'
|
|
run: |
|
|
sudo xcode-select -s /Applications/Xcode_26.3.app || sudo xcode-select -s /Applications/Xcode_26.2.app
|
|
xcodebuild -version
|
|
|
|
- name: Enable git long paths (Windows)
|
|
if: matrix.platform == 'windows'
|
|
run: git config --global core.longpaths true
|
|
|
|
- name: Install dependencies
|
|
run: flutter pub get
|
|
|
|
# Compiles the desktop runners and the native plugin code that only the
|
|
# tagged release matrix built before; the bundle itself is discarded.
|
|
- name: Build debug bundle
|
|
run: flutter build ${{ matrix.platform }} --debug
|
|
|
|
go:
|
|
name: Go core
|
|
if: *checks
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
- name: Checkout
|
|
uses: actions/checkout@v4
|
|
with:
|
|
submodules: recursive
|
|
|
|
- name: Setup Go
|
|
uses: actions/setup-go@v5
|
|
with:
|
|
go-version: ${{ env.GO_VERSION }}
|
|
cache: ${{ env.BRANCH_CACHE }}
|
|
cache-dependency-path: core/go.sum
|
|
|
|
- name: Validate Go core wrapper
|
|
working-directory: core
|
|
env:
|
|
CGO_ENABLED: 0
|
|
run: |
|
|
if ! compgen -G '*_test.go' > /dev/null; then
|
|
echo 'No Go tests found in core/; the test step would pass vacuously.' >&2
|
|
exit 1
|
|
fi
|
|
test -z "$(gofmt -l ./*.go ./cmd ./mobile ./platform ./tun)" || {
|
|
gofmt -l ./*.go ./cmd ./mobile ./platform ./tun >&2
|
|
exit 1
|
|
}
|
|
go test -count=1 .
|
|
go vet . ./cmd/...
|
|
GOOS=linux go vet ./platform
|
|
|
|
- name: Validate the Android core library
|
|
working-directory: core
|
|
env:
|
|
CGO_ENABLED: 0
|
|
run: |
|
|
tags="$(sed -n 's/^tags: *//p' ../build_config.yaml)"
|
|
test -n "$tags" || { echo 'Could not read tags from build_config.yaml' >&2; exit 1; }
|
|
GOOS=android GOARCH=arm64 go vet "-tags=$tags" . ./mobile ./tun ./platform
|
|
|
|
android:
|
|
name: Android unit tests
|
|
if: *checks
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
- name: Checkout
|
|
uses: actions/checkout@v4
|
|
with:
|
|
submodules: recursive
|
|
|
|
- name: Setup Go
|
|
uses: actions/setup-go@v5
|
|
with:
|
|
go-version: ${{ env.GO_VERSION }}
|
|
cache: ${{ env.BRANCH_CACHE }}
|
|
cache-dependency-path: |
|
|
core/go.sum
|
|
android/core/build.gradle.kts
|
|
|
|
- name: Setup Flutter
|
|
uses: subosito/flutter-action@v2
|
|
with:
|
|
channel: stable
|
|
flutter-version: ${{ env.FLUTTER_VERSION }}
|
|
cache: ${{ env.BRANCH_CACHE }}
|
|
|
|
- name: Install dependencies
|
|
run: flutter pub get
|
|
|
|
- name: Setup NDK
|
|
uses: nttld/setup-ndk@v1
|
|
id: ndk
|
|
with:
|
|
ndk-version: ${{ env.NDK_VERSION }}
|
|
link-to-sdk: true
|
|
|
|
- name: Setup Java
|
|
uses: actions/setup-java@v4
|
|
with:
|
|
distribution: temurin
|
|
java-version: '17'
|
|
|
|
- name: Setup Gradle
|
|
uses: gradle/actions/setup-gradle@v6
|
|
with:
|
|
cache-read-only: ${{ github.ref != 'refs/heads/main' && github.ref != 'refs/heads/dev' }}
|
|
|
|
- name: Run Android unit tests
|
|
working-directory: android
|
|
env:
|
|
ANDROID_NDK_HOME: ${{ steps.ndk.outputs.ndk-path }}
|
|
run: |
|
|
{
|
|
echo "flutter.sdk=$FLUTTER_ROOT"
|
|
echo "sdk.dir=$ANDROID_HOME"
|
|
} > local.properties
|
|
wrapper="$FLUTTER_ROOT/bin/cache/artifacts/gradle_wrapper"
|
|
test -d "$wrapper" || flutter precache
|
|
cp -R "$wrapper/." .
|
|
chmod +x gradlew
|
|
# No JVM unit test loads the Core or the Flutter build, but `:core` compiles against
|
|
# the gomobile AAR, so one ABI of it is built.
|
|
./gradlew :common:testDebugUnitTest :service:testDebugUnitTest :app:testDebugUnitTest \
|
|
-Ptarget-platform=android-arm64 \
|
|
-x :app:compileFlutterBuildDebug -x :app:copyFlutterAssetsDebug
|
|
|
|
rust:
|
|
name: Rust
|
|
if: *checks
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
- name: Checkout
|
|
uses: actions/checkout@v4
|
|
|
|
- name: Setup Rust cache
|
|
uses: Swatinem/rust-cache@v2
|
|
with:
|
|
workspaces: |
|
|
services/helper
|
|
plugins/rust_api/rust
|
|
save-if: ${{ env.BRANCH_CACHE }}
|
|
|
|
- name: Check format
|
|
run: |
|
|
cargo fmt --manifest-path services/helper/Cargo.toml -- --check
|
|
cargo fmt --manifest-path plugins/rust_api/rust/Cargo.toml -- --check
|
|
|
|
- name: Run tests
|
|
run: |
|
|
cargo test --manifest-path services/helper/Cargo.toml
|
|
cargo test --manifest-path plugins/rust_api/rust/Cargo.toml
|
|
|
|
windows-helper-test:
|
|
name: Windows helper test
|
|
if: *checks
|
|
runs-on: windows-2022
|
|
steps:
|
|
- name: Checkout
|
|
uses: actions/checkout@v4
|
|
|
|
- name: Setup Rust cache
|
|
uses: Swatinem/rust-cache@v2
|
|
with:
|
|
workspaces: services/helper
|
|
save-if: ${{ env.BRANCH_CACHE }}
|
|
|
|
- name: Check format
|
|
run: cargo fmt --manifest-path services/helper/Cargo.toml -- --check
|
|
|
|
- name: Run Windows service tests
|
|
run: cargo test --manifest-path services/helper/Cargo.toml --features windows-service
|
|
|
|
build:
|
|
# Releases publish where the release links point: the public repository.
|
|
if: github.repository == 'chen08209/FlClash' && github.event_name == 'push' && startsWith(github.ref, 'refs/tags/v')
|
|
runs-on: ${{ matrix.os }}
|
|
strategy:
|
|
matrix:
|
|
include:
|
|
- platform: android
|
|
os: ubuntu-latest
|
|
- platform: windows
|
|
os: Windows-2022
|
|
arch: amd64
|
|
- platform: linux
|
|
os: ubuntu-22.04
|
|
arch: amd64
|
|
- platform: macos
|
|
os: macos-15-intel
|
|
arch: amd64
|
|
- platform: macos
|
|
os: macos-latest
|
|
arch: arm64
|
|
- platform: windows
|
|
os: windows-11-arm
|
|
arch: arm64
|
|
flutter-channel: master
|
|
- platform: linux
|
|
os: ubuntu-24.04-arm
|
|
arch: arm64
|
|
flutter-channel: master
|
|
|
|
steps:
|
|
- name: Setup rust
|
|
if: startsWith(matrix.os, 'windows-11-arm')
|
|
shell: pwsh
|
|
run: |
|
|
Invoke-WebRequest -Uri "https://win.rustup.rs/aarch64" -OutFile rustup-init.exe
|
|
.\rustup-init.exe -y --default-toolchain stable
|
|
$cargoPath = "$env:USERPROFILE\.cargo\bin"
|
|
Add-Content $env:GITHUB_PATH $cargoPath
|
|
|
|
- name: Checkout
|
|
uses: actions/checkout@v4
|
|
with:
|
|
submodules: recursive
|
|
|
|
- name: Setup Android Signing
|
|
if: startsWith(matrix.platform,'android')
|
|
run: |
|
|
echo "${{ secrets.KEYSTORE }}" | base64 --decode > android/app/keystore.jks
|
|
echo "${{ secrets.SERVICE_JSON }}" | base64 --decode > android/app/google-services.json
|
|
{
|
|
echo "keyAlias=${{ secrets.KEY_ALIAS }}"
|
|
echo "storePassword=${{ secrets.STORE_PASSWORD }}"
|
|
echo "keyPassword=${{ secrets.KEY_PASSWORD }}"
|
|
} >> android/local.properties
|
|
|
|
- name: Setup Go
|
|
uses: actions/setup-go@v5
|
|
with:
|
|
go-version: ${{ env.GO_VERSION }}
|
|
cache: ${{ env.BRANCH_CACHE }}
|
|
cache-dependency-path: core/go.sum
|
|
|
|
- name: Setup Gradle
|
|
if: startsWith(matrix.platform,'android')
|
|
uses: gradle/actions/setup-gradle@v6
|
|
|
|
- name: Enable git long paths (Windows)
|
|
if: startsWith(matrix.os, 'windows')
|
|
run: git config --global core.longpaths true
|
|
|
|
# Stable publishes no Windows or Linux arm64 archive; master makes the action
|
|
# clone the SDK at the pinned tag and let it fetch the host's own Dart SDK.
|
|
- name: Setup Flutter
|
|
uses: subosito/flutter-action@v2
|
|
with:
|
|
channel: ${{ matrix.flutter-channel || 'stable' }}
|
|
flutter-version: ${{ env.FLUTTER_VERSION }}
|
|
cache: ${{ env.BRANCH_CACHE }}
|
|
|
|
- name: Setup Xcode
|
|
if: startsWith(matrix.platform,'macos')
|
|
run: |
|
|
sudo xcode-select -s /Applications/Xcode_26.3.app || sudo xcode-select -s /Applications/Xcode_26.2.app
|
|
xcodebuild -version
|
|
|
|
- name: Setup NDK
|
|
if: startsWith(matrix.platform,'android')
|
|
uses: nttld/setup-ndk@v1
|
|
id: build-ndk
|
|
with:
|
|
ndk-version: ${{ env.NDK_VERSION }}
|
|
add-to-path: true
|
|
link-to-sdk: true
|
|
|
|
- name: Get Flutter Dependency
|
|
run: |
|
|
flutter --version
|
|
flutter pub get
|
|
|
|
# See .agents/flutter-upgrade.md before bumping FLUTTER_VERSION.
|
|
- name: Disable Impeller on Intel macOS
|
|
if: matrix.platform == 'macos' && matrix.arch == 'amd64'
|
|
run: plutil -replace FLTEnableImpeller -bool NO macos/Runner/Info.plist
|
|
|
|
# The core embeds this bundle and FlClashHttpOverrides.trustCoreRoots loads it for Dart.
|
|
# Pinned by hash, so a new bundle from https://curl.se/docs/caextract.html is a reviewed bump.
|
|
- name: Fetch CA certificates
|
|
shell: bash
|
|
env:
|
|
CA_BUNDLE: cacert-2026-09-25.pem
|
|
CA_BUNDLE_SHA256: a41b5d356aea97a529fe27e0f7316d2f9d946d75927476cf9cf1b90637d00505
|
|
run: |
|
|
bundle=core/Clash.Meta/component/ca/ca-certificates.crt
|
|
curl -fsSL --retry 3 -o "$bundle" "https://curl.se/ca/$CA_BUNDLE"
|
|
echo "$CA_BUNDLE_SHA256 $bundle" | sha256sum -c -
|
|
|
|
- name: Setup
|
|
env:
|
|
ANDROID_NDK_HOME: ${{ steps.build-ndk.outputs.ndk-path }}
|
|
run: dart setup.dart ${{ matrix.platform }} ${{ env.IS_STABLE == 'true' && '--env stable' || '' }} -v
|
|
|
|
- name: Upload
|
|
uses: actions/upload-artifact@v4
|
|
with:
|
|
name: artifact-${{ matrix.platform }}${{ matrix.arch && format('-{0}', matrix.arch) }}
|
|
path: ./dist
|
|
overwrite: true
|
|
|
|
upload:
|
|
permissions:
|
|
contents: write
|
|
needs: [ build, dart, plugins, go, android, rust, windows-helper-test ]
|
|
runs-on: ubuntu-latest
|
|
services:
|
|
telegram-bot-api:
|
|
image: aiogram/telegram-bot-api:latest
|
|
env:
|
|
TELEGRAM_API_ID: ${{ secrets.TELEGRAM_API_ID }}
|
|
TELEGRAM_API_HASH: ${{ secrets.TELEGRAM_API_HASH }}
|
|
ports:
|
|
- 8081:8081
|
|
steps:
|
|
- name: Checkout
|
|
uses: actions/checkout@v4
|
|
with:
|
|
fetch-depth: 0
|
|
|
|
- name: Download
|
|
uses: actions/download-artifact@v4
|
|
with:
|
|
path: ./dist/
|
|
pattern: artifact-*
|
|
merge-multiple: true
|
|
|
|
- name: Setup Flutter
|
|
uses: subosito/flutter-action@v2
|
|
with:
|
|
channel: stable
|
|
flutter-version: ${{ env.FLUTTER_VERSION }}
|
|
cache: ${{ env.BRANCH_CACHE }}
|
|
|
|
- name: Install dependencies
|
|
run: flutter pub get
|
|
|
|
- name: Skip native build hooks
|
|
run: |
|
|
yq -i '.hooks.user_defines.setup.build_assets = false | .hooks.user_defines.rust_api.build_assets = false' pubspec.yaml
|
|
|
|
- name: Generate release notes
|
|
shell: bash
|
|
env:
|
|
IS_STABLE: ${{ env.IS_STABLE }}
|
|
TAG: ${{ github.ref_name }}
|
|
run: |
|
|
target=()
|
|
if [[ "$IS_STABLE" == 'true' ]]; then
|
|
target=(--tag "$TAG")
|
|
else
|
|
dart run tool/changelog.dart build --unreleased
|
|
fi
|
|
dart run tool/changelog.dart render release "${target[@]}" --out release.md
|
|
dart run tool/changelog.dart render telegram "${target[@]}" --out telegram.md
|
|
|
|
- name: Push to telegram
|
|
env:
|
|
TELEGRAM_BOT_TOKEN: ${{ secrets.TELEGRAM_BOT_TOKEN }}
|
|
TAG: ${{ github.ref_name }}
|
|
RUN_ID: ${{ github.run_id }}
|
|
run: |
|
|
python -m pip install --upgrade pip
|
|
pip install requests
|
|
python release_telegram.py
|
|
|
|
- name: Patch release.md
|
|
run: |
|
|
version=$(echo "${{ github.ref_name }}" | sed 's/^v//')
|
|
sed "s|VERSION|$version|g" ./.github/release_template.md >> release.md
|
|
|
|
- name: Generate sha256
|
|
if: ${{ env.IS_STABLE == 'true' }}
|
|
run: |
|
|
cd ./dist
|
|
sha256sum ./* > SHA256SUMS
|
|
|
|
- name: Release
|
|
if: ${{ env.IS_STABLE == 'true' }}
|
|
uses: softprops/action-gh-release@v2
|
|
with:
|
|
files: ./dist/*
|
|
body_path: './release.md'
|
|
|
|
- name: Create Homebrew Cask Source Dir
|
|
if: ${{ env.IS_STABLE == 'true' }}
|
|
run: |
|
|
version=$(echo "${{ github.ref_name }}" | sed 's/^v//')
|
|
arm_sha=$(sha256sum "./dist/FlClash-${version}-macos-arm64.dmg" | awk '{print $1}')
|
|
amd_sha=$(sha256sum "./dist/FlClash-${version}-macos-amd64.dmg" | awk '{print $1}')
|
|
|
|
mkdir -p ./brew/Casks
|
|
sed \
|
|
-e "s|VERSION|$version|g" \
|
|
-e "s|ARM_SHA256|$arm_sha|g" \
|
|
-e "s|AMD_SHA256|$amd_sha|g" \
|
|
./.github/homebrew_cask_template.rb > ./brew/Casks/flclash.rb
|
|
|
|
- name: Push to Homebrew tap
|
|
if: ${{ env.IS_STABLE == 'true' }}
|
|
uses: cpina/github-action-push-to-another-repository@v1.7.2
|
|
env:
|
|
SSH_DEPLOY_KEY: ${{ secrets.HOMEBREW_SSH_DEPLOY_KEY }}
|
|
with:
|
|
source-directory: ./brew/
|
|
destination-github-username: chen08209
|
|
destination-repository-name: homebrew-tap
|
|
user-name: 'chen08029'
|
|
user-email: 'chen08209@gmail.com'
|
|
target-branch: main
|
|
commit-message: Update Homebrew cask from ${{ github.ref_name }}
|
|
target-directory: /
|
|
|
|
- name: Create Fdroid Source Dir
|
|
if: ${{ env.IS_STABLE == 'true' }}
|
|
run: |
|
|
mkdir -p ./tmp
|
|
cp ./dist/*android-arm64-v8a* ./tmp/ || true
|
|
echo "Files copied successfully"
|
|
|
|
- name: Push to fdroid repo
|
|
if: ${{ env.IS_STABLE == 'true' }}
|
|
uses: cpina/github-action-push-to-another-repository@v1.7.2
|
|
env:
|
|
SSH_DEPLOY_KEY: ${{ secrets.SSH_DEPLOY_KEY }}
|
|
with:
|
|
source-directory: ./tmp/
|
|
destination-github-username: chen08209
|
|
destination-repository-name: FlClash-fdroid-repo
|
|
user-name: 'chen08029'
|
|
user-email: 'chen08209@gmail.com'
|
|
target-branch: main
|
|
commit-message: Update from ${{ github.ref_name }}
|
|
target-directory: /tmp/
|