diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 00000000..55583aa1 --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,613 @@ +name: Build + +on: + workflow_dispatch: + inputs: + version: + description: "Version name" + required: true + type: string + prerelease: + description: "Is prerelease" + required: true + type: boolean + default: true + build: + description: "Build type" + required: true + type: choice + default: "All" + options: + - All + - Binary + - Android + - Apple + - app-store + - iOS + - macOS + - tvOS + - macOS-standalone + - publish-android + macos_project_version: + description: "macOS project version" + required: false + type: string + push: + branches: + - main-next + - dev-next + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }}-${{ github.event_name }}-${{ inputs.build }} + cancel-in-progress: true + +jobs: + calculate_version: + name: Calculate version + runs-on: ubuntu-latest + outputs: + version: ${{ steps.outputs.outputs.version }} + prerelease: ${{ steps.outputs.outputs.prerelease }} + steps: + - name: Checkout + uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4 + with: + fetch-depth: 0 + - name: Setup Go + uses: actions/setup-go@v5 + with: + go-version: ^1.23 + - name: Check input version + if: github.event_name == 'workflow_dispatch' + run: |- + echo "version=${{ inputs.version }}" + echo "prerelease=${{ inputs.prerelease }}" + echo "version=${{ inputs.version }}" >> "$GITHUB_ENV" + echo "prerelease=${{ inputs.prerelease }}" >> "$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" + echo "prerelease=$prerelease" >> "$GITHUB_OUTPUT" + build: + name: Build binary + if: github.event_name != 'workflow_dispatch' || inputs.build == 'All' || inputs.build == 'Binary' + runs-on: ubuntu-latest + needs: + - calculate_version + strategy: + matrix: + 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 + steps: + - name: Checkout + uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4 + with: + fetch-depth: 0 + - name: Setup Go + uses: actions/setup-go@v5 + with: + go-version: ^1.23 + - 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 == 'true' && 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' + uses: nttld/setup-ndk@v1 + with: + ndk-version: r28-beta2 + local-cache: true + - name: Setup Goreleaser + uses: goreleaser/goreleaser-action@v6 + with: + distribution: goreleaser-pro + version: latest + install-only: true + - name: Extract signing key + run: |- + mkdir -p $HOME/.gnupg + cat > $HOME/.gnupg/sagernet.key <> "$GITHUB_ENV" + - name: Set tag + run: |- + git tag v${{ needs.calculate_version.outputs.version }} + - name: Build + if: matrix.goos != 'android' + run: |- + goreleaser release --clean --split + env: + GOOS: ${{ matrix.goos }} + GOARCH: ${{ matrix.goarch }} + GOPATH: ${{ env.HOME }}/go + GOARM: ${{ matrix.goarm }} + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GORELEASER_KEY: ${{ secrets.GORELEASER_KEY }} + NFPM_KEY_PATH: ${{ env.HOME }}/.gnupg/sagernet.key + NFPM_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }} + - name: Build Android + if: matrix.goos == 'android' + run: |- + go install -v ./cmd/internal/build + GOOS=$BUILD_GOOS GOARCH=$BUILD_GOARCH build goreleaser release --clean --split + env: + BUILD_GOOS: ${{ matrix.goos }} + BUILD_GOARCH: ${{ matrix.goarch }} + GOARM: ${{ matrix.goarm }} + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GORELEASER_KEY: ${{ secrets.GORELEASER_KEY }} + NFPM_KEY_PATH: ${{ env.HOME }}/.gnupg/sagernet.key + NFPM_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }} + - name: Upload artifact + if: github.event_name == 'workflow_dispatch' + uses: actions/upload-artifact@v4 + with: + name: binary-${{ matrix.name }} + path: 'dist' + build_android: + name: Build Android + if: github.event_name != 'workflow_dispatch' || inputs.build == 'All' || inputs.build == 'Android' + runs-on: ubuntu-latest + needs: + - calculate_version + steps: + - name: Checkout + uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4 + with: + fetch-depth: 0 + submodules: 'recursive' + - name: Setup Go + uses: actions/setup-go@v5 + with: + go-version: ^1.23 + - name: Setup Android NDK + id: setup-ndk + uses: nttld/setup-ndk@v1 + with: + ndk-version: r28-beta2 + - name: Setup OpenJDK + run: |- + sudo apt update && sudo apt install -y openjdk-17-jdk-headless + /usr/lib/jvm/java-17-openjdk-amd64/bin/java --version + - name: Set tag + run: |- + git tag v${{ needs.calculate_version.outputs.version }} + - name: Build library + run: |- + make lib_install + export PATH="$PATH:$(go env GOPATH)/bin" + make lib_android + env: + JAVA_HOME: /usr/lib/jvm/java-17-openjdk-amd64 + ANDROID_NDK_HOME: ${{ steps.setup-ndk.outputs.ndk-path }} + - name: Checkout main branch + if: needs.calculate_version.outputs.prerelease == 'false' + run: |- + cd clients/android + git checkout main + - name: Checkout dev branch + if: needs.calculate_version.outputs.prerelease == 'true' + run: |- + cd clients/android + git checkout dev + - name: Gradle cache + uses: actions/cache@v4 + with: + path: ~/.gradle + key: gradle-${{ hashFiles('**/*.gradle') }} + - name: Build + run: |- + go run -v ./cmd/internal/update_android_version --ci + mkdir clients/android/app/libs + cp libbox.aar clients/android/app/libs + cd clients/android + ./gradlew :app:assemblePlayRelease :app:assembleOtherRelease + env: + JAVA_HOME: /usr/lib/jvm/java-17-openjdk-amd64 + ANDROID_NDK_HOME: ${{ steps.setup-ndk.outputs.ndk-path }} + LOCAL_PROPERTIES: ${{ secrets.LOCAL_PROPERTIES }} + - name: Prepare upload + if: github.event_name == 'workflow_dispatch' + run: |- + mkdir -p dist/release + cp clients/android/app/build/outputs/apk/play/release/*.apk dist/release + cp clients/android/app/build/outputs/apk/other/release/*-universal.apk dist/release + - name: Upload artifact + if: github.event_name == 'workflow_dispatch' + uses: actions/upload-artifact@v4 + with: + name: binary-android-apks + path: 'dist' + publish_android: + name: Publish Android + if: github.event_name == 'workflow_dispatch' && inputs.build == 'publish-android' + runs-on: ubuntu-latest + needs: + - calculate_version + steps: + - name: Checkout + uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4 + with: + fetch-depth: 0 + submodules: 'recursive' + - name: Setup Go + uses: actions/setup-go@v5 + with: + go-version: ^1.23 + - name: Setup Android NDK + id: setup-ndk + uses: nttld/setup-ndk@v1 + with: + ndk-version: r28-beta2 + - name: Setup OpenJDK + run: |- + sudo apt update && sudo apt install -y openjdk-17-jdk-headless + /usr/lib/jvm/java-17-openjdk-amd64/bin/java --version + - name: Set tag + run: |- + git tag v${{ needs.calculate_version.outputs.version }} + - name: Build library + run: |- + make lib_install + export PATH="$PATH:$(go env GOPATH)/bin" + make lib_android + env: + JAVA_HOME: /usr/lib/jvm/java-17-openjdk-amd64 + ANDROID_NDK_HOME: ${{ steps.setup-ndk.outputs.ndk-path }} + - name: Checkout main branch + if: needs.calculate_version.outputs.prerelease == 'false' + run: |- + cd clients/android + git checkout main + - name: Checkout dev branch + if: needs.calculate_version.outputs.prerelease == 'true' + run: |- + cd clients/android + git checkout dev + - name: Gradle cache + uses: actions/cache@v4 + with: + path: ~/.gradle + key: gradle-${{ hashFiles('**/*.gradle') }} + - name: Build + run: |- + go run -v ./cmd/internal/update_android_version --ci + mkdir clients/android/app/libs + cp libbox.aar clients/android/app/libs + cd clients/android + ./gradlew :app:publishPlayReleaseBundle + env: + JAVA_HOME: /usr/lib/jvm/java-17-openjdk-amd64 + ANDROID_NDK_HOME: ${{ steps.setup-ndk.outputs.ndk-path }} + LOCAL_PROPERTIES: ${{ secrets.LOCAL_PROPERTIES }} + build_apple_library: + name: Build Apple library + if: 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' + runs-on: macos-15 + needs: + - calculate_version + steps: + - name: Checkout + uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4 + with: + fetch-depth: 0 + submodules: 'recursive' + - name: Setup Go + uses: actions/setup-go@v5 + with: + go-version: ^1.23 + - name: Setup Xcode + run: |- + sudo xcode-select -s /Applications/Xcode_16.2_beta_3.app + - name: Set tag + run: |- + git tag v${{ needs.calculate_version.outputs.version }} + - name: Build library + run: |- + make lib_install + export PATH="$PATH:$(go env GOPATH)/bin" + make lib_ios + - name: Upload library + uses: actions/upload-artifact@v4 + with: + name: library-apple + path: 'Libbox.xcframework' + build_apple: + name: Build Apple clients + runs-on: macos-15 + needs: + - calculate_version + - build_apple_library + strategy: + matrix: + include: + - name: iOS + if: ${{ github.event_name != 'workflow_dispatch' || inputs.build == 'All' || inputs.build == 'Apple' || inputs.build == 'app-store'|| inputs.build == 'iOS' }} + scheme: SFI + destination: 'generic/platform=iOS' + archive: build/SFI.xcarchive + upload: SFI/Upload.plist + - name: macOS + if: ${{ github.event_name != 'workflow_dispatch' || inputs.build == 'All' || inputs.build == 'Apple' || inputs.build == 'app-store'|| inputs.build == 'macOS' }} + scheme: SFM + destination: 'generic/platform=macOS' + archive: build/SFM.xcarchive + upload: SFI/Upload.plist + - name: tvOS + if: ${{ github.event_name != 'workflow_dispatch' || inputs.build == 'All' || inputs.build == 'Apple' || inputs.build == 'app-store'|| inputs.build == 'tvOS' }} + scheme: SFT + destination: 'generic/platform=tvOS' + archive: build/SFT.xcarchive + upload: SFI/Upload.plist + - name: macOS-standalone + if: ${{ github.event_name != 'workflow_dispatch' || inputs.build == 'All' || inputs.build == 'Apple' || inputs.build == 'macOS-standalone' }} + scheme: SFM.System + destination: 'generic/platform=macOS' + archive: build/SFM.System.xcarchive + export: SFM.System/Export.plist + export_path: build/SFM.System + steps: + - name: Checkout + if: matrix.if + uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4 + with: + fetch-depth: 0 + submodules: 'recursive' + - name: Setup Go + if: matrix.if + uses: actions/setup-go@v5 + with: + go-version: ^1.23 + - name: Setup Xcode + if: matrix.if + run: |- + sudo xcode-select -s /Applications/Xcode_16.2_beta_3.app + - name: Set tag + if: matrix.if + run: |- + git tag v${{ needs.calculate_version.outputs.version }} + echo "VERSION=${{ needs.calculate_version.outputs.version }}" >> "$GITHUB_ENV" + - name: Checkout main branch + if: matrix.if && needs.calculate_version.outputs.prerelease == 'false' + run: |- + cd clients/apple + git checkout main + - name: Checkout dev branch + if: matrix.if && needs.calculate_version.outputs.prerelease == 'true' + run: |- + cd clients/apple + git checkout dev + - name: Setup certificates + if: matrix.if + run: |- + CERTIFICATE_PATH=$RUNNER_TEMP/Certificates.p12 + KEYCHAIN_PATH=$RUNNER_TEMP/certificates.keychain-db + echo -n "$CERTIFICATES_P12" | base64 --decode -o $CERTIFICATE_PATH + security create-keychain -p "$KEYCHAIN_PASSWORD" $KEYCHAIN_PATH + security set-keychain-settings -lut 21600 $KEYCHAIN_PATH + security unlock-keychain -p "$KEYCHAIN_PASSWORD" $KEYCHAIN_PATH + security import $CERTIFICATE_PATH -P "$P12_PASSWORD" -A -t cert -f pkcs12 -k $KEYCHAIN_PATH + security set-key-partition-list -S apple-tool:,apple: -k "$KEYCHAIN_PASSWORD" $KEYCHAIN_PATH + security list-keychain -d user -s $KEYCHAIN_PATH + + PROFILES_ZIP_PATH=$RUNNER_TEMP/Profiles.zip + echo -n "$PROVISIONING_PROFILES" | base64 --decode -o $PROFILES_ZIP_PATH + + PROFILES_PATH="$HOME/Library/MobileDevice/Provisioning Profiles" + mkdir -p "$PROFILES_PATH" + unzip $PROFILES_ZIP_PATH -d "$PROFILES_PATH" + + ASC_KEY_PATH=$RUNNER_TEMP/Key.p12 + echo -n "$ASC_KEY" | base64 --decode -o $ASC_KEY_PATH + + xcrun notarytool store-credentials "notarytool-password" \ + --key $ASC_KEY_PATH \ + --key-id $ASC_KEY_ID \ + --issuer $ASC_KEY_ISSUER_ID + env: + CERTIFICATES_P12: ${{ secrets.CERTIFICATES_P12 }} + P12_PASSWORD: ${{ secrets.P12_PASSWORD }} + KEYCHAIN_PASSWORD: ${{ secrets.P12_PASSWORD }} + PROVISIONING_PROFILES: ${{ secrets.PROVISIONING_PROFILES }} + ASC_KEY: ${{ secrets.ASC_KEY }} + ASC_KEY_ID: ${{ secrets.ASC_KEY_ID }} + ASC_KEY_ISSUER_ID: ${{ secrets.ASC_KEY_ISSUER_ID }} + - name: Download library + if: matrix.if + uses: actions/download-artifact@v4 + with: + name: library-apple + path: clients/apple/Libbox.xcframework + - name: Build + if: matrix.if + run: |- + go run -v ./cmd/internal/update_apple_version --ci + cd clients/apple + xcodebuild archive \ + -scheme "${{ matrix.scheme }}" \ + -configuration Release \ + -destination "${{ matrix.destination }}" \ + -archivePath "${{ matrix.archive }}" \ + -allowProvisioningUpdates \ + -authenticationKeyPath $RUNNER_TEMP/Key.p12 \ + -authenticationKeyID $ASC_KEY_ID \ + -authenticationKeyIssuerID $ASC_KEY_ISSUER_ID + env: + MACOS_PROJECT_VERSION: ${{ inputs.macos_project_version }} + ASC_KEY_ID: ${{ secrets.ASC_KEY_ID }} + ASC_KEY_ISSUER_ID: ${{ secrets.ASC_KEY_ISSUER_ID }} + - name: Upload to App Store Connect + if: matrix.if && matrix.name != 'macOS-standalone' && github.event_name == 'workflow_dispatch' + run: |- + cd clients/apple + xcodebuild -exportArchive \ + -archivePath "${{ matrix.archive }}" \ + -exportOptionsPlist ${{ matrix.upload }} \ + -allowProvisioningUpdates \ + -authenticationKeyPath $RUNNER_TEMP/Key.p12 \ + -authenticationKeyID $ASC_KEY_ID \ + -authenticationKeyIssuerID $ASC_KEY_ISSUER_ID + env: + ASC_KEY_ID: ${{ secrets.ASC_KEY_ID }} + ASC_KEY_ISSUER_ID: ${{ secrets.ASC_KEY_ISSUER_ID }} + - name: Build image + if: matrix.if && matrix.name == 'macOS-standalone' && github.event_name == 'workflow_dispatch' + run: |- + pushd clients/apple + xcodebuild -exportArchive \ + -archivePath "${{ matrix.archive }}" \ + -exportOptionsPlist ${{ matrix.export }} \ + -exportPath "${{ matrix.export_path }}" + brew install create-dmg + create-dmg \ + --volname "sing-box" \ + --volicon "${{ matrix.export_path }}/SFM.app/Contents/Resources/AppIcon.icns" \ + --icon "SFM.app" 0 0 \ + --hide-extension "SFM.app" \ + --app-drop-link 0 0 \ + --skip-jenkins \ + SFM.dmg "${{ matrix.export_path }}/SFM.app" + xcrun notarytool submit "SFM.dmg" --wait --keychain-profile "notarytool-password" + 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" + - name: Upload image + if: matrix.if && matrix.name == 'macOS-standalone' && github.event_name == 'workflow_dispatch' + uses: actions/upload-artifact@v4 + with: + name: binary-macos-dmg + path: 'dist' + upload: + name: Upload builds + if: always() && github.event_name == 'workflow_dispatch' + runs-on: ubuntu-latest + needs: + - calculate_version + - build + - build_android + - build_apple + steps: + - name: Checkout + uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4 + with: + fetch-depth: 0 + - name: Setup Goreleaser + uses: goreleaser/goreleaser-action@v6 + with: + distribution: goreleaser-pro + version: latest + install-only: true + - name: Cache ghr + uses: actions/cache@v4 + id: cache-ghr + with: + path: | + ~/go/bin/ghr + key: ghr + - name: Setup ghr + if: steps.cache-ghr.outputs.cache-hit != 'true' + run: |- + cd $HOME + git clone https://github.com/nekohasekai/ghr ghr + cd ghr + go install -v . + - name: Set tag + run: |- + git tag v${{ needs.calculate_version.outputs.version }} + echo "VERSION=${{ needs.calculate_version.outputs.version }}" >> "$GITHUB_ENV" + - name: Download builds + uses: actions/download-artifact@v4 + 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: ${{ secrets.GORELEASER_KEY }} + - name: Upload builds + run: |- + export PATH="$PATH:$HOME/go/bin" + ghr --replace --draft --prerelease -p 5 "v${VERSION}" dist/release + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} \ No newline at end of file diff --git a/.github/workflows/debug.yml b/.github/workflows/debug.yml deleted file mode 100644 index 09499732..00000000 --- a/.github/workflows/debug.yml +++ /dev/null @@ -1,219 +0,0 @@ -name: Debug build - -on: - push: - branches: - - stable-next - - main-next - - dev-next - paths-ignore: - - '**.md' - - '.github/**' - - '!.github/workflows/debug.yml' - pull_request: - branches: - - stable-next - - main-next - - dev-next - -jobs: - build: - name: Debug build - runs-on: ubuntu-latest - steps: - - name: Checkout - uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4 - with: - fetch-depth: 0 - - name: Setup Go - uses: actions/setup-go@v5 - with: - go-version: ^1.23 - - name: Run Test - run: | - go test -v ./... - build_go120: - name: Debug build (Go 1.20) - runs-on: ubuntu-latest - steps: - - name: Checkout - uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4 - with: - fetch-depth: 0 - - name: Setup Go - uses: actions/setup-go@v5 - with: - go-version: ~1.20 - - name: Cache go module - uses: actions/cache@v4 - with: - path: | - ~/go/pkg/mod - key: go120-${{ hashFiles('**/go.sum') }} - - name: Run Test - run: make ci_build_go120 - build_go121: - name: Debug build (Go 1.21) - runs-on: ubuntu-latest - steps: - - name: Checkout - uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4 - with: - fetch-depth: 0 - - name: Setup Go - uses: actions/setup-go@v5 - with: - go-version: ~1.21 - - name: Cache go module - uses: actions/cache@v4 - with: - path: | - ~/go/pkg/mod - key: go121-${{ hashFiles('**/go.sum') }} - - name: Run Test - run: make ci_build - build_go122: - name: Debug build (Go 1.22) - runs-on: ubuntu-latest - steps: - - name: Checkout - uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4 - with: - fetch-depth: 0 - - name: Setup Go - uses: actions/setup-go@v5 - with: - go-version: ~1.22 - - name: Cache go module - uses: actions/cache@v4 - with: - path: | - ~/go/pkg/mod - key: go122-${{ hashFiles('**/go.sum') }} - - name: Run Test - run: make ci_build - cross: - strategy: - matrix: - include: - # windows - - name: windows-amd64 - goos: windows - goarch: amd64 - goamd64: v1 - - name: windows-amd64-v3 - goos: windows - goarch: amd64 - goamd64: v3 - - name: windows-386 - goos: windows - goarch: 386 - - name: windows-arm64 - goos: windows - goarch: arm64 - - name: windows-arm32v7 - goos: windows - goarch: arm - goarm: 7 - - # linux - - name: linux-amd64 - goos: linux - goarch: amd64 - goamd64: v1 - - name: linux-amd64-v3 - goos: linux - goarch: amd64 - goamd64: v3 - - name: linux-386 - goos: linux - goarch: 386 - - name: linux-arm64 - goos: linux - goarch: arm64 - - name: linux-armv5 - goos: linux - goarch: arm - goarm: 5 - - name: linux-armv6 - goos: linux - goarch: arm - goarm: 6 - - name: linux-armv7 - goos: linux - goarch: arm - goarm: 7 - - name: linux-mips-softfloat - goos: linux - goarch: mips - gomips: softfloat - - name: linux-mips-hardfloat - goos: linux - goarch: mips - gomips: hardfloat - - name: linux-mipsel-softfloat - goos: linux - goarch: mipsle - gomips: softfloat - - name: linux-mipsel-hardfloat - goos: linux - goarch: mipsle - gomips: hardfloat - - name: linux-mips64 - goos: linux - goarch: mips64 - - name: linux-mips64el - goos: linux - goarch: mips64le - - name: linux-s390x - goos: linux - goarch: s390x - # darwin - - name: darwin-amd64 - goos: darwin - goarch: amd64 - goamd64: v1 - - name: darwin-amd64-v3 - goos: darwin - goarch: amd64 - goamd64: v3 - - name: darwin-arm64 - goos: darwin - goarch: arm64 - # freebsd - - name: freebsd-amd64 - goos: freebsd - goarch: amd64 - goamd64: v1 - - name: freebsd-amd64-v3 - goos: freebsd - goarch: amd64 - goamd64: v3 - - name: freebsd-386 - goos: freebsd - goarch: 386 - - name: freebsd-arm64 - goos: freebsd - goarch: arm64 - fail-fast: true - runs-on: ubuntu-latest - env: - GOOS: ${{ matrix.goos }} - GOARCH: ${{ matrix.goarch }} - GOAMD64: ${{ matrix.goamd64 }} - GOARM: ${{ matrix.goarm }} - GOMIPS: ${{ matrix.gomips }} - CGO_ENABLED: 0 - TAGS: with_gvisor,with_dhcp,with_wireguard,with_clash_api,with_quic,with_utls,with_ech - steps: - - name: Checkout - uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4 - with: - fetch-depth: 0 - - name: Setup Go - uses: actions/setup-go@v5 - with: - go-version: ^1.21 - - name: Build - id: build - run: make \ No newline at end of file diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml index 521477ed..613c057a 100644 --- a/.github/workflows/linux.yml +++ b/.github/workflows/linux.yml @@ -22,7 +22,6 @@ jobs: mkdir -p $HOME/.gnupg cat > $HOME/.gnupg/sagernet.key <> "$GITHUB_ENV" EOF echo "HOME=$HOME" >> "$GITHUB_ENV" - name: Publish release diff --git a/.goreleaser.yaml b/.goreleaser.yaml index 2d0b234f..62136cfc 100644 --- a/.goreleaser.yaml +++ b/.goreleaser.yaml @@ -200,4 +200,6 @@ release: ids: - archive - package - skip_upload: true \ No newline at end of file + skip_upload: true +partial: + by: target \ No newline at end of file diff --git a/cmd/internal/build_libbox/main.go b/cmd/internal/build_libbox/main.go index fc9308ff..e6b9bbd7 100644 --- a/cmd/internal/build_libbox/main.go +++ b/cmd/internal/build_libbox/main.go @@ -10,7 +10,9 @@ import ( _ "github.com/sagernet/gomobile" "github.com/sagernet/sing-box/cmd/internal/build_shared" "github.com/sagernet/sing-box/log" + E "github.com/sagernet/sing/common/exceptions" "github.com/sagernet/sing/common/rw" + "github.com/sagernet/sing/common/shell" ) var ( @@ -62,6 +64,22 @@ func init() { func buildAndroid() { build_shared.FindSDK() + var javaPath string + javaHome := os.Getenv("JAVA_HOME") + if javaHome == "" { + javaPath = "java" + } else { + javaPath = filepath.Join(javaHome, "bin", "java") + } + + javaVersion, err := shell.Exec(javaPath, "--version").ReadOutput() + if err != nil { + log.Fatal(E.Cause(err, "check java version")) + } + if !strings.Contains(javaVersion, "openjdk 17") { + log.Fatal("java version should be openjdk 17") + } + args := []string{ "bind", "-v", @@ -86,7 +104,7 @@ func buildAndroid() { command := exec.Command(build_shared.GoBinPath+"/gomobile", args...) command.Stdout = os.Stdout command.Stderr = os.Stderr - err := command.Run() + err = command.Run() if err != nil { log.Fatal(err) } diff --git a/cmd/internal/build_shared/sdk.go b/cmd/internal/build_shared/sdk.go index fae57b53..5dbefa4a 100644 --- a/cmd/internal/build_shared/sdk.go +++ b/cmd/internal/build_shared/sdk.go @@ -11,9 +11,7 @@ import ( "github.com/sagernet/sing-box/log" "github.com/sagernet/sing/common" - E "github.com/sagernet/sing/common/exceptions" "github.com/sagernet/sing/common/rw" - "github.com/sagernet/sing/common/shell" ) var ( @@ -42,14 +40,6 @@ func FindSDK() { log.Fatal("android NDK not found") } - javaVersion, err := shell.Exec("java", "--version").ReadOutput() - if err != nil { - log.Fatal(E.Cause(err, "check java version")) - } - if !strings.Contains(javaVersion, "openjdk 17") { - log.Fatal("java version should be openjdk 17") - } - os.Setenv("ANDROID_HOME", androidSDKPath) os.Setenv("ANDROID_SDK_HOME", androidSDKPath) os.Setenv("ANDROID_NDK_HOME", androidNDKPath) @@ -64,6 +54,10 @@ func findNDK() bool { androidNDKPath = fixedPath return true } + if ndkHomeEnv := os.Getenv("ANDROID_NDK_HOME"); rw.IsFile(filepath.Join(ndkHomeEnv, versionFile)) { + androidNDKPath = ndkHomeEnv + return true + } ndkVersions, err := os.ReadDir(filepath.Join(androidSDKPath, "ndk")) if err != nil { return false diff --git a/cmd/internal/build_shared/tag.go b/cmd/internal/build_shared/tag.go index 608b5877..16a97f3e 100644 --- a/cmd/internal/build_shared/tag.go +++ b/cmd/internal/build_shared/tag.go @@ -20,6 +20,11 @@ func ReadTag() (string, error) { return version.String() + "-" + shortCommit, nil } +func ReadTagVersionRev() (badversion.Version, error) { + currentTagRev := common.Must1(shell.Exec("git", "describe", "--tags", "--abbrev=0").ReadOutput()) + return badversion.Parse(currentTagRev[1:]), nil +} + func ReadTagVersion() (badversion.Version, error) { currentTag := common.Must1(shell.Exec("git", "describe", "--tags").ReadOutput()) currentTagRev := common.Must1(shell.Exec("git", "describe", "--tags", "--abbrev=0").ReadOutput()) @@ -31,3 +36,11 @@ func ReadTagVersion() (badversion.Version, error) { } return version, nil } + +func IsDevBranch() bool { + branch, err := shell.Exec("git", "branch", "--show-current").ReadOutput() + if err != nil { + return false + } + return branch == "dev-next" +} diff --git a/cmd/internal/read_tag/main.go b/cmd/internal/read_tag/main.go index d319f771..ae496ec6 100644 --- a/cmd/internal/read_tag/main.go +++ b/cmd/internal/read_tag/main.go @@ -1,21 +1,74 @@ package main import ( + "flag" "os" "github.com/sagernet/sing-box/cmd/internal/build_shared" "github.com/sagernet/sing-box/log" + F "github.com/sagernet/sing/common/format" ) +var nightly bool + +func init() { + flag.BoolVar(&nightly, "nightly", false, "Print nightly tag") +} + func main() { - currentTag, err := build_shared.ReadTag() - if err != nil { - log.Error(err) - _, err = os.Stdout.WriteString("unknown\n") + flag.Parse() + if nightly { + version, err := build_shared.ReadTagVersionRev() + if err != nil { + log.Fatal(err) + } + var ( + versionStr string + isPrerelease bool + ) + if version.PreReleaseIdentifier != "" { + isPrerelease = true + versionStr = version.VersionString() + "-nightly" + } else { + version.Patch++ + versionStr = version.VersionString() + "-nightly" + } + if build_shared.IsDevBranch() { + isPrerelease = true + } + err = setGitHubOutput("version", versionStr) + if err != nil { + log.Fatal(err) + } + err = setGitHubOutput("prerelease", F.ToString(isPrerelease)) + if err != nil { + log.Fatal(err) + } } else { - _, err = os.Stdout.WriteString(currentTag + "\n") - } - if err != nil { - log.Error(err) + tag, err := build_shared.ReadTag() + if err != nil { + log.Error(err) + os.Stdout.WriteString("unknown\n") + } else { + os.Stdout.WriteString(tag + "\n") + } } } + +func setGitHubOutput(name string, value string) error { + outputFile, err := os.OpenFile(os.Getenv("GITHUB_ENV"), os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0o644) + if err != nil { + return err + } + _, err = outputFile.WriteString(name + "=" + value + "\n") + if err != nil { + outputFile.Close() + return err + } + err = outputFile.Close() + if err != nil { + return err + } + os.Stderr.WriteString(name + "=" + value + "\n") + return nil +} diff --git a/cmd/internal/update_android_version/main.go b/cmd/internal/update_android_version/main.go index 040ba5d2..0c6528c6 100644 --- a/cmd/internal/update_android_version/main.go +++ b/cmd/internal/update_android_version/main.go @@ -1,6 +1,7 @@ package main import ( + "flag" "os" "path/filepath" "runtime" @@ -12,9 +13,22 @@ import ( "github.com/sagernet/sing/common" ) +var flagRunInCI bool + +func init() { + flag.BoolVar(&flagRunInCI, "ci", false, "Run in CI") +} + func main() { - newVersion := common.Must1(build_shared.ReadTagVersion()) - androidPath, err := filepath.Abs("../sing-box-for-android") + flag.Parse() + newVersion := common.Must1(build_shared.ReadTag()) + var androidPath string + if flagRunInCI { + androidPath = "clients/android" + } else { + androidPath = "../sing-box-for-android" + } + androidPath, err := filepath.Abs(androidPath) if err != nil { log.Fatal(err) } @@ -31,10 +45,10 @@ func main() { for _, propPair := range propsList { switch propPair[0] { case "VERSION_NAME": - if propPair[1] != newVersion.String() { + if propPair[1] != newVersion { versionUpdated = true - propPair[1] = newVersion.String() - log.Info("updated version to ", newVersion.String()) + propPair[1] = newVersion + log.Info("updated version to ", newVersion) } case "GO_VERSION": if propPair[1] != runtime.Version() { diff --git a/cmd/internal/update_apple_version/main.go b/cmd/internal/update_apple_version/main.go index 0da0f649..93388e49 100644 --- a/cmd/internal/update_apple_version/main.go +++ b/cmd/internal/update_apple_version/main.go @@ -1,6 +1,7 @@ package main import ( + "flag" "os" "path/filepath" "regexp" @@ -13,9 +14,22 @@ import ( "howett.net/plist" ) +var flagRunInCI bool + +func init() { + flag.BoolVar(&flagRunInCI, "ci", false, "Run in CI") +} + func main() { + flag.Parse() newVersion := common.Must1(build_shared.ReadTagVersion()) - applePath, err := filepath.Abs("../sing-box-for-apple") + var applePath string + if flagRunInCI { + applePath = "clients/apple" + } else { + applePath = "../sing-box-for-apple" + } + applePath, err := filepath.Abs(applePath) if err != nil { log.Fatal(err) }