3x-ui/.github/workflows/release.yml

352 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}"
cd /tmp/xray
unzip -q "$XRAY_FILE"
rm -f "$XRAY_FILE"
- 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 &
curl -fL -sS --retry 3 -o geoip_IR.dat \
https://github.com/chocolate4u/Iran-v2ray-rules/releases/latest/download/geoip.dat &
curl -fL -sS --retry 3 -o geosite_IR.dat \
https://github.com/chocolate4u/Iran-v2ray-rules/releases/latest/download/geosite.dat &
curl -fL -sS --retry 3 -o geoip_RU.dat \
https://github.com/runetfreedom/russia-v2ray-rules-dat/releases/latest/download/geoip.dat &
curl -fL -sS --retry 3 -o geosite_RU.dat \
https://github.com/runetfreedom/russia-v2ray-rules-dat/releases/latest/download/geosite.dat &
wait
- 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"
tar -xf "$(basename "$TARBALL_URL")"
TOOLCHAIN_DIR=$(find . -maxdepth 1 -type d -name "${BOOTLIN_ARCH}--musl--stable-*" | head -n1)
cd -
else
echo "Using cached toolchain: $TOOLCHAIN_DIR"
fi
export PATH="$(realpath "/tmp/toolchain/$TOOLCHAIN_DIR")/bin:$PATH"
export CC=$(realpath "$(find "/tmp/toolchain/$TOOLCHAIN_DIR/bin" -name '*-gcc.br_real' -type f -executable | head -n1)")
[ -z "$CC" ] && { echo "No gcc.br_real found in /tmp/toolchain/$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 }}"
fi
gh release upload "$TAG" x-ui-linux-${{ matrix.platform }}.tar.gz --clobber
# =================================
# 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"
# Invoke-WebRequest -Uri "https://github.com/chocolate4u/Iran-v2ray-rules/releases/latest/download/geoip.dat" -OutFile "geoip_IR.dat"
# Invoke-WebRequest -Uri "https://github.com/chocolate4u/Iran-v2ray-rules/releases/latest/download/geosite.dat" -OutFile "geosite_IR.dat"
# Invoke-WebRequest -Uri "https://github.com/runetfreedom/russia-v2ray-rules-dat/releases/latest/download/geoip.dat" -OutFile "geoip_RU.dat"
# Invoke-WebRequest -Uri "https://github.com/runetfreedom/russia-v2ray-rules-dat/releases/latest/download/geosite.dat" -OutFile "geosite_RU.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