build.yml 36 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860
  1. name: Build
  2. on:
  3. workflow_dispatch:
  4. inputs:
  5. version:
  6. description: "Version name"
  7. required: true
  8. type: string
  9. build:
  10. description: "Build type"
  11. required: true
  12. type: choice
  13. default: "All"
  14. options:
  15. - All
  16. - Binary
  17. - Android
  18. - Apple
  19. - app-store
  20. - iOS
  21. - macOS
  22. - tvOS
  23. - macOS-standalone
  24. - publish-android
  25. push:
  26. branches:
  27. - main-next
  28. - dev-next
  29. concurrency:
  30. group: ${{ github.workflow }}-${{ github.ref }}-${{ github.event_name }}-${{ inputs.build }}
  31. cancel-in-progress: true
  32. jobs:
  33. calculate_version:
  34. name: Calculate version
  35. runs-on: ubuntu-latest
  36. outputs:
  37. version: ${{ steps.outputs.outputs.version }}
  38. steps:
  39. - name: Checkout
  40. uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5
  41. with:
  42. fetch-depth: 0
  43. - name: Setup Go
  44. uses: actions/setup-go@v5
  45. with:
  46. go-version: ^1.25.4
  47. - name: Check input version
  48. if: github.event_name == 'workflow_dispatch'
  49. run: |-
  50. echo "version=${{ inputs.version }}"
  51. echo "version=${{ inputs.version }}" >> "$GITHUB_ENV"
  52. - name: Calculate version
  53. if: github.event_name != 'workflow_dispatch'
  54. run: |-
  55. go run -v ./cmd/internal/read_tag --ci --nightly
  56. - name: Set outputs
  57. id: outputs
  58. run: |-
  59. echo "version=$version" >> "$GITHUB_OUTPUT"
  60. build:
  61. name: Build binary
  62. if: github.event_name != 'workflow_dispatch' || inputs.build == 'All' || inputs.build == 'Binary'
  63. runs-on: ubuntu-latest
  64. needs:
  65. - calculate_version
  66. strategy:
  67. matrix:
  68. include:
  69. - { os: linux, arch: amd64, debian: amd64, rpm: x86_64, pacman: x86_64, openwrt: "x86_64" }
  70. - { os: linux, arch: "386", go386: sse2, debian: i386, rpm: i386, openwrt: "i386_pentium4" }
  71. - { os: linux, arch: "386", go386: softfloat, openwrt: "i386_pentium-mmx" }
  72. - { os: linux, arch: arm64, debian: arm64, rpm: aarch64, pacman: aarch64, openwrt: "aarch64_cortex-a53 aarch64_cortex-a72 aarch64_cortex-a76 aarch64_generic" }
  73. - { os: linux, arch: arm, goarm: "5", openwrt: "arm_arm926ej-s arm_cortex-a7 arm_cortex-a9 arm_fa526 arm_xscale" }
  74. - { os: linux, arch: arm, goarm: "6", debian: armel, rpm: armv6hl, openwrt: "arm_arm1176jzf-s_vfp" }
  75. - { os: linux, arch: arm, goarm: "7", debian: armhf, rpm: armv7hl, pacman: armv7hl, openwrt: "arm_cortex-a5_vfpv4 arm_cortex-a7_neon-vfpv4 arm_cortex-a7_vfpv4 arm_cortex-a8_vfpv3 arm_cortex-a9_neon arm_cortex-a9_vfpv3-d16 arm_cortex-a15_neon-vfpv4" }
  76. - { os: linux, arch: mips, gomips: softfloat, openwrt: "mips_24kc mips_4kec mips_mips32" }
  77. - { os: linux, arch: mipsle, gomips: hardfloat, debian: mipsel, rpm: mipsel, openwrt: "mipsel_24kc_24kf" }
  78. - { os: linux, arch: mipsle, gomips: softfloat, openwrt: "mipsel_24kc mipsel_74kc mipsel_mips32" }
  79. - { os: linux, arch: mips64, gomips: softfloat, openwrt: "mips64_mips64r2 mips64_octeonplus" }
  80. - { os: linux, arch: mips64le, gomips: hardfloat, debian: mips64el, rpm: mips64el }
  81. - { os: linux, arch: mips64le, gomips: softfloat, openwrt: "mips64el_mips64r2" }
  82. - { os: linux, arch: s390x, debian: s390x, rpm: s390x }
  83. - { os: linux, arch: ppc64le, debian: ppc64el, rpm: ppc64le }
  84. - { os: linux, arch: riscv64, debian: riscv64, rpm: riscv64, openwrt: "riscv64_generic" }
  85. - { os: linux, arch: loong64, debian: loongarch64, rpm: loongarch64, openwrt: "loongarch64_generic" }
  86. - { os: windows, arch: amd64 }
  87. - { os: windows, arch: amd64, legacy_win7: true, legacy_name: "windows-7" }
  88. - { os: windows, arch: "386" }
  89. - { os: windows, arch: "386", legacy_win7: true, legacy_name: "windows-7" }
  90. - { os: windows, arch: arm64 }
  91. - { os: android, arch: arm64, ndk: "aarch64-linux-android21" }
  92. - { os: android, arch: arm, ndk: "armv7a-linux-androideabi21" }
  93. - { os: android, arch: amd64, ndk: "x86_64-linux-android21" }
  94. - { os: android, arch: "386", ndk: "i686-linux-android21" }
  95. steps:
  96. - name: Checkout
  97. uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5
  98. with:
  99. fetch-depth: 0
  100. - name: Setup Go
  101. if: ${{ ! (matrix.legacy_win7 || matrix.legacy_go124) }}
  102. uses: actions/setup-go@v5
  103. with:
  104. go-version: ^1.25.4
  105. - name: Setup Go 1.24
  106. if: matrix.legacy_go124
  107. uses: actions/setup-go@v5
  108. with:
  109. go-version: ~1.24.10
  110. - name: Cache Go for Windows 7
  111. if: matrix.legacy_win7
  112. id: cache-go-for-windows7
  113. uses: actions/cache@v4
  114. with:
  115. path: |
  116. ~/go/go_win7
  117. key: go_win7_1254
  118. - name: Setup Go for Windows 7
  119. if: matrix.legacy_win7 && steps.cache-go-for-windows7.outputs.cache-hit != 'true'
  120. run: |-
  121. .github/setup_go_for_windows7.sh
  122. - name: Setup Go for Windows 7
  123. if: matrix.legacy_win7
  124. run: |-
  125. echo "PATH=$HOME/go/go_win7/bin:$PATH" >> $GITHUB_ENV
  126. echo "GOROOT=$HOME/go/go_win7" >> $GITHUB_ENV
  127. - name: Setup Android NDK
  128. if: matrix.os == 'android'
  129. uses: nttld/setup-ndk@v1
  130. with:
  131. ndk-version: r28
  132. local-cache: true
  133. - name: Set tag
  134. run: |-
  135. git ls-remote --exit-code --tags origin v${{ needs.calculate_version.outputs.version }} || echo "PUBLISHED=false" >> "$GITHUB_ENV"
  136. git tag v${{ needs.calculate_version.outputs.version }} -f
  137. - name: Set build tags
  138. run: |
  139. set -xeuo pipefail
  140. TAGS='with_gvisor,with_quic,with_dhcp,with_wireguard,with_utls,with_acme,with_clash_api,with_tailscale,with_ccm,badlinkname,tfogo_checklinkname0'
  141. if [[ "${{ matrix.os }}" == "android" ]]; then
  142. TAGS="${TAGS},with_naive_outbound"
  143. fi
  144. echo "BUILD_TAGS=${TAGS}" >> "${GITHUB_ENV}"
  145. - name: Build
  146. if: matrix.os != 'android'
  147. run: |
  148. set -xeuo pipefail
  149. mkdir -p dist
  150. go build -v -trimpath -o dist/sing-box -tags "${BUILD_TAGS}" \
  151. -ldflags '-s -buildid= -X github.com/sagernet/sing-box/constant.Version=${{ needs.calculate_version.outputs.version }} -checklinkname=0' \
  152. ./cmd/sing-box
  153. env:
  154. CGO_ENABLED: "0"
  155. GOOS: ${{ matrix.os }}
  156. GOARCH: ${{ matrix.arch }}
  157. GO386: ${{ matrix.go386 }}
  158. GOARM: ${{ matrix.goarm }}
  159. GOMIPS: ${{ matrix.gomips }}
  160. GOMIPS64: ${{ matrix.gomips }}
  161. GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
  162. - name: Build Android
  163. if: matrix.os == 'android'
  164. run: |
  165. set -xeuo pipefail
  166. go install -v ./cmd/internal/build
  167. export CC='${{ matrix.ndk }}-clang'
  168. export CXX="${CC}++"
  169. mkdir -p dist
  170. GOOS=$BUILD_GOOS GOARCH=$BUILD_GOARCH build go build -v -trimpath -o dist/sing-box -tags "${BUILD_TAGS}" \
  171. -ldflags '-s -buildid= -X github.com/sagernet/sing-box/constant.Version=${{ needs.calculate_version.outputs.version }} -checklinkname=0' \
  172. ./cmd/sing-box
  173. env:
  174. CGO_ENABLED: "1"
  175. BUILD_GOOS: ${{ matrix.os }}
  176. BUILD_GOARCH: ${{ matrix.arch }}
  177. GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
  178. - name: Set name
  179. run: |-
  180. DIR_NAME="sing-box-${{ needs.calculate_version.outputs.version }}-${{ matrix.os }}-${{ matrix.arch }}"
  181. if [[ -n "${{ matrix.goarm }}" ]]; then
  182. DIR_NAME="${DIR_NAME}v${{ matrix.goarm }}"
  183. elif [[ -n "${{ matrix.go386 }}" && "${{ matrix.go386 }}" != 'sse2' ]]; then
  184. DIR_NAME="${DIR_NAME}-${{ matrix.go386 }}"
  185. elif [[ -n "${{ matrix.gomips }}" && "${{ matrix.gomips }}" != 'hardfloat' ]]; then
  186. DIR_NAME="${DIR_NAME}-${{ matrix.gomips }}"
  187. elif [[ -n "${{ matrix.legacy_name }}" ]]; then
  188. DIR_NAME="${DIR_NAME}-legacy-${{ matrix.legacy_name }}"
  189. fi
  190. echo "DIR_NAME=${DIR_NAME}" >> "${GITHUB_ENV}"
  191. PKG_VERSION="${{ needs.calculate_version.outputs.version }}"
  192. PKG_VERSION="${PKG_VERSION//-/\~}"
  193. echo "PKG_VERSION=${PKG_VERSION}" >> "${GITHUB_ENV}"
  194. - name: Package DEB
  195. if: matrix.debian != ''
  196. run: |
  197. set -xeuo pipefail
  198. sudo gem install fpm
  199. sudo apt-get update
  200. sudo apt-get install -y debsigs
  201. cp .fpm_systemd .fpm
  202. fpm -t deb \
  203. -v "$PKG_VERSION" \
  204. -p "dist/sing-box_${{ needs.calculate_version.outputs.version }}_${{ matrix.os }}_${{ matrix.debian }}.deb" \
  205. --architecture ${{ matrix.debian }} \
  206. dist/sing-box=/usr/bin/sing-box
  207. curl -Lo '/tmp/debsigs.diff' 'https://gitlab.com/debsigs/debsigs/-/commit/160138f5de1ec110376d3c807b60a37388bc7c90.diff'
  208. sudo patch /usr/bin/debsigs < '/tmp/debsigs.diff'
  209. rm -rf $HOME/.gnupg
  210. gpg --pinentry-mode loopback --passphrase "${{ secrets.GPG_PASSPHRASE }}" --import <<EOF
  211. ${{ secrets.GPG_KEY }}
  212. EOF
  213. debsigs --sign=origin -k ${{ secrets.GPG_KEY_ID }} --gpgopts '--pinentry-mode loopback --passphrase "${{ secrets.GPG_PASSPHRASE }}"' dist/*.deb
  214. - name: Package RPM
  215. if: matrix.rpm != ''
  216. run: |-
  217. set -xeuo pipefail
  218. sudo gem install fpm
  219. cp .fpm_systemd .fpm
  220. fpm -t rpm \
  221. -v "$PKG_VERSION" \
  222. -p "dist/sing-box_${{ needs.calculate_version.outputs.version }}_${{ matrix.os }}_${{ matrix.rpm }}.rpm" \
  223. --architecture ${{ matrix.rpm }} \
  224. dist/sing-box=/usr/bin/sing-box
  225. cat > $HOME/.rpmmacros <<EOF
  226. %_gpg_name ${{ secrets.GPG_KEY_ID }}
  227. %_gpg_sign_cmd_extra_args --pinentry-mode loopback --passphrase ${{ secrets.GPG_PASSPHRASE }}
  228. EOF
  229. gpg --pinentry-mode loopback --passphrase "${{ secrets.GPG_PASSPHRASE }}" --import <<EOF
  230. ${{ secrets.GPG_KEY }}
  231. EOF
  232. rpmsign --addsign dist/*.rpm
  233. - name: Package Pacman
  234. if: matrix.pacman != ''
  235. run: |-
  236. set -xeuo pipefail
  237. sudo gem install fpm
  238. sudo apt-get update
  239. sudo apt-get install -y libarchive-tools
  240. cp .fpm_systemd .fpm
  241. fpm -t pacman \
  242. -v "$PKG_VERSION" \
  243. -p "dist/sing-box_${{ needs.calculate_version.outputs.version }}_${{ matrix.os }}_${{ matrix.pacman }}.pkg.tar.zst" \
  244. --architecture ${{ matrix.pacman }} \
  245. dist/sing-box=/usr/bin/sing-box
  246. - name: Package OpenWrt
  247. if: matrix.openwrt != ''
  248. run: |-
  249. set -xeuo pipefail
  250. sudo gem install fpm
  251. cp .fpm_openwrt .fpm
  252. fpm -t deb \
  253. -v "$PKG_VERSION" \
  254. -p "dist/openwrt.deb" \
  255. --architecture all \
  256. dist/sing-box=/usr/bin/sing-box
  257. for architecture in ${{ matrix.openwrt }}; do
  258. .github/deb2ipk.sh "$architecture" "dist/openwrt.deb" "dist/sing-box_${{ needs.calculate_version.outputs.version }}_openwrt_${architecture}.ipk"
  259. done
  260. rm "dist/openwrt.deb"
  261. - name: Archive
  262. run: |
  263. set -xeuo pipefail
  264. cd dist
  265. mkdir -p "${DIR_NAME}"
  266. cp ../LICENSE "${DIR_NAME}"
  267. if [ '${{ matrix.os }}' = 'windows' ]; then
  268. cp sing-box "${DIR_NAME}/sing-box.exe"
  269. zip -r "${DIR_NAME}.zip" "${DIR_NAME}"
  270. else
  271. cp sing-box "${DIR_NAME}"
  272. tar -czvf "${DIR_NAME}.tar.gz" "${DIR_NAME}"
  273. fi
  274. rm -r "${DIR_NAME}"
  275. - name: Cleanup
  276. run: rm dist/sing-box
  277. - name: Upload artifact
  278. uses: actions/upload-artifact@v4
  279. with:
  280. name: binary-${{ matrix.os }}_${{ matrix.arch }}${{ matrix.goarm && format('v{0}', matrix.goarm) }}${{ matrix.go386 && format('_{0}', matrix.go386) }}${{ matrix.gomips && format('_{0}', matrix.gomips) }}${{ matrix.legacy_name && format('-legacy-{0}', matrix.legacy_name) }}
  281. path: "dist"
  282. build_darwin:
  283. name: Build Darwin binaries
  284. if: github.event_name != 'workflow_dispatch' || inputs.build == 'All' || inputs.build == 'Binary'
  285. runs-on: macos-latest
  286. needs:
  287. - calculate_version
  288. strategy:
  289. matrix:
  290. include:
  291. - { arch: amd64 }
  292. - { arch: arm64 }
  293. - { arch: amd64, legacy_go124: true, legacy_name: "macos-11" }
  294. steps:
  295. - name: Checkout
  296. uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5
  297. with:
  298. fetch-depth: 0
  299. - name: Setup Go
  300. if: ${{ ! matrix.legacy_go124 }}
  301. uses: actions/setup-go@v5
  302. with:
  303. go-version: ^1.25.3
  304. - name: Setup Go 1.24
  305. if: matrix.legacy_go124
  306. uses: actions/setup-go@v5
  307. with:
  308. go-version: ~1.24.6
  309. - name: Set tag
  310. run: |-
  311. git ls-remote --exit-code --tags origin v${{ needs.calculate_version.outputs.version }} || echo "PUBLISHED=false" >> "$GITHUB_ENV"
  312. git tag v${{ needs.calculate_version.outputs.version }} -f
  313. - name: Set build tags
  314. run: |
  315. set -xeuo pipefail
  316. TAGS='with_gvisor,with_quic,with_dhcp,with_wireguard,with_utls,with_acme,with_clash_api,with_tailscale,with_ccm,with_naive_outbound,badlinkname,tfogo_checklinkname0'
  317. echo "BUILD_TAGS=${TAGS}" >> "${GITHUB_ENV}"
  318. - name: Build
  319. run: |
  320. set -xeuo pipefail
  321. mkdir -p dist
  322. go build -v -trimpath -o dist/sing-box -tags "${BUILD_TAGS}" \
  323. -ldflags '-s -buildid= -X github.com/sagernet/sing-box/constant.Version=${{ needs.calculate_version.outputs.version }} -checklinkname=0' \
  324. ./cmd/sing-box
  325. env:
  326. CGO_ENABLED: "1"
  327. GOOS: darwin
  328. GOARCH: ${{ matrix.arch }}
  329. GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
  330. - name: Set name
  331. run: |-
  332. DIR_NAME="sing-box-${{ needs.calculate_version.outputs.version }}-darwin-${{ matrix.arch }}"
  333. if [[ -n "${{ matrix.legacy_name }}" ]]; then
  334. DIR_NAME="${DIR_NAME}-legacy-${{ matrix.legacy_name }}"
  335. fi
  336. echo "DIR_NAME=${DIR_NAME}" >> "${GITHUB_ENV}"
  337. - name: Archive
  338. run: |
  339. set -xeuo pipefail
  340. cd dist
  341. mkdir -p "${DIR_NAME}"
  342. cp ../LICENSE "${DIR_NAME}"
  343. cp sing-box "${DIR_NAME}"
  344. tar -czvf "${DIR_NAME}.tar.gz" "${DIR_NAME}"
  345. rm -r "${DIR_NAME}"
  346. - name: Cleanup
  347. run: rm dist/sing-box
  348. - name: Upload artifact
  349. uses: actions/upload-artifact@v4
  350. with:
  351. name: binary-darwin_${{ matrix.arch }}${{ matrix.legacy_name && format('-legacy-{0}', matrix.legacy_name) }}
  352. path: "dist"
  353. build_naive_linux:
  354. name: Build Linux with naive outbound
  355. if: github.event_name != 'workflow_dispatch' || inputs.build == 'All' || inputs.build == 'Binary'
  356. runs-on: ubuntu-latest
  357. needs:
  358. - calculate_version
  359. strategy:
  360. matrix:
  361. include:
  362. # Linux glibc (dynamic linking with Debian Bullseye sysroot)
  363. - { arch: amd64, sysroot_arch: amd64, sysroot_sha: "36a164623d03f525e3dfb783a5e9b8a00e98e1ddd2b5cff4e449bd016dd27e50", cc_target: "x86_64-linux-gnu", suffix: "-naive" }
  364. - { arch: arm64, sysroot_arch: arm64, sysroot_sha: "2f915d821eec27515c0c6d21b69898e23762908d8d7ccc1aa2a8f5f25e8b7e18", cc_target: "aarch64-linux-gnu", suffix: "-naive" }
  365. - { arch: "386", sysroot_arch: i386, sysroot_sha: "63f0e5128b84f7b0421956a4a40affa472be8da0e58caf27e9acbc84072daee7", cc_target: "i686-linux-gnu", suffix: "-naive" }
  366. - { arch: arm, goarm: "7", sysroot_arch: armhf, sysroot_sha: "47b3a0b161ca011b2b33d4fc1ef6ef269b8208a0b7e4c900700c345acdfd1814", cc_target: "arm-linux-gnueabihf", suffix: "-naive" }
  367. # Linux musl (static linking)
  368. - { arch: amd64, musl: true, cc_target: "x86_64-linux-musl", suffix: "-naive-musl" }
  369. - { arch: arm64, musl: true, cc_target: "aarch64-linux-musl", suffix: "-naive-musl" }
  370. - { arch: "386", musl: true, cc_target: "i686-linux-musl", suffix: "-naive-musl" }
  371. - { arch: arm, goarm: "7", musl: true, cc_target: "arm-linux-musleabihf", suffix: "-naive-musl" }
  372. steps:
  373. - name: Checkout
  374. uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5
  375. with:
  376. fetch-depth: 0
  377. - name: Setup Go
  378. uses: actions/setup-go@v5
  379. with:
  380. go-version: ^1.25.4
  381. - name: Set tag
  382. run: |-
  383. git ls-remote --exit-code --tags origin v${{ needs.calculate_version.outputs.version }} || echo "PUBLISHED=false" >> "$GITHUB_ENV"
  384. git tag v${{ needs.calculate_version.outputs.version }} -f
  385. - name: Download sysroot (glibc)
  386. if: ${{ ! matrix.musl }}
  387. run: |
  388. set -xeuo pipefail
  389. wget -q "https://commondatastorage.googleapis.com/chrome-linux-sysroot/${{ matrix.sysroot_sha }}" -O sysroot.tar.xz
  390. mkdir -p /tmp/sysroot
  391. tar -xf sysroot.tar.xz -C /tmp/sysroot
  392. - name: Install cross compiler (glibc)
  393. if: ${{ ! matrix.musl }}
  394. run: |
  395. set -xeuo pipefail
  396. sudo apt-get update
  397. sudo apt-get install -y clang lld
  398. if [[ "${{ matrix.arch }}" == "arm64" ]]; then
  399. sudo apt-get install -y libc6-dev-arm64-cross
  400. elif [[ "${{ matrix.arch }}" == "386" ]]; then
  401. sudo apt-get install -y libc6-dev-i386-cross
  402. elif [[ "${{ matrix.arch }}" == "arm" ]]; then
  403. sudo apt-get install -y libc6-dev-armhf-cross
  404. fi
  405. - name: Install musl cross compiler
  406. if: matrix.musl
  407. run: |
  408. set -xeuo pipefail
  409. .github/setup_musl_cross.sh "${{ matrix.cc_target }}"
  410. echo "PATH=$HOME/musl-cross/bin:$PATH" >> $GITHUB_ENV
  411. - name: Set build tags
  412. run: |
  413. set -xeuo pipefail
  414. TAGS='with_gvisor,with_quic,with_dhcp,with_wireguard,with_utls,with_acme,with_clash_api,with_tailscale,with_ccm,with_naive_outbound,badlinkname,tfogo_checklinkname0'
  415. if [[ "${{ matrix.musl }}" == "true" ]]; then
  416. TAGS="${TAGS},with_musl"
  417. fi
  418. echo "BUILD_TAGS=${TAGS}" >> "${GITHUB_ENV}"
  419. - name: Build (glibc)
  420. if: ${{ ! matrix.musl }}
  421. run: |
  422. set -xeuo pipefail
  423. mkdir -p dist
  424. go build -v -trimpath -o dist/sing-box -tags "${BUILD_TAGS}" \
  425. -ldflags '-s -buildid= -X github.com/sagernet/sing-box/constant.Version=${{ needs.calculate_version.outputs.version }} -checklinkname=0 -linkmode=external -extldflags "-fuse-ld=lld --sysroot=/tmp/sysroot"' \
  426. ./cmd/sing-box
  427. env:
  428. CGO_ENABLED: "1"
  429. GOOS: linux
  430. GOARCH: ${{ matrix.arch }}
  431. GOARM: ${{ matrix.goarm }}
  432. CC: "clang --target=${{ matrix.cc_target }} --sysroot=/tmp/sysroot"
  433. CXX: "clang++ --target=${{ matrix.cc_target }} --sysroot=/tmp/sysroot"
  434. GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
  435. - name: Build (musl)
  436. if: matrix.musl
  437. run: |
  438. set -xeuo pipefail
  439. mkdir -p dist
  440. go build -v -trimpath -o dist/sing-box -tags "${BUILD_TAGS}" \
  441. -ldflags '-s -buildid= -X github.com/sagernet/sing-box/constant.Version=${{ needs.calculate_version.outputs.version }} -checklinkname=0 -linkmode=external -extldflags "-static"' \
  442. ./cmd/sing-box
  443. env:
  444. CGO_ENABLED: "1"
  445. GOOS: linux
  446. GOARCH: ${{ matrix.arch }}
  447. GOARM: ${{ matrix.goarm }}
  448. CC: "${{ matrix.cc_target }}-gcc"
  449. CXX: "${{ matrix.cc_target }}-g++"
  450. GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
  451. - name: Set name
  452. run: |-
  453. DIR_NAME="sing-box-${{ needs.calculate_version.outputs.version }}-linux-${{ matrix.arch }}"
  454. if [[ -n "${{ matrix.goarm }}" ]]; then
  455. DIR_NAME="${DIR_NAME}v${{ matrix.goarm }}"
  456. fi
  457. DIR_NAME="${DIR_NAME}${{ matrix.suffix }}"
  458. echo "DIR_NAME=${DIR_NAME}" >> "${GITHUB_ENV}"
  459. - name: Archive
  460. run: |
  461. set -xeuo pipefail
  462. cd dist
  463. mkdir -p "${DIR_NAME}"
  464. cp ../LICENSE "${DIR_NAME}"
  465. cp sing-box "${DIR_NAME}"
  466. tar -czvf "${DIR_NAME}.tar.gz" "${DIR_NAME}"
  467. rm -r "${DIR_NAME}"
  468. - name: Cleanup
  469. run: rm dist/sing-box
  470. - name: Upload artifact
  471. uses: actions/upload-artifact@v4
  472. with:
  473. name: binary-linux_${{ matrix.arch }}${{ matrix.goarm && format('v{0}', matrix.goarm) }}${{ matrix.suffix }}
  474. path: "dist"
  475. build_android:
  476. name: Build Android
  477. if: github.event_name != 'workflow_dispatch' || inputs.build == 'All' || inputs.build == 'Android'
  478. runs-on: ubuntu-latest
  479. needs:
  480. - calculate_version
  481. steps:
  482. - name: Checkout
  483. uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5
  484. with:
  485. fetch-depth: 0
  486. submodules: 'recursive'
  487. - name: Setup Go
  488. uses: actions/setup-go@v5
  489. with:
  490. go-version: ^1.25.4
  491. - name: Setup Android NDK
  492. id: setup-ndk
  493. uses: nttld/setup-ndk@v1
  494. with:
  495. ndk-version: r28
  496. - name: Setup OpenJDK
  497. run: |-
  498. sudo apt update && sudo apt install -y openjdk-17-jdk-headless
  499. /usr/lib/jvm/java-17-openjdk-amd64/bin/java --version
  500. - name: Set tag
  501. run: |-
  502. git ls-remote --exit-code --tags origin v${{ needs.calculate_version.outputs.version }} || echo "PUBLISHED=false" >> "$GITHUB_ENV"
  503. git tag v${{ needs.calculate_version.outputs.version }} -f
  504. - name: Build library
  505. run: |-
  506. make lib_install
  507. export PATH="$PATH:$(go env GOPATH)/bin"
  508. make lib_android
  509. env:
  510. JAVA_HOME: /usr/lib/jvm/java-17-openjdk-amd64
  511. ANDROID_NDK_HOME: ${{ steps.setup-ndk.outputs.ndk-path }}
  512. - name: Checkout main branch
  513. if: github.ref == 'refs/heads/main-next' && github.event_name != 'workflow_dispatch'
  514. run: |-
  515. cd clients/android
  516. git checkout main
  517. - name: Checkout dev branch
  518. if: github.ref == 'refs/heads/dev-next'
  519. run: |-
  520. cd clients/android
  521. git checkout dev
  522. - name: Gradle cache
  523. uses: actions/cache@v4
  524. with:
  525. path: ~/.gradle
  526. key: gradle-${{ hashFiles('**/*.gradle') }}
  527. - name: Update version
  528. if: github.event_name == 'workflow_dispatch'
  529. run: |-
  530. go run -v ./cmd/internal/update_android_version --ci
  531. - name: Update nightly version
  532. if: github.event_name != 'workflow_dispatch'
  533. run: |-
  534. go run -v ./cmd/internal/update_android_version --ci --nightly
  535. - name: Build
  536. run: |-
  537. mkdir clients/android/app/libs
  538. cp libbox.aar clients/android/app/libs
  539. cd clients/android
  540. ./gradlew :app:assemblePlayRelease :app:assembleOtherRelease
  541. env:
  542. JAVA_HOME: /usr/lib/jvm/java-17-openjdk-amd64
  543. ANDROID_NDK_HOME: ${{ steps.setup-ndk.outputs.ndk-path }}
  544. LOCAL_PROPERTIES: ${{ secrets.LOCAL_PROPERTIES }}
  545. - name: Prepare upload
  546. run: |-
  547. mkdir -p dist
  548. cp clients/android/app/build/outputs/apk/play/release/*.apk dist
  549. cp clients/android/app/build/outputs/apk/other/release/*-universal.apk dist
  550. - name: Upload artifact
  551. uses: actions/upload-artifact@v4
  552. with:
  553. name: binary-android-apks
  554. path: 'dist'
  555. publish_android:
  556. name: Publish Android
  557. if: github.event_name == 'workflow_dispatch' && inputs.build == 'publish-android'
  558. runs-on: ubuntu-latest
  559. needs:
  560. - calculate_version
  561. steps:
  562. - name: Checkout
  563. uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5
  564. with:
  565. fetch-depth: 0
  566. submodules: 'recursive'
  567. - name: Setup Go
  568. uses: actions/setup-go@v5
  569. with:
  570. go-version: ^1.25.4
  571. - name: Setup Android NDK
  572. id: setup-ndk
  573. uses: nttld/setup-ndk@v1
  574. with:
  575. ndk-version: r28
  576. - name: Setup OpenJDK
  577. run: |-
  578. sudo apt update && sudo apt install -y openjdk-17-jdk-headless
  579. /usr/lib/jvm/java-17-openjdk-amd64/bin/java --version
  580. - name: Set tag
  581. run: |-
  582. git ls-remote --exit-code --tags origin v${{ needs.calculate_version.outputs.version }} || echo "PUBLISHED=false" >> "$GITHUB_ENV"
  583. git tag v${{ needs.calculate_version.outputs.version }} -f
  584. - name: Build library
  585. run: |-
  586. make lib_install
  587. export PATH="$PATH:$(go env GOPATH)/bin"
  588. make lib_android
  589. env:
  590. JAVA_HOME: /usr/lib/jvm/java-17-openjdk-amd64
  591. ANDROID_NDK_HOME: ${{ steps.setup-ndk.outputs.ndk-path }}
  592. - name: Checkout main branch
  593. if: github.ref == 'refs/heads/main-next' && github.event_name != 'workflow_dispatch'
  594. run: |-
  595. cd clients/android
  596. git checkout main
  597. - name: Checkout dev branch
  598. if: github.ref == 'refs/heads/dev-next'
  599. run: |-
  600. cd clients/android
  601. git checkout dev
  602. - name: Gradle cache
  603. uses: actions/cache@v4
  604. with:
  605. path: ~/.gradle
  606. key: gradle-${{ hashFiles('**/*.gradle') }}
  607. - name: Build
  608. run: |-
  609. go run -v ./cmd/internal/update_android_version --ci
  610. mkdir clients/android/app/libs
  611. cp libbox.aar clients/android/app/libs
  612. cd clients/android
  613. echo -n "$SERVICE_ACCOUNT_CREDENTIALS" | base64 --decode > service-account-credentials.json
  614. ./gradlew :app:publishPlayReleaseBundle
  615. env:
  616. JAVA_HOME: /usr/lib/jvm/java-17-openjdk-amd64
  617. ANDROID_NDK_HOME: ${{ steps.setup-ndk.outputs.ndk-path }}
  618. LOCAL_PROPERTIES: ${{ secrets.LOCAL_PROPERTIES }}
  619. SERVICE_ACCOUNT_CREDENTIALS: ${{ secrets.SERVICE_ACCOUNT_CREDENTIALS }}
  620. build_apple:
  621. name: Build Apple clients
  622. runs-on: macos-26
  623. if: false
  624. needs:
  625. - calculate_version
  626. strategy:
  627. matrix:
  628. include:
  629. - name: iOS
  630. if: ${{ github.event_name != 'workflow_dispatch' || inputs.build == 'All' || inputs.build == 'Apple' || inputs.build == 'app-store'|| inputs.build == 'iOS' }}
  631. platform: ios
  632. scheme: SFI
  633. destination: 'generic/platform=iOS'
  634. archive: build/SFI.xcarchive
  635. upload: SFI/Upload.plist
  636. - name: macOS
  637. if: ${{ github.event_name != 'workflow_dispatch' || inputs.build == 'All' || inputs.build == 'Apple' || inputs.build == 'app-store'|| inputs.build == 'macOS' }}
  638. platform: macos
  639. scheme: SFM
  640. destination: 'generic/platform=macOS'
  641. archive: build/SFM.xcarchive
  642. upload: SFI/Upload.plist
  643. - name: tvOS
  644. if: ${{ github.event_name != 'workflow_dispatch' || inputs.build == 'All' || inputs.build == 'Apple' || inputs.build == 'app-store'|| inputs.build == 'tvOS' }}
  645. platform: tvos
  646. scheme: SFT
  647. destination: 'generic/platform=tvOS'
  648. archive: build/SFT.xcarchive
  649. upload: SFI/Upload.plist
  650. - name: macOS-standalone
  651. if: ${{ github.event_name != 'workflow_dispatch' || inputs.build == 'All' || inputs.build == 'Apple' || inputs.build == 'macOS-standalone' }}
  652. platform: macos
  653. scheme: SFM.System
  654. destination: 'generic/platform=macOS'
  655. archive: build/SFM.System.xcarchive
  656. export: SFM.System/Export.plist
  657. export_path: build/SFM.System
  658. steps:
  659. - name: Checkout
  660. if: matrix.if
  661. uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5
  662. with:
  663. fetch-depth: 0
  664. submodules: 'recursive'
  665. - name: Setup Go
  666. if: matrix.if
  667. uses: actions/setup-go@v5
  668. with:
  669. go-version: ^1.25.4
  670. - name: Set tag
  671. if: matrix.if
  672. run: |-
  673. git ls-remote --exit-code --tags origin v${{ needs.calculate_version.outputs.version }} || echo "PUBLISHED=false" >> "$GITHUB_ENV"
  674. git tag v${{ needs.calculate_version.outputs.version }} -f
  675. echo "VERSION=${{ needs.calculate_version.outputs.version }}" >> "$GITHUB_ENV"
  676. - name: Checkout main branch
  677. if: matrix.if && github.ref == 'refs/heads/main-next' && github.event_name != 'workflow_dispatch'
  678. run: |-
  679. cd clients/apple
  680. git checkout main
  681. - name: Checkout dev branch
  682. if: matrix.if && github.ref == 'refs/heads/dev-next'
  683. run: |-
  684. cd clients/apple
  685. git checkout dev
  686. - name: Setup certificates
  687. if: matrix.if
  688. run: |-
  689. CERTIFICATE_PATH=$RUNNER_TEMP/Certificates.p12
  690. KEYCHAIN_PATH=$RUNNER_TEMP/certificates.keychain-db
  691. echo -n "$CERTIFICATES_P12" | base64 --decode -o $CERTIFICATE_PATH
  692. security create-keychain -p "$KEYCHAIN_PASSWORD" $KEYCHAIN_PATH
  693. security set-keychain-settings -lut 21600 $KEYCHAIN_PATH
  694. security unlock-keychain -p "$KEYCHAIN_PASSWORD" $KEYCHAIN_PATH
  695. security import $CERTIFICATE_PATH -P "$P12_PASSWORD" -A -t cert -f pkcs12 -k $KEYCHAIN_PATH
  696. security set-key-partition-list -S apple-tool:,apple: -k "$KEYCHAIN_PASSWORD" $KEYCHAIN_PATH
  697. security list-keychain -d user -s $KEYCHAIN_PATH
  698. PROFILES_ZIP_PATH=$RUNNER_TEMP/Profiles.zip
  699. echo -n "$PROVISIONING_PROFILES" | base64 --decode -o $PROFILES_ZIP_PATH
  700. PROFILES_PATH="$HOME/Library/MobileDevice/Provisioning Profiles"
  701. mkdir -p "$PROFILES_PATH"
  702. unzip $PROFILES_ZIP_PATH -d "$PROFILES_PATH"
  703. ASC_KEY_PATH=$RUNNER_TEMP/Key.p12
  704. echo -n "$ASC_KEY" | base64 --decode -o $ASC_KEY_PATH
  705. xcrun notarytool store-credentials "notarytool-password" \
  706. --key $ASC_KEY_PATH \
  707. --key-id $ASC_KEY_ID \
  708. --issuer $ASC_KEY_ISSUER_ID
  709. echo "ASC_KEY_PATH=$ASC_KEY_PATH" >> "$GITHUB_ENV"
  710. echo "ASC_KEY_ID=$ASC_KEY_ID" >> "$GITHUB_ENV"
  711. echo "ASC_KEY_ISSUER_ID=$ASC_KEY_ISSUER_ID" >> "$GITHUB_ENV"
  712. env:
  713. CERTIFICATES_P12: ${{ secrets.CERTIFICATES_P12 }}
  714. P12_PASSWORD: ${{ secrets.P12_PASSWORD }}
  715. KEYCHAIN_PASSWORD: ${{ secrets.P12_PASSWORD }}
  716. PROVISIONING_PROFILES: ${{ secrets.PROVISIONING_PROFILES }}
  717. ASC_KEY: ${{ secrets.ASC_KEY }}
  718. ASC_KEY_ID: ${{ secrets.ASC_KEY_ID }}
  719. ASC_KEY_ISSUER_ID: ${{ secrets.ASC_KEY_ISSUER_ID }}
  720. - name: Build library
  721. if: matrix.if
  722. run: |-
  723. make lib_install
  724. export PATH="$PATH:$(go env GOPATH)/bin"
  725. go run ./cmd/internal/build_libbox -target apple -platform ${{ matrix.platform }}
  726. mv Libbox.xcframework clients/apple
  727. - name: Update macOS version
  728. if: matrix.if && matrix.name == 'macOS' && github.event_name == 'workflow_dispatch'
  729. run: |-
  730. MACOS_PROJECT_VERSION=$(go run -v ./cmd/internal/app_store_connect next_macos_project_version)
  731. echo "MACOS_PROJECT_VERSION=$MACOS_PROJECT_VERSION"
  732. echo "MACOS_PROJECT_VERSION=$MACOS_PROJECT_VERSION" >> "$GITHUB_ENV"
  733. - name: Update version
  734. if: matrix.if && matrix.name != 'iOS'
  735. run: |-
  736. go run -v ./cmd/internal/update_apple_version --ci
  737. - name: Build
  738. if: matrix.if
  739. run: |-
  740. cd clients/apple
  741. xcodebuild archive \
  742. -scheme "${{ matrix.scheme }}" \
  743. -configuration Release \
  744. -destination "${{ matrix.destination }}" \
  745. -archivePath "${{ matrix.archive }}" \
  746. -allowProvisioningUpdates \
  747. -authenticationKeyPath $ASC_KEY_PATH \
  748. -authenticationKeyID $ASC_KEY_ID \
  749. -authenticationKeyIssuerID $ASC_KEY_ISSUER_ID
  750. - name: Upload to App Store Connect
  751. if: matrix.if && matrix.name != 'macOS-standalone' && github.event_name == 'workflow_dispatch'
  752. run: |-
  753. go run -v ./cmd/internal/app_store_connect cancel_app_store ${{ matrix.platform }}
  754. cd clients/apple
  755. xcodebuild -exportArchive \
  756. -archivePath "${{ matrix.archive }}" \
  757. -exportOptionsPlist ${{ matrix.upload }} \
  758. -allowProvisioningUpdates \
  759. -authenticationKeyPath $ASC_KEY_PATH \
  760. -authenticationKeyID $ASC_KEY_ID \
  761. -authenticationKeyIssuerID $ASC_KEY_ISSUER_ID
  762. - name: Publish to TestFlight
  763. if: matrix.if && matrix.name != 'macOS-standalone' && github.event_name == 'workflow_dispatch' && github.ref =='refs/heads/dev-next'
  764. run: |-
  765. go run -v ./cmd/internal/app_store_connect publish_testflight ${{ matrix.platform }}
  766. - name: Build image
  767. if: matrix.if && matrix.name == 'macOS-standalone' && github.event_name == 'workflow_dispatch'
  768. run: |-
  769. pushd clients/apple
  770. xcodebuild -exportArchive \
  771. -archivePath "${{ matrix.archive }}" \
  772. -exportOptionsPlist ${{ matrix.export }} \
  773. -exportPath "${{ matrix.export_path }}"
  774. brew install create-dmg
  775. create-dmg \
  776. --volname "sing-box" \
  777. --volicon "${{ matrix.export_path }}/SFM.app/Contents/Resources/AppIcon.icns" \
  778. --icon "SFM.app" 0 0 \
  779. --hide-extension "SFM.app" \
  780. --app-drop-link 0 0 \
  781. --skip-jenkins \
  782. SFM.dmg "${{ matrix.export_path }}/SFM.app"
  783. xcrun notarytool submit "SFM.dmg" --wait --keychain-profile "notarytool-password"
  784. cd "${{ matrix.archive }}"
  785. zip -r SFM.dSYMs.zip dSYMs
  786. popd
  787. mkdir -p dist
  788. cp clients/apple/SFM.dmg "dist/SFM-${VERSION}-universal.dmg"
  789. cp "clients/apple/${{ matrix.archive }}/SFM.dSYMs.zip" "dist/SFM-${VERSION}-universal.dSYMs.zip"
  790. - name: Upload image
  791. if: matrix.if && matrix.name == 'macOS-standalone' && github.event_name == 'workflow_dispatch'
  792. uses: actions/upload-artifact@v4
  793. with:
  794. name: binary-macos-dmg
  795. path: 'dist'
  796. upload:
  797. name: Upload builds
  798. if: "!failure() && github.event_name == 'workflow_dispatch' && (inputs.build == 'All' || inputs.build == 'Binary' || inputs.build == 'Android' || inputs.build == 'Apple' || inputs.build == 'macOS-standalone')"
  799. runs-on: ubuntu-latest
  800. needs:
  801. - calculate_version
  802. - build
  803. - build_darwin
  804. - build_naive_linux
  805. - build_android
  806. - build_apple
  807. steps:
  808. - name: Checkout
  809. uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5
  810. with:
  811. fetch-depth: 0
  812. - name: Cache ghr
  813. uses: actions/cache@v4
  814. id: cache-ghr
  815. with:
  816. path: |
  817. ~/go/bin/ghr
  818. key: ghr
  819. - name: Setup ghr
  820. if: steps.cache-ghr.outputs.cache-hit != 'true'
  821. run: |-
  822. cd $HOME
  823. git clone https://github.com/nekohasekai/ghr ghr
  824. cd ghr
  825. go install -v .
  826. - name: Set tag
  827. run: |-
  828. git ls-remote --exit-code --tags origin v${{ needs.calculate_version.outputs.version }} || echo "PUBLISHED=false" >> "$GITHUB_ENV"
  829. git tag v${{ needs.calculate_version.outputs.version }} -f
  830. echo "VERSION=${{ needs.calculate_version.outputs.version }}" >> "$GITHUB_ENV"
  831. - name: Download builds
  832. uses: actions/download-artifact@v5
  833. with:
  834. path: dist
  835. merge-multiple: true
  836. - name: Upload builds
  837. if: ${{ env.PUBLISHED == 'false' }}
  838. run: |-
  839. export PATH="$PATH:$HOME/go/bin"
  840. ghr --replace --draft --prerelease -p 5 "v${VERSION}" dist
  841. env:
  842. GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
  843. - name: Replace builds
  844. if: ${{ env.PUBLISHED != 'false' }}
  845. run: |-
  846. export PATH="$PATH:$HOME/go/bin"
  847. ghr --replace -p 5 "v${VERSION}" dist
  848. env:
  849. GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}