chore(agents,tool): rebuild agent config, lint gates, CI, and release tooling

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
This commit is contained in:
chen08209
2026-09-05 20:09:26 +08:00
parent 62addf738a
commit 96fcb9ab5e
60 changed files with 5482 additions and 1010 deletions
-28
View File
@@ -1,28 +0,0 @@
#!/usr/bin/env bash
set -euo pipefail
previous_tag="${1:-}"
output="${2:-release.md}"
: > "$output"
append_range() {
local range="$1"
git log --no-merges --pretty=format:'%B' "$range" |
awk '!/Update changelog/ && NF {print "- " $0 "\n"}' >> "$output"
}
current_tag=""
while IFS= read -r next_tag; do
if [[ -n "$current_tag" ]]; then
[[ "$current_tag" == "$previous_tag" ]] && break
append_range "$next_tag..$current_tag"
fi
current_tag="$next_tag"
done < <(git tag --merged HEAD --sort=-creatordate)
if [[ -n "$current_tag" && "$current_tag" != "$previous_tag" ]]; then
append_range "$current_tag"
fi
@@ -1,66 +0,0 @@
#!/usr/bin/env bash
set -euo pipefail
script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
generator="$script_dir/generate_release_notes.sh"
temp_dir="$(mktemp -d)"
trap 'rm -rf "$temp_dir"' EXIT
repo="$temp_dir/repo"
git init --quiet --initial-branch=main "$repo"
git -C "$repo" config user.email "release-notes-test@example.com"
git -C "$repo" config user.name "Release notes test"
commit() {
local message="$1"
local date="$2"
GIT_AUTHOR_DATE="$date" GIT_COMMITTER_DATE="$date" \
git -C "$repo" commit --allow-empty --quiet --message "$message"
}
commit "Initial release" "2025-01-01T00:00:00Z"
git -C "$repo" tag v1.0.0
commit "First release change" "2025-01-02T00:00:00Z"
commit "Update changelog" "2025-01-03T00:00:00Z"
git -C "$repo" tag v1.1.0
git -C "$repo" branch feature
git -C "$repo" checkout --quiet feature
commit $'Feature branch change\n\nFeature detail' "2025-01-04T00:00:00Z"
git -C "$repo" checkout --quiet main
GIT_AUTHOR_DATE="2025-01-05T00:00:00Z" \
GIT_COMMITTER_DATE="2025-01-05T00:00:00Z" \
git -C "$repo" merge --no-ff --quiet --message "Merge feature" feature
git -C "$repo" tag v1.2.0
expected="$temp_dir/expected.md"
actual="$temp_dir/actual.md"
printf '%s\n' \
"- Feature branch change" \
"" \
"- Feature detail" \
"" \
"- First release change" \
"" > "$expected"
(
cd "$repo"
bash "$generator" v1.0.0 "$actual"
)
diff -u "$expected" "$actual"
printf '%s\n' "stale content" > "$actual"
(
cd "$repo"
bash "$generator" v1.2.0 "$actual"
)
[[ ! -s "$actual" ]]
(
cd "$repo"
bash "$generator" "" "$actual"
)
grep -q -- "- Initial release" "$actual"
+129 -97
View File
@@ -2,11 +2,18 @@ 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.44.4'
FLUTTER_VERSION: '3.47.1'
jobs:
test:
@@ -17,10 +24,15 @@ jobs:
uses: actions/checkout@v4
with:
submodules: recursive
fetch-depth: 0
- name: Test release notes generation
- name: Test commit message hook
shell: bash
run: bash .github/scripts/generate_release_notes_test.sh
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
@@ -32,41 +44,15 @@ jobs:
- 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: Run tests
run: flutter test --reporter expanded
- name: Validate local Flutter packages
shell: bash
run: |
for package in plugins/proxy plugins/wifi_ssid plugins/window_ext plugins/setup; do
(
cd "$package"
flutter pub get
flutter analyze --no-fatal-infos
)
done
- name: Run local plugin tests
shell: bash
run: |
(
cd plugins/proxy
flutter test --reporter expanded
)
(
cd plugins/wifi_ssid
flutter test --reporter expanded
)
- name: Validate setup build tool
working-directory: plugins/setup/buildkit/build_tool
run: |
dart pub get
dart analyze
dart test
- 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
@@ -74,13 +60,96 @@ jobs:
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 .
# `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: |
@@ -144,6 +213,7 @@ jobs:
uses: actions/checkout@v4
with:
submodules: recursive
fetch-depth: 0
- name: Setup Android Signing
if: startsWith(matrix.platform,'android')
@@ -203,61 +273,6 @@ jobs:
path: ./dist
overwrite: true
changelog:
if: ${{ !contains(github.ref, '-') }}
permissions:
contents: write
runs-on: ubuntu-latest
needs: [ build ]
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
ref: refs/heads/main
- name: Generate
run: |
last_ver=$(grep -m1 '^## ' CHANGELOG.md 2>/dev/null | sed 's/^## //')
mapfile -t tags < <(git tag --merged HEAD --sort=-creatordate)
temp="NEW_CHANGELOG.md"
: > "$temp"
for i in "${!tags[@]}"; do
curr="${tags[i]}"
[[ "$curr" == "$last_ver" ]] && break
prev="${tags[i+1]}"
range="${prev:+$prev..}$curr"
echo -e "## $curr\n" >> "$temp"
git log --no-merges --pretty=format:"%B" "$range" | \
awk '!/Update changelog/ && NF {print "- " $0 "\n"}' >> "$temp"
done
[ -f CHANGELOG.md ] && cat CHANGELOG.md >> "$temp"
mv "$temp" CHANGELOG.md
- name: Commit
run: |
git add CHANGELOG.md
if ! git diff --cached --quiet; then
echo "Commit pushing"
git config --local user.email "chen08209@gmail.com"
git config --local user.name "chen08209"
git commit -m "Update changelog"
if git push; then
echo "Push succeeded"
else
echo "Push failed"
exit 1
fi
fi
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
upload:
permissions:
contents: write
@@ -284,14 +299,31 @@ jobs:
pattern: artifact-*
merge-multiple: true
- name: Generate release.md
- 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: |
pre_tag=$(
curl --silent "https://api.github.com/repos/chen08209/FlClash/releases/latest" |
sed -nE 's/.*"tag_name": "([^"]+)".*/\1/p'
)
bash .github/scripts/generate_release_notes.sh "$pre_tag" release.md
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 }}