diff --git a/.fpm b/.fpm new file mode 100644 index 00000000..718244b2 --- /dev/null +++ b/.fpm @@ -0,0 +1,19 @@ +-s dir +--name sing-box +--category net +--license GPLv3-or-later +--description "The universal proxy platform." +--url "https://sing-box.sagernet.org/" +--maintainer "nekohasekai " +--deb-field "Bug: https://github.com/SagerNet/sing-box/issues" + +release/config/config.json=/etc/sing-box/config.json + +release/config/sing-box.service=/usr/lib/systemd/system/sing-box.service +release/config/sing-box@.service=/usr/lib/systemd/system/sing-box@.service + +release/completions/sing-box.bash=/usr/share/bash-completion/completions/sing-box.bash +release/completions/sing-box.fish=/usr/share/fish/vendor_completions.d/sing-box.fish +release/completions/sing-box.zsh=/usr/share/zsh/site-functions/_sing-box + +LICENSE=/usr/share/licenses/sing-box/LICENSE diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 22302af4..f34ef4a2 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -50,7 +50,7 @@ jobs: - name: Check input version if: github.event_name == 'workflow_dispatch' run: |- - echo "version=${{ inputs.version }}" + echo "version=${{ inputs.version }}" echo "version=${{ inputs.version }}" >> "$GITHUB_ENV" - name: Calculate version if: github.event_name != 'workflow_dispatch' @@ -68,147 +68,171 @@ jobs: - calculate_version strategy: matrix: + os: [ linux, windows, darwin, android ] + arch: [ "386", amd64, arm64 ] + legacy_go: [ false ] include: - - name: linux_386 - goos: linux - goarch: 386 - - name: linux_amd64 - goos: linux - goarch: amd64 - - name: linux_arm64 - goos: linux - goarch: arm64 - - name: linux_arm - goos: linux - goarch: arm - goarm: 6 - - name: linux_arm_v7 - goos: linux - goarch: arm - goarm: 7 - - name: linux_s390x - goos: linux - goarch: s390x - - name: linux_riscv64 - goos: linux - goarch: riscv64 - - name: linux_mips64le - goos: linux - goarch: mips64le - - name: windows_amd64 - goos: windows - goarch: amd64 - require_legacy_go: true - - name: windows_386 - goos: windows - goarch: 386 - require_legacy_go: true - - name: windows_arm64 - goos: windows - goarch: arm64 - - name: darwin_arm64 - goos: darwin - goarch: arm64 - - name: darwin_amd64 - goos: darwin - goarch: amd64 - require_legacy_go: true - - name: android_arm64 - goos: android - goarch: arm64 - - name: android_arm - goos: android - goarch: arm - goarm: 7 - - name: android_amd64 - goos: android - goarch: amd64 - - name: android_386 - goos: android - goarch: 386 + - { os: linux, arch: amd64, debian: amd64, rpm: x86_64, pacman: x86_64 } + - { os: linux, arch: "386", debian: i386, rpm: i386 } + - { os: linux, arch: arm, goarm: "6", debian: armel, rpm: armv6hl } + - { os: linux, arch: arm, goarm: "7", debian: armhf, rpm: armv7hl, pacman: armv7hl } + - { os: linux, arch: arm64, debian: arm64, rpm: aarch64, pacman: aarch64 } + - { os: linux, arch: mips64le, debian: mips64el, rpm: mips64el } + - { os: linux, arch: mipsle, debian: mipsel, rpm: mipsel } + - { os: linux, arch: s390x, debian: s390x, rpm: s390x } + - { os: linux, arch: ppc64le, debian: ppc64el, rpm: ppc64le } + - { os: linux, arch: riscv64, debian: riscv64, rpm: riscv64 } + - { os: linux, arch: loong64, debian: loongarch64, rpm: loongarch64 } + + - { os: windows, arch: "386", legacy_go: true } + - { os: windows, arch: amd64, legacy_go: true } + - { os: darwin, arch: amd64, legacy_go: true } + + - { os: android, arch: "386", ndk: "i686-linux-android21" } + - { os: android, arch: amd64, ndk: "x86_64-linux-android21" } + - { os: android, arch: arm64, ndk: "aarch64-linux-android21" } + - { os: android, arch: arm, ndk: "armv7a-linux-androideabi21" } + exclude: + - { os: darwin, arch: "386" } steps: - name: Checkout uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4 with: fetch-depth: 0 - name: Setup Go + if: matrix.legacy_go + uses: actions/setup-go@v5 + with: + go-version: ~1.20 + - name: Setup Go + if: ${{ ! matrix.legacy_go }} uses: actions/setup-go@v5 with: go-version: ^1.24 - - name: Setup Goreleaser - uses: goreleaser/goreleaser-action@v6 - with: - distribution: goreleaser-pro - version: 2.8.1 - install-only: true - - name: Setup MITM - run: |- - git checkout dev-test-mitm - .github/goreleaser/configure.sh - git checkout ${{ github.ref }} - - name: Cache legacy Go - if: matrix.require_legacy_go - id: cache-legacy-go - uses: actions/cache@v4 - with: - path: | - ~/go/go1.20.14 - key: go120 - - name: Setup legacy Go - if: matrix.require_legacy_go && steps.cache-legacy-go.outputs.cache-hit != 'true' - run: |- - wget https://dl.google.com/go/go1.20.14.linux-amd64.tar.gz - tar -xzf go1.20.14.linux-amd64.tar.gz - mv go $HOME/go/go1.20.14 - name: Setup Android NDK - if: matrix.goos == 'android' + if: matrix.os == 'android' uses: nttld/setup-ndk@v1 with: ndk-version: r28 local-cache: true - - name: Extract signing key - run: |- - mkdir -p $HOME/.gnupg - cat > $HOME/.gnupg/sagernet.key <> "$GITHUB_ENV" - name: Set tag run: |- git ls-remote --exit-code --tags origin v${{ needs.calculate_version.outputs.version }} || echo "PUBLISHED=false" >> "$GITHUB_ENV" git tag v${{ needs.calculate_version.outputs.version }} -f + - name: Set build tags + run: | + set -xeuo pipefail + TAGS='with_gvisor,with_quic,with_dhcp,with_wireguard,with_utls,with_reality_server,with_acme,with_clash_api' + if [ ! '${{ matrix.legacy_go }}' = 'true' ]; then + TAGS="${TAGS},with_ech" + fi + echo "BUILD_TAGS=${TAGS}" >> "${GITHUB_ENV}" - name: Build - if: matrix.goos != 'android' - run: |- - goreleaser release --clean --split + if: matrix.os != 'android' + run: | + set -xeuo pipefail + mkdir -p dist + go build -v -trimpath -o dist/sing-box -tags "${BUILD_TAGS}" \ + -ldflags '-s -buildid= -X github.com/sagernet/sing-box/constant.Version=${{ needs.calculate_version.outputs.version }}' \ + ./cmd/sing-box env: - GOOS: ${{ matrix.goos }} - GOARCH: ${{ matrix.goarch }} - GOPATH: ${{ env.HOME }}/go + CGO_ENABLED: "0" + GOOS: ${{ matrix.os }} + GOARCH: ${{ matrix.arch }} GOARM: ${{ matrix.goarm }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - GORELEASER_KEY: fake-key - NFPM_KEY_PATH: ${{ env.HOME }}/.gnupg/sagernet.key - NFPM_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }} - name: Build Android - if: matrix.goos == 'android' - run: |- + if: matrix.os == 'android' + run: | + set -xeuo pipefail go install -v ./cmd/internal/build - GOOS=$BUILD_GOOS GOARCH=$BUILD_GOARCH build goreleaser release --clean --split + export CC='${{ matrix.ndk }}-clang' + export CXX="${CC}++" + mkdir -p dist + GOOS=$BUILD_GOOS GOARCH=$BUILD_GOARCH build go build -v -trimpath -o dist/sing-box -tags "${BUILD_TAGS}" \ + -ldflags '-s -buildid= -X github.com/sagernet/sing-box/constant.Version=${{ needs.calculate_version.outputs.version }}' \ + ./cmd/sing-box env: - BUILD_GOOS: ${{ matrix.goos }} - BUILD_GOARCH: ${{ matrix.goarch }} - GOARM: ${{ matrix.goarm }} + CGO_ENABLED: "1" + BUILD_GOOS: ${{ matrix.os }} + BUILD_GOARCH: ${{ matrix.arch }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - GORELEASER_KEY: fake-key - NFPM_KEY_PATH: ${{ env.HOME }}/.gnupg/sagernet.key - NFPM_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }} + - name: Set name + run: |- + ARM_VERSION=$([ -n '${{ matrix.goarm}}' ] && echo 'v${{ matrix.goarm}}' || true) + LEGACY=$([ '${{ matrix.legacy_go }}' = 'true' ] && echo "-legacy" || true) + DIR_NAME="sing-box-${{ needs.calculate_version.outputs.version }}-${{ matrix.os }}-${{ matrix.arch }}${ARM_VERSION}${LEGACY}" + PKG_NAME="sing-box_${{ needs.calculate_version.outputs.version }}_${{ matrix.os }}_${{ matrix.arch }}${ARM_VERSION}" + echo "DIR_NAME=${DIR_NAME}" >> "${GITHUB_ENV}" + echo "PKG_NAME=${PKG_NAME}" >> "${GITHUB_ENV}" + - name: Package DEB + if: matrix.debian != '' + run: | + set -xeuo pipefail + sudo gem install fpm + sudo apt-get install -y debsigs + fpm -t deb \ + -v "${{ needs.calculate_version.outputs.version }}" \ + -p "dist/${PKG_NAME}.deb" \ + --architecture ${{ matrix.debian }} \ + dist/sing-box=/usr/bin/sing-box + curl -Lo '/tmp/debsigs.diff' 'https://gitlab.com/debsigs/debsigs/-/commit/160138f5de1ec110376d3c807b60a37388bc7c90.diff' + sudo patch /usr/bin/debsigs < '/tmp/debsigs.diff' + rm -rf $HOME/.gnupg + gpg --pinentry-mode loopback --passphrase "${{ secrets.GPG_PASSPHRASE }}" --import < $HOME/.rpmmacros <> "$GITHUB_ENV" echo "ASC_KEY_ID=$ASC_KEY_ID" >> "$GITHUB_ENV" echo "ASC_KEY_ISSUER_ID=$ASC_KEY_ISSUER_ID" >> "$GITHUB_ENV" @@ -532,7 +554,7 @@ jobs: cd "${{ matrix.archive }}" zip -r SFM.dSYMs.zip dSYMs popd - + mkdir -p dist/release cp clients/apple/SFM.dmg "dist/release/SFM-${VERSION}-universal.dmg" cp "clients/apple/${{ matrix.archive }}/SFM.dSYMs.zip" "dist/release/SFM-${VERSION}-universal.dSYMs.zip" @@ -556,17 +578,6 @@ jobs: uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4 with: fetch-depth: 0 - - name: Setup Goreleaser - uses: goreleaser/goreleaser-action@v6 - with: - distribution: goreleaser-pro - version: 2.8.1 - install-only: true - - name: Setup MITM - run: |- - git checkout dev-test-mitm - .github/goreleaser/configure.sh - git checkout ${{ github.ref }} - name: Cache ghr uses: actions/cache@v4 id: cache-ghr @@ -591,26 +602,17 @@ jobs: with: path: dist merge-multiple: true - - name: Merge builds - if: github.event_name != 'workflow_dispatch' || inputs.build == 'All' || inputs.build == 'Binary' - run: |- - goreleaser continue --merge --skip publish - mkdir -p dist/release - mv dist/*/sing-box*{tar.gz,zip,deb,rpm,_amd64.pkg.tar.zst,_arm64.pkg.tar.zst} dist/release - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - GORELEASER_KEY: fake-key - name: Upload builds if: ${{ env.PUBLISHED == 'false' }} run: |- export PATH="$PATH:$HOME/go/bin" - ghr --replace --draft --prerelease -p 5 "v${VERSION}" dist/release + ghr --replace --draft --prerelease -p 5 "v${VERSION}" dist env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - name: Replace builds if: ${{ env.PUBLISHED != 'false' }} run: |- export PATH="$PATH:$HOME/go/bin" - ghr --replace -p 5 "v${VERSION}" dist/release + ghr --replace -p 5 "v${VERSION}" dist env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml index fffd1dcc..87b0738f 100644 --- a/.github/workflows/linux.yml +++ b/.github/workflows/linux.yml @@ -1,13 +1,22 @@ -name: Release to Linux repository +name: Build Linux Packages on: + workflow_dispatch: + inputs: + version: + description: "Version name" + required: true + type: string release: types: - published jobs: - build: + calculate_version: + name: Calculate version runs-on: ubuntu-latest + outputs: + version: ${{ steps.outputs.outputs.version }} steps: - name: Checkout uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4 @@ -17,30 +26,155 @@ jobs: uses: actions/setup-go@v5 with: go-version: ^1.24 - - name: Setup Goreleaser - uses: goreleaser/goreleaser-action@v6 + - name: Check input version + if: github.event_name == 'workflow_dispatch' + run: |- + echo "version=${{ inputs.version }}" + echo "version=${{ inputs.version }}" >> "$GITHUB_ENV" + - name: Calculate version + if: github.event_name != 'workflow_dispatch' + run: |- + go run -v ./cmd/internal/read_tag --nightly + - name: Set outputs + id: outputs + run: |- + echo "version=$version" >> "$GITHUB_OUTPUT" + build: + name: Build binary + runs-on: ubuntu-latest + needs: + - calculate_version + strategy: + matrix: + include: + - { os: linux, arch: amd64, debian: amd64, rpm: x86_64, pacman: x86_64 } + - { os: linux, arch: "386", debian: i386, rpm: i386 } + - { os: linux, arch: arm, goarm: "6", debian: armel, rpm: armv6hl } + - { os: linux, arch: arm, goarm: "7", debian: armhf, rpm: armv7hl, pacman: armv7hl } + - { os: linux, arch: arm64, debian: arm64, rpm: aarch64, pacman: aarch64 } + - { os: linux, arch: mips64le, debian: mips64el, rpm: mips64el } + - { os: linux, arch: mipsle, debian: mipsel, rpm: mipsel } + - { os: linux, arch: s390x, debian: s390x, rpm: s390x } + - { os: linux, arch: ppc64le, debian: ppc64el, rpm: ppc64le } + - { os: linux, arch: riscv64, debian: riscv64, rpm: riscv64 } + - { os: linux, arch: loong64, debian: loongarch64, rpm: loongarch64 } + steps: + - name: Checkout + uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4 with: - distribution: goreleaser-pro - version: 2.8.1 - install-only: true - - name: Setup MITM + fetch-depth: 0 + - name: Setup Go + uses: actions/setup-go@v5 + with: + go-version: ^1.24 + - name: Setup Android NDK + if: matrix.os == 'android' + uses: nttld/setup-ndk@v1 + with: + ndk-version: r28 + local-cache: true + - name: Set tag run: |- - git checkout dev-test-mitm - .github/goreleaser/configure.sh - git checkout ${{ github.ref }} - - name: Extract signing key + git ls-remote --exit-code --tags origin v${{ needs.calculate_version.outputs.version }} || echo "PUBLISHED=false" >> "$GITHUB_ENV" + git tag v${{ needs.calculate_version.outputs.version }} -f + - name: Set build tags + run: | + set -xeuo pipefail + TAGS='with_gvisor,with_quic,with_dhcp,with_wireguard,with_utls,with_reality_server,with_acme,with_clash_api' + if [ ! '${{ matrix.legacy_go }}' = 'true' ]; then + TAGS="${TAGS},with_ech" + fi + echo "BUILD_TAGS=${TAGS}" >> "${GITHUB_ENV}" + - name: Build + run: | + set -xeuo pipefail + mkdir -p dist + go build -v -trimpath -o dist/sing-box -tags "${BUILD_TAGS}" \ + -ldflags '-s -buildid= -X github.com/sagernet/sing-box/constant.Version=${{ needs.calculate_version.outputs.version }}' \ + ./cmd/sing-box + env: + CGO_ENABLED: "0" + GOOS: ${{ matrix.os }} + GOARCH: ${{ matrix.arch }} + GOARM: ${{ matrix.goarm }} + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + - name: Set mtime run: |- - mkdir -p $HOME/.gnupg - cat > $HOME/.gnupg/sagernet.key <> "$GITHUB_ENV" + - name: Set beta name + if: contains(needs.calculate_version.outputs.version, '-') + run: |- + echo "NAME=sing-box-beta" >> "$GITHUB_ENV" + - name: Package DEB + if: matrix.debian != '' + run: | + set -xeuo pipefail + sudo gem install fpm + sudo apt-get install -y debsigs + fpm -t deb \ + -v "${{ needs.calculate_version.outputs.version }}" \ + -p "dist/${NAME}_${{ needs.calculate_version.outputs.version }}_linux_${{ matrix.debian }}.deb" \ + --architecture ${{ matrix.debian }} \ + dist/sing-box=/usr/bin/${NAME} + curl -Lo '/tmp/debsigs.diff' 'https://gitlab.com/debsigs/debsigs/-/commit/160138f5de1ec110376d3c807b60a37388bc7c90.diff' + sudo patch /usr/bin/debsigs < '/tmp/debsigs.diff' + rm -rf $HOME/.gnupg + gpg --pinentry-mode loopback --passphrase "${{ secrets.GPG_PASSPHRASE }}" --import <> "$GITHUB_ENV" - - name: Publish release + debsigs --sign=origin -k ${{ secrets.GPG_KEY_ID }} --gpgopts '--pinentry-mode loopback --passphrase "${{ secrets.GPG_PASSPHRASE }}"' dist/*.deb + - name: Package RPM + if: matrix.rpm != '' run: |- - goreleaser release -f .goreleaser.fury.yaml --clean - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - GORELEASER_KEY: ${{ secrets.GORELEASER_KEY }} - FURY_TOKEN: ${{ secrets.FURY_TOKEN }} - NFPM_KEY_PATH: ${{ env.HOME }}/.gnupg/sagernet.key - NFPM_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }} + set -xeuo pipefail + sudo gem install fpm + fpm -t rpm \ + -v "${{ needs.calculate_version.outputs.version }}" \ + -p "dist/${NAME}_${{ needs.calculate_version.outputs.version }}_linux_${{ matrix.rpm }}.rpm" \ + --architecture ${{ matrix.rpm }} \ + dist/sing-box=/usr/bin/${NAME} + cat > $HOME/.rpmmacros <> "$GITHUB_ENV" + git tag v${{ needs.calculate_version.outputs.version }} -f + echo "VERSION=${{ needs.calculate_version.outputs.version }}" >> "$GITHUB_ENV" + - name: Download builds + uses: actions/download-artifact@v4 + with: + path: dist + merge-multiple: true + - name: Publish packages + run: |- + wget -O fury-cli.deb https://github.com/gemfury/cli/releases/download/v0.23.0/fury-cli_0.23.0_linux_amd64.deb + sudo dpkg -i fury-cli.deb + fury migrate dist --as=sagernet --api-token ${{ secrets.FURY_TOKEN }}