release.yml 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289
  1. name: Release 3X-UI
  2. on:
  3. workflow_dispatch:
  4. push:
  5. branches:
  6. - "**"
  7. tags:
  8. - "v*.*.*"
  9. paths:
  10. - "**.js"
  11. - "**.css"
  12. - "**.html"
  13. - "**.sh"
  14. - "**.go"
  15. - "go.mod"
  16. - "go.sum"
  17. - "x-ui.service.debian"
  18. - "x-ui.service.arch"
  19. - "x-ui.service.rhel"
  20. pull_request:
  21. paths:
  22. - "**.js"
  23. - "**.css"
  24. - "**.html"
  25. - "**.sh"
  26. - "**.go"
  27. - "go.mod"
  28. - "go.sum"
  29. - "x-ui.service.debian"
  30. - "x-ui.service.arch"
  31. - "x-ui.service.rhel"
  32. jobs:
  33. build:
  34. permissions:
  35. contents: write
  36. strategy:
  37. matrix:
  38. platform:
  39. - amd64
  40. - arm64
  41. - armv7
  42. - armv6
  43. - 386
  44. - armv5
  45. - s390x
  46. runs-on: ubuntu-latest
  47. steps:
  48. - name: Checkout repository
  49. uses: actions/checkout@v6
  50. - name: Setup Go
  51. uses: actions/setup-go@v6
  52. with:
  53. go-version-file: go.mod
  54. check-latest: true
  55. # Frontend dist must be built BEFORE go build — Go's //go:embed
  56. # all:dist directive in web/web.go requires web/dist/ to exist
  57. # at compile time. web/dist/ is .gitignored, so on a fresh CI
  58. # checkout it doesn't exist until vite emits it.
  59. - name: Setup Node.js
  60. uses: actions/setup-node@v6
  61. with:
  62. node-version: '22'
  63. cache: 'npm'
  64. cache-dependency-path: frontend/package-lock.json
  65. - name: Build frontend bundle
  66. run: |
  67. npm ci
  68. npm run build
  69. working-directory: frontend
  70. - name: Build 3X-UI
  71. run: |
  72. export CGO_ENABLED=1
  73. export GOOS=linux
  74. export GOARCH=${{ matrix.platform }}
  75. # Use Bootlin prebuilt cross-toolchains (musl 1.2.5 in stable series)
  76. case "${{ matrix.platform }}" in
  77. amd64) BOOTLIN_ARCH="x86-64" ;;
  78. arm64) BOOTLIN_ARCH="aarch64" ;;
  79. armv7) BOOTLIN_ARCH="armv7-eabihf"; export GOARCH=arm GOARM=7 ;;
  80. armv6) BOOTLIN_ARCH="armv6-eabihf"; export GOARCH=arm GOARM=6 ;;
  81. armv5) BOOTLIN_ARCH="armv5-eabi"; export GOARCH=arm GOARM=5 ;;
  82. 386) BOOTLIN_ARCH="x86-i686" ;;
  83. s390x) BOOTLIN_ARCH="s390x-z13" ;;
  84. esac
  85. echo "Resolving Bootlin musl toolchain for arch=$BOOTLIN_ARCH (platform=${{ matrix.platform }})"
  86. TARBALL_BASE="https://toolchains.bootlin.com/downloads/releases/toolchains/$BOOTLIN_ARCH/tarballs/"
  87. TARBALL_URL=$(curl -fsSL "$TARBALL_BASE" | grep -oE "${BOOTLIN_ARCH}--musl--stable-[^\"]+\\.tar\\.xz" | sort -r | head -n1)
  88. [ -z "$TARBALL_URL" ] && { echo "Failed to locate Bootlin musl toolchain for arch=$BOOTLIN_ARCH" >&2; exit 1; }
  89. echo "Downloading: $TARBALL_URL"
  90. cd /tmp
  91. curl -fL -sS -o "$(basename "$TARBALL_URL")" "$TARBALL_BASE/$TARBALL_URL"
  92. tar -xf "$(basename "$TARBALL_URL")"
  93. TOOLCHAIN_DIR=$(find . -maxdepth 1 -type d -name "${BOOTLIN_ARCH}--musl--stable-*" | head -n1)
  94. export PATH="$(realpath "$TOOLCHAIN_DIR")/bin:$PATH"
  95. export CC=$(realpath "$(find "$TOOLCHAIN_DIR/bin" -name '*-gcc.br_real' -type f -executable | head -n1)")
  96. [ -z "$CC" ] && { echo "No gcc.br_real found in $TOOLCHAIN_DIR/bin" >&2; exit 1; }
  97. cd -
  98. go build -ldflags "-w -s -linkmode external -extldflags '-static'" -o xui-release -v main.go
  99. file xui-release
  100. ldd xui-release || echo "Static binary confirmed"
  101. mkdir x-ui
  102. cp xui-release x-ui/
  103. cp x-ui.service.debian x-ui/
  104. cp x-ui.service.arch x-ui/
  105. cp x-ui.service.rhel x-ui/
  106. cp x-ui.sh x-ui/
  107. mv x-ui/xui-release x-ui/x-ui
  108. mkdir x-ui/bin
  109. cd x-ui/bin
  110. # Download dependencies
  111. Xray_URL="https://github.com/XTLS/Xray-core/releases/download/v26.4.25/"
  112. if [ "${{ matrix.platform }}" == "amd64" ]; then
  113. wget -q ${Xray_URL}Xray-linux-64.zip
  114. unzip Xray-linux-64.zip
  115. rm -f Xray-linux-64.zip
  116. elif [ "${{ matrix.platform }}" == "arm64" ]; then
  117. wget -q ${Xray_URL}Xray-linux-arm64-v8a.zip
  118. unzip Xray-linux-arm64-v8a.zip
  119. rm -f Xray-linux-arm64-v8a.zip
  120. elif [ "${{ matrix.platform }}" == "armv7" ]; then
  121. wget -q ${Xray_URL}Xray-linux-arm32-v7a.zip
  122. unzip Xray-linux-arm32-v7a.zip
  123. rm -f Xray-linux-arm32-v7a.zip
  124. elif [ "${{ matrix.platform }}" == "armv6" ]; then
  125. wget -q ${Xray_URL}Xray-linux-arm32-v6.zip
  126. unzip Xray-linux-arm32-v6.zip
  127. rm -f Xray-linux-arm32-v6.zip
  128. elif [ "${{ matrix.platform }}" == "386" ]; then
  129. wget -q ${Xray_URL}Xray-linux-32.zip
  130. unzip Xray-linux-32.zip
  131. rm -f Xray-linux-32.zip
  132. elif [ "${{ matrix.platform }}" == "armv5" ]; then
  133. wget -q ${Xray_URL}Xray-linux-arm32-v5.zip
  134. unzip Xray-linux-arm32-v5.zip
  135. rm -f Xray-linux-arm32-v5.zip
  136. elif [ "${{ matrix.platform }}" == "s390x" ]; then
  137. wget -q ${Xray_URL}Xray-linux-s390x.zip
  138. unzip Xray-linux-s390x.zip
  139. rm -f Xray-linux-s390x.zip
  140. fi
  141. rm -f geoip.dat geosite.dat
  142. wget -q https://github.com/Loyalsoldier/v2ray-rules-dat/releases/latest/download/geoip.dat
  143. wget -q https://github.com/Loyalsoldier/v2ray-rules-dat/releases/latest/download/geosite.dat
  144. wget -q -O geoip_IR.dat https://github.com/chocolate4u/Iran-v2ray-rules/releases/latest/download/geoip.dat
  145. wget -q -O geosite_IR.dat https://github.com/chocolate4u/Iran-v2ray-rules/releases/latest/download/geosite.dat
  146. wget -q -O geoip_RU.dat https://github.com/runetfreedom/russia-v2ray-rules-dat/releases/latest/download/geoip.dat
  147. wget -q -O geosite_RU.dat https://github.com/runetfreedom/russia-v2ray-rules-dat/releases/latest/download/geosite.dat
  148. mv xray xray-linux-${{ matrix.platform }}
  149. cd ../..
  150. - name: Package
  151. run: tar -zcvf x-ui-linux-${{ matrix.platform }}.tar.gz x-ui
  152. - name: Upload files to Artifacts
  153. uses: actions/upload-artifact@v7
  154. with:
  155. name: x-ui-linux-${{ matrix.platform }}
  156. path: ./x-ui-linux-${{ matrix.platform }}.tar.gz
  157. - name: Upload files to GH release
  158. uses: svenstaro/upload-release-action@v2
  159. if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/')
  160. with:
  161. repo_token: ${{ secrets.GITHUB_TOKEN }}
  162. tag: ${{ github.ref_name }}
  163. file: x-ui-linux-${{ matrix.platform }}.tar.gz
  164. asset_name: x-ui-linux-${{ matrix.platform }}.tar.gz
  165. overwrite: true
  166. prerelease: true
  167. # =================================
  168. # Windows Build
  169. # =================================
  170. build-windows:
  171. name: Build for Windows
  172. permissions:
  173. contents: write
  174. strategy:
  175. matrix:
  176. platform:
  177. - amd64
  178. runs-on: windows-latest
  179. steps:
  180. - name: Checkout repository
  181. uses: actions/checkout@v6
  182. - name: Setup Go
  183. uses: actions/setup-go@v6
  184. with:
  185. go-version-file: go.mod
  186. check-latest: true
  187. # Frontend dist must be built BEFORE go build — see comment on the
  188. # Linux job above. This step is identical except npm runs on the
  189. # Windows runner here.
  190. - name: Setup Node.js
  191. uses: actions/setup-node@v6
  192. with:
  193. node-version: '22'
  194. cache: 'npm'
  195. cache-dependency-path: frontend/package-lock.json
  196. - name: Build frontend bundle
  197. shell: pwsh
  198. run: |
  199. npm ci
  200. npm run build
  201. working-directory: frontend
  202. - name: Install MSYS2
  203. uses: msys2/setup-msys2@v2
  204. with:
  205. msystem: MINGW64
  206. update: true
  207. install: >-
  208. mingw-w64-x86_64-gcc
  209. mingw-w64-x86_64-sqlite3
  210. mingw-w64-x86_64-pkg-config
  211. - name: Build 3X-UI for Windows (CGO)
  212. shell: msys2 {0}
  213. run: |
  214. export PATH="/c/hostedtoolcache/windows/go/$(ls /c/hostedtoolcache/windows/go | sort -V | tail -n1)/x64/bin:$PATH"
  215. export CGO_ENABLED=1
  216. export GOOS=windows
  217. export GOARCH=amd64
  218. export CC=x86_64-w64-mingw32-gcc
  219. which go
  220. go version
  221. gcc --version
  222. go build -ldflags "-w -s" -o xui-release.exe -v main.go
  223. - name: Copy and download resources
  224. shell: pwsh
  225. run: |
  226. mkdir x-ui
  227. Copy-Item xui-release.exe x-ui\x-ui.exe
  228. mkdir x-ui\bin
  229. cd x-ui\bin
  230. # Download Xray for Windows
  231. $Xray_URL = "https://github.com/XTLS/Xray-core/releases/download/v26.4.25/"
  232. Invoke-WebRequest -Uri "${Xray_URL}Xray-windows-64.zip" -OutFile "Xray-windows-64.zip"
  233. Expand-Archive -Path "Xray-windows-64.zip" -DestinationPath .
  234. Remove-Item "Xray-windows-64.zip"
  235. Remove-Item geoip.dat, geosite.dat -ErrorAction SilentlyContinue
  236. Invoke-WebRequest -Uri "https://github.com/Loyalsoldier/v2ray-rules-dat/releases/latest/download/geoip.dat" -OutFile "geoip.dat"
  237. Invoke-WebRequest -Uri "https://github.com/Loyalsoldier/v2ray-rules-dat/releases/latest/download/geosite.dat" -OutFile "geosite.dat"
  238. Invoke-WebRequest -Uri "https://github.com/chocolate4u/Iran-v2ray-rules/releases/latest/download/geoip.dat" -OutFile "geoip_IR.dat"
  239. Invoke-WebRequest -Uri "https://github.com/chocolate4u/Iran-v2ray-rules/releases/latest/download/geosite.dat" -OutFile "geosite_IR.dat"
  240. Invoke-WebRequest -Uri "https://github.com/runetfreedom/russia-v2ray-rules-dat/releases/latest/download/geoip.dat" -OutFile "geoip_RU.dat"
  241. Invoke-WebRequest -Uri "https://github.com/runetfreedom/russia-v2ray-rules-dat/releases/latest/download/geosite.dat" -OutFile "geosite_RU.dat"
  242. Rename-Item xray.exe xray-windows-amd64.exe
  243. cd ..
  244. Copy-Item -Path ..\windows_files\* -Destination . -Recurse
  245. cd ..
  246. - name: Package to Zip
  247. shell: pwsh
  248. run: |
  249. Compress-Archive -Path .\x-ui -DestinationPath "x-ui-windows-amd64.zip"
  250. - name: Upload files to Artifacts
  251. uses: actions/upload-artifact@v7
  252. with:
  253. name: x-ui-windows-amd64
  254. path: ./x-ui-windows-amd64.zip
  255. - name: Upload files to GH release
  256. uses: svenstaro/upload-release-action@v2
  257. if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/')
  258. with:
  259. repo_token: ${{ secrets.GITHUB_TOKEN }}
  260. tag: ${{ github.ref_name }}
  261. file: x-ui-windows-amd64.zip
  262. asset_name: x-ui-windows-amd64.zip
  263. overwrite: true
  264. prerelease: true