633 lines
24 KiB
YAML
633 lines
24 KiB
YAML
name: Full build and Forgejo release
|
|
|
|
on:
|
|
push:
|
|
tags: ["v*"]
|
|
workflow_dispatch:
|
|
|
|
permissions:
|
|
contents: write
|
|
|
|
env:
|
|
GO_VERSION: "1.22"
|
|
SINGBOX_GO_VERSION: "1.24.x"
|
|
SINGBOX_VERSION: "1.13.0"
|
|
SINGBOX_SOURCE_URL: "https://github.com/SagerNet/sing-box.git"
|
|
SINGBOX_TAGS: "with_quic,with_wireguard,with_utls,with_clash_api,badlinkname,tfogo_checklinkname0"
|
|
XRAY_VERSION: "stable"
|
|
XRAY_SOURCE_URL: "https://github.com/XTLS/Xray-core.git"
|
|
ANDROID_API: "23"
|
|
ANDROID_NDK_VERSION: "27.2.12479018"
|
|
ANDROID_HOME: "/opt/android-sdk"
|
|
ANDROID_SDK_ROOT: "/opt/android-sdk"
|
|
|
|
jobs:
|
|
# ============================================================
|
|
# Job -1: Privacy/static policy guardrails
|
|
# ============================================================
|
|
privacy-lint:
|
|
name: Privacy Lint
|
|
runs-on: android
|
|
steps:
|
|
- uses: https://data.forgejo.org/actions/checkout@v6
|
|
|
|
- name: Check APK privacy surface
|
|
run: bash scripts/ci/check_apk_privacy.sh
|
|
|
|
- name: Check reset.lock script contract
|
|
run: bash scripts/ci/check_reset_lock_contract.sh
|
|
|
|
- name: Check runtime snapshot contract
|
|
run: python3 scripts/ci/check_runtime_snapshot_contract.py
|
|
|
|
- name: Check release manifest metadata
|
|
run: bash scripts/ci/check_release_manifest.sh
|
|
|
|
# ============================================================
|
|
# Job -0.5: Local-first runtime architecture guardrail
|
|
# ============================================================
|
|
local-first-runtime-guardrail:
|
|
name: Local-first Runtime Guardrail
|
|
runs-on: android
|
|
steps:
|
|
- uses: https://data.forgejo.org/actions/checkout@v6
|
|
|
|
- name: Check local-first runtime boundaries
|
|
run: bash scripts/ci/check_local_first_runtime.sh
|
|
|
|
# ============================================================
|
|
# Job 0: Go unit/policy tests
|
|
# ============================================================
|
|
go-test:
|
|
name: Go Tests
|
|
runs-on: android
|
|
steps:
|
|
- uses: https://data.forgejo.org/actions/checkout@v6
|
|
|
|
- name: Setup Go
|
|
uses: https://data.forgejo.org/actions/setup-go@v6
|
|
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: android
|
|
needs: [privacy-lint, local-first-runtime-guardrail]
|
|
steps:
|
|
- uses: https://data.forgejo.org/actions/checkout@v6
|
|
|
|
- name: Validate the preinstalled JDK 17
|
|
run: |
|
|
set -euo pipefail
|
|
java_major="$(java -XshowSettings:properties -version 2>&1 \
|
|
| awk -F'= ' '/java.specification.version =/{print $2}')"
|
|
test "$java_major" = "17"
|
|
java -version
|
|
javac -version
|
|
|
|
- name: Check local-first runtime boundaries
|
|
run: bash scripts/ci/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: Require instrumentation tests to use a KVM build host
|
|
working-directory: app
|
|
run: |
|
|
set -euo pipefail
|
|
if find app/src/androidTest -type f \( -name '*.kt' -o -name '*.java' \) 2>/dev/null | grep -q .; then
|
|
echo 'Android instrumentation tests require the external KVM build host.' >&2
|
|
exit 1
|
|
fi
|
|
echo 'No Android instrumentation tests are present.'
|
|
|
|
# ============================================================
|
|
# Job 1: Resolve sing-box release tag
|
|
# ============================================================
|
|
resolve-singbox:
|
|
name: Resolve sing-box release
|
|
runs-on: android
|
|
outputs:
|
|
version: ${{ steps.resolve.outputs.version }}
|
|
ref: ${{ steps.resolve.outputs.ref }}
|
|
steps:
|
|
- name: Resolve sing-box release
|
|
id: resolve
|
|
run: |
|
|
set -euo pipefail
|
|
VERSION="v${SINGBOX_VERSION#v}"
|
|
git ls-remote --exit-code --tags "${SINGBOX_SOURCE_URL}" \
|
|
"refs/tags/${VERSION}" >/dev/null
|
|
echo "version=${VERSION#v}" >> "$FORGEJO_OUTPUT"
|
|
echo "ref=$VERSION" >> "$FORGEJO_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: android
|
|
needs: [go-test]
|
|
strategy:
|
|
fail-fast: false
|
|
matrix:
|
|
include:
|
|
- arch: arm64
|
|
goarch: arm64
|
|
goarm: ""
|
|
- arch: armv7
|
|
goarch: arm
|
|
goarm: "7"
|
|
steps:
|
|
- uses: https://data.forgejo.org/actions/checkout@v6
|
|
|
|
- name: Setup Go
|
|
uses: https://data.forgejo.org/actions/setup-go@v6
|
|
with:
|
|
go-version: ${{ env.GO_VERSION }}
|
|
|
|
- name: Build runtime CLI
|
|
working-directory: runtime
|
|
run: |
|
|
if [[ "${FORGEJO_REF_NAME}" =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
|
|
VERSION="${FORGEJO_REF_NAME}"
|
|
CODE="$(../tools/version.sh code "$VERSION")"
|
|
else
|
|
VERSION="dev-${FORGEJO_RUN_NUMBER}-${FORGEJO_SHA::7}"
|
|
CODE="${FORGEJO_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: https://data.forgejo.org/forgejo/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: android
|
|
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
|
|
run: |
|
|
git clone --depth 1 --branch "${SINGBOX_REF}" \
|
|
"${SINGBOX_SOURCE_URL}" sing-box-src
|
|
|
|
- name: Setup Go for sing-box
|
|
uses: https://data.forgejo.org/actions/setup-go@v6
|
|
with:
|
|
go-version: ${{ env.SINGBOX_GO_VERSION }}
|
|
cache-dependency-path: sing-box-src/go.sum
|
|
|
|
- name: Validate the preinstalled Android NDK
|
|
run: |
|
|
set -euo pipefail
|
|
ndk_home="${ANDROID_HOME}/ndk/${ANDROID_NDK_VERSION}"
|
|
test -x "$ndk_home/toolchains/llvm/prebuilt/linux-x86_64/bin/aarch64-linux-android23-clang"
|
|
test -x "$ndk_home/toolchains/llvm/prebuilt/linux-x86_64/bin/armv7a-linux-androideabi23-clang"
|
|
echo "ANDROID_NDK_HOME=$ndk_home" >> "$FORGEJO_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: https://data.forgejo.org/forgejo/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: android
|
|
outputs:
|
|
version: ${{ steps.resolve.outputs.version }}
|
|
ref: ${{ steps.resolve.outputs.ref }}
|
|
steps:
|
|
- name: Resolve Xray-core release
|
|
id: resolve
|
|
run: |
|
|
set -euo pipefail
|
|
case "${XRAY_VERSION}" in
|
|
stable|"")
|
|
VERSION="$(curl -fsSL --retry 3 --connect-timeout 15 \
|
|
https://api.github.com/repos/XTLS/Xray-core/releases/latest \
|
|
| jq -r .tag_name)"
|
|
;;
|
|
latest|alpha|prerelease)
|
|
VERSION="$(curl -fsSL --retry 3 --connect-timeout 15 \
|
|
'https://api.github.com/repos/XTLS/Xray-core/releases?per_page=1' \
|
|
| jq -r '.[0].tag_name')"
|
|
;;
|
|
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}" >> "$FORGEJO_OUTPUT"
|
|
echo "ref=$VERSION" >> "$FORGEJO_OUTPUT"
|
|
echo "Resolved Xray-core $VERSION"
|
|
|
|
build-xray:
|
|
name: Build Xray-core (${{ matrix.arch }})
|
|
runs-on: android
|
|
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
|
|
env:
|
|
XRAY_REF: ${{ needs.resolve-xray.outputs.ref }}
|
|
run: |
|
|
git clone --depth 1 --branch "${XRAY_REF}" \
|
|
"${XRAY_SOURCE_URL}" xray-src
|
|
|
|
- name: Setup Go for Xray-core
|
|
uses: https://data.forgejo.org/actions/setup-go@v6
|
|
with:
|
|
# Xray-core raises its minimum Go version over time. The selected
|
|
# upstream tag is the source of truth, so a new stable release cannot
|
|
# silently outgrow a stale version duplicated in this workflow.
|
|
go-version-file: xray-src/go.mod
|
|
cache-dependency-path: xray-src/go.sum
|
|
|
|
- name: Validate the preinstalled Android NDK
|
|
run: |
|
|
set -euo pipefail
|
|
ndk_home="${ANDROID_HOME}/ndk/${ANDROID_NDK_VERSION}"
|
|
test -x "$ndk_home/toolchains/llvm/prebuilt/linux-x86_64/bin/aarch64-linux-android23-clang"
|
|
test -x "$ndk_home/toolchains/llvm/prebuilt/linux-x86_64/bin/armv7a-linux-androideabi23-clang"
|
|
echo "ANDROID_NDK_HOME=$ndk_home" >> "$FORGEJO_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: https://data.forgejo.org/forgejo/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: android
|
|
needs: [resolve-singbox, build-runtime, build-singbox, build-xray]
|
|
outputs:
|
|
singbox_version: ${{ needs.resolve-singbox.outputs.version }}
|
|
steps:
|
|
- uses: https://data.forgejo.org/actions/checkout@v6
|
|
|
|
- name: Download runtime CLI
|
|
uses: https://data.forgejo.org/forgejo/download-artifact@v4
|
|
with:
|
|
name: runtime-cli-arm64
|
|
path: module/binaries/arm64/
|
|
|
|
- name: Download runtime CLI (armv7)
|
|
uses: https://data.forgejo.org/forgejo/download-artifact@v4
|
|
with:
|
|
name: runtime-cli-armv7
|
|
path: module/binaries/armv7/
|
|
|
|
- name: Download sing-box binary
|
|
uses: https://data.forgejo.org/forgejo/download-artifact@v4
|
|
with:
|
|
name: singbox-arm64
|
|
path: module/binaries/arm64/
|
|
|
|
- name: Download sing-box binary (armv7)
|
|
uses: https://data.forgejo.org/forgejo/download-artifact@v4
|
|
with:
|
|
name: singbox-armv7
|
|
path: module/binaries/armv7/
|
|
|
|
- name: Download Xray-core binary
|
|
uses: https://data.forgejo.org/forgejo/download-artifact@v4
|
|
with:
|
|
name: xray-arm64
|
|
path: module/binaries/arm64/
|
|
|
|
- name: Download Xray-core binary (armv7)
|
|
uses: https://data.forgejo.org/forgejo/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 [[ "${FORGEJO_REF_NAME}" =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
|
|
VERSION="${FORGEJO_REF_NAME}"
|
|
CODE="$(tools/version.sh code "$VERSION")"
|
|
else
|
|
VERSION="dev-${FORGEJO_RUN_NUMBER}-${FORGEJO_SHA::7}"
|
|
CODE="${FORGEJO_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: https://data.forgejo.org/forgejo/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: android
|
|
needs: [privacy-lint, android-test]
|
|
steps:
|
|
- uses: https://data.forgejo.org/actions/checkout@v6
|
|
|
|
- name: Validate the preinstalled JDK 17
|
|
run: |
|
|
set -euo pipefail
|
|
java_major="$(java -XshowSettings:properties -version 2>&1 \
|
|
| awk -F'= ' '/java.specification.version =/{print $2}')"
|
|
test "$java_major" = "17"
|
|
java -version
|
|
javac -version
|
|
|
|
- name: Stamp version
|
|
run: |
|
|
if [[ "${FORGEJO_REF_NAME}" =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
|
|
VERSION="${FORGEJO_REF_NAME}"
|
|
VER_NAME="$VERSION"
|
|
VER_CODE="$(tools/version.sh code "$VERSION")"
|
|
else
|
|
VER_NAME="dev-${FORGEJO_RUN_NUMBER}-${FORGEJO_SHA::7}"
|
|
VER_CODE="${FORGEJO_RUN_NUMBER}"
|
|
fi
|
|
echo "RKNNOVPN_VERSION=${VER_NAME}" >> "$FORGEJO_ENV"
|
|
echo "RKNNOVPN_VERSION_CODE=${VER_CODE}" >> "$FORGEJO_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 [[ "${FORGEJO_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-${FORGEJO_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 ../scripts/ci/check_release_apk_privacy.sh ../rknnovpn-panel.apk
|
|
sha256sum ../rknnovpn-panel.apk
|
|
|
|
- name: Upload APK
|
|
uses: https://data.forgejo.org/forgejo/upload-artifact@v4
|
|
with:
|
|
name: rknnovpn-apk
|
|
path: rknnovpn-panel.apk
|
|
retention-days: 5
|
|
|
|
# ============================================================
|
|
# Job 6: Create Forgejo Release
|
|
# ============================================================
|
|
release:
|
|
name: Create Release
|
|
runs-on: android
|
|
if: forgejo.event_name == 'push' && startsWith(forgejo.ref, 'refs/tags/v')
|
|
needs: [build-module, build-apk]
|
|
steps:
|
|
- uses: https://data.forgejo.org/actions/checkout@v6
|
|
with:
|
|
fetch-depth: 0
|
|
|
|
- name: Resolve release version
|
|
id: version
|
|
run: |
|
|
if [[ "${FORGEJO_REF_NAME}" =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
|
|
VERSION="${FORGEJO_REF_NAME}"
|
|
STABLE=true
|
|
elif [[ "${FORGEJO_REF_NAME}" == v* ]]; then
|
|
echo "ERROR: release tags must be strict vMAJOR.MINOR.PATCH, got ${FORGEJO_REF_NAME}" >&2
|
|
exit 1
|
|
else
|
|
VERSION="dev-${FORGEJO_RUN_NUMBER}-${FORGEJO_SHA::7}"
|
|
STABLE=false
|
|
fi
|
|
echo "version=${VERSION}" >> "$FORGEJO_OUTPUT"
|
|
echo "stable=${STABLE}" >> "$FORGEJO_OUTPUT"
|
|
|
|
- name: Download Magisk module
|
|
uses: https://data.forgejo.org/forgejo/download-artifact@v4
|
|
with:
|
|
name: rknnovpn-module
|
|
path: release/
|
|
|
|
- name: Download APK
|
|
uses: https://data.forgejo.org/forgejo/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: Validate update.json for Magisk manager
|
|
if: steps.version.outputs.stable == 'true'
|
|
run: |
|
|
set -euo pipefail
|
|
VERSION="${{ steps.version.outputs.version }}"
|
|
VERSION_CODE="$(tools/version.sh code "$VERSION")"
|
|
test "$(jq -r .version update.json)" = "$VERSION"
|
|
test "$(jq -r .versionCode update.json)" = "$VERSION_CODE"
|
|
test "$(jq -r .zipUrl update.json)" = \
|
|
"https://git.zapret.moe/zapretdiscordyoutube/RKNnoVPN/releases/download/${VERSION}/rknnovpn-${VERSION}-module.zip"
|
|
test "$(jq -r .changelog update.json)" = \
|
|
"https://git.zapret.moe/zapretdiscordyoutube/RKNnoVPN/releases/tag/${VERSION}"
|
|
|
|
- name: Create Forgejo Release
|
|
uses: https://data.forgejo.org/actions/forgejo-release@v2.13.4
|
|
with:
|
|
direction: upload
|
|
tag: ${{ steps.version.outputs.version }}
|
|
sha: ${{ forgejo.sha }}
|
|
title: "RKNnoVPN ${{ steps.version.outputs.version }}"
|
|
release-dir: release
|
|
override: true
|
|
prerelease: ${{ steps.version.outputs.stable != 'true' }}
|
|
release-notes: |
|
|
## 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
|