mirror of
https://github.com/MHSanaei/3x-ui.git
synced 2026-06-06 21:24:10 +00:00
356 lines
12 KiB
YAML
356 lines
12 KiB
YAML
name: Release 3X-UI
|
|
|
|
on:
|
|
workflow_dispatch:
|
|
push:
|
|
tags:
|
|
- "v*.*"
|
|
pull_request:
|
|
paths:
|
|
- '**.js'
|
|
- '**.css'
|
|
- '**.html'
|
|
- '**.sh'
|
|
- '**.go'
|
|
- 'go.mod'
|
|
- 'go.sum'
|
|
- 'x-ui.service.debian'
|
|
- 'x-ui.service.arch'
|
|
- 'x-ui.service.rhel'
|
|
|
|
concurrency:
|
|
group: ${{ github.workflow }}-${{ github.ref }}
|
|
cancel-in-progress: true
|
|
|
|
jobs:
|
|
analyze:
|
|
name: Analyze Go code
|
|
permissions:
|
|
contents: read
|
|
runs-on: ubuntu-latest
|
|
timeout-minutes: 20
|
|
steps:
|
|
- name: Checkout repository
|
|
uses: actions/checkout@v6
|
|
|
|
- name: Set up Go
|
|
uses: actions/setup-go@v6
|
|
with:
|
|
go-version-file: go.mod
|
|
cache: true
|
|
|
|
- name: Check formatting
|
|
run: |
|
|
unformatted=$(gofmt -l .)
|
|
if [ -n "$unformatted" ]; then
|
|
echo "These files are not gofmt-formatted:"
|
|
echo "$unformatted"
|
|
exit 1
|
|
fi
|
|
|
|
- name: Run go vet
|
|
run: go vet ./...
|
|
|
|
- name: Run staticcheck
|
|
uses: dominikh/staticcheck-action@v1
|
|
with:
|
|
version: "latest"
|
|
install-go: false
|
|
|
|
- name: Run tests
|
|
run: go test -race -shuffle=on ./...
|
|
|
|
build:
|
|
timeout-minutes: 30
|
|
permissions:
|
|
contents: write
|
|
strategy:
|
|
matrix:
|
|
platform:
|
|
- amd64
|
|
# 手动注释,请勿更改
|
|
# - arm64
|
|
# 手动注释,请勿更改
|
|
# - armv7
|
|
# 手动注释,请勿更改
|
|
# - armv6
|
|
# 手动注释,请勿更改
|
|
# - 386
|
|
# 手动注释,请勿更改
|
|
# - armv5
|
|
# 手动注释,请勿更改
|
|
# - s390x
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
- name: Checkout repository
|
|
uses: actions/checkout@v6
|
|
|
|
- name: Setup Go
|
|
uses: actions/setup-go@v6
|
|
with:
|
|
go-version-file: go.mod
|
|
check-latest: true
|
|
|
|
- name: Cache Go build
|
|
uses: actions/cache@v4
|
|
with:
|
|
path: |
|
|
~/.cache/go-build
|
|
key: gobuild-${{ matrix.platform }}-${{ hashFiles('**/*.go', 'go.mod', 'go.sum') }}
|
|
restore-keys: |
|
|
gobuild-${{ matrix.platform }}-
|
|
|
|
- name: Get latest Xray version
|
|
id: xray_version
|
|
run: |
|
|
LATEST=$(curl -fsSL https://api.github.com/repos/XTLS/Xray-core/releases/latest | jq -r '.tag_name')
|
|
echo "version=$LATEST" >> "$GITHUB_OUTPUT"
|
|
echo "Latest Xray version: $LATEST"
|
|
|
|
- name: Cache Xray binary
|
|
id: cache_xray
|
|
uses: actions/cache@v4
|
|
with:
|
|
path: /tmp/xray
|
|
key: xray-${{ matrix.platform }}-${{ steps.xray_version.outputs.version }}
|
|
restore-keys: |
|
|
xray-${{ matrix.platform }}-
|
|
|
|
- name: Download Xray
|
|
if: steps.cache_xray.outputs.cache-hit != 'true'
|
|
run: |
|
|
Xray_URL="https://github.com/XTLS/Xray-core/releases/download/${{ steps.xray_version.outputs.version }}/"
|
|
declare -A XRAY_MAP=(
|
|
[amd64]="Xray-linux-64.zip"
|
|
[arm64]="Xray-linux-arm64-v8a.zip"
|
|
[armv7]="Xray-linux-arm32-v7a.zip"
|
|
[armv6]="Xray-linux-arm32-v6.zip"
|
|
[386]="Xray-linux-32.zip"
|
|
[armv5]="Xray-linux-arm32-v5.zip"
|
|
[s390x]="Xray-linux-s390x.zip"
|
|
)
|
|
XRAY_FILE="${XRAY_MAP[${{ matrix.platform }}]}"
|
|
[ -z "$XRAY_FILE" ] && { echo "Unknown platform: ${{ matrix.platform }}" >&2; exit 1; }
|
|
mkdir -p /tmp/xray
|
|
curl -fL -sS --retry 3 --retry-delay 5 -o "/tmp/xray/$XRAY_FILE" "${Xray_URL}${XRAY_FILE}" || { echo "Failed to download Xray" >&2; exit 1; }
|
|
cd /tmp/xray
|
|
unzip -q "$XRAY_FILE" || { echo "Failed to unzip Xray" >&2; exit 1; }
|
|
rm -f "$XRAY_FILE"
|
|
[ -f xray ] || { echo "xray binary not found after extraction" >&2; exit 1; }
|
|
|
|
- name: Get cache date
|
|
id: cache_date
|
|
run: echo "date=$(date -u +%Y-%m-%d)" >> "$GITHUB_OUTPUT"
|
|
|
|
- name: Cache geo data
|
|
id: cache_geo
|
|
uses: actions/cache@v4
|
|
with:
|
|
path: /tmp/geo
|
|
key: geo-${{ steps.cache_date.outputs.date }}
|
|
|
|
- name: Download geo data
|
|
if: steps.cache_geo.outputs.cache-hit != 'true'
|
|
run: |
|
|
mkdir -p /tmp/geo
|
|
cd /tmp/geo
|
|
curl -fL -sS --retry 3 -o geoip.dat \
|
|
https://github.com/Loyalsoldier/v2ray-rules-dat/releases/latest/download/geoip.dat &
|
|
curl -fL -sS --retry 3 -o geosite.dat \
|
|
https://github.com/Loyalsoldier/v2ray-rules-dat/releases/latest/download/geosite.dat &
|
|
FAILED=0
|
|
for job in $(jobs -p); do wait "$job" || FAILED=1; done
|
|
if [ "$FAILED" -eq 1 ]; then
|
|
echo "One or more geo data downloads failed" >&2
|
|
exit 1
|
|
fi
|
|
for f in *.dat; do
|
|
if [ ! -s "$f" ]; then
|
|
echo "Geo file $f is empty" >&2; exit 1
|
|
fi
|
|
done
|
|
|
|
- name: Cache Bootlin toolchain
|
|
uses: actions/cache@v4
|
|
with:
|
|
path: /tmp/toolchain
|
|
key: bootlin-${{ matrix.platform }}-${{ hashFiles('.github/workflows/release.yml') }}
|
|
restore-keys: |
|
|
bootlin-${{ matrix.platform }}-
|
|
|
|
- name: Build 3X-UI
|
|
run: |
|
|
export CGO_ENABLED=1
|
|
export GOOS=linux
|
|
export GOARCH=${{ matrix.platform }}
|
|
# Use Bootlin prebuilt cross-toolchains (musl 1.2.5 in stable series)
|
|
case "${{ matrix.platform }}" in
|
|
amd64) BOOTLIN_ARCH="x86-64" ;;
|
|
arm64) BOOTLIN_ARCH="aarch64" ;;
|
|
armv7) BOOTLIN_ARCH="armv7-eabihf"; export GOARCH=arm GOARM=7 ;;
|
|
armv6) BOOTLIN_ARCH="armv6-eabihf"; export GOARCH=arm GOARM=6 ;;
|
|
armv5) BOOTLIN_ARCH="armv5-eabi"; export GOARCH=arm GOARM=5 ;;
|
|
386) BOOTLIN_ARCH="x86-i686" ;;
|
|
s390x) BOOTLIN_ARCH="s390x-z13" ;;
|
|
esac
|
|
|
|
TOOLCHAIN_DIR=$(find /tmp/toolchain -maxdepth 1 -type d -name "${BOOTLIN_ARCH}--musl--stable-*" 2>/dev/null | head -n1)
|
|
|
|
if [ -z "$TOOLCHAIN_DIR" ]; then
|
|
echo "Cache miss, downloading toolchain..."
|
|
mkdir -p /tmp/toolchain
|
|
TARBALL_BASE="https://toolchains.bootlin.com/downloads/releases/toolchains/$BOOTLIN_ARCH/tarballs/"
|
|
TARBALL_URL=$(curl -fsSL "$TARBALL_BASE" | grep -oE "${BOOTLIN_ARCH}--musl--stable-[^\"]+\\.tar\\.xz" | sort -r | head -n1)
|
|
[ -z "$TARBALL_URL" ] && { echo "Failed to locate Bootlin musl toolchain for arch=$BOOTLIN_ARCH" >&2; exit 1; }
|
|
echo "Downloading: $TARBALL_URL"
|
|
cd /tmp/toolchain
|
|
curl -fL -sS --retry 3 --retry-delay 5 -o "$(basename "$TARBALL_URL")" "$TARBALL_BASE/$TARBALL_URL" || { echo "Failed to download toolchain" >&2; exit 1; }
|
|
tar -xf "$(basename "$TARBALL_URL")" || { echo "Failed to extract toolchain tarball" >&2; exit 1; }
|
|
TOOLCHAIN_DIR=$(find /tmp/toolchain -maxdepth 1 -type d -name "${BOOTLIN_ARCH}--musl--stable-*" | head -n1)
|
|
cd -
|
|
else
|
|
echo "Using cached toolchain: $TOOLCHAIN_DIR"
|
|
fi
|
|
|
|
export PATH="$(realpath "$TOOLCHAIN_DIR")/bin:$PATH"
|
|
export CC=$(realpath "$(find "$TOOLCHAIN_DIR/bin" -name '*-gcc.br_real' -type f -executable | head -n1)")
|
|
[ -z "$CC" ] && { echo "No gcc.br_real found in $TOOLCHAIN_DIR/bin" >&2; exit 1; }
|
|
go build -ldflags "-w -s -linkmode external -extldflags '-static'" -o xui-release -v main.go
|
|
file xui-release
|
|
ldd xui-release || echo "Static binary confirmed"
|
|
|
|
mkdir x-ui
|
|
cp xui-release x-ui/
|
|
cp x-ui.service.debian x-ui/
|
|
cp x-ui.service.arch x-ui/
|
|
cp x-ui.service.rhel x-ui/
|
|
cp x-ui.sh x-ui/
|
|
mv x-ui/xui-release x-ui/x-ui
|
|
mkdir x-ui/bin
|
|
cd x-ui/bin
|
|
|
|
# Copy Xray from cache
|
|
cp /tmp/xray/xray .
|
|
|
|
# Copy geo data from cache
|
|
cp /tmp/geo/* .
|
|
|
|
mv xray xray-linux-${{ matrix.platform }}
|
|
cd ../..
|
|
|
|
- name: Package
|
|
run: tar -zcvf x-ui-linux-${{ matrix.platform }}.tar.gz x-ui
|
|
|
|
- name: Upload files to Artifacts
|
|
uses: actions/upload-artifact@v7
|
|
with:
|
|
name: x-ui-linux-${{ matrix.platform }}
|
|
path: ./x-ui-linux-${{ matrix.platform }}.tar.gz
|
|
|
|
- name: Create release and upload assets
|
|
if: startsWith(github.ref, 'refs/tags/')
|
|
env:
|
|
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
run: |
|
|
TAG="${{ github.ref_name }}"
|
|
if ! gh release view "$TAG" > /dev/null 2>&1; then
|
|
gh release create "$TAG" --prerelease --title "$TAG" --generate-notes \
|
|
--notes "**Xray-core:** ${{ steps.xray_version.outputs.version }}"
|
|
sleep 5
|
|
fi
|
|
for i in 1 2 3; do
|
|
gh release upload "$TAG" x-ui-linux-${{ matrix.platform }}.tar.gz --clobber && break
|
|
echo "Upload attempt $i failed, retrying in 5s..."
|
|
sleep 5
|
|
done
|
|
|
|
# =================================
|
|
# Windows Build (disabled)
|
|
# =================================
|
|
# build-windows:
|
|
# name: Build for Windows
|
|
# permissions:
|
|
# contents: write
|
|
# strategy:
|
|
# matrix:
|
|
# platform:
|
|
# - amd64
|
|
# runs-on: windows-latest
|
|
# steps:
|
|
# - name: Checkout repository
|
|
# uses: actions/checkout@v6
|
|
#
|
|
# - name: Setup Go
|
|
# uses: actions/setup-go@v6
|
|
# with:
|
|
# go-version-file: go.mod
|
|
# check-latest: true
|
|
#
|
|
# - name: Install MSYS2
|
|
# uses: msys2/setup-msys2@v2
|
|
# with:
|
|
# msystem: MINGW64
|
|
# update: true
|
|
# install: >-
|
|
# mingw-w64-x86_64-gcc
|
|
# mingw-w64-x86_64-sqlite3
|
|
# mingw-w64-x86_64-pkg-config
|
|
#
|
|
# - name: Build 3X-UI for Windows (CGO)
|
|
# shell: msys2 {0}
|
|
# run: |
|
|
# export PATH="/c/hostedtoolcache/windows/go/$(ls /c/hostedtoolcache/windows/go | sort -V | tail -n1)/x64/bin:$PATH"
|
|
#
|
|
# export CGO_ENABLED=1
|
|
# export GOOS=windows
|
|
# export GOARCH=amd64
|
|
# export CC=x86_64-w64-mingw32-gcc
|
|
#
|
|
# which go
|
|
# go version
|
|
# gcc --version
|
|
#
|
|
# go build -ldflags "-w -s" -o xui-release.exe -v main.go
|
|
#
|
|
# - name: Copy and download resources
|
|
# shell: pwsh
|
|
# run: |
|
|
# mkdir x-ui
|
|
# Copy-Item xui-release.exe x-ui\x-ui.exe
|
|
# mkdir x-ui\bin
|
|
# cd x-ui\bin
|
|
#
|
|
# # Download Xray for Windows
|
|
# $Xray_URL = "https://github.com/XTLS/Xray-core/releases/download/v26.2.6/"
|
|
# Invoke-WebRequest -Uri "${Xray_URL}Xray-windows-64.zip" -OutFile "Xray-windows-64.zip"
|
|
# Expand-Archive -Path "Xray-windows-64.zip" -DestinationPath .
|
|
# Remove-Item "Xray-windows-64.zip"
|
|
# Remove-Item geoip.dat, geosite.dat -ErrorAction SilentlyContinue
|
|
# Invoke-WebRequest -Uri "https://github.com/Loyalsoldier/v2ray-rules-dat/releases/latest/download/geoip.dat" -OutFile "geoip.dat"
|
|
# Invoke-WebRequest -Uri "https://github.com/Loyalsoldier/v2ray-rules-dat/releases/latest/download/geosite.dat" -OutFile "geosite.dat"
|
|
# Rename-Item xray.exe xray-windows-amd64.exe
|
|
# cd ..
|
|
# Copy-Item -Path ..\windows_files\* -Destination . -Recurse
|
|
# cd ..
|
|
#
|
|
# - name: Package to Zip
|
|
# shell: pwsh
|
|
# run: |
|
|
# Compress-Archive -Path .\x-ui -DestinationPath "x-ui-windows-amd64.zip"
|
|
#
|
|
# - name: Upload files to Artifacts
|
|
# uses: actions/upload-artifact@v7
|
|
# with:
|
|
# name: x-ui-windows-amd64
|
|
# path: ./x-ui-windows-amd64.zip
|
|
#
|
|
# - name: Upload files to GH release
|
|
# uses: svenstaro/upload-release-action@v2
|
|
# if: github.event_name == 'release'
|
|
# with:
|
|
# repo_token: ${{ secrets.GITHUB_TOKEN }}
|
|
# tag: ${{ github.ref_name }}
|
|
# file: x-ui-windows-amd64.zip
|
|
# asset_name: x-ui-windows-amd64.zip
|
|
# overwrite: true
|
|
# prerelease: true
|