build.yml 42 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019
  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. - stable
  28. - testing
  29. - unstable
  30. concurrency:
  31. group: ${{ github.workflow }}-${{ github.ref }}-${{ github.event_name }}-${{ inputs.build }}
  32. cancel-in-progress: true
  33. jobs:
  34. calculate_version:
  35. name: Calculate version
  36. runs-on: ubuntu-latest
  37. outputs:
  38. version: ${{ steps.outputs.outputs.version }}
  39. steps:
  40. - name: Checkout
  41. uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5
  42. with:
  43. fetch-depth: 0
  44. - name: Setup Go
  45. uses: actions/setup-go@v5
  46. with:
  47. go-version: ~1.25.9
  48. - name: Check input version
  49. if: github.event_name == 'workflow_dispatch'
  50. run: |-
  51. echo "version=${{ inputs.version }}"
  52. echo "version=${{ inputs.version }}" >> "$GITHUB_ENV"
  53. - name: Calculate version
  54. if: github.event_name != 'workflow_dispatch'
  55. run: |-
  56. go run -v ./cmd/internal/read_tag --ci --nightly
  57. - name: Set outputs
  58. id: outputs
  59. run: |-
  60. echo "version=$version" >> "$GITHUB_OUTPUT"
  61. build:
  62. name: Build binary
  63. if: github.event_name != 'workflow_dispatch' || inputs.build == 'All' || inputs.build == 'Binary'
  64. runs-on: ubuntu-latest
  65. needs:
  66. - calculate_version
  67. strategy:
  68. matrix:
  69. include:
  70. - { os: linux, arch: amd64, variant: purego, naive: true }
  71. - { os: linux, arch: amd64, variant: glibc, naive: true }
  72. - { os: linux, arch: amd64, variant: musl, naive: true, debian: amd64, rpm: x86_64, pacman: x86_64, alpine: x86_64, openwrt: "x86_64" }
  73. - { os: linux, arch: arm64, variant: purego, naive: true }
  74. - { os: linux, arch: arm64, variant: glibc, naive: true }
  75. - { os: linux, arch: arm64, variant: musl, naive: true, debian: arm64, rpm: aarch64, pacman: aarch64, alpine: aarch64, openwrt: "aarch64_cortex-a53 aarch64_cortex-a72 aarch64_cortex-a76 aarch64_generic" }
  76. - { os: linux, arch: "386", go386: sse2 }
  77. - { os: linux, arch: "386", variant: glibc, naive: true, go386: sse2 }
  78. - { os: linux, arch: "386", variant: musl, naive: true, go386: sse2, debian: i386, rpm: i386, alpine: x86, openwrt: "i386_pentium4" }
  79. - { os: linux, arch: arm, goarm: "7" }
  80. - { os: linux, arch: arm, variant: glibc, naive: true, goarm: "7" }
  81. - { os: linux, arch: arm, variant: musl, naive: true, goarm: "7", debian: armhf, rpm: armv7hl, pacman: armv7hl, alpine: armv7, 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" }
  82. - { os: linux, arch: mipsle, gomips: hardfloat, naive: true, variant: glibc }
  83. - { os: linux, arch: mipsle, gomips: softfloat, naive: true, variant: musl, debian: mipsel, rpm: mipsel, openwrt: "mipsel_24kc mipsel_74kc mipsel_mips32" }
  84. - { os: linux, arch: mips64le, gomips: hardfloat, naive: true, variant: glibc, debian: mips64el, rpm: mips64el }
  85. - { os: linux, arch: riscv64, naive: true, variant: glibc }
  86. - { os: linux, arch: riscv64, naive: true, variant: musl, debian: riscv64, rpm: riscv64, alpine: riscv64, openwrt: "riscv64_generic" }
  87. - { os: linux, arch: loong64, naive: true, variant: glibc }
  88. - { os: linux, arch: loong64, naive: true, variant: musl, debian: loongarch64, rpm: loongarch64, alpine: loongarch64, openwrt: "loongarch64_generic" }
  89. - { os: linux, arch: "386", go386: softfloat, openwrt: "i386_pentium-mmx" }
  90. - { os: linux, arch: arm, goarm: "5", openwrt: "arm_arm926ej-s arm_cortex-a7 arm_cortex-a9 arm_fa526 arm_xscale" }
  91. - { os: linux, arch: arm, goarm: "6", debian: armel, rpm: armv6hl, openwrt: "arm_arm1176jzf-s_vfp" }
  92. - { os: linux, arch: mips, gomips: softfloat, openwrt: "mips_24kc mips_4kec mips_mips32" }
  93. - { os: linux, arch: mipsle, gomips: hardfloat, openwrt: "mipsel_24kc_24kf" }
  94. - { os: linux, arch: mipsle, gomips: softfloat }
  95. - { os: linux, arch: mips64, gomips: softfloat, openwrt: "mips64_mips64r2 mips64_octeonplus" }
  96. - { os: linux, arch: mips64le, gomips: hardfloat }
  97. - { os: linux, arch: mips64le, gomips: softfloat, openwrt: "mips64el_mips64r2" }
  98. - { os: linux, arch: s390x, debian: s390x, rpm: s390x }
  99. - { os: linux, arch: ppc64le, debian: ppc64el, rpm: ppc64le }
  100. - { os: linux, arch: riscv64 }
  101. - { os: linux, arch: loong64 }
  102. - { os: windows, arch: amd64, legacy_win7: true, legacy_name: "windows-7" }
  103. - { os: windows, arch: "386", legacy_win7: true, legacy_name: "windows-7" }
  104. - { os: android, arch: arm64, ndk: "aarch64-linux-android23" }
  105. - { os: android, arch: arm, ndk: "armv7a-linux-androideabi23" }
  106. - { os: android, arch: amd64, ndk: "x86_64-linux-android23" }
  107. - { os: android, arch: "386", ndk: "i686-linux-android23" }
  108. steps:
  109. - name: Checkout
  110. uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5
  111. with:
  112. fetch-depth: 0
  113. - name: Setup Go
  114. if: ${{ ! matrix.legacy_win7 }}
  115. uses: actions/setup-go@v5
  116. with:
  117. go-version: ~1.25.9
  118. - name: Cache Go for Windows 7
  119. if: matrix.legacy_win7
  120. id: cache-go-for-windows7
  121. uses: actions/cache@v4
  122. with:
  123. path: |
  124. ~/go/go_win7
  125. key: go_win7_1258
  126. - name: Setup Go for Windows 7
  127. if: matrix.legacy_win7 && steps.cache-go-for-windows7.outputs.cache-hit != 'true'
  128. env:
  129. GITHUB_TOKEN: ${{ github.token }}
  130. run: |-
  131. .github/setup_go_for_windows7.sh
  132. - name: Setup Go for Windows 7
  133. if: matrix.legacy_win7
  134. run: |-
  135. echo "PATH=$HOME/go/go_win7/bin:$PATH" >> $GITHUB_ENV
  136. echo "GOROOT=$HOME/go/go_win7" >> $GITHUB_ENV
  137. - name: Setup Android NDK
  138. if: matrix.os == 'android'
  139. uses: nttld/setup-ndk@v1
  140. with:
  141. ndk-version: r28
  142. local-cache: true
  143. - name: Clone cronet-go
  144. if: matrix.naive
  145. run: |
  146. set -xeuo pipefail
  147. CRONET_GO_VERSION=$(cat .github/CRONET_GO_VERSION)
  148. git init ~/cronet-go
  149. git -C ~/cronet-go remote add origin https://github.com/sagernet/cronet-go.git
  150. git -C ~/cronet-go fetch --depth=1 origin "$CRONET_GO_VERSION"
  151. git -C ~/cronet-go checkout FETCH_HEAD
  152. git -C ~/cronet-go submodule update --init --recursive --depth=1
  153. - name: Regenerate Debian keyring
  154. if: matrix.naive
  155. run: |
  156. set -xeuo pipefail
  157. rm -f ~/cronet-go/naiveproxy/src/build/linux/sysroot_scripts/keyring.gpg
  158. cd ~/cronet-go
  159. GPG_TTY=/dev/null ./naiveproxy/src/build/linux/sysroot_scripts/generate_keyring.sh
  160. - name: Cache Chromium toolchain
  161. if: matrix.naive
  162. id: cache-chromium-toolchain
  163. uses: actions/cache@v4
  164. with:
  165. path: |
  166. ~/cronet-go/naiveproxy/src/third_party/llvm-build/
  167. ~/cronet-go/naiveproxy/src/gn/out/
  168. ~/cronet-go/naiveproxy/src/chrome/build/pgo_profiles/
  169. ~/cronet-go/naiveproxy/src/out/sysroot-build/
  170. key: chromium-toolchain-${{ matrix.arch }}-${{ matrix.variant }}-${{ hashFiles('.github/CRONET_GO_VERSION') }}
  171. - name: Download Chromium toolchain
  172. if: matrix.naive
  173. run: |
  174. set -xeuo pipefail
  175. cd ~/cronet-go
  176. if [[ "${{ matrix.variant }}" == "musl" ]]; then
  177. go run ./cmd/build-naive --target=linux/${{ matrix.arch }} --libc=musl download-toolchain
  178. else
  179. go run ./cmd/build-naive --target=linux/${{ matrix.arch }} download-toolchain
  180. fi
  181. - name: Set Chromium toolchain environment
  182. if: matrix.naive
  183. run: |
  184. set -xeuo pipefail
  185. cd ~/cronet-go
  186. if [[ "${{ matrix.variant }}" == "musl" ]]; then
  187. go run ./cmd/build-naive --target=linux/${{ matrix.arch }} --libc=musl env >> $GITHUB_ENV
  188. else
  189. go run ./cmd/build-naive --target=linux/${{ matrix.arch }} env >> $GITHUB_ENV
  190. fi
  191. - name: Set tag
  192. run: |-
  193. git ls-remote --exit-code --tags origin v${{ needs.calculate_version.outputs.version }} || echo "PUBLISHED=false" >> "$GITHUB_ENV"
  194. git tag v${{ needs.calculate_version.outputs.version }} -f
  195. - name: Set build tags
  196. run: |
  197. set -xeuo pipefail
  198. if [[ "${{ matrix.naive }}" == "true" ]]; then
  199. TAGS=$(cat release/DEFAULT_BUILD_TAGS)
  200. else
  201. TAGS=$(cat release/DEFAULT_BUILD_TAGS_OTHERS)
  202. fi
  203. if [[ "${{ matrix.variant }}" == "purego" ]]; then
  204. TAGS="${TAGS},with_purego"
  205. elif [[ "${{ matrix.variant }}" == "musl" ]]; then
  206. TAGS="${TAGS},with_musl"
  207. fi
  208. echo "BUILD_TAGS=${TAGS}" >> "${GITHUB_ENV}"
  209. - name: Set shared ldflags
  210. run: |
  211. echo "LDFLAGS_SHARED=$(cat release/LDFLAGS)" >> "${GITHUB_ENV}"
  212. - name: Build (purego)
  213. if: matrix.variant == 'purego'
  214. run: |
  215. set -xeuo pipefail
  216. mkdir -p dist
  217. go build -v -trimpath -o dist/sing-box -tags "${BUILD_TAGS}" \
  218. -ldflags "-X 'github.com/sagernet/sing-box/constant.Version=${{ needs.calculate_version.outputs.version }}' ${LDFLAGS_SHARED} -s -w -buildid=" \
  219. ./cmd/sing-box
  220. env:
  221. CGO_ENABLED: "0"
  222. GOOS: ${{ matrix.os }}
  223. GOARCH: ${{ matrix.arch }}
  224. GO386: ${{ matrix.go386 }}
  225. GOARM: ${{ matrix.goarm }}
  226. GOMIPS: ${{ matrix.gomips }}
  227. GOMIPS64: ${{ matrix.gomips }}
  228. GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
  229. - name: Extract libcronet.so
  230. if: matrix.variant == 'purego' && matrix.naive
  231. run: |
  232. cd ~/cronet-go
  233. CGO_ENABLED=0 go run -v ./cmd/build-naive extract-lib --target ${{ matrix.os }}/${{ matrix.arch }} -o $GITHUB_WORKSPACE/dist
  234. - name: Build (glibc)
  235. if: matrix.variant == 'glibc'
  236. run: |
  237. set -xeuo pipefail
  238. mkdir -p dist
  239. go build -v -trimpath -o dist/sing-box -tags "${BUILD_TAGS}" \
  240. -ldflags "-X 'github.com/sagernet/sing-box/constant.Version=${{ needs.calculate_version.outputs.version }}' ${LDFLAGS_SHARED} -s -w -buildid=" \
  241. ./cmd/sing-box
  242. env:
  243. CGO_ENABLED: "1"
  244. GOOS: linux
  245. GOARCH: ${{ matrix.arch }}
  246. GO386: ${{ matrix.go386 }}
  247. GOARM: ${{ matrix.goarm }}
  248. GOMIPS: ${{ matrix.gomips }}
  249. GOMIPS64: ${{ matrix.gomips }}
  250. GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
  251. - name: Build (musl)
  252. if: matrix.variant == 'musl'
  253. run: |
  254. set -xeuo pipefail
  255. mkdir -p dist
  256. go build -v -trimpath -o dist/sing-box -tags "${BUILD_TAGS}" \
  257. -ldflags "-X 'github.com/sagernet/sing-box/constant.Version=${{ needs.calculate_version.outputs.version }}' ${LDFLAGS_SHARED} -s -w -buildid=" \
  258. ./cmd/sing-box
  259. env:
  260. CGO_ENABLED: "1"
  261. GOOS: linux
  262. GOARCH: ${{ matrix.arch }}
  263. GO386: ${{ matrix.go386 }}
  264. GOARM: ${{ matrix.goarm }}
  265. GOMIPS: ${{ matrix.gomips }}
  266. GOMIPS64: ${{ matrix.gomips }}
  267. GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
  268. - name: Build (non-variant)
  269. if: matrix.os != 'android' && matrix.variant == ''
  270. run: |
  271. set -xeuo pipefail
  272. mkdir -p dist
  273. go build -v -trimpath -o dist/sing-box -tags "${BUILD_TAGS}" \
  274. -ldflags "-X 'github.com/sagernet/sing-box/constant.Version=${{ needs.calculate_version.outputs.version }}' ${LDFLAGS_SHARED} -s -w -buildid=" \
  275. ./cmd/sing-box
  276. env:
  277. CGO_ENABLED: "0"
  278. GOOS: ${{ matrix.os }}
  279. GOARCH: ${{ matrix.arch }}
  280. GO386: ${{ matrix.go386 }}
  281. GOARM: ${{ matrix.goarm }}
  282. GOMIPS: ${{ matrix.gomips }}
  283. GOMIPS64: ${{ matrix.gomips }}
  284. GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
  285. - name: Build Android
  286. if: matrix.os == 'android'
  287. run: |
  288. set -xeuo pipefail
  289. go install -v ./cmd/internal/build
  290. export CC='${{ matrix.ndk }}-clang'
  291. export CXX="${CC}++"
  292. mkdir -p dist
  293. GOOS=$BUILD_GOOS GOARCH=$BUILD_GOARCH build go build -v -trimpath -o dist/sing-box -tags "${BUILD_TAGS}" \
  294. -ldflags "-X 'github.com/sagernet/sing-box/constant.Version=${{ needs.calculate_version.outputs.version }}' ${LDFLAGS_SHARED} -s -w -buildid=" \
  295. ./cmd/sing-box
  296. env:
  297. CGO_ENABLED: "1"
  298. BUILD_GOOS: ${{ matrix.os }}
  299. BUILD_GOARCH: ${{ matrix.arch }}
  300. GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
  301. - name: Set name
  302. run: |-
  303. DIR_NAME="sing-box-${{ needs.calculate_version.outputs.version }}-${{ matrix.os }}-${{ matrix.arch }}"
  304. if [[ -n "${{ matrix.goarm }}" ]]; then
  305. DIR_NAME="${DIR_NAME}v${{ matrix.goarm }}"
  306. elif [[ -n "${{ matrix.go386 }}" && "${{ matrix.go386 }}" != 'sse2' ]]; then
  307. DIR_NAME="${DIR_NAME}-${{ matrix.go386 }}"
  308. elif [[ -n "${{ matrix.gomips }}" && "${{ matrix.gomips }}" != 'hardfloat' ]]; then
  309. DIR_NAME="${DIR_NAME}-${{ matrix.gomips }}"
  310. elif [[ -n "${{ matrix.legacy_name }}" ]]; then
  311. DIR_NAME="${DIR_NAME}-legacy-${{ matrix.legacy_name }}"
  312. fi
  313. if [[ "${{ matrix.variant }}" == "glibc" ]]; then
  314. DIR_NAME="${DIR_NAME}-glibc"
  315. elif [[ "${{ matrix.variant }}" == "musl" ]]; then
  316. DIR_NAME="${DIR_NAME}-musl"
  317. fi
  318. echo "DIR_NAME=${DIR_NAME}" >> "${GITHUB_ENV}"
  319. PKG_VERSION="${{ needs.calculate_version.outputs.version }}"
  320. PKG_VERSION="${PKG_VERSION//-/\~}"
  321. echo "PKG_VERSION=${PKG_VERSION}" >> "${GITHUB_ENV}"
  322. - name: Package DEB
  323. if: matrix.debian != ''
  324. run: |
  325. set -xeuo pipefail
  326. sudo gem install fpm
  327. sudo apt-get update
  328. sudo apt-get install -y debsigs
  329. cp .fpm_systemd .fpm
  330. fpm -t deb \
  331. -v "$PKG_VERSION" \
  332. -p "dist/sing-box_${{ needs.calculate_version.outputs.version }}_${{ matrix.os }}_${{ matrix.debian }}.deb" \
  333. --architecture ${{ matrix.debian }} \
  334. dist/sing-box=/usr/bin/sing-box
  335. curl -Lo '/tmp/debsigs.diff' 'https://gitlab.com/debsigs/debsigs/-/commit/160138f5de1ec110376d3c807b60a37388bc7c90.diff'
  336. sudo patch /usr/bin/debsigs < '/tmp/debsigs.diff'
  337. rm -rf $HOME/.gnupg
  338. gpg --pinentry-mode loopback --passphrase "${{ secrets.GPG_PASSPHRASE }}" --import <<EOF
  339. ${{ secrets.GPG_KEY }}
  340. EOF
  341. debsigs --sign=origin -k ${{ secrets.GPG_KEY_ID }} --gpgopts '--pinentry-mode loopback --passphrase "${{ secrets.GPG_PASSPHRASE }}"' dist/*.deb
  342. - name: Package RPM
  343. if: matrix.rpm != ''
  344. run: |-
  345. set -xeuo pipefail
  346. sudo gem install fpm
  347. cp .fpm_systemd .fpm
  348. fpm -t rpm \
  349. -v "$PKG_VERSION" \
  350. -p "dist/sing-box_${{ needs.calculate_version.outputs.version }}_${{ matrix.os }}_${{ matrix.rpm }}.rpm" \
  351. --architecture ${{ matrix.rpm }} \
  352. dist/sing-box=/usr/bin/sing-box
  353. cat > $HOME/.rpmmacros <<EOF
  354. %_gpg_name ${{ secrets.GPG_KEY_ID }}
  355. %_gpg_sign_cmd_extra_args --pinentry-mode loopback --passphrase ${{ secrets.GPG_PASSPHRASE }}
  356. EOF
  357. gpg --pinentry-mode loopback --passphrase "${{ secrets.GPG_PASSPHRASE }}" --import <<EOF
  358. ${{ secrets.GPG_KEY }}
  359. EOF
  360. rpmsign --addsign dist/*.rpm
  361. - name: Package Pacman
  362. if: matrix.pacman != ''
  363. run: |-
  364. set -xeuo pipefail
  365. sudo gem install fpm
  366. sudo apt-get update
  367. sudo apt-get install -y libarchive-tools
  368. cp .fpm_pacman .fpm
  369. fpm -t pacman \
  370. -v "$PKG_VERSION" \
  371. -p "dist/sing-box_${{ needs.calculate_version.outputs.version }}_${{ matrix.os }}_${{ matrix.pacman }}.pkg.tar.zst" \
  372. --architecture ${{ matrix.pacman }} \
  373. dist/sing-box=/usr/bin/sing-box
  374. - name: Package OpenWrt
  375. if: matrix.openwrt != ''
  376. run: |-
  377. set -xeuo pipefail
  378. sudo gem install fpm
  379. cp .fpm_openwrt .fpm
  380. fpm -t deb \
  381. -v "$PKG_VERSION" \
  382. -p "dist/openwrt.deb" \
  383. --architecture all \
  384. dist/sing-box=/usr/bin/sing-box
  385. for architecture in ${{ matrix.openwrt }}; do
  386. .github/deb2ipk.sh "$architecture" "dist/openwrt.deb" "dist/sing-box_${{ needs.calculate_version.outputs.version }}_openwrt_${architecture}.ipk"
  387. done
  388. rm "dist/openwrt.deb"
  389. - name: Install apk-tools
  390. if: matrix.openwrt != '' || matrix.alpine != ''
  391. run: |-
  392. docker run --rm -v /usr/local/bin:/mnt alpine:edge sh -c "apk add --no-cache apk-tools-static && cp /sbin/apk.static /mnt/apk && chmod +x /mnt/apk"
  393. - name: Package OpenWrt APK
  394. if: matrix.openwrt != ''
  395. run: |-
  396. set -xeuo pipefail
  397. for architecture in ${{ matrix.openwrt }}; do
  398. .github/build_openwrt_apk.sh \
  399. "$architecture" \
  400. "${{ needs.calculate_version.outputs.version }}" \
  401. "dist/sing-box" \
  402. "dist/sing-box_${{ needs.calculate_version.outputs.version }}_openwrt_${architecture}.apk"
  403. done
  404. - name: Package Alpine APK
  405. if: matrix.alpine != ''
  406. run: |-
  407. set -xeuo pipefail
  408. .github/build_alpine_apk.sh \
  409. "${{ matrix.alpine }}" \
  410. "${{ needs.calculate_version.outputs.version }}" \
  411. "dist/sing-box" \
  412. "dist/sing-box_${{ needs.calculate_version.outputs.version }}_linux_${{ matrix.alpine }}.apk"
  413. - name: Archive
  414. run: |
  415. set -xeuo pipefail
  416. cd dist
  417. mkdir -p "${DIR_NAME}"
  418. cp ../LICENSE "${DIR_NAME}"
  419. if [ '${{ matrix.os }}' = 'windows' ]; then
  420. cp sing-box "${DIR_NAME}/sing-box.exe"
  421. zip -r "${DIR_NAME}.zip" "${DIR_NAME}"
  422. else
  423. cp sing-box "${DIR_NAME}"
  424. if [ -f libcronet.so ]; then
  425. cp libcronet.so "${DIR_NAME}"
  426. fi
  427. tar -czvf "${DIR_NAME}.tar.gz" "${DIR_NAME}"
  428. fi
  429. rm -r "${DIR_NAME}"
  430. - name: Cleanup
  431. run: rm -f dist/sing-box dist/libcronet.so
  432. - name: Upload artifact
  433. uses: actions/upload-artifact@v4
  434. with:
  435. 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) }}${{ matrix.variant && format('-{0}', matrix.variant) }}
  436. path: "dist"
  437. build_darwin:
  438. name: Build Darwin binaries
  439. if: github.event_name != 'workflow_dispatch' || inputs.build == 'All' || inputs.build == 'Binary'
  440. runs-on: macos-latest
  441. needs:
  442. - calculate_version
  443. strategy:
  444. matrix:
  445. include:
  446. - { arch: amd64 }
  447. - { arch: arm64 }
  448. - { arch: amd64, legacy_osx: true, legacy_name: "macos-10.13" }
  449. steps:
  450. - name: Checkout
  451. uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5
  452. with:
  453. fetch-depth: 0
  454. - name: Setup Go
  455. if: ${{ ! matrix.legacy_osx }}
  456. uses: actions/setup-go@v5
  457. with:
  458. go-version: ^1.25.3
  459. - name: Cache Go for macOS 10.13
  460. if: matrix.legacy_osx
  461. id: cache-go-for-macos1013
  462. uses: actions/cache@v4
  463. with:
  464. path: |
  465. ~/go/go_osx
  466. key: go_osx_1258
  467. - name: Setup Go for macOS 10.13
  468. if: matrix.legacy_osx && steps.cache-go-for-macos1013.outputs.cache-hit != 'true'
  469. env:
  470. GITHUB_TOKEN: ${{ github.token }}
  471. run: |-
  472. .github/setup_go_for_macos1013.sh
  473. - name: Setup Go for macOS 10.13
  474. if: matrix.legacy_osx
  475. run: |-
  476. echo "PATH=$HOME/go/go_osx/bin:$PATH" >> $GITHUB_ENV
  477. echo "GOROOT=$HOME/go/go_osx" >> $GITHUB_ENV
  478. - name: Set tag
  479. run: |-
  480. git ls-remote --exit-code --tags origin v${{ needs.calculate_version.outputs.version }} || echo "PUBLISHED=false" >> "$GITHUB_ENV"
  481. git tag v${{ needs.calculate_version.outputs.version }} -f
  482. - name: Set build tags
  483. run: |
  484. set -xeuo pipefail
  485. if [[ "${{ matrix.legacy_osx }}" != "true" ]]; then
  486. TAGS=$(cat release/DEFAULT_BUILD_TAGS)
  487. else
  488. TAGS=$(cat release/DEFAULT_BUILD_TAGS_OTHERS)
  489. fi
  490. echo "BUILD_TAGS=${TAGS}" >> "${GITHUB_ENV}"
  491. - name: Set shared ldflags
  492. run: |
  493. echo "LDFLAGS_SHARED=$(cat release/LDFLAGS)" >> "${GITHUB_ENV}"
  494. - name: Build
  495. run: |
  496. set -xeuo pipefail
  497. mkdir -p dist
  498. go build -v -trimpath -o dist/sing-box -tags "${BUILD_TAGS}" \
  499. -ldflags "-X 'github.com/sagernet/sing-box/constant.Version=${{ needs.calculate_version.outputs.version }}' ${LDFLAGS_SHARED} -s -w -buildid=" \
  500. ./cmd/sing-box
  501. env:
  502. CGO_ENABLED: "1"
  503. GOOS: darwin
  504. GOARCH: ${{ matrix.arch }}
  505. MACOSX_DEPLOYMENT_TARGET: ${{ matrix.legacy_osx && '10.13' || '' }}
  506. GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
  507. - name: Set name
  508. run: |-
  509. DIR_NAME="sing-box-${{ needs.calculate_version.outputs.version }}-darwin-${{ matrix.arch }}"
  510. if [[ -n "${{ matrix.legacy_name }}" ]]; then
  511. DIR_NAME="${DIR_NAME}-legacy-${{ matrix.legacy_name }}"
  512. fi
  513. echo "DIR_NAME=${DIR_NAME}" >> "${GITHUB_ENV}"
  514. - name: Archive
  515. run: |
  516. set -xeuo pipefail
  517. cd dist
  518. mkdir -p "${DIR_NAME}"
  519. cp ../LICENSE "${DIR_NAME}"
  520. cp sing-box "${DIR_NAME}"
  521. tar -czvf "${DIR_NAME}.tar.gz" "${DIR_NAME}"
  522. rm -r "${DIR_NAME}"
  523. - name: Cleanup
  524. run: rm dist/sing-box
  525. - name: Upload artifact
  526. uses: actions/upload-artifact@v4
  527. with:
  528. name: binary-darwin_${{ matrix.arch }}${{ matrix.legacy_name && format('-legacy-{0}', matrix.legacy_name) }}
  529. path: "dist"
  530. build_windows:
  531. name: Build Windows binaries
  532. if: github.event_name != 'workflow_dispatch' || inputs.build == 'All' || inputs.build == 'Binary'
  533. runs-on: windows-latest
  534. needs:
  535. - calculate_version
  536. strategy:
  537. matrix:
  538. include:
  539. - { arch: amd64, naive: true }
  540. - { arch: "386" }
  541. - { arch: arm64, naive: true }
  542. steps:
  543. - name: Checkout
  544. uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5
  545. with:
  546. fetch-depth: 0
  547. - name: Setup Go
  548. uses: actions/setup-go@v5
  549. with:
  550. go-version: ^1.25.4
  551. - name: Set tag
  552. run: |-
  553. git ls-remote --exit-code --tags origin v${{ needs.calculate_version.outputs.version }} || echo "PUBLISHED=false" >> "$env:GITHUB_ENV"
  554. git tag v${{ needs.calculate_version.outputs.version }} -f
  555. - name: Build
  556. if: matrix.naive
  557. run: |
  558. $TAGS = Get-Content release/DEFAULT_BUILD_TAGS_WINDOWS
  559. $LDFLAGS_SHARED = Get-Content release/LDFLAGS
  560. mkdir -p dist
  561. go build -v -trimpath -o dist/sing-box.exe -tags "$TAGS" `
  562. -ldflags "-X 'github.com/sagernet/sing-box/constant.Version=${{ needs.calculate_version.outputs.version }}' $LDFLAGS_SHARED -s -w -buildid=" `
  563. ./cmd/sing-box
  564. env:
  565. CGO_ENABLED: "0"
  566. GOOS: windows
  567. GOARCH: ${{ matrix.arch }}
  568. GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
  569. - name: Build
  570. if: ${{ !matrix.naive }}
  571. run: |
  572. $TAGS = Get-Content release/DEFAULT_BUILD_TAGS_OTHERS
  573. $LDFLAGS_SHARED = Get-Content release/LDFLAGS
  574. mkdir -p dist
  575. go build -v -trimpath -o dist/sing-box.exe -tags "$TAGS" `
  576. -ldflags "-X 'github.com/sagernet/sing-box/constant.Version=${{ needs.calculate_version.outputs.version }}' $LDFLAGS_SHARED -s -w -buildid=" `
  577. ./cmd/sing-box
  578. env:
  579. CGO_ENABLED: "0"
  580. GOOS: windows
  581. GOARCH: ${{ matrix.arch }}
  582. GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
  583. - name: Extract libcronet.dll
  584. if: matrix.naive
  585. run: |
  586. $CRONET_GO_VERSION = Get-Content .github/CRONET_GO_VERSION
  587. $env:CGO_ENABLED = "0"
  588. go run -v "github.com/sagernet/cronet-go/cmd/build-naive@$CRONET_GO_VERSION" extract-lib --target windows/${{ matrix.arch }} -o dist
  589. - name: Archive
  590. if: matrix.naive
  591. run: |
  592. $DIR_NAME = "sing-box-${{ needs.calculate_version.outputs.version }}-windows-${{ matrix.arch }}"
  593. mkdir "dist/$DIR_NAME"
  594. Copy-Item LICENSE "dist/$DIR_NAME"
  595. Copy-Item "dist/sing-box.exe" "dist/$DIR_NAME"
  596. Copy-Item "dist/libcronet.dll" "dist/$DIR_NAME"
  597. Compress-Archive -Path "dist/$DIR_NAME" -DestinationPath "dist/$DIR_NAME.zip"
  598. Remove-Item -Recurse "dist/$DIR_NAME"
  599. - name: Archive
  600. if: ${{ !matrix.naive }}
  601. run: |
  602. $DIR_NAME = "sing-box-${{ needs.calculate_version.outputs.version }}-windows-${{ matrix.arch }}"
  603. mkdir "dist/$DIR_NAME"
  604. Copy-Item LICENSE "dist/$DIR_NAME"
  605. Copy-Item "dist/sing-box.exe" "dist/$DIR_NAME"
  606. Compress-Archive -Path "dist/$DIR_NAME" -DestinationPath "dist/$DIR_NAME.zip"
  607. Remove-Item -Recurse "dist/$DIR_NAME"
  608. - name: Cleanup
  609. if: matrix.naive
  610. run: Remove-Item dist/sing-box.exe, dist/libcronet.dll
  611. - name: Cleanup
  612. if: ${{ !matrix.naive }}
  613. run: Remove-Item dist/sing-box.exe
  614. - name: Upload artifact
  615. uses: actions/upload-artifact@v4
  616. with:
  617. name: binary-windows_${{ matrix.arch }}
  618. path: "dist"
  619. build_android:
  620. name: Build Android
  621. if: (github.event_name != 'workflow_dispatch' || inputs.build == 'All' || inputs.build == 'Android') && github.ref != 'refs/heads/oldstable'
  622. runs-on: ubuntu-latest
  623. needs:
  624. - calculate_version
  625. steps:
  626. - name: Checkout
  627. uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5
  628. with:
  629. fetch-depth: 0
  630. submodules: 'recursive'
  631. - name: Setup Go
  632. uses: actions/setup-go@v5
  633. with:
  634. go-version: ~1.25.9
  635. - name: Setup Android NDK
  636. id: setup-ndk
  637. uses: nttld/setup-ndk@v1
  638. with:
  639. ndk-version: r28
  640. - name: Setup OpenJDK
  641. run: |-
  642. sudo apt update && sudo apt install -y openjdk-17-jdk-headless
  643. /usr/lib/jvm/java-17-openjdk-amd64/bin/java --version
  644. - name: Set tag
  645. run: |-
  646. git ls-remote --exit-code --tags origin v${{ needs.calculate_version.outputs.version }} || echo "PUBLISHED=false" >> "$GITHUB_ENV"
  647. git tag v${{ needs.calculate_version.outputs.version }} -f
  648. - name: Build library
  649. run: |-
  650. make lib_install
  651. export PATH="$PATH:$(go env GOPATH)/bin"
  652. make lib_android
  653. env:
  654. JAVA_HOME: /usr/lib/jvm/java-17-openjdk-amd64
  655. ANDROID_NDK_HOME: ${{ steps.setup-ndk.outputs.ndk-path }}
  656. - name: Checkout main branch
  657. if: github.ref == 'refs/heads/stable' && github.event_name != 'workflow_dispatch'
  658. run: |-
  659. cd clients/android
  660. git checkout main
  661. - name: Checkout dev branch
  662. if: github.ref == 'refs/heads/testing'
  663. run: |-
  664. cd clients/android
  665. git checkout dev
  666. - name: Gradle cache
  667. uses: actions/cache@v4
  668. with:
  669. path: ~/.gradle
  670. key: gradle-${{ hashFiles('**/*.gradle') }}
  671. - name: Update version
  672. if: github.event_name == 'workflow_dispatch'
  673. run: |-
  674. go run -v ./cmd/internal/update_android_version --ci
  675. - name: Update nightly version
  676. if: github.event_name != 'workflow_dispatch'
  677. run: |-
  678. go run -v ./cmd/internal/update_android_version --ci --nightly
  679. - name: Build
  680. run: |-
  681. mkdir clients/android/app/libs
  682. cp *.aar clients/android/app/libs
  683. cd clients/android
  684. ./gradlew :app:assembleOtherRelease :app:assembleOtherLegacyRelease
  685. env:
  686. JAVA_HOME: /usr/lib/jvm/java-17-openjdk-amd64
  687. ANDROID_NDK_HOME: ${{ steps.setup-ndk.outputs.ndk-path }}
  688. LOCAL_PROPERTIES: ${{ secrets.LOCAL_PROPERTIES }}
  689. - name: Prepare upload
  690. run: |-
  691. mkdir -p dist
  692. #cp clients/android/app/build/outputs/apk/play/release/*.apk dist
  693. cp clients/android/app/build/outputs/apk/other/release/*.apk dist
  694. cp clients/android/app/build/outputs/apk/otherLegacy/release/*.apk dist
  695. VERSION_CODE=$(grep VERSION_CODE clients/android/version.properties | cut -d= -f2)
  696. VERSION_NAME=$(grep VERSION_NAME clients/android/version.properties | cut -d= -f2)
  697. cat > dist/SFA-version-metadata.json << EOF
  698. {
  699. "version_code": ${VERSION_CODE},
  700. "version_name": "${VERSION_NAME}"
  701. }
  702. EOF
  703. cat dist/SFA-version-metadata.json
  704. - name: Upload artifact
  705. uses: actions/upload-artifact@v4
  706. with:
  707. name: binary-android-apks
  708. path: 'dist'
  709. publish_android:
  710. name: Publish Android
  711. if: github.event_name == 'workflow_dispatch' && inputs.build == 'publish-android' && github.ref != 'refs/heads/oldstable'
  712. runs-on: ubuntu-latest
  713. needs:
  714. - calculate_version
  715. steps:
  716. - name: Checkout
  717. uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5
  718. with:
  719. fetch-depth: 0
  720. submodules: 'recursive'
  721. - name: Setup Go
  722. uses: actions/setup-go@v5
  723. with:
  724. go-version: ~1.25.9
  725. - name: Setup Android NDK
  726. id: setup-ndk
  727. uses: nttld/setup-ndk@v1
  728. with:
  729. ndk-version: r28
  730. - name: Setup OpenJDK
  731. run: |-
  732. sudo apt update && sudo apt install -y openjdk-17-jdk-headless
  733. /usr/lib/jvm/java-17-openjdk-amd64/bin/java --version
  734. - name: Set tag
  735. run: |-
  736. git ls-remote --exit-code --tags origin v${{ needs.calculate_version.outputs.version }} || echo "PUBLISHED=false" >> "$GITHUB_ENV"
  737. git tag v${{ needs.calculate_version.outputs.version }} -f
  738. - name: Build library
  739. run: |-
  740. make lib_install
  741. export PATH="$PATH:$(go env GOPATH)/bin"
  742. make lib_android
  743. env:
  744. JAVA_HOME: /usr/lib/jvm/java-17-openjdk-amd64
  745. ANDROID_NDK_HOME: ${{ steps.setup-ndk.outputs.ndk-path }}
  746. - name: Checkout main branch
  747. if: github.ref == 'refs/heads/stable' && github.event_name != 'workflow_dispatch'
  748. run: |-
  749. cd clients/android
  750. git checkout main
  751. - name: Checkout dev branch
  752. if: github.ref == 'refs/heads/testing'
  753. run: |-
  754. cd clients/android
  755. git checkout dev
  756. - name: Gradle cache
  757. uses: actions/cache@v4
  758. with:
  759. path: ~/.gradle
  760. key: gradle-${{ hashFiles('**/*.gradle') }}
  761. - name: Build
  762. run: |-
  763. go run -v ./cmd/internal/update_android_version --ci
  764. mkdir clients/android/app/libs
  765. cp *.aar clients/android/app/libs
  766. cd clients/android
  767. echo -n "$SERVICE_ACCOUNT_CREDENTIALS" | base64 --decode > service-account-credentials.json
  768. ./gradlew :app:publishPlayReleaseBundle
  769. env:
  770. JAVA_HOME: /usr/lib/jvm/java-17-openjdk-amd64
  771. ANDROID_NDK_HOME: ${{ steps.setup-ndk.outputs.ndk-path }}
  772. LOCAL_PROPERTIES: ${{ secrets.LOCAL_PROPERTIES }}
  773. SERVICE_ACCOUNT_CREDENTIALS: ${{ secrets.SERVICE_ACCOUNT_CREDENTIALS }}
  774. build_apple:
  775. name: Build Apple clients
  776. runs-on: macos-26
  777. if: false # github.event_name != 'workflow_dispatch' || inputs.build == 'All' || inputs.build == 'Apple' || inputs.build == 'app-store' || inputs.build == 'iOS' || inputs.build == 'macOS' || inputs.build == 'tvOS' || inputs.build == 'macOS-standalone'
  778. needs:
  779. - calculate_version
  780. strategy:
  781. matrix:
  782. include:
  783. - name: iOS
  784. if: ${{ github.event_name != 'workflow_dispatch' || inputs.build == 'All' || inputs.build == 'Apple' || inputs.build == 'app-store'|| inputs.build == 'iOS' }}
  785. platform: ios
  786. scheme: SFI
  787. destination: 'generic/platform=iOS'
  788. archive: build/SFI.xcarchive
  789. upload: SFI/Upload.plist
  790. - name: macOS
  791. if: ${{ github.event_name != 'workflow_dispatch' || inputs.build == 'All' || inputs.build == 'Apple' || inputs.build == 'app-store'|| inputs.build == 'macOS' }}
  792. platform: macos
  793. scheme: SFM
  794. destination: 'generic/platform=macOS'
  795. archive: build/SFM.xcarchive
  796. upload: SFI/Upload.plist
  797. - name: tvOS
  798. if: ${{ github.event_name != 'workflow_dispatch' || inputs.build == 'All' || inputs.build == 'Apple' || inputs.build == 'app-store'|| inputs.build == 'tvOS' }}
  799. platform: tvos
  800. scheme: SFT
  801. destination: 'generic/platform=tvOS'
  802. archive: build/SFT.xcarchive
  803. upload: SFI/Upload.plist
  804. - name: macOS-standalone
  805. if: ${{ github.event_name != 'workflow_dispatch' || inputs.build == 'All' || inputs.build == 'Apple' || inputs.build == 'macOS-standalone' }}
  806. platform: macos
  807. scheme: SFM.System
  808. destination: 'generic/platform=macOS'
  809. archive: build/SFM.System.xcarchive
  810. export: SFM.System/Export.plist
  811. export_path: build/SFM.System
  812. steps:
  813. - name: Checkout
  814. if: matrix.if
  815. uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5
  816. with:
  817. fetch-depth: 0
  818. submodules: 'recursive'
  819. - name: Setup Go
  820. if: matrix.if
  821. uses: actions/setup-go@v5
  822. with:
  823. go-version: ~1.25.9
  824. - name: Set tag
  825. if: matrix.if
  826. run: |-
  827. git ls-remote --exit-code --tags origin v${{ needs.calculate_version.outputs.version }} || echo "PUBLISHED=false" >> "$GITHUB_ENV"
  828. git tag v${{ needs.calculate_version.outputs.version }} -f
  829. echo "VERSION=${{ needs.calculate_version.outputs.version }}" >> "$GITHUB_ENV"
  830. - name: Checkout main branch
  831. if: matrix.if && github.ref == 'refs/heads/stable' && github.event_name != 'workflow_dispatch'
  832. run: |-
  833. cd clients/apple
  834. git checkout main
  835. - name: Checkout dev branch
  836. if: matrix.if && github.ref == 'refs/heads/testing'
  837. run: |-
  838. cd clients/apple
  839. git checkout dev
  840. - name: Setup certificates
  841. if: matrix.if
  842. run: |-
  843. CERTIFICATE_PATH=$RUNNER_TEMP/Certificates.p12
  844. KEYCHAIN_PATH=$RUNNER_TEMP/certificates.keychain-db
  845. echo -n "$CERTIFICATES_P12" | base64 --decode -o $CERTIFICATE_PATH
  846. security create-keychain -p "$KEYCHAIN_PASSWORD" $KEYCHAIN_PATH
  847. security set-keychain-settings -lut 21600 $KEYCHAIN_PATH
  848. security unlock-keychain -p "$KEYCHAIN_PASSWORD" $KEYCHAIN_PATH
  849. security import $CERTIFICATE_PATH -P "$P12_PASSWORD" -A -t cert -f pkcs12 -k $KEYCHAIN_PATH
  850. security set-key-partition-list -S apple-tool:,apple: -k "$KEYCHAIN_PASSWORD" $KEYCHAIN_PATH
  851. security list-keychain -d user -s $KEYCHAIN_PATH
  852. PROFILES_ZIP_PATH=$RUNNER_TEMP/Profiles.zip
  853. echo -n "$PROVISIONING_PROFILES" | base64 --decode -o $PROFILES_ZIP_PATH
  854. PROFILES_PATH="$HOME/Library/MobileDevice/Provisioning Profiles"
  855. mkdir -p "$PROFILES_PATH"
  856. unzip $PROFILES_ZIP_PATH -d "$PROFILES_PATH"
  857. ASC_KEY_PATH=$RUNNER_TEMP/Key.p12
  858. echo -n "$ASC_KEY" | base64 --decode -o $ASC_KEY_PATH
  859. xcrun notarytool store-credentials "notarytool-password" \
  860. --key $ASC_KEY_PATH \
  861. --key-id $ASC_KEY_ID \
  862. --issuer $ASC_KEY_ISSUER_ID
  863. echo "ASC_KEY_PATH=$ASC_KEY_PATH" >> "$GITHUB_ENV"
  864. echo "ASC_KEY_ID=$ASC_KEY_ID" >> "$GITHUB_ENV"
  865. echo "ASC_KEY_ISSUER_ID=$ASC_KEY_ISSUER_ID" >> "$GITHUB_ENV"
  866. env:
  867. CERTIFICATES_P12: ${{ secrets.CERTIFICATES_P12 }}
  868. P12_PASSWORD: ${{ secrets.P12_PASSWORD }}
  869. KEYCHAIN_PASSWORD: ${{ secrets.P12_PASSWORD }}
  870. PROVISIONING_PROFILES: ${{ secrets.PROVISIONING_PROFILES }}
  871. ASC_KEY: ${{ secrets.ASC_KEY }}
  872. ASC_KEY_ID: ${{ secrets.ASC_KEY_ID }}
  873. ASC_KEY_ISSUER_ID: ${{ secrets.ASC_KEY_ISSUER_ID }}
  874. - name: Build library
  875. if: matrix.if
  876. run: |-
  877. make lib_install
  878. export PATH="$PATH:$(go env GOPATH)/bin"
  879. go run ./cmd/internal/build_libbox -target apple -platform ${{ matrix.platform }}
  880. mv Libbox.xcframework clients/apple
  881. - name: Update macOS version
  882. if: matrix.if && matrix.name == 'macOS' && github.event_name == 'workflow_dispatch'
  883. run: |-
  884. MACOS_PROJECT_VERSION=$(go run -v ./cmd/internal/app_store_connect next_macos_project_version)
  885. echo "MACOS_PROJECT_VERSION=$MACOS_PROJECT_VERSION"
  886. echo "MACOS_PROJECT_VERSION=$MACOS_PROJECT_VERSION" >> "$GITHUB_ENV"
  887. - name: Update version
  888. if: matrix.if && matrix.name != 'iOS'
  889. run: |-
  890. go run -v ./cmd/internal/update_apple_version --ci
  891. - name: Build
  892. if: matrix.if
  893. run: |-
  894. cd clients/apple
  895. xcodebuild archive \
  896. -scheme "${{ matrix.scheme }}" \
  897. -configuration Release \
  898. -destination "${{ matrix.destination }}" \
  899. -archivePath "${{ matrix.archive }}" \
  900. -allowProvisioningUpdates \
  901. -authenticationKeyPath $ASC_KEY_PATH \
  902. -authenticationKeyID $ASC_KEY_ID \
  903. -authenticationKeyIssuerID $ASC_KEY_ISSUER_ID
  904. - name: Upload to App Store Connect
  905. if: matrix.if && matrix.name != 'macOS-standalone' && github.event_name == 'workflow_dispatch'
  906. run: |-
  907. go run -v ./cmd/internal/app_store_connect cancel_app_store ${{ matrix.platform }}
  908. cd clients/apple
  909. xcodebuild -exportArchive \
  910. -archivePath "${{ matrix.archive }}" \
  911. -exportOptionsPlist ${{ matrix.upload }} \
  912. -allowProvisioningUpdates \
  913. -authenticationKeyPath $ASC_KEY_PATH \
  914. -authenticationKeyID $ASC_KEY_ID \
  915. -authenticationKeyIssuerID $ASC_KEY_ISSUER_ID
  916. - name: Publish to TestFlight
  917. if: matrix.if && matrix.name != 'macOS-standalone' && github.event_name == 'workflow_dispatch' && github.ref =='refs/heads/testing'
  918. run: |-
  919. go run -v ./cmd/internal/app_store_connect publish_testflight ${{ matrix.platform }}
  920. - name: Build image
  921. if: matrix.if && matrix.name == 'macOS-standalone' && github.event_name == 'workflow_dispatch'
  922. run: |-
  923. pushd clients/apple
  924. xcodebuild -exportArchive \
  925. -archivePath "${{ matrix.archive }}" \
  926. -exportOptionsPlist ${{ matrix.export }} \
  927. -exportPath "${{ matrix.export_path }}"
  928. brew install create-dmg
  929. create-dmg \
  930. --volname "sing-box" \
  931. --volicon "${{ matrix.export_path }}/SFM.app/Contents/Resources/AppIcon.icns" \
  932. --icon "SFM.app" 0 0 \
  933. --hide-extension "SFM.app" \
  934. --app-drop-link 0 0 \
  935. --skip-jenkins \
  936. SFM.dmg "${{ matrix.export_path }}/SFM.app"
  937. xcrun notarytool submit "SFM.dmg" --wait --keychain-profile "notarytool-password"
  938. cd "${{ matrix.archive }}"
  939. zip -r SFM.dSYMs.zip dSYMs
  940. popd
  941. mkdir -p dist
  942. cp clients/apple/SFM.dmg "dist/SFM-${VERSION}-universal.dmg"
  943. cp "clients/apple/${{ matrix.archive }}/SFM.dSYMs.zip" "dist/SFM-${VERSION}-universal.dSYMs.zip"
  944. - name: Upload image
  945. if: matrix.if && matrix.name == 'macOS-standalone' && github.event_name == 'workflow_dispatch'
  946. uses: actions/upload-artifact@v4
  947. with:
  948. name: binary-macos-dmg
  949. path: 'dist'
  950. upload:
  951. name: Upload builds
  952. if: "!failure() && github.event_name == 'workflow_dispatch' && (inputs.build == 'All' || inputs.build == 'Binary' || inputs.build == 'Android' || inputs.build == 'Apple' || inputs.build == 'macOS-standalone')"
  953. runs-on: ubuntu-latest
  954. needs:
  955. - calculate_version
  956. - build
  957. - build_darwin
  958. - build_windows
  959. - build_android
  960. - build_apple
  961. steps:
  962. - name: Checkout
  963. uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5
  964. with:
  965. fetch-depth: 0
  966. - name: Cache ghr
  967. uses: actions/cache@v4
  968. id: cache-ghr
  969. with:
  970. path: |
  971. ~/go/bin/ghr
  972. key: ghr
  973. - name: Setup ghr
  974. if: steps.cache-ghr.outputs.cache-hit != 'true'
  975. run: |-
  976. cd $HOME
  977. git clone https://github.com/nekohasekai/ghr ghr
  978. cd ghr
  979. go install -v .
  980. - name: Set tag
  981. run: |-
  982. git ls-remote --exit-code --tags origin v${{ needs.calculate_version.outputs.version }} || echo "PUBLISHED=false" >> "$GITHUB_ENV"
  983. git tag v${{ needs.calculate_version.outputs.version }} -f
  984. echo "VERSION=${{ needs.calculate_version.outputs.version }}" >> "$GITHUB_ENV"
  985. - name: Download builds
  986. uses: actions/download-artifact@v5
  987. with:
  988. path: dist
  989. merge-multiple: true
  990. - name: Upload builds
  991. if: ${{ env.PUBLISHED == 'false' }}
  992. run: |-
  993. export PATH="$PATH:$HOME/go/bin"
  994. ghr --replace --draft --prerelease -p 5 "v${VERSION}" dist
  995. env:
  996. GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
  997. - name: Replace builds
  998. if: ${{ env.PUBLISHED != 'false' }}
  999. run: |-
  1000. export PATH="$PATH:$HOME/go/bin"
  1001. ghr --replace -p 5 "v${VERSION}" dist
  1002. env:
  1003. GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}