686 lines
24 KiB
YAML
686 lines
24 KiB
YAML
name: Build & Release
|
|
|
|
on:
|
|
push:
|
|
branches: [main]
|
|
tags: ["v*"]
|
|
pull_request:
|
|
branches: [main]
|
|
|
|
permissions:
|
|
contents: write
|
|
|
|
env:
|
|
GO_VERSION: "1.22"
|
|
SINGBOX_GO_VERSION: "1.24.x"
|
|
SINGBOX_VERSION: "1.13.0"
|
|
SINGBOX_TAGS: "with_quic,with_wireguard,with_utls,with_clash_api,badlinkname,tfogo_checklinkname0"
|
|
XRAY_GO_VERSION: "1.24.x"
|
|
XRAY_VERSION: "stable"
|
|
ANDROID_API: "23"
|
|
ANDROID_NDK_VERSION: "27.2.12479018"
|
|
|
|
jobs:
|
|
# ============================================================
|
|
# Job -1: Privacy/static policy guardrails
|
|
# ============================================================
|
|
privacy-lint:
|
|
name: Privacy Lint
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
|
|
- name: Check APK privacy surface
|
|
run: bash .github/scripts/check_apk_privacy.sh
|
|
|
|
- name: Check reset.lock script contract
|
|
run: bash .github/scripts/check_reset_lock_contract.sh
|
|
|
|
- name: Check runtime snapshot contract
|
|
run: python3 .github/scripts/check_runtime_snapshot_contract.py
|
|
|
|
- name: Check release manifest metadata
|
|
run: bash .github/scripts/check_release_manifest.sh
|
|
|
|
# ============================================================
|
|
# Job -0.5: Local-first runtime architecture guardrail
|
|
# ============================================================
|
|
local-first-runtime-guardrail:
|
|
name: Local-first Runtime Guardrail
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
|
|
- name: Check local-first runtime boundaries
|
|
run: bash .github/scripts/check_local_first_runtime.sh
|
|
|
|
# ============================================================
|
|
# Job 0: Go unit/policy tests
|
|
# ============================================================
|
|
go-test:
|
|
name: Go Tests
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
|
|
- name: Setup Go
|
|
uses: actions/setup-go@v5
|
|
with:
|
|
go-version: ${{ env.GO_VERSION }}
|
|
|
|
- name: Test runtime packages
|
|
working-directory: runtime
|
|
run: go test ./...
|
|
|
|
# ============================================================
|
|
# Job 0.5: Android debug build + unit/instrumentation tests
|
|
# ============================================================
|
|
android-test:
|
|
name: Android Guardrails & Tests
|
|
runs-on: ubuntu-latest
|
|
needs: [privacy-lint, local-first-runtime-guardrail]
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
|
|
- name: Setup JDK 17
|
|
uses: actions/setup-java@v4
|
|
with:
|
|
distribution: temurin
|
|
java-version: 17
|
|
|
|
- name: Setup Android SDK
|
|
uses: android-actions/setup-android@v3
|
|
|
|
- name: Setup Gradle
|
|
uses: gradle/actions/setup-gradle@v4
|
|
|
|
- name: Check local-first runtime boundaries
|
|
run: bash .github/scripts/check_local_first_runtime.sh
|
|
|
|
- name: Assemble debug APK
|
|
working-directory: app
|
|
run: ./gradlew :app:assembleDebug --no-daemon --stacktrace
|
|
|
|
- name: Run Android unit tests
|
|
working-directory: app
|
|
run: ./gradlew :app:testDebugUnitTest --no-daemon --stacktrace
|
|
|
|
- name: Detect Android instrumentation tests
|
|
id: android_instrumentation_tests
|
|
working-directory: app
|
|
run: |
|
|
if find app/src/androidTest -type f \( -name '*.kt' -o -name '*.java' \) 2>/dev/null | grep -q .; then
|
|
echo "present=true" >> "$GITHUB_OUTPUT"
|
|
else
|
|
echo "present=false" >> "$GITHUB_OUTPUT"
|
|
fi
|
|
|
|
- name: Run Android instrumentation tests
|
|
if: steps.android_instrumentation_tests.outputs.present == 'true'
|
|
uses: reactivecircus/android-emulator-runner@v2
|
|
with:
|
|
api-level: 35
|
|
arch: x86_64
|
|
working-directory: app
|
|
script: ./gradlew connectedDebugAndroidTest --no-daemon --stacktrace
|
|
|
|
# ============================================================
|
|
# Job 1: Resolve sing-box release tag
|
|
# ============================================================
|
|
resolve-singbox:
|
|
name: Resolve sing-box release
|
|
runs-on: ubuntu-latest
|
|
outputs:
|
|
version: ${{ steps.resolve.outputs.version }}
|
|
ref: ${{ steps.resolve.outputs.ref }}
|
|
steps:
|
|
- name: Resolve sing-box release
|
|
id: resolve
|
|
env:
|
|
GH_TOKEN: ${{ github.token }}
|
|
run: |
|
|
case "${SINGBOX_VERSION}" in
|
|
stable|"")
|
|
VERSION="$(gh release view --repo SagerNet/sing-box --json tagName --jq .tagName)"
|
|
;;
|
|
latest|alpha|prerelease)
|
|
VERSION="$(gh release list --repo SagerNet/sing-box --limit 1 --json tagName --jq '.[0].tagName')"
|
|
;;
|
|
*)
|
|
VERSION="v${SINGBOX_VERSION#v}"
|
|
;;
|
|
esac
|
|
if [[ -z "$VERSION" || "$VERSION" == "null" ]]; then
|
|
echo "Failed to resolve sing-box release" >&2
|
|
exit 1
|
|
fi
|
|
echo "version=${VERSION#v}" >> "$GITHUB_OUTPUT"
|
|
echo "ref=$VERSION" >> "$GITHUB_OUTPUT"
|
|
echo "Resolved sing-box $VERSION"
|
|
|
|
# ============================================================
|
|
# Job 2: Build root runtime CLI for Android ABIs
|
|
# ============================================================
|
|
build-runtime:
|
|
name: Build Runtime CLI (${{ matrix.arch }})
|
|
runs-on: ubuntu-latest
|
|
needs: [go-test]
|
|
strategy:
|
|
fail-fast: false
|
|
matrix:
|
|
include:
|
|
- arch: arm64
|
|
goarch: arm64
|
|
goarm: ""
|
|
- arch: armv7
|
|
goarch: arm
|
|
goarm: "7"
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
|
|
- name: Setup Go
|
|
uses: actions/setup-go@v5
|
|
with:
|
|
go-version: ${{ env.GO_VERSION }}
|
|
|
|
- name: Build runtime CLI
|
|
working-directory: runtime
|
|
run: |
|
|
if [[ "${GITHUB_REF_NAME}" =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
|
|
VERSION="${GITHUB_REF_NAME}"
|
|
CODE="$(../tools/version.sh code "$VERSION")"
|
|
else
|
|
VERSION="dev-${GITHUB_RUN_NUMBER}-${GITHUB_SHA::7}"
|
|
CODE="${GITHUB_RUN_NUMBER}"
|
|
fi
|
|
mkdir -p "../out/${{ matrix.arch }}"
|
|
CGO_ENABLED=0 GOOS=linux GOARCH=${{ matrix.goarch }} GOARM=${{ matrix.goarm }} go build \
|
|
-ldflags="-s -w -X main.Version=${VERSION} -X main.VersionCode=${CODE}" \
|
|
-o "../out/${{ matrix.arch }}/rknnovpn-runtime" ./cmd/rknnovpn-runtime
|
|
|
|
- name: Upload runtime CLI artifact
|
|
uses: actions/upload-artifact@v4
|
|
with:
|
|
name: runtime-cli-${{ matrix.arch }}
|
|
path: out/${{ matrix.arch }}/
|
|
retention-days: 1
|
|
|
|
# ============================================================
|
|
# Job 3: Build Android sing-box binaries for Android module runtime
|
|
# ============================================================
|
|
build-singbox:
|
|
name: Build sing-box (${{ matrix.arch }})
|
|
runs-on: ubuntu-latest
|
|
needs: [resolve-singbox]
|
|
env:
|
|
SINGBOX_RESOLVED_VERSION: ${{ needs.resolve-singbox.outputs.version }}
|
|
SINGBOX_REF: ${{ needs.resolve-singbox.outputs.ref }}
|
|
strategy:
|
|
fail-fast: false
|
|
matrix:
|
|
include:
|
|
- arch: arm64
|
|
goarch: arm64
|
|
goarm: ""
|
|
cc: aarch64-linux-android23-clang
|
|
linker: /system/bin/linker64
|
|
- arch: armv7
|
|
goarch: arm
|
|
goarm: "7"
|
|
cc: armv7a-linux-androideabi23-clang
|
|
linker: /system/bin/linker
|
|
steps:
|
|
- name: Checkout sing-box
|
|
uses: actions/checkout@v4
|
|
with:
|
|
repository: SagerNet/sing-box
|
|
ref: ${{ env.SINGBOX_REF }}
|
|
path: sing-box-src
|
|
|
|
- name: Setup Go for sing-box
|
|
uses: actions/setup-go@v5
|
|
with:
|
|
go-version: ${{ env.SINGBOX_GO_VERSION }}
|
|
cache-dependency-path: sing-box-src/go.sum
|
|
|
|
- name: Setup Android SDK
|
|
uses: android-actions/setup-android@v3
|
|
|
|
- name: Install Android NDK
|
|
run: |
|
|
SDKMANAGER="${ANDROID_HOME}/cmdline-tools/latest/bin/sdkmanager"
|
|
if [[ ! -x "$SDKMANAGER" ]]; then
|
|
SDKMANAGER="$(command -v sdkmanager || true)"
|
|
fi
|
|
if [[ -z "$SDKMANAGER" ]]; then
|
|
echo "sdkmanager not found" >&2
|
|
exit 127
|
|
fi
|
|
yes | "$SDKMANAGER" --licenses >/dev/null
|
|
"$SDKMANAGER" --install "ndk;${ANDROID_NDK_VERSION}"
|
|
echo "ANDROID_NDK_HOME=${ANDROID_HOME}/ndk/${ANDROID_NDK_VERSION}" >> "$GITHUB_ENV"
|
|
|
|
- name: Build Android sing-box
|
|
run: |
|
|
mkdir -p "out/${{ matrix.arch }}"
|
|
cd sing-box-src
|
|
if [[ -f release/LDFLAGS ]]; then
|
|
LDFLAGS="$(cat release/LDFLAGS)"
|
|
else
|
|
LDFLAGS="-X 'internal/godebug.defaultGODEBUG=multipathtcp=0' -checklinkname=0"
|
|
fi
|
|
export CC="${ANDROID_NDK_HOME}/toolchains/llvm/prebuilt/linux-x86_64/bin/${{ matrix.cc }}"
|
|
CGO_ENABLED=1 GOOS=android GOARCH=${{ matrix.goarch }} GOARM=${{ matrix.goarm }} go build \
|
|
-trimpath \
|
|
-tags "${SINGBOX_TAGS}" \
|
|
-ldflags "-X 'github.com/sagernet/sing-box/constant.Version=${SINGBOX_RESOLVED_VERSION}' ${LDFLAGS} -s -w -buildid=" \
|
|
-o "../out/${{ matrix.arch }}/sing-box" ./cmd/sing-box
|
|
cd ..
|
|
chmod 755 "out/${{ matrix.arch }}/sing-box"
|
|
if ! readelf -l "out/${{ matrix.arch }}/sing-box" | grep -q "Requesting program interpreter: ${{ matrix.linker }}"; then
|
|
echo "ERROR: sing-box is not an Android PIE binary for ${{ matrix.arch }}"
|
|
readelf -l "out/${{ matrix.arch }}/sing-box"
|
|
exit 1
|
|
fi
|
|
readelf -h "out/${{ matrix.arch }}/sing-box"
|
|
sha256sum "out/${{ matrix.arch }}/sing-box"
|
|
|
|
- name: Upload sing-box artifact
|
|
uses: actions/upload-artifact@v4
|
|
with:
|
|
name: singbox-${{ matrix.arch }}
|
|
path: out/${{ matrix.arch }}/
|
|
retention-days: 1
|
|
|
|
# ============================================================
|
|
# Job 4: Build Android Xray-core sidecar for VLESS/XHTTP only
|
|
# ============================================================
|
|
resolve-xray:
|
|
name: Resolve Xray-core release
|
|
runs-on: ubuntu-latest
|
|
outputs:
|
|
version: ${{ steps.resolve.outputs.version }}
|
|
ref: ${{ steps.resolve.outputs.ref }}
|
|
steps:
|
|
- name: Resolve Xray-core release
|
|
id: resolve
|
|
env:
|
|
GH_TOKEN: ${{ github.token }}
|
|
run: |
|
|
case "${XRAY_VERSION}" in
|
|
stable|"")
|
|
VERSION="$(gh release view --repo XTLS/Xray-core --json tagName --jq .tagName)"
|
|
;;
|
|
latest|alpha|prerelease)
|
|
VERSION="$(gh release list --repo XTLS/Xray-core --limit 1 --json tagName --jq '.[0].tagName')"
|
|
;;
|
|
v*)
|
|
VERSION="${XRAY_VERSION}"
|
|
;;
|
|
*)
|
|
VERSION="v${XRAY_VERSION#v}"
|
|
;;
|
|
esac
|
|
if [[ -z "$VERSION" || "$VERSION" == "null" ]]; then
|
|
echo "Failed to resolve Xray-core release" >&2
|
|
exit 1
|
|
fi
|
|
echo "version=${VERSION#v}" >> "$GITHUB_OUTPUT"
|
|
echo "ref=$VERSION" >> "$GITHUB_OUTPUT"
|
|
echo "Resolved Xray-core $VERSION"
|
|
|
|
build-xray:
|
|
name: Build Xray-core (${{ matrix.arch }})
|
|
runs-on: ubuntu-latest
|
|
needs: [resolve-xray]
|
|
strategy:
|
|
fail-fast: false
|
|
matrix:
|
|
include:
|
|
- arch: arm64
|
|
goarch: arm64
|
|
goarm: ""
|
|
cc: aarch64-linux-android23-clang
|
|
linker: /system/bin/linker64
|
|
- arch: armv7
|
|
goarch: arm
|
|
goarm: "7"
|
|
cc: armv7a-linux-androideabi23-clang
|
|
linker: /system/bin/linker
|
|
steps:
|
|
- name: Checkout Xray-core
|
|
uses: actions/checkout@v4
|
|
with:
|
|
repository: XTLS/Xray-core
|
|
ref: ${{ needs.resolve-xray.outputs.ref }}
|
|
path: xray-src
|
|
|
|
- name: Setup Go for Xray-core
|
|
uses: actions/setup-go@v5
|
|
with:
|
|
go-version: ${{ env.XRAY_GO_VERSION }}
|
|
cache-dependency-path: xray-src/go.sum
|
|
|
|
- name: Setup Android SDK
|
|
uses: android-actions/setup-android@v3
|
|
|
|
- name: Install Android NDK
|
|
run: |
|
|
SDKMANAGER="${ANDROID_HOME}/cmdline-tools/latest/bin/sdkmanager"
|
|
if [[ ! -x "$SDKMANAGER" ]]; then
|
|
SDKMANAGER="$(command -v sdkmanager || true)"
|
|
fi
|
|
if [[ -z "$SDKMANAGER" ]]; then
|
|
echo "sdkmanager not found" >&2
|
|
exit 127
|
|
fi
|
|
yes | "$SDKMANAGER" --licenses >/dev/null
|
|
"$SDKMANAGER" --install "ndk;${ANDROID_NDK_VERSION}"
|
|
echo "ANDROID_NDK_HOME=${ANDROID_HOME}/ndk/${ANDROID_NDK_VERSION}" >> "$GITHUB_ENV"
|
|
|
|
- name: Build Android Xray-core
|
|
run: |
|
|
mkdir -p "out/${{ matrix.arch }}"
|
|
cd xray-src
|
|
export CC="${ANDROID_NDK_HOME}/toolchains/llvm/prebuilt/linux-x86_64/bin/${{ matrix.cc }}"
|
|
CGO_ENABLED=1 GOOS=android GOARCH=${{ matrix.goarch }} GOARM=${{ matrix.goarm }} go build \
|
|
-trimpath \
|
|
-ldflags "-s -w -buildid=" \
|
|
-o "../out/${{ matrix.arch }}/xray" ./main
|
|
cd ..
|
|
chmod 755 "out/${{ matrix.arch }}/xray"
|
|
if ! readelf -l "out/${{ matrix.arch }}/xray" | grep -q "Requesting program interpreter: ${{ matrix.linker }}"; then
|
|
echo "ERROR: xray is not an Android PIE binary for ${{ matrix.arch }}"
|
|
readelf -l "out/${{ matrix.arch }}/xray"
|
|
exit 1
|
|
fi
|
|
readelf -h "out/${{ matrix.arch }}/xray"
|
|
sha256sum "out/${{ matrix.arch }}/xray"
|
|
|
|
- name: Upload Xray-core artifact
|
|
uses: actions/upload-artifact@v4
|
|
with:
|
|
name: xray-${{ matrix.arch }}
|
|
path: out/${{ matrix.arch }}/
|
|
retention-days: 1
|
|
|
|
# ============================================================
|
|
# Job 5: Assemble Magisk module ZIP
|
|
# ============================================================
|
|
build-module:
|
|
name: Build Magisk Module
|
|
runs-on: ubuntu-latest
|
|
needs: [resolve-singbox, build-runtime, build-singbox, build-xray]
|
|
outputs:
|
|
singbox_version: ${{ needs.resolve-singbox.outputs.version }}
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
|
|
- name: Download runtime CLI
|
|
uses: actions/download-artifact@v4
|
|
with:
|
|
name: runtime-cli-arm64
|
|
path: module/binaries/arm64/
|
|
|
|
- name: Download runtime CLI (armv7)
|
|
uses: actions/download-artifact@v4
|
|
with:
|
|
name: runtime-cli-armv7
|
|
path: module/binaries/armv7/
|
|
|
|
- name: Download sing-box binary
|
|
uses: actions/download-artifact@v4
|
|
with:
|
|
name: singbox-arm64
|
|
path: module/binaries/arm64/
|
|
|
|
- name: Download sing-box binary (armv7)
|
|
uses: actions/download-artifact@v4
|
|
with:
|
|
name: singbox-armv7
|
|
path: module/binaries/armv7/
|
|
|
|
- name: Download Xray-core binary
|
|
uses: actions/download-artifact@v4
|
|
with:
|
|
name: xray-arm64
|
|
path: module/binaries/arm64/
|
|
|
|
- name: Download Xray-core binary (armv7)
|
|
uses: actions/download-artifact@v4
|
|
with:
|
|
name: xray-armv7
|
|
path: module/binaries/armv7/
|
|
|
|
- name: Set permissions
|
|
run: chmod 755 module/binaries/arm64/* module/binaries/armv7/*
|
|
|
|
- name: Stamp version in module.prop
|
|
run: |
|
|
if [[ "${GITHUB_REF_NAME}" =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
|
|
VERSION="${GITHUB_REF_NAME}"
|
|
CODE="$(tools/version.sh code "$VERSION")"
|
|
else
|
|
VERSION="dev-${GITHUB_RUN_NUMBER}-${GITHUB_SHA::7}"
|
|
CODE="${GITHUB_RUN_NUMBER}"
|
|
fi
|
|
sed -i "s/^version=.*/version=${VERSION}/" module/module.prop
|
|
sed -i "s/^versionCode=.*/versionCode=${CODE}/" module/module.prop
|
|
cat module/module.prop
|
|
|
|
- name: Build ZIP
|
|
run: |
|
|
cd module
|
|
zip -r ../rknnovpn-module.zip . \
|
|
-x "*.git*" "*.DS_Store"
|
|
cd ..
|
|
for dir in arm64 armv7; do
|
|
unzip -l rknnovpn-module.zip "binaries/${dir}/rknnovpn-runtime" "binaries/${dir}/sing-box" "binaries/${dir}/xray" >/dev/null
|
|
done
|
|
sha256sum rknnovpn-module.zip
|
|
|
|
- name: Upload module ZIP
|
|
uses: actions/upload-artifact@v4
|
|
with:
|
|
name: rknnovpn-module
|
|
path: rknnovpn-module.zip
|
|
retention-days: 5
|
|
|
|
# ============================================================
|
|
# Job 5: Build Android APK
|
|
# ============================================================
|
|
build-apk:
|
|
name: Build APK
|
|
runs-on: ubuntu-latest
|
|
needs: [privacy-lint, android-test]
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
|
|
- name: Setup JDK 17
|
|
uses: actions/setup-java@v4
|
|
with:
|
|
distribution: temurin
|
|
java-version: 17
|
|
|
|
- name: Setup Android SDK
|
|
uses: android-actions/setup-android@v3
|
|
|
|
- name: Setup Gradle
|
|
uses: gradle/actions/setup-gradle@v4
|
|
|
|
- name: Stamp version
|
|
run: |
|
|
if [[ "${GITHUB_REF_NAME}" =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
|
|
VERSION="${GITHUB_REF_NAME}"
|
|
VER_NAME="$VERSION"
|
|
VER_CODE="$(tools/version.sh code "$VERSION")"
|
|
else
|
|
VER_NAME="dev-${GITHUB_RUN_NUMBER}-${GITHUB_SHA::7}"
|
|
VER_CODE="${GITHUB_RUN_NUMBER}"
|
|
fi
|
|
echo "RKNNOVPN_VERSION=${VER_NAME}" >> "$GITHUB_ENV"
|
|
echo "RKNNOVPN_VERSION_CODE=${VER_CODE}" >> "$GITHUB_ENV"
|
|
|
|
- name: Build and sign APK
|
|
working-directory: app
|
|
env:
|
|
RKNNOVPN_RELEASE_KEYSTORE_BASE64: ${{ secrets.RKNNOVPN_RELEASE_KEYSTORE_BASE64 }}
|
|
RKNNOVPN_RELEASE_STORE_PASSWORD: ${{ secrets.RKNNOVPN_RELEASE_STORE_PASSWORD }}
|
|
RKNNOVPN_RELEASE_KEY_ALIAS: ${{ secrets.RKNNOVPN_RELEASE_KEY_ALIAS }}
|
|
RKNNOVPN_RELEASE_KEY_PASSWORD: ${{ secrets.RKNNOVPN_RELEASE_KEY_PASSWORD }}
|
|
run: |
|
|
STORE_FILE="${RUNNER_TEMP}/rknnovpn-release.jks"
|
|
if [ -n "${RKNNOVPN_RELEASE_KEYSTORE_BASE64:-}" ]; then
|
|
printf '%s' "${RKNNOVPN_RELEASE_KEYSTORE_BASE64}" | base64 -d > "${STORE_FILE}"
|
|
elif [[ "${GITHUB_REF_NAME}" =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
|
|
echo "ERROR: stable tag builds require RKNNOVPN_RELEASE_KEYSTORE_BASE64 signing secret" >&2
|
|
exit 1
|
|
else
|
|
RKNNOVPN_RELEASE_STORE_PASSWORD="dev-release-store-${GITHUB_RUN_ID}"
|
|
RKNNOVPN_RELEASE_KEY_PASSWORD="${RKNNOVPN_RELEASE_STORE_PASSWORD}"
|
|
RKNNOVPN_RELEASE_KEY_ALIAS="rknnovpn-dev"
|
|
keytool -genkeypair \
|
|
-keystore "${STORE_FILE}" \
|
|
-storepass "${RKNNOVPN_RELEASE_STORE_PASSWORD}" \
|
|
-keypass "${RKNNOVPN_RELEASE_KEY_PASSWORD}" \
|
|
-alias "${RKNNOVPN_RELEASE_KEY_ALIAS}" \
|
|
-keyalg RSA \
|
|
-keysize 4096 \
|
|
-validity 30 \
|
|
-dname "CN=RKNnoVPN Dev Release,O=RKNnoVPN,C=RU"
|
|
fi
|
|
export RKNNOVPN_RELEASE_STORE_FILE="${STORE_FILE}"
|
|
export RKNNOVPN_RELEASE_STORE_PASSWORD
|
|
export RKNNOVPN_RELEASE_KEY_ALIAS
|
|
export RKNNOVPN_RELEASE_KEY_PASSWORD
|
|
|
|
./gradlew assembleRelease --no-daemon --stacktrace
|
|
APK=$(find app/build/outputs/apk/release -name "*.apk" | head -1)
|
|
if [ -z "$APK" ]; then
|
|
echo "ERROR: No APK found!"
|
|
find . -name "*.apk" -ls
|
|
exit 1
|
|
fi
|
|
echo "APK found: $APK"
|
|
cp "$APK" ../rknnovpn-panel.apk
|
|
bash ../.github/scripts/check_release_apk_privacy.sh ../rknnovpn-panel.apk
|
|
sha256sum ../rknnovpn-panel.apk
|
|
|
|
- name: Upload APK
|
|
uses: actions/upload-artifact@v4
|
|
with:
|
|
name: rknnovpn-apk
|
|
path: rknnovpn-panel.apk
|
|
retention-days: 5
|
|
|
|
# ============================================================
|
|
# Job 6: Create GitHub Release
|
|
# ============================================================
|
|
release:
|
|
name: Create Release
|
|
runs-on: ubuntu-latest
|
|
if: github.event_name == 'push' && (github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/v'))
|
|
needs: [build-module, build-apk]
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
with:
|
|
fetch-depth: 0
|
|
|
|
- name: Resolve release version
|
|
id: version
|
|
run: |
|
|
if [[ "${GITHUB_REF_NAME}" =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
|
|
VERSION="${GITHUB_REF_NAME}"
|
|
STABLE=true
|
|
elif [[ "${GITHUB_REF_NAME}" == v* ]]; then
|
|
echo "ERROR: release tags must be strict vMAJOR.MINOR.PATCH, got ${GITHUB_REF_NAME}" >&2
|
|
exit 1
|
|
else
|
|
VERSION="dev-${GITHUB_RUN_NUMBER}-${GITHUB_SHA::7}"
|
|
STABLE=false
|
|
fi
|
|
echo "version=${VERSION}" >> "$GITHUB_OUTPUT"
|
|
echo "stable=${STABLE}" >> "$GITHUB_OUTPUT"
|
|
|
|
- name: Download Magisk module
|
|
uses: actions/download-artifact@v4
|
|
with:
|
|
name: rknnovpn-module
|
|
path: release/
|
|
|
|
- name: Download APK
|
|
uses: actions/download-artifact@v4
|
|
with:
|
|
name: rknnovpn-apk
|
|
path: release/
|
|
|
|
- name: Rename with version
|
|
run: |
|
|
VERSION="${{ steps.version.outputs.version }}"
|
|
mv release/rknnovpn-module.zip "release/rknnovpn-${VERSION}-module.zip"
|
|
mv release/rknnovpn-panel.apk "release/rknnovpn-${VERSION}-panel.apk"
|
|
# Generate checksums
|
|
cd release && sha256sum * > SHA256SUMS.txt && cd ..
|
|
|
|
- name: Update update.json for Magisk manager
|
|
if: steps.version.outputs.stable == 'true'
|
|
run: |
|
|
VERSION="${{ steps.version.outputs.version }}"
|
|
VERSION_CODE="$(tools/version.sh code "$VERSION")"
|
|
cat > update.json <<ENDJSON
|
|
{
|
|
"version": "${VERSION}",
|
|
"versionCode": ${VERSION_CODE},
|
|
"zipUrl": "https://github.com/youtubediscord/RKNnoVPN/releases/download/${VERSION}/rknnovpn-${VERSION}-module.zip",
|
|
"changelog": "https://github.com/youtubediscord/RKNnoVPN/releases/tag/${VERSION}"
|
|
}
|
|
ENDJSON
|
|
# Commit and push the updated update.json to main
|
|
git config user.name "github-actions[bot]"
|
|
git config user.email "github-actions[bot]@users.noreply.github.com"
|
|
git add update.json
|
|
git diff --cached --quiet || git commit -m "chore: update update.json for ${VERSION} [skip ci]"
|
|
git fetch origin main
|
|
git merge-base --is-ancestor origin/main HEAD
|
|
git push origin HEAD:main
|
|
|
|
- name: Create Release
|
|
uses: softprops/action-gh-release@v2
|
|
with:
|
|
tag_name: ${{ steps.version.outputs.version }}
|
|
target_commitish: ${{ github.sha }}
|
|
name: "RKNnoVPN ${{ steps.version.outputs.version }}"
|
|
draft: false
|
|
prerelease: ${{ steps.version.outputs.stable != 'true' }}
|
|
generate_release_notes: true
|
|
body: |
|
|
## RKNnoVPN ${{ steps.version.outputs.version }}
|
|
|
|
### Downloads
|
|
|
|
| File | Description |
|
|
|------|-------------|
|
|
| `rknnovpn-${{ steps.version.outputs.version }}-module.zip` | Magisk/KernelSU/APatch module - flash via manager |
|
|
| `rknnovpn-${{ steps.version.outputs.version }}-panel.apk` | Controller APK - install on same device |
|
|
|
|
### Quick Start
|
|
|
|
1. Flash the module ZIP via Magisk Manager / KSU Manager / APatch
|
|
2. Reboot
|
|
3. Install the APK
|
|
4. Open RKNnoVPN Panel and add a server (paste VLESS/Trojan/SS link)
|
|
5. Select apps to proxy in the Apps tab
|
|
6. Tap Connect
|
|
|
|
### What's inside
|
|
|
|
- **No VPN, no TUN, no icon** - uses tproxy+iptables at kernel level
|
|
- **Whitelist mode** - only selected apps go through proxy
|
|
- **Full IPv4+IPv6** - mirrored iptables/ip6tables rules
|
|
- **sing-box v${{ needs.build-module.outputs.singbox_version }}** - VLESS, Trojan, VMess, SS, SOCKS, Hysteria2, TUIC
|
|
- **Xray-core sidecar** - used only for VLESS/XHTTP profiles
|
|
- **Explicit cleanup** - reset/cleanup scripts for root networking state
|
|
files: |
|
|
release/rknnovpn-${{ steps.version.outputs.version }}-module.zip
|
|
release/rknnovpn-${{ steps.version.outputs.version }}-panel.apk
|
|
release/SHA256SUMS.txt
|