build.yml 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734
  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.5
  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.5
  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_1255
  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. echo "BUILD_TAGS=${TAGS}" >> "${GITHUB_ENV}"
  142. - name: Build
  143. if: matrix.os != 'android'
  144. run: |
  145. set -xeuo pipefail
  146. mkdir -p dist
  147. go build -v -trimpath -o dist/sing-box -tags "${BUILD_TAGS}" \
  148. -ldflags '-s -buildid= -X github.com/sagernet/sing-box/constant.Version=${{ needs.calculate_version.outputs.version }} -checklinkname=0' \
  149. ./cmd/sing-box
  150. env:
  151. CGO_ENABLED: "0"
  152. GOOS: ${{ matrix.os }}
  153. GOARCH: ${{ matrix.arch }}
  154. GO386: ${{ matrix.go386 }}
  155. GOARM: ${{ matrix.goarm }}
  156. GOMIPS: ${{ matrix.gomips }}
  157. GOMIPS64: ${{ matrix.gomips }}
  158. GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
  159. - name: Build Android
  160. if: matrix.os == 'android'
  161. run: |
  162. set -xeuo pipefail
  163. go install -v ./cmd/internal/build
  164. export CC='${{ matrix.ndk }}-clang'
  165. export CXX="${CC}++"
  166. mkdir -p dist
  167. GOOS=$BUILD_GOOS GOARCH=$BUILD_GOARCH build go build -v -trimpath -o dist/sing-box -tags "${BUILD_TAGS}" \
  168. -ldflags '-s -buildid= -X github.com/sagernet/sing-box/constant.Version=${{ needs.calculate_version.outputs.version }} -checklinkname=0' \
  169. ./cmd/sing-box
  170. env:
  171. CGO_ENABLED: "1"
  172. BUILD_GOOS: ${{ matrix.os }}
  173. BUILD_GOARCH: ${{ matrix.arch }}
  174. GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
  175. - name: Set name
  176. run: |-
  177. DIR_NAME="sing-box-${{ needs.calculate_version.outputs.version }}-${{ matrix.os }}-${{ matrix.arch }}"
  178. if [[ -n "${{ matrix.goarm }}" ]]; then
  179. DIR_NAME="${DIR_NAME}v${{ matrix.goarm }}"
  180. elif [[ -n "${{ matrix.go386 }}" && "${{ matrix.go386 }}" != 'sse2' ]]; then
  181. DIR_NAME="${DIR_NAME}-${{ matrix.go386 }}"
  182. elif [[ -n "${{ matrix.gomips }}" && "${{ matrix.gomips }}" != 'hardfloat' ]]; then
  183. DIR_NAME="${DIR_NAME}-${{ matrix.gomips }}"
  184. elif [[ -n "${{ matrix.legacy_name }}" ]]; then
  185. DIR_NAME="${DIR_NAME}-legacy-${{ matrix.legacy_name }}"
  186. fi
  187. echo "DIR_NAME=${DIR_NAME}" >> "${GITHUB_ENV}"
  188. PKG_VERSION="${{ needs.calculate_version.outputs.version }}"
  189. PKG_VERSION="${PKG_VERSION//-/\~}"
  190. echo "PKG_VERSION=${PKG_VERSION}" >> "${GITHUB_ENV}"
  191. - name: Package DEB
  192. if: matrix.debian != ''
  193. run: |
  194. set -xeuo pipefail
  195. sudo gem install fpm
  196. sudo apt-get update
  197. sudo apt-get install -y debsigs
  198. cp .fpm_systemd .fpm
  199. fpm -t deb \
  200. -v "$PKG_VERSION" \
  201. -p "dist/sing-box_${{ needs.calculate_version.outputs.version }}_${{ matrix.os }}_${{ matrix.debian }}.deb" \
  202. --architecture ${{ matrix.debian }} \
  203. dist/sing-box=/usr/bin/sing-box
  204. curl -Lo '/tmp/debsigs.diff' 'https://gitlab.com/debsigs/debsigs/-/commit/160138f5de1ec110376d3c807b60a37388bc7c90.diff'
  205. sudo patch /usr/bin/debsigs < '/tmp/debsigs.diff'
  206. rm -rf $HOME/.gnupg
  207. gpg --pinentry-mode loopback --passphrase "${{ secrets.GPG_PASSPHRASE }}" --import <<EOF
  208. ${{ secrets.GPG_KEY }}
  209. EOF
  210. debsigs --sign=origin -k ${{ secrets.GPG_KEY_ID }} --gpgopts '--pinentry-mode loopback --passphrase "${{ secrets.GPG_PASSPHRASE }}"' dist/*.deb
  211. - name: Package RPM
  212. if: matrix.rpm != ''
  213. run: |-
  214. set -xeuo pipefail
  215. sudo gem install fpm
  216. cp .fpm_systemd .fpm
  217. fpm -t rpm \
  218. -v "$PKG_VERSION" \
  219. -p "dist/sing-box_${{ needs.calculate_version.outputs.version }}_${{ matrix.os }}_${{ matrix.rpm }}.rpm" \
  220. --architecture ${{ matrix.rpm }} \
  221. dist/sing-box=/usr/bin/sing-box
  222. cat > $HOME/.rpmmacros <<EOF
  223. %_gpg_name ${{ secrets.GPG_KEY_ID }}
  224. %_gpg_sign_cmd_extra_args --pinentry-mode loopback --passphrase ${{ secrets.GPG_PASSPHRASE }}
  225. EOF
  226. gpg --pinentry-mode loopback --passphrase "${{ secrets.GPG_PASSPHRASE }}" --import <<EOF
  227. ${{ secrets.GPG_KEY }}
  228. EOF
  229. rpmsign --addsign dist/*.rpm
  230. - name: Package Pacman
  231. if: matrix.pacman != ''
  232. run: |-
  233. set -xeuo pipefail
  234. sudo gem install fpm
  235. sudo apt-get update
  236. sudo apt-get install -y libarchive-tools
  237. cp .fpm_systemd .fpm
  238. fpm -t pacman \
  239. -v "$PKG_VERSION" \
  240. -p "dist/sing-box_${{ needs.calculate_version.outputs.version }}_${{ matrix.os }}_${{ matrix.pacman }}.pkg.tar.zst" \
  241. --architecture ${{ matrix.pacman }} \
  242. dist/sing-box=/usr/bin/sing-box
  243. - name: Package OpenWrt
  244. if: matrix.openwrt != ''
  245. run: |-
  246. set -xeuo pipefail
  247. sudo gem install fpm
  248. cp .fpm_openwrt .fpm
  249. fpm -t deb \
  250. -v "$PKG_VERSION" \
  251. -p "dist/openwrt.deb" \
  252. --architecture all \
  253. dist/sing-box=/usr/bin/sing-box
  254. for architecture in ${{ matrix.openwrt }}; do
  255. .github/deb2ipk.sh "$architecture" "dist/openwrt.deb" "dist/sing-box_${{ needs.calculate_version.outputs.version }}_openwrt_${architecture}.ipk"
  256. done
  257. rm "dist/openwrt.deb"
  258. - name: Archive
  259. run: |
  260. set -xeuo pipefail
  261. cd dist
  262. mkdir -p "${DIR_NAME}"
  263. cp ../LICENSE "${DIR_NAME}"
  264. if [ '${{ matrix.os }}' = 'windows' ]; then
  265. cp sing-box "${DIR_NAME}/sing-box.exe"
  266. zip -r "${DIR_NAME}.zip" "${DIR_NAME}"
  267. else
  268. cp sing-box "${DIR_NAME}"
  269. tar -czvf "${DIR_NAME}.tar.gz" "${DIR_NAME}"
  270. fi
  271. rm -r "${DIR_NAME}"
  272. - name: Cleanup
  273. run: rm dist/sing-box
  274. - name: Upload artifact
  275. uses: actions/upload-artifact@v4
  276. with:
  277. 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) }}
  278. path: "dist"
  279. build_darwin:
  280. name: Build Darwin binaries
  281. if: github.event_name != 'workflow_dispatch' || inputs.build == 'All' || inputs.build == 'Binary'
  282. runs-on: macos-latest
  283. needs:
  284. - calculate_version
  285. strategy:
  286. matrix:
  287. include:
  288. - { arch: amd64 }
  289. - { arch: arm64 }
  290. - { arch: amd64, legacy_go124: true, legacy_name: "macos-11" }
  291. steps:
  292. - name: Checkout
  293. uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5
  294. with:
  295. fetch-depth: 0
  296. - name: Setup Go
  297. if: ${{ ! matrix.legacy_go124 }}
  298. uses: actions/setup-go@v5
  299. with:
  300. go-version: ^1.25.3
  301. - name: Setup Go 1.24
  302. if: matrix.legacy_go124
  303. uses: actions/setup-go@v5
  304. with:
  305. go-version: ~1.24.6
  306. - name: Set tag
  307. run: |-
  308. git ls-remote --exit-code --tags origin v${{ needs.calculate_version.outputs.version }} || echo "PUBLISHED=false" >> "$GITHUB_ENV"
  309. git tag v${{ needs.calculate_version.outputs.version }} -f
  310. - name: Set build tags
  311. run: |
  312. set -xeuo pipefail
  313. TAGS='with_gvisor,with_quic,with_dhcp,with_wireguard,with_utls,with_acme,with_clash_api,with_tailscale,with_ccm,badlinkname,tfogo_checklinkname0'
  314. echo "BUILD_TAGS=${TAGS}" >> "${GITHUB_ENV}"
  315. - name: Build
  316. run: |
  317. set -xeuo pipefail
  318. mkdir -p dist
  319. go build -v -trimpath -o dist/sing-box -tags "${BUILD_TAGS}" \
  320. -ldflags '-s -buildid= -X github.com/sagernet/sing-box/constant.Version=${{ needs.calculate_version.outputs.version }} -checklinkname=0' \
  321. ./cmd/sing-box
  322. env:
  323. CGO_ENABLED: "1"
  324. GOOS: darwin
  325. GOARCH: ${{ matrix.arch }}
  326. GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
  327. - name: Set name
  328. run: |-
  329. DIR_NAME="sing-box-${{ needs.calculate_version.outputs.version }}-darwin-${{ matrix.arch }}"
  330. if [[ -n "${{ matrix.legacy_name }}" ]]; then
  331. DIR_NAME="${DIR_NAME}-legacy-${{ matrix.legacy_name }}"
  332. fi
  333. echo "DIR_NAME=${DIR_NAME}" >> "${GITHUB_ENV}"
  334. - name: Archive
  335. run: |
  336. set -xeuo pipefail
  337. cd dist
  338. mkdir -p "${DIR_NAME}"
  339. cp ../LICENSE "${DIR_NAME}"
  340. cp sing-box "${DIR_NAME}"
  341. tar -czvf "${DIR_NAME}.tar.gz" "${DIR_NAME}"
  342. rm -r "${DIR_NAME}"
  343. - name: Cleanup
  344. run: rm dist/sing-box
  345. - name: Upload artifact
  346. uses: actions/upload-artifact@v4
  347. with:
  348. name: binary-darwin_${{ matrix.arch }}${{ matrix.legacy_name && format('-legacy-{0}', matrix.legacy_name) }}
  349. path: "dist"
  350. build_android:
  351. name: Build Android
  352. if: github.event_name != 'workflow_dispatch' || inputs.build == 'All' || inputs.build == 'Android'
  353. runs-on: ubuntu-latest
  354. needs:
  355. - calculate_version
  356. steps:
  357. - name: Checkout
  358. uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5
  359. with:
  360. fetch-depth: 0
  361. submodules: 'recursive'
  362. - name: Setup Go
  363. uses: actions/setup-go@v5
  364. with:
  365. go-version: ^1.25.5
  366. - name: Setup Android NDK
  367. id: setup-ndk
  368. uses: nttld/setup-ndk@v1
  369. with:
  370. ndk-version: r28
  371. - name: Setup OpenJDK
  372. run: |-
  373. sudo apt update && sudo apt install -y openjdk-17-jdk-headless
  374. /usr/lib/jvm/java-17-openjdk-amd64/bin/java --version
  375. - name: Set tag
  376. run: |-
  377. git ls-remote --exit-code --tags origin v${{ needs.calculate_version.outputs.version }} || echo "PUBLISHED=false" >> "$GITHUB_ENV"
  378. git tag v${{ needs.calculate_version.outputs.version }} -f
  379. - name: Build library
  380. run: |-
  381. make lib_install
  382. export PATH="$PATH:$(go env GOPATH)/bin"
  383. make lib_android
  384. env:
  385. JAVA_HOME: /usr/lib/jvm/java-17-openjdk-amd64
  386. ANDROID_NDK_HOME: ${{ steps.setup-ndk.outputs.ndk-path }}
  387. - name: Checkout main branch
  388. if: github.ref == 'refs/heads/main-next' && github.event_name != 'workflow_dispatch'
  389. run: |-
  390. cd clients/android
  391. git checkout main
  392. - name: Checkout dev branch
  393. if: github.ref == 'refs/heads/dev-next'
  394. run: |-
  395. cd clients/android
  396. git checkout dev
  397. - name: Gradle cache
  398. uses: actions/cache@v4
  399. with:
  400. path: ~/.gradle
  401. key: gradle-${{ hashFiles('**/*.gradle') }}
  402. - name: Update version
  403. if: github.event_name == 'workflow_dispatch'
  404. run: |-
  405. go run -v ./cmd/internal/update_android_version --ci
  406. - name: Update nightly version
  407. if: github.event_name != 'workflow_dispatch'
  408. run: |-
  409. go run -v ./cmd/internal/update_android_version --ci --nightly
  410. - name: Build
  411. run: |-
  412. mkdir clients/android/app/libs
  413. cp libbox.aar clients/android/app/libs
  414. cd clients/android
  415. ./gradlew :app:assemblePlayRelease :app:assembleOtherRelease
  416. env:
  417. JAVA_HOME: /usr/lib/jvm/java-17-openjdk-amd64
  418. ANDROID_NDK_HOME: ${{ steps.setup-ndk.outputs.ndk-path }}
  419. LOCAL_PROPERTIES: ${{ secrets.LOCAL_PROPERTIES }}
  420. - name: Prepare upload
  421. run: |-
  422. mkdir -p dist
  423. cp clients/android/app/build/outputs/apk/play/release/*.apk dist
  424. cp clients/android/app/build/outputs/apk/other/release/*-universal.apk dist
  425. - name: Upload artifact
  426. uses: actions/upload-artifact@v4
  427. with:
  428. name: binary-android-apks
  429. path: 'dist'
  430. publish_android:
  431. name: Publish Android
  432. if: github.event_name == 'workflow_dispatch' && inputs.build == 'publish-android'
  433. runs-on: ubuntu-latest
  434. needs:
  435. - calculate_version
  436. steps:
  437. - name: Checkout
  438. uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5
  439. with:
  440. fetch-depth: 0
  441. submodules: 'recursive'
  442. - name: Setup Go
  443. uses: actions/setup-go@v5
  444. with:
  445. go-version: ^1.25.5
  446. - name: Setup Android NDK
  447. id: setup-ndk
  448. uses: nttld/setup-ndk@v1
  449. with:
  450. ndk-version: r28
  451. - name: Setup OpenJDK
  452. run: |-
  453. sudo apt update && sudo apt install -y openjdk-17-jdk-headless
  454. /usr/lib/jvm/java-17-openjdk-amd64/bin/java --version
  455. - name: Set tag
  456. run: |-
  457. git ls-remote --exit-code --tags origin v${{ needs.calculate_version.outputs.version }} || echo "PUBLISHED=false" >> "$GITHUB_ENV"
  458. git tag v${{ needs.calculate_version.outputs.version }} -f
  459. - name: Build library
  460. run: |-
  461. make lib_install
  462. export PATH="$PATH:$(go env GOPATH)/bin"
  463. make lib_android
  464. env:
  465. JAVA_HOME: /usr/lib/jvm/java-17-openjdk-amd64
  466. ANDROID_NDK_HOME: ${{ steps.setup-ndk.outputs.ndk-path }}
  467. - name: Checkout main branch
  468. if: github.ref == 'refs/heads/main-next' && github.event_name != 'workflow_dispatch'
  469. run: |-
  470. cd clients/android
  471. git checkout main
  472. - name: Checkout dev branch
  473. if: github.ref == 'refs/heads/dev-next'
  474. run: |-
  475. cd clients/android
  476. git checkout dev
  477. - name: Gradle cache
  478. uses: actions/cache@v4
  479. with:
  480. path: ~/.gradle
  481. key: gradle-${{ hashFiles('**/*.gradle') }}
  482. - name: Build
  483. run: |-
  484. go run -v ./cmd/internal/update_android_version --ci
  485. mkdir clients/android/app/libs
  486. cp libbox.aar clients/android/app/libs
  487. cd clients/android
  488. echo -n "$SERVICE_ACCOUNT_CREDENTIALS" | base64 --decode > service-account-credentials.json
  489. ./gradlew :app:publishPlayReleaseBundle
  490. env:
  491. JAVA_HOME: /usr/lib/jvm/java-17-openjdk-amd64
  492. ANDROID_NDK_HOME: ${{ steps.setup-ndk.outputs.ndk-path }}
  493. LOCAL_PROPERTIES: ${{ secrets.LOCAL_PROPERTIES }}
  494. SERVICE_ACCOUNT_CREDENTIALS: ${{ secrets.SERVICE_ACCOUNT_CREDENTIALS }}
  495. build_apple:
  496. name: Build Apple clients
  497. runs-on: macos-26
  498. if: false
  499. needs:
  500. - calculate_version
  501. strategy:
  502. matrix:
  503. include:
  504. - name: iOS
  505. if: ${{ github.event_name != 'workflow_dispatch' || inputs.build == 'All' || inputs.build == 'Apple' || inputs.build == 'app-store'|| inputs.build == 'iOS' }}
  506. platform: ios
  507. scheme: SFI
  508. destination: 'generic/platform=iOS'
  509. archive: build/SFI.xcarchive
  510. upload: SFI/Upload.plist
  511. - name: macOS
  512. if: ${{ github.event_name != 'workflow_dispatch' || inputs.build == 'All' || inputs.build == 'Apple' || inputs.build == 'app-store'|| inputs.build == 'macOS' }}
  513. platform: macos
  514. scheme: SFM
  515. destination: 'generic/platform=macOS'
  516. archive: build/SFM.xcarchive
  517. upload: SFI/Upload.plist
  518. - name: tvOS
  519. if: ${{ github.event_name != 'workflow_dispatch' || inputs.build == 'All' || inputs.build == 'Apple' || inputs.build == 'app-store'|| inputs.build == 'tvOS' }}
  520. platform: tvos
  521. scheme: SFT
  522. destination: 'generic/platform=tvOS'
  523. archive: build/SFT.xcarchive
  524. upload: SFI/Upload.plist
  525. - name: macOS-standalone
  526. if: ${{ github.event_name != 'workflow_dispatch' || inputs.build == 'All' || inputs.build == 'Apple' || inputs.build == 'macOS-standalone' }}
  527. platform: macos
  528. scheme: SFM.System
  529. destination: 'generic/platform=macOS'
  530. archive: build/SFM.System.xcarchive
  531. export: SFM.System/Export.plist
  532. export_path: build/SFM.System
  533. steps:
  534. - name: Checkout
  535. if: matrix.if
  536. uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5
  537. with:
  538. fetch-depth: 0
  539. submodules: 'recursive'
  540. - name: Setup Go
  541. if: matrix.if
  542. uses: actions/setup-go@v5
  543. with:
  544. go-version: ^1.25.5
  545. - name: Set tag
  546. if: matrix.if
  547. run: |-
  548. git ls-remote --exit-code --tags origin v${{ needs.calculate_version.outputs.version }} || echo "PUBLISHED=false" >> "$GITHUB_ENV"
  549. git tag v${{ needs.calculate_version.outputs.version }} -f
  550. echo "VERSION=${{ needs.calculate_version.outputs.version }}" >> "$GITHUB_ENV"
  551. - name: Checkout main branch
  552. if: matrix.if && github.ref == 'refs/heads/main-next' && github.event_name != 'workflow_dispatch'
  553. run: |-
  554. cd clients/apple
  555. git checkout main
  556. - name: Checkout dev branch
  557. if: matrix.if && github.ref == 'refs/heads/dev-next'
  558. run: |-
  559. cd clients/apple
  560. git checkout dev
  561. - name: Setup certificates
  562. if: matrix.if
  563. run: |-
  564. CERTIFICATE_PATH=$RUNNER_TEMP/Certificates.p12
  565. KEYCHAIN_PATH=$RUNNER_TEMP/certificates.keychain-db
  566. echo -n "$CERTIFICATES_P12" | base64 --decode -o $CERTIFICATE_PATH
  567. security create-keychain -p "$KEYCHAIN_PASSWORD" $KEYCHAIN_PATH
  568. security set-keychain-settings -lut 21600 $KEYCHAIN_PATH
  569. security unlock-keychain -p "$KEYCHAIN_PASSWORD" $KEYCHAIN_PATH
  570. security import $CERTIFICATE_PATH -P "$P12_PASSWORD" -A -t cert -f pkcs12 -k $KEYCHAIN_PATH
  571. security set-key-partition-list -S apple-tool:,apple: -k "$KEYCHAIN_PASSWORD" $KEYCHAIN_PATH
  572. security list-keychain -d user -s $KEYCHAIN_PATH
  573. PROFILES_ZIP_PATH=$RUNNER_TEMP/Profiles.zip
  574. echo -n "$PROVISIONING_PROFILES" | base64 --decode -o $PROFILES_ZIP_PATH
  575. PROFILES_PATH="$HOME/Library/MobileDevice/Provisioning Profiles"
  576. mkdir -p "$PROFILES_PATH"
  577. unzip $PROFILES_ZIP_PATH -d "$PROFILES_PATH"
  578. ASC_KEY_PATH=$RUNNER_TEMP/Key.p12
  579. echo -n "$ASC_KEY" | base64 --decode -o $ASC_KEY_PATH
  580. xcrun notarytool store-credentials "notarytool-password" \
  581. --key $ASC_KEY_PATH \
  582. --key-id $ASC_KEY_ID \
  583. --issuer $ASC_KEY_ISSUER_ID
  584. echo "ASC_KEY_PATH=$ASC_KEY_PATH" >> "$GITHUB_ENV"
  585. echo "ASC_KEY_ID=$ASC_KEY_ID" >> "$GITHUB_ENV"
  586. echo "ASC_KEY_ISSUER_ID=$ASC_KEY_ISSUER_ID" >> "$GITHUB_ENV"
  587. env:
  588. CERTIFICATES_P12: ${{ secrets.CERTIFICATES_P12 }}
  589. P12_PASSWORD: ${{ secrets.P12_PASSWORD }}
  590. KEYCHAIN_PASSWORD: ${{ secrets.P12_PASSWORD }}
  591. PROVISIONING_PROFILES: ${{ secrets.PROVISIONING_PROFILES }}
  592. ASC_KEY: ${{ secrets.ASC_KEY }}
  593. ASC_KEY_ID: ${{ secrets.ASC_KEY_ID }}
  594. ASC_KEY_ISSUER_ID: ${{ secrets.ASC_KEY_ISSUER_ID }}
  595. - name: Build library
  596. if: matrix.if
  597. run: |-
  598. make lib_install
  599. export PATH="$PATH:$(go env GOPATH)/bin"
  600. go run ./cmd/internal/build_libbox -target apple -platform ${{ matrix.platform }}
  601. mv Libbox.xcframework clients/apple
  602. - name: Update macOS version
  603. if: matrix.if && matrix.name == 'macOS' && github.event_name == 'workflow_dispatch'
  604. run: |-
  605. MACOS_PROJECT_VERSION=$(go run -v ./cmd/internal/app_store_connect next_macos_project_version)
  606. echo "MACOS_PROJECT_VERSION=$MACOS_PROJECT_VERSION"
  607. echo "MACOS_PROJECT_VERSION=$MACOS_PROJECT_VERSION" >> "$GITHUB_ENV"
  608. - name: Update version
  609. if: matrix.if && matrix.name != 'iOS'
  610. run: |-
  611. go run -v ./cmd/internal/update_apple_version --ci
  612. - name: Build
  613. if: matrix.if
  614. run: |-
  615. cd clients/apple
  616. xcodebuild archive \
  617. -scheme "${{ matrix.scheme }}" \
  618. -configuration Release \
  619. -destination "${{ matrix.destination }}" \
  620. -archivePath "${{ matrix.archive }}" \
  621. -allowProvisioningUpdates \
  622. -authenticationKeyPath $ASC_KEY_PATH \
  623. -authenticationKeyID $ASC_KEY_ID \
  624. -authenticationKeyIssuerID $ASC_KEY_ISSUER_ID
  625. - name: Upload to App Store Connect
  626. if: matrix.if && matrix.name != 'macOS-standalone' && github.event_name == 'workflow_dispatch'
  627. run: |-
  628. go run -v ./cmd/internal/app_store_connect cancel_app_store ${{ matrix.platform }}
  629. cd clients/apple
  630. xcodebuild -exportArchive \
  631. -archivePath "${{ matrix.archive }}" \
  632. -exportOptionsPlist ${{ matrix.upload }} \
  633. -allowProvisioningUpdates \
  634. -authenticationKeyPath $ASC_KEY_PATH \
  635. -authenticationKeyID $ASC_KEY_ID \
  636. -authenticationKeyIssuerID $ASC_KEY_ISSUER_ID
  637. - name: Publish to TestFlight
  638. if: matrix.if && matrix.name != 'macOS-standalone' && github.event_name == 'workflow_dispatch' && github.ref =='refs/heads/dev-next'
  639. run: |-
  640. go run -v ./cmd/internal/app_store_connect publish_testflight ${{ matrix.platform }}
  641. - name: Build image
  642. if: matrix.if && matrix.name == 'macOS-standalone' && github.event_name == 'workflow_dispatch'
  643. run: |-
  644. pushd clients/apple
  645. xcodebuild -exportArchive \
  646. -archivePath "${{ matrix.archive }}" \
  647. -exportOptionsPlist ${{ matrix.export }} \
  648. -exportPath "${{ matrix.export_path }}"
  649. brew install create-dmg
  650. create-dmg \
  651. --volname "sing-box" \
  652. --volicon "${{ matrix.export_path }}/SFM.app/Contents/Resources/AppIcon.icns" \
  653. --icon "SFM.app" 0 0 \
  654. --hide-extension "SFM.app" \
  655. --app-drop-link 0 0 \
  656. --skip-jenkins \
  657. SFM.dmg "${{ matrix.export_path }}/SFM.app"
  658. xcrun notarytool submit "SFM.dmg" --wait --keychain-profile "notarytool-password"
  659. cd "${{ matrix.archive }}"
  660. zip -r SFM.dSYMs.zip dSYMs
  661. popd
  662. mkdir -p dist
  663. cp clients/apple/SFM.dmg "dist/SFM-${VERSION}-universal.dmg"
  664. cp "clients/apple/${{ matrix.archive }}/SFM.dSYMs.zip" "dist/SFM-${VERSION}-universal.dSYMs.zip"
  665. - name: Upload image
  666. if: matrix.if && matrix.name == 'macOS-standalone' && github.event_name == 'workflow_dispatch'
  667. uses: actions/upload-artifact@v4
  668. with:
  669. name: binary-macos-dmg
  670. path: 'dist'
  671. upload:
  672. name: Upload builds
  673. if: "!failure() && github.event_name == 'workflow_dispatch' && (inputs.build == 'All' || inputs.build == 'Binary' || inputs.build == 'Android' || inputs.build == 'Apple' || inputs.build == 'macOS-standalone')"
  674. runs-on: ubuntu-latest
  675. needs:
  676. - calculate_version
  677. - build
  678. - build_darwin
  679. - build_android
  680. - build_apple
  681. steps:
  682. - name: Checkout
  683. uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5
  684. with:
  685. fetch-depth: 0
  686. - name: Cache ghr
  687. uses: actions/cache@v4
  688. id: cache-ghr
  689. with:
  690. path: |
  691. ~/go/bin/ghr
  692. key: ghr
  693. - name: Setup ghr
  694. if: steps.cache-ghr.outputs.cache-hit != 'true'
  695. run: |-
  696. cd $HOME
  697. git clone https://github.com/nekohasekai/ghr ghr
  698. cd ghr
  699. go install -v .
  700. - name: Set tag
  701. run: |-
  702. git ls-remote --exit-code --tags origin v${{ needs.calculate_version.outputs.version }} || echo "PUBLISHED=false" >> "$GITHUB_ENV"
  703. git tag v${{ needs.calculate_version.outputs.version }} -f
  704. echo "VERSION=${{ needs.calculate_version.outputs.version }}" >> "$GITHUB_ENV"
  705. - name: Download builds
  706. uses: actions/download-artifact@v5
  707. with:
  708. path: dist
  709. merge-multiple: true
  710. - name: Upload builds
  711. if: ${{ env.PUBLISHED == 'false' }}
  712. run: |-
  713. export PATH="$PATH:$HOME/go/bin"
  714. ghr --replace --draft --prerelease -p 5 "v${VERSION}" dist
  715. env:
  716. GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
  717. - name: Replace builds
  718. if: ${{ env.PUBLISHED != 'false' }}
  719. run: |-
  720. export PATH="$PATH:$HOME/go/bin"
  721. ghr --replace -p 5 "v${VERSION}" dist
  722. env:
  723. GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}