96fcb9ab5e
Rework .agents/, Claude/Codex config, CI workflows, lint gates and lint-rule tests, and the release/changelog tooling. The 5% comment density gate stays diff-scoped inside git hooks, where GIT_DIR outranks the -C the gate uses to locate the repository. The tray icon generator writes multi-size ICO files from the SVG sources into the per-platform tray asset directories. The custom code-review subagents are gone: the built-in /code-review never dispatched them, and the subagent model now lives in a user-level setting outside the repository. The Makefile, build_config.yaml and CI hand the Core and Helper builds to the setup build hook, so the buildkit launchers go, and setup.dart packages AppImage and rpm on arm64 with flutter_distributor pinned to the fork tag that normalizes packaging permissions. Changelog: skip
405 lines
13 KiB
YAML
405 lines
13 KiB
YAML
name: build
|
|
|
|
on:
|
|
push:
|
|
branches:
|
|
- '**'
|
|
tags:
|
|
- 'v*'
|
|
|
|
concurrency:
|
|
group: ${{ github.workflow }}-${{ github.ref }}
|
|
cancel-in-progress: ${{ !startsWith(github.ref, 'refs/tags/v') }}
|
|
|
|
env:
|
|
IS_STABLE: ${{ !contains(github.ref, '-') }}
|
|
FLUTTER_VERSION: '3.47.1'
|
|
|
|
jobs:
|
|
test:
|
|
name: Test
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
- name: Checkout
|
|
uses: actions/checkout@v4
|
|
with:
|
|
submodules: recursive
|
|
fetch-depth: 0
|
|
|
|
- name: Test commit message hook
|
|
shell: bash
|
|
run: bash tool/check_commit_msg_test.sh
|
|
|
|
- name: Test comment density hook
|
|
shell: bash
|
|
run: bash tool/check_comment_density_test.sh
|
|
|
|
- name: Setup Flutter
|
|
uses: subosito/flutter-action@v2
|
|
with:
|
|
channel: stable
|
|
flutter-version: ${{ env.FLUTTER_VERSION }}
|
|
cache: true
|
|
|
|
- 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: Verify changelog
|
|
if: github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/')
|
|
run: dart run tool/changelog.dart verify
|
|
|
|
- name: Setup Go
|
|
uses: actions/setup-go@v5
|
|
with:
|
|
go-version: '1.26.4'
|
|
cache-dependency-path: core/go.sum
|
|
|
|
- name: Run tests
|
|
run: flutter test --reporter expanded --coverage
|
|
|
|
- name: Check coverage
|
|
run: dart run tool/check_coverage.dart coverage/lcov.info 75
|
|
|
|
- name: Validate local Flutter packages
|
|
shell: bash
|
|
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
|
|
|
|
- name: Validate Go core wrapper
|
|
working-directory: core
|
|
shell: bash
|
|
env:
|
|
CGO_ENABLED: 0
|
|
run: |
|
|
# `go test` succeeds silently when a package has no test files, so
|
|
# assert the wrapper actually ships tests before trusting the result.
|
|
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 ./platform ./tun)" || {
|
|
gofmt -l ./*.go ./platform ./tun >&2
|
|
exit 1
|
|
}
|
|
go test -count=1 .
|
|
go vet .
|
|
# `go vet .` only reaches the root package. `platform` and `tun` are
|
|
# behind build constraints, so they need an explicit GOOS to compile
|
|
# at all; without this they ship unvetted.
|
|
GOOS=linux go vet ./platform
|
|
|
|
- name: Setup NDK
|
|
uses: nttld/setup-ndk@v1
|
|
id: core-ndk
|
|
with:
|
|
ndk-version: r28c
|
|
|
|
- name: Validate the Android core library
|
|
working-directory: core
|
|
shell: bash
|
|
env:
|
|
ANDROID_NDK_HOME: ${{ steps.core-ndk.outputs.ndk-path }}
|
|
run: |
|
|
# The step above pins CGO_ENABLED=0, which excludes every `android &&
|
|
# cgo` file: lib.go, bride.go, tun/ and platform/limit.go. The job
|
|
# that builds them needs the NDK and only runs on a `v*` tag, so
|
|
# without this the entire Android surface of the core first compiles
|
|
# at release time.
|
|
CC="$(echo "$ANDROID_NDK_HOME"/toolchains/llvm/prebuilt/*/bin/aarch64-linux-android21-clang)"
|
|
test -x "$CC" || { echo "NDK clang not found at $CC" >&2; exit 1; }
|
|
# Same tags the shipped library is built with; read from the build
|
|
# config at the repository root so the two cannot drift.
|
|
tags="$(sed -n 's/^tags: *//p' ../build_config.yaml)"
|
|
test -n "$tags" || { echo 'Could not read tags from build_config.yaml' >&2; exit 1; }
|
|
export CGO_ENABLED=1 GOOS=android GOARCH=arm64 CC
|
|
go vet "-tags=$tags" . ./tun ./platform
|
|
|
|
- name: Setup Java
|
|
uses: actions/setup-java@v4
|
|
with:
|
|
distribution: temurin
|
|
java-version: '17'
|
|
|
|
- name: Run Android unit tests
|
|
working-directory: android
|
|
shell: bash
|
|
run: |
|
|
# settings.gradle.kts resolves the Flutter tooling through local.properties,
|
|
# which only the Flutter build itself would otherwise generate.
|
|
{
|
|
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
|
|
# Modules are listed explicitly: the aggregate testDebugUnitTest task would
|
|
# also run third-party Flutter plugin suites. `:core` is omitted because its
|
|
# only class loads the native library through System.loadLibrary.
|
|
./gradlew :common:testDebugUnitTest :service:testDebugUnitTest :app:testDebugUnitTest
|
|
|
|
- name: Validate Rust components
|
|
run: |
|
|
cargo fmt --manifest-path services/helper/Cargo.toml -- --check
|
|
cargo test --manifest-path services/helper/Cargo.toml
|
|
cargo fmt --manifest-path plugins/rust_api/rust/Cargo.toml -- --check
|
|
cargo test --manifest-path plugins/rust_api/rust/Cargo.toml
|
|
|
|
windows-helper-test:
|
|
name: Windows helper test
|
|
runs-on: windows-2022
|
|
steps:
|
|
- name: Checkout
|
|
uses: actions/checkout@v4
|
|
|
|
- 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:
|
|
if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/v')
|
|
needs: [ test, windows-helper-test ]
|
|
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
|
|
- platform: linux
|
|
os: ubuntu-24.04-arm
|
|
arch: arm64
|
|
|
|
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
|
|
fetch-depth: 0
|
|
|
|
- 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: '1.26.4'
|
|
cache-dependency-path: |
|
|
core/go.sum
|
|
|
|
- name: Enable git long paths (Windows)
|
|
if: startsWith(matrix.os, 'windows')
|
|
run: git config --global core.longpaths true
|
|
|
|
- name: Setup Flutter
|
|
uses: subosito/flutter-action@v2
|
|
with:
|
|
channel: master
|
|
flutter-version: ${{ env.FLUTTER_VERSION }}
|
|
cache: true
|
|
|
|
- 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
|
|
with:
|
|
ndk-version: r28c
|
|
add-to-path: true
|
|
link-to-sdk: true
|
|
|
|
- name: Get Flutter Dependency
|
|
run: |
|
|
flutter --version
|
|
flutter pub get
|
|
|
|
- name: Setup
|
|
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 ]
|
|
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: true
|
|
|
|
- name: Install dependencies
|
|
run: flutter pub get
|
|
|
|
- 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/
|