From 58d2ed88aa436aa23d180c7ce96543ea3c76c2d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=96=E7=95=8C?= Date: Tue, 14 Jul 2026 14:59:14 +0800 Subject: [PATCH] Add windows build --- .github/workflows/build.yml | 839 +++++++++---------------------- .gitmodules | 3 + clients/desktop | 1 + docs/clients/desktop/features.md | 9 + docs/clients/desktop/index.md | 22 + docs/clients/index.md | 3 +- docs/clients/index.zh.md | 3 +- mkdocs.yml | 3 + 8 files changed, 286 insertions(+), 597 deletions(-) create mode 160000 clients/desktop create mode 100644 docs/clients/desktop/features.md create mode 100644 docs/clients/desktop/index.md diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 8f4a8d72..6e19660f 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -16,17 +16,14 @@ on: - All - Binary - Android + - Windows - Apple - - Apple-release + - app-store - iOS - macOS - tvOS - macOS-standalone - publish-android - test_message: - description: "TestFlight test message" - required: false - type: string push: branches: - stable @@ -52,19 +49,6 @@ jobs: uses: actions/setup-go@v5 with: go-version: 1.25.12 - cache: false - - name: Cache Go modules - uses: actions/cache/restore@v4 - with: - path: ~/go/pkg/mod - key: go-mod-linux-${{ hashFiles('go.sum') }} - restore-keys: go-mod-linux- - - name: Cache Go tool builds - uses: actions/cache/restore@v4 - with: - path: ~/.cache/go-build - key: go-build-tools-linux-${{ hashFiles('go.sum') }} - restore-keys: go-build-tools-linux- - name: Check input version if: github.event_name == 'workflow_dispatch' run: |- @@ -81,11 +65,10 @@ jobs: build: name: Build binary if: github.event_name != 'workflow_dispatch' || inputs.build == 'All' || inputs.build == 'Binary' - runs-on: ubuntu-26.04 + runs-on: ubuntu-latest needs: - calculate_version strategy: - max-parallel: 13 matrix: include: - { os: linux, arch: amd64, variant: purego, naive: true } @@ -137,19 +120,12 @@ jobs: - name: Checkout uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5 with: - fetch-depth: 1 + fetch-depth: 0 - name: Setup Go if: ${{ ! matrix.legacy_win7 }} uses: actions/setup-go@v5 with: go-version: 1.25.12 - cache: false - - name: Cache Go modules - uses: actions/cache/restore@v4 - with: - path: ~/go/pkg/mod - key: go-mod-linux-${{ hashFiles('go.sum') }} - restore-keys: go-mod-linux- - name: Cache Go for Windows 7 if: matrix.legacy_win7 id: cache-go-for-windows7 @@ -174,6 +150,7 @@ jobs: uses: nttld/setup-ndk@v1 with: ndk-version: r28 + local-cache: true - name: Clone cronet-go if: matrix.naive && matrix.variant != 'purego' run: | @@ -192,6 +169,17 @@ jobs: rm -f ~/cronet-go/naiveproxy/src/build/linux/sysroot_scripts/keyring.gpg cd ~/cronet-go GPG_TTY=/dev/null ./naiveproxy/src/build/linux/sysroot_scripts/generate_keyring.sh + - name: Cache Chromium toolchain + if: matrix.naive + id: cache-chromium-toolchain + uses: actions/cache@v4 + with: + path: | + ~/cronet-go/naiveproxy/src/third_party/llvm-build/ + ~/cronet-go/naiveproxy/src/gn/out/ + ~/cronet-go/naiveproxy/src/chrome/build/pgo_profiles/ + ~/cronet-go/naiveproxy/src/out/sysroot-build/ + key: chromium-toolchain-${{ matrix.arch }}-${{ matrix.variant }}-${{ hashFiles('.github/CRONET_GO_VERSION') }} - name: Download Chromium toolchain if: matrix.naive && matrix.variant != 'purego' run: | @@ -355,6 +343,7 @@ jobs: run: | set -xeuo pipefail sudo gem install fpm + sudo apt-get update sudo apt-get install -y debsigs cp .fpm_systemd .fpm fpm -t deb \ @@ -362,6 +351,8 @@ jobs: -p "dist/sing-box_${{ needs.calculate_version.outputs.version }}_${{ matrix.os }}_${{ matrix.debian }}.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 <> "$env:GITHUB_ENV" @@ -665,46 +641,31 @@ jobs: with: name: binary-windows_${{ matrix.arch }} path: "dist" - build_android_library: - name: Build Android library (${{ matrix.go_arch }}) - if: (github.event_name != 'workflow_dispatch' || inputs.build == 'All' || inputs.build == 'Android' || inputs.build == 'publish-android') && github.ref != 'refs/heads/oldstable' + build_android: + name: Build Android + if: (github.event_name != 'workflow_dispatch' || inputs.build == 'All' || inputs.build == 'Android') && github.ref != 'refs/heads/oldstable' runs-on: ubuntu-latest needs: - calculate_version - strategy: - fail-fast: false - matrix: - go_arch: - - "386" - - amd64 - - arm - - arm64 steps: - name: Checkout uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5 with: - fetch-depth: 1 + fetch-depth: 0 + submodules: 'recursive' - name: Setup Go uses: actions/setup-go@v5 with: go-version: 1.25.12 - cache: false - - name: Cache Go modules - uses: actions/cache@v4 - with: - path: ~/go/pkg/mod - key: go-mod-linux-${{ hashFiles('go.sum') }} - restore-keys: go-mod-linux- - name: Setup Android NDK id: setup-ndk uses: nttld/setup-ndk@v1 with: ndk-version: r28 - name: Setup OpenJDK - uses: actions/setup-java@03ad4de0992f5dab5e18fcb136590ce7c4a0ac95 # v5.6.0 - with: - distribution: temurin - java-version: 17 + 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 ls-remote --exit-code --tags origin v${{ needs.calculate_version.outputs.version }} || echo "PUBLISHED=false" >> "$GITHUB_ENV" @@ -713,82 +674,10 @@ jobs: run: |- make lib_install export PATH="$PATH:$(go env GOPATH)/bin" - go run ./cmd/internal/build_libbox -target android -platform android/${{ matrix.go_arch }} - mkdir -p dist/${{ matrix.go_arch }} - mv libbox.aar libbox-legacy.aar dist/${{ matrix.go_arch }} + make lib_android env: + JAVA_HOME: /usr/lib/jvm/java-17-openjdk-amd64 ANDROID_NDK_HOME: ${{ steps.setup-ndk.outputs.ndk-path }} - - name: Upload library - uses: actions/upload-artifact@v4 - with: - name: libbox-android-${{ matrix.go_arch }} - path: dist - compression-level: 0 - build_android: - name: Build Android (${{ matrix.variant }}) - if: (github.event_name != 'workflow_dispatch' || inputs.build == 'All' || inputs.build == 'Android') && github.ref != 'refs/heads/oldstable' - runs-on: ubuntu-latest - needs: - - calculate_version - - build_android_library - strategy: - fail-fast: false - matrix: - include: - - variant: other - task: :app:assembleOtherRelease - output: app/build/outputs/apk/other/release/*.apk - - variant: other-legacy - task: :app:assembleOtherLegacyRelease - output: app/build/outputs/apk/otherLegacy/release/*.apk - steps: - - name: Checkout - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5 - with: - fetch-depth: 1 - - name: Checkout Android client - run: git submodule update --init clients/android - - name: Setup Go - uses: actions/setup-go@v5 - with: - go-version: 1.25.12 - cache: false - - name: Cache Go modules - uses: actions/cache/restore@v4 - with: - path: ~/go/pkg/mod - key: go-mod-linux-${{ hashFiles('go.sum') }} - restore-keys: go-mod-linux- - - name: Cache Go tool builds - uses: actions/cache@v4 - with: - path: ~/.cache/go-build - key: go-build-tools-linux-${{ hashFiles('go.sum') }} - restore-keys: go-build-tools-linux- - - name: Setup Android NDK - id: setup-ndk - uses: nttld/setup-ndk@v1 - with: - ndk-version: r28 - - name: Setup OpenJDK - uses: actions/setup-java@03ad4de0992f5dab5e18fcb136590ce7c4a0ac95 # v5.6.0 - with: - distribution: temurin - java-version: 17 - - 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: Download libraries - uses: actions/download-artifact@v4 - with: - pattern: libbox-android-* - path: libbox-inputs - merge-multiple: true - - name: Merge libraries - run: |- - go run ./cmd/internal/merge_aar -output libbox.aar libbox-inputs/*/libbox.aar - go run ./cmd/internal/merge_aar -output libbox-legacy.aar libbox-inputs/*/libbox-legacy.aar - name: Checkout main branch if: github.ref == 'refs/heads/stable' && github.event_name != 'workflow_dispatch' run: |- @@ -803,10 +692,11 @@ jobs: run: |- cd clients/android git submodule update --init --recursive -v - - name: Setup Gradle - uses: gradle/actions/setup-gradle@3f131e8634966bd73d06cc69884922b02e6faf92 # v6.2.0 + - name: Gradle cache + uses: actions/cache@v4 with: - cache-provider: basic + path: ~/.gradle + key: gradle-${{ hashFiles('**/*.gradle') }} - name: Update version if: github.event_name == 'workflow_dispatch' run: |- @@ -817,17 +707,20 @@ jobs: go run -v ./cmd/internal/update_android_version --ci --nightly - name: Build run: |- - mkdir -p clients/android/app/libs + mkdir clients/android/app/libs cp *.aar clients/android/app/libs cd clients/android - ./gradlew --build-cache --parallel ${{ matrix.task }} + ./gradlew :app:assembleOtherRelease :app:assembleOtherLegacyRelease 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 run: |- mkdir -p dist - cp clients/android/${{ matrix.output }} dist + #cp clients/android/app/build/outputs/apk/play/release/*.apk dist + cp clients/android/app/build/outputs/apk/other/release/*.apk dist + cp clients/android/app/build/outputs/apk/otherLegacy/release/*.apk dist VERSION_CODE=$(grep VERSION_CODE clients/android/version.properties | cut -d= -f2) VERSION_NAME=$(grep VERSION_NAME clients/android/version.properties | cut -d= -f2) cat > dist/SFA-version-metadata.json << EOF @@ -840,63 +733,45 @@ jobs: - name: Upload artifact uses: actions/upload-artifact@v4 with: - name: binary-android-apks-${{ matrix.variant }} + name: binary-android-apks path: 'dist' publish_android: name: Publish Android - if: github.event_name == 'workflow_dispatch' && inputs.build == 'publish-android' && (github.ref == 'refs/heads/stable' || github.ref == 'refs/heads/testing') + if: github.event_name == 'workflow_dispatch' && inputs.build == 'publish-android' && github.ref != 'refs/heads/oldstable' runs-on: ubuntu-latest needs: - calculate_version - - build_android_library steps: - name: Checkout uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5 with: - fetch-depth: 1 - - name: Checkout Android client - run: git submodule update --init clients/android + fetch-depth: 0 + submodules: 'recursive' - name: Setup Go uses: actions/setup-go@v5 with: go-version: 1.25.12 - cache: false - - name: Cache Go modules - uses: actions/cache/restore@v4 - with: - path: ~/go/pkg/mod - key: go-mod-linux-${{ hashFiles('go.sum') }} - restore-keys: go-mod-linux- - - name: Cache Go tool builds - uses: actions/cache@v4 - with: - path: ~/.cache/go-build - key: go-build-tools-linux-${{ hashFiles('go.sum') }} - restore-keys: go-build-tools-linux- - name: Setup Android NDK id: setup-ndk uses: nttld/setup-ndk@v1 with: ndk-version: r28 - name: Setup OpenJDK - uses: actions/setup-java@03ad4de0992f5dab5e18fcb136590ce7c4a0ac95 # v5.6.0 - with: - distribution: temurin - java-version: 17 + 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 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: Download libraries - uses: actions/download-artifact@v4 - with: - pattern: libbox-android-* - path: libbox-inputs - merge-multiple: true - - name: Merge libraries + - name: Build library run: |- - go run ./cmd/internal/merge_aar -output libbox.aar libbox-inputs/*/libbox.aar - go run ./cmd/internal/merge_aar -output libbox-legacy.aar libbox-inputs/*/libbox-legacy.aar + 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: github.ref == 'refs/heads/stable' && github.event_name != 'workflow_dispatch' run: |- @@ -907,156 +782,158 @@ jobs: run: |- cd clients/android git checkout dev - - name: Checkout submodule recursive - run: |- - cd clients/android - git submodule update --init --recursive -v - - name: Setup Gradle - uses: gradle/actions/setup-gradle@3f131e8634966bd73d06cc69884922b02e6faf92 # v6.2.0 + - name: Gradle cache + uses: actions/cache@v4 with: - cache-provider: basic - - name: Prepare build + path: ~/.gradle + key: gradle-${{ hashFiles('**/*.gradle') }} + - name: Build run: |- go run -v ./cmd/internal/update_android_version --ci - mkdir -p clients/android/app/libs + mkdir clients/android/app/libs cp *.aar clients/android/app/libs cd clients/android echo -n "$SERVICE_ACCOUNT_CREDENTIALS" | base64 --decode > service-account-credentials.json - chmod 600 service-account-credentials.json - jq -e ' - .type == "service_account" and - (.project_id | type == "string" and length > 0) and - (.private_key | type == "string" and length > 0) and - (.client_email | type == "string" and length > 0) and - (.token_uri | type == "string" and length > 0) - ' service-account-credentials.json > /dev/null - jq -r '.private_key' service-account-credentials.json | openssl pkey -check -noout - env: - LOCAL_PROPERTIES: ${{ secrets.LOCAL_PROPERTIES }} - SERVICE_ACCOUNT_CREDENTIALS: ${{ secrets.SERVICE_ACCOUNT_CREDENTIALS }} - - name: Publish to Google Play - run: |- - cd clients/android - ./gradlew --build-cache --parallel :app:publishPlayReleaseBundle + ./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 (${{ matrix.artifact }}) - runs-on: ${{ matrix.build && 'macos-26' || 'ubuntu-latest' }} - if: github.event_name != 'workflow_dispatch' || inputs.build == 'All' || inputs.build == 'Apple' || inputs.build == 'Apple-release' || inputs.build == 'iOS' || inputs.build == 'macOS' || inputs.build == 'tvOS' || inputs.build == 'macOS-standalone' + SERVICE_ACCOUNT_CREDENTIALS: ${{ secrets.SERVICE_ACCOUNT_CREDENTIALS }} + build_windows_client: + name: Build Windows client + if: (github.event_name != 'workflow_dispatch' || inputs.build == 'All' || inputs.build == 'Windows') && github.ref != 'refs/heads/oldstable' + runs-on: windows-latest needs: - calculate_version - strategy: - fail-fast: false - matrix: - include: - - artifact: ios-arm64 - platform: ios/arm64 - build: ${{ github.event_name != 'workflow_dispatch' || inputs.build == 'All' || inputs.build == 'Apple' || inputs.build == 'Apple-release' || inputs.build == 'iOS' }} - - artifact: tvos-arm64 - platform: tvos/arm64 - build: ${{ github.event_name == 'workflow_dispatch' && (inputs.build == 'All' || inputs.build == 'Apple' || inputs.build == 'tvOS') }} - - artifact: macos-arm64 - platform: macos/arm64 - build: ${{ github.event_name != 'workflow_dispatch' || inputs.build == 'All' || inputs.build == 'Apple' || inputs.build == 'Apple-release' || inputs.build == 'macOS' || inputs.build == 'macOS-standalone' }} - - artifact: macos-amd64 - platform: macos/amd64 - build: ${{ github.event_name != 'workflow_dispatch' || inputs.build == 'All' || inputs.build == 'Apple' || inputs.build == 'Apple-release' || inputs.build == 'macOS' || inputs.build == 'macOS-standalone' }} steps: - name: Checkout - if: matrix.build uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5 with: - fetch-depth: 1 + fetch-depth: 0 + submodules: 'recursive' - name: Setup Go - if: matrix.build uses: actions/setup-go@v5 with: - go-version: 1.25.12 - cache: false - - name: Cache Go modules - if: matrix.build - uses: actions/cache@v4 + go-version: ~1.25.11 + - name: Setup pnpm + uses: pnpm/action-setup@v4 with: - path: ~/go/pkg/mod - key: go-mod-macos-${{ hashFiles('go.sum') }} - restore-keys: go-mod-macos- - - name: Cache Go build - if: matrix.build - uses: actions/cache@v4 + package_json_file: clients/desktop/package.json + - name: Setup Node.js + uses: actions/setup-node@v4 with: - path: ~/Library/Caches/go-build - key: libbox-go-build-${{ matrix.artifact }}-1.25.12-${{ hashFiles('go.sum') }} - restore-keys: libbox-go-build-${{ matrix.artifact }}-1.25.12- + node-version: 26 + cache: pnpm + cache-dependency-path: | + clients/desktop/pnpm-lock.yaml + clients/desktop/dashboard/pnpm-lock.yaml - name: Set tag - if: matrix.build - run: git tag v${{ needs.calculate_version.outputs.version }} -f - - name: Build library - if: matrix.build run: |- - make lib_install - export PATH="$PATH:$(go env GOPATH)/bin" - go run ./cmd/internal/build_libbox -target apple -platform ${{ matrix.platform }} - mkdir -p dist - tar -cf dist/Libbox-${{ matrix.artifact }}.tar Libbox.xcframework - - name: Upload library - if: matrix.build + git ls-remote --exit-code --tags origin v${{ needs.calculate_version.outputs.version }} + if ($LASTEXITCODE -ne 0) { + "PUBLISHED=false" >> $env:GITHUB_ENV + } + git tag v${{ needs.calculate_version.outputs.version }} -f + - name: Checkout main branch + if: github.ref == 'refs/heads/testing' + run: |- + git -C clients/desktop checkout main + - name: Checkout submodule recursive + run: |- + git -C clients/desktop submodule update --init --recursive + - name: Update version + if: github.event_name == 'workflow_dispatch' + run: |- + go run -v ./cmd/internal/update_desktop_version --ci + - name: Update nightly version + if: github.event_name != 'workflow_dispatch' + run: |- + go run -v ./cmd/internal/update_desktop_version --ci --nightly + - name: Setup signing + run: |- + $certificatePath = Join-Path $env:RUNNER_TEMP "windows-signing.p12" + [IO.File]::WriteAllBytes($certificatePath, [Convert]::FromBase64String($env:WINDOWS_CERTIFICATES_P12)) + @{ + windows = @{ + certificateFile = $certificatePath + certificatePassword = $env:WINDOWS_P12_PASSWORD + } + } | ConvertTo-Json -Depth 3 | Set-Content -Encoding utf8NoBOM clients/desktop/signing.local.json + env: + WINDOWS_CERTIFICATES_P12: ${{ secrets.WINDOWS_CERTIFICATES_P12 }} + WINDOWS_P12_PASSWORD: ${{ secrets.WINDOWS_P12_PASSWORD }} + - name: Install dependencies + run: |- + pnpm -C clients/desktop install + - name: Build + run: |- + pnpm -C clients/desktop package:win + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + - name: Prepare upload + run: |- + $artifacts = @(Get-ChildItem clients/desktop/release/SFW-*.exe -File) + if ($artifacts.Count -ne 3) { + throw "expected 3 Windows installers, found $($artifacts.Count)" + } + New-Item -ItemType Directory -Force dist | Out-Null + Copy-Item $artifacts.FullName dist + - name: Upload artifact uses: actions/upload-artifact@v4 with: - name: libbox-apple-${{ matrix.artifact }} - path: dist - compression-level: 1 + name: binary-windows-client + path: 'dist' build_apple: - name: Build Apple release clients - runs-on: ${{ matrix.if && 'macos-26' || 'ubuntu-latest' }} - if: github.event_name != 'workflow_dispatch' || inputs.build == 'All' || inputs.build == 'Apple-release' || inputs.build == 'macOS-standalone' + name: Build Apple clients + runs-on: macos-26 + 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' needs: - calculate_version - - build_apple_library strategy: - fail-fast: false matrix: include: - - name: macOS-standalone - if: ${{ github.event_name != 'workflow_dispatch' || inputs.build == 'All' || inputs.build == 'Apple-release' || inputs.build == 'macOS-standalone' }} - type: standalone + - name: iOS + if: ${{ github.event_name != 'workflow_dispatch' || inputs.build == 'All' || inputs.build == 'Apple' || inputs.build == 'app-store'|| inputs.build == 'iOS' }} + platform: 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' }} platform: macos - variant: all - library_arch: '*' - scheme: '' - destination: '' - archive: '' - upload: '' - steps: &build_apple_steps + 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' }} + platform: 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' }} + platform: macos + 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@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5 with: - fetch-depth: 1 - - name: Checkout Apple client - if: matrix.if - run: git submodule update --init --recursive clients/apple + fetch-depth: 0 + submodules: 'recursive' - name: Setup Go if: matrix.if uses: actions/setup-go@v5 with: go-version: 1.25.12 - cache: false - - name: Cache Go modules - if: matrix.if - uses: actions/cache/restore@v4 - with: - path: ~/go/pkg/mod - key: go-mod-macos-${{ hashFiles('go.sum') }} - restore-keys: go-mod-macos- - - name: Cache Go tool builds - if: matrix.if - uses: actions/cache@v4 - with: - path: ~/Library/Caches/go-build - key: go-build-tools-macos-${{ hashFiles('go.sum') }} - restore-keys: go-build-tools-macos- - name: Set tag if: matrix.if run: |- @@ -1073,140 +950,52 @@ jobs: run: |- cd clients/apple git checkout dev - - name: Cache Swift packages + - name: Setup certificates if: matrix.if - uses: actions/cache@v4 - with: - path: ${{ runner.temp }}/SourcePackages - key: apple-spm-${{ hashFiles('clients/apple/sing-box.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved') }} - restore-keys: apple-spm- - - name: Cache Xcode build data - if: matrix.if - uses: actions/cache@v4 - with: - path: ${{ matrix.type == 'standalone' && 'clients/apple/build/SFM.System-*.dd' || matrix.type == 'jailbreak' && 'clients/apple/build/jailbreak/DerivedData' || format('{0}/DerivedData', runner.temp) }} - key: apple-derived-data-${{ matrix.type }}-${{ matrix.platform }}-${{ matrix.variant || 'default' }}-${{ hashFiles('clients/apple/sing-box.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved') }}-${{ github.sha }} - restore-keys: | - apple-derived-data-${{ matrix.type }}-${{ matrix.platform }}-${{ matrix.variant || 'default' }}-${{ hashFiles('clients/apple/sing-box.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved') }}- - apple-derived-data-${{ matrix.type }}-${{ matrix.platform }}-${{ matrix.variant || 'default' }}- - - name: Setup App Store Connect key - if: matrix.if && matrix.type != 'jailbreak' && github.event_name == 'workflow_dispatch' run: |- - ASC_KEY_PATH=$RUNNER_TEMP/AuthKey.p8 + 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 + echo "ASC_KEY_PATH=$ASC_KEY_PATH" >> "$GITHUB_ENV" echo "ASC_KEY_ID=$ASC_KEY_ID" >> "$GITHUB_ENV" echo "ASC_KEY_ISSUER_ID=$ASC_KEY_ISSUER_ID" >> "$GITHUB_ENV" env: - ASC_KEY: ${{ secrets.LEGACY_ASC_KEY }} - ASC_KEY_ID: ${{ secrets.LEGACY_ASC_KEY_ID }} - ASC_KEY_ISSUER_ID: ${{ secrets.LEGACY_ASC_KEY_ISSUER_ID }} - - name: Install Apple provisioning profiles - if: matrix.if && matrix.type != 'jailbreak' - run: |- - PROFILES_ZIP_PATH=$RUNNER_TEMP/AppleProvisioningProfiles.zip - for variable in $(compgen -v | grep -E '^PROVISIONING_PROFILES_[0-9]+$' | sort -t_ -k3 -n); do - printf '%s' "${!variable}" - done | base64 --decode -o $PROFILES_ZIP_PATH - PROFILES_PATH="$HOME/Library/Developer/Xcode/UserData/Provisioning Profiles" - mkdir -p "$PROFILES_PATH" - unzip -q $PROFILES_ZIP_PATH -d "$PROFILES_PATH" - env: - PROVISIONING_PROFILES_1: ${{ secrets.LEGACY_APPLE_PROVISIONING_PROFILES_1 }} - PROVISIONING_PROFILES_2: ${{ secrets.LEGACY_APPLE_PROVISIONING_PROFILES_2 }} - PROVISIONING_PROFILES_3: ${{ secrets.LEGACY_APPLE_PROVISIONING_PROFILES_3 }} - PROVISIONING_PROFILES_4: ${{ secrets.LEGACY_APPLE_PROVISIONING_PROFILES_4 }} - PROVISIONING_PROFILES_5: ${{ secrets.LEGACY_APPLE_PROVISIONING_PROFILES_5 }} - PROVISIONING_PROFILES_6: ${{ secrets.LEGACY_APPLE_PROVISIONING_PROFILES_6 }} - PROVISIONING_PROFILES_7: ${{ secrets.LEGACY_APPLE_PROVISIONING_PROFILES_7 }} - PROVISIONING_PROFILES_8: ${{ secrets.LEGACY_APPLE_PROVISIONING_PROFILES_8 }} - PROVISIONING_PROFILES_9: ${{ secrets.LEGACY_APPLE_PROVISIONING_PROFILES_9 }} - PROVISIONING_PROFILES_10: ${{ secrets.LEGACY_APPLE_PROVISIONING_PROFILES_10 }} - PROVISIONING_PROFILES_11: ${{ secrets.LEGACY_APPLE_PROVISIONING_PROFILES_11 }} - PROVISIONING_PROFILES_12: ${{ secrets.LEGACY_APPLE_PROVISIONING_PROFILES_12 }} - PROVISIONING_PROFILES_13: ${{ secrets.LEGACY_APPLE_PROVISIONING_PROFILES_13 }} - PROVISIONING_PROFILES_14: ${{ secrets.LEGACY_APPLE_PROVISIONING_PROFILES_14 }} - PROVISIONING_PROFILES_15: ${{ secrets.LEGACY_APPLE_PROVISIONING_PROFILES_15 }} - PROVISIONING_PROFILES_16: ${{ secrets.LEGACY_APPLE_PROVISIONING_PROFILES_16 }} - - name: Setup Apple Development certificate - if: matrix.if && matrix.type != 'jailbreak' - run: |- - CERTIFICATE_PATH=$RUNNER_TEMP/AppleDevelopment.p12 - KEYCHAIN_PATH=$RUNNER_TEMP/apple-development.keychain-db - echo -n "$APPLE_DEVELOPMENT_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-keychains -d user -s $KEYCHAIN_PATH - security default-keychain -d user -s $KEYCHAIN_PATH - security find-identity -v -p codesigning $KEYCHAIN_PATH - echo "APPLE_DEVELOPMENT_KEYCHAIN_PATH=$KEYCHAIN_PATH" >> "$GITHUB_ENV" - env: - APPLE_DEVELOPMENT_CERTIFICATES_P12: ${{ secrets.LEGACY_APPLE_DEVELOPMENT_CERTIFICATES_P12 }} - P12_PASSWORD: ${{ secrets.LEGACY_APPLE_DEVELOPMENT_P12_PASSWORD }} - KEYCHAIN_PASSWORD: ${{ secrets.APPLE_KEYCHAIN_PASSWORD }} - - name: Setup Developer ID certificates - if: matrix.if && matrix.type == 'standalone' - run: |- - CERTIFICATE_PATH=$RUNNER_TEMP/DeveloperIDCertificates.p12 - KEYCHAIN_PATH=$RUNNER_TEMP/developer-id.keychain-db - echo -n "$DEVELOPER_ID_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-keychains -d user -s $KEYCHAIN_PATH $APPLE_DEVELOPMENT_KEYCHAIN_PATH - security default-keychain -d user -s $KEYCHAIN_PATH - security find-identity -v -p codesigning $KEYCHAIN_PATH - echo "SIGNING_KEYCHAIN_PATH=$KEYCHAIN_PATH" >> "$GITHUB_ENV" - env: - DEVELOPER_ID_CERTIFICATES_P12: ${{ secrets.LEGACY_DEVELOPER_ID_CERTIFICATES_P12 }} - P12_PASSWORD: ${{ secrets.LEGACY_DEVELOPER_ID_P12_PASSWORD }} - KEYCHAIN_PASSWORD: ${{ secrets.APPLE_KEYCHAIN_PASSWORD }} - - name: Setup notarization credentials - if: matrix.if && matrix.type == 'standalone' && github.event_name == 'workflow_dispatch' - run: |- - security unlock-keychain -p "$KEYCHAIN_PASSWORD" $SIGNING_KEYCHAIN_PATH - xcrun notarytool store-credentials "notarytool-password" \ - --key $ASC_KEY_PATH \ - --key-id $ASC_KEY_ID \ - --issuer $ASC_KEY_ISSUER_ID \ - --keychain $SIGNING_KEYCHAIN_PATH - env: - KEYCHAIN_PASSWORD: ${{ secrets.APPLE_KEYCHAIN_PASSWORD }} - - name: Install jailbreak packaging tools - if: matrix.if && matrix.type == 'jailbreak' - run: |- - brew install dpkg ldid - - name: Trust build plugins - if: matrix.if && matrix.type != 'jailbreak' - run: |- - defaults write com.apple.dt.Xcode IDESkipPackagePluginFingerprintValidatation -bool YES - - name: Download library - if: matrix.if - uses: actions/download-artifact@v4 - with: - pattern: ${{ matrix.type == 'standalone' && format('libbox-apple-macos-{0}', matrix.library_arch) || format('libbox-apple-{0}-*', matrix.platform) }} - path: libbox-inputs - merge-multiple: true - - name: Prepare library + 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: Build library if: matrix.if run: |- - mkdir -p libbox-slices - library_inputs=() - for library_archive in libbox-inputs/*.tar; do - slice_name=$(basename "$library_archive" .tar) - slice_path="libbox-slices/$slice_name" - mkdir -p "$slice_path" - tar -xf "$library_archive" -C "$slice_path" - library_inputs+=("$slice_path/Libbox.xcframework") - done - go run ./cmd/internal/merge_apple_xcframework \ - -output clients/apple/Libbox.xcframework \ - "${library_inputs[@]}" + make lib_install + export PATH="$PATH:$(go env GOPATH)/bin" + go run ./cmd/internal/build_libbox -target apple -platform ${{ matrix.platform }} + mv Libbox.xcframework clients/apple - name: Update macOS version if: matrix.if && matrix.name == 'macOS' && github.event_name == 'workflow_dispatch' run: |- @@ -1217,151 +1006,83 @@ jobs: if: matrix.if && matrix.name != 'iOS' run: |- go run -v ./cmd/internal/update_apple_version --ci - - name: Archive App Store build - if: matrix.if && matrix.type == 'app-store' + - name: Build + if: matrix.if run: |- cd clients/apple xcodebuild archive \ - -clonedSourcePackagesDirPath "$RUNNER_TEMP/SourcePackages" \ - -derivedDataPath "$RUNNER_TEMP/DerivedData" \ -scheme "${{ matrix.scheme }}" \ -configuration Release \ -destination "${{ matrix.destination }}" \ - -archivePath "${{ matrix.archive }}" - env: - DISABLE_SWIFTLINT: "1" + -archivePath "${{ matrix.archive }}" \ + -allowProvisioningUpdates \ + -authenticationKeyPath $ASC_KEY_PATH \ + -authenticationKeyID $ASC_KEY_ID \ + -authenticationKeyIssuerID $ASC_KEY_ISSUER_ID - name: Upload to App Store Connect - if: matrix.if && matrix.type == 'app-store' && github.event_name == 'workflow_dispatch' + if: matrix.if && matrix.name != 'macOS-standalone' && github.event_name == 'workflow_dispatch' run: |- go run -v ./cmd/internal/app_store_connect cancel_app_store ${{ matrix.platform }} cd clients/apple xcodebuild -exportArchive \ -archivePath "${{ matrix.archive }}" \ -exportOptionsPlist ${{ matrix.upload }} \ + -allowProvisioningUpdates \ -authenticationKeyPath $ASC_KEY_PATH \ -authenticationKeyID $ASC_KEY_ID \ -authenticationKeyIssuerID $ASC_KEY_ISSUER_ID - name: Publish to TestFlight - if: matrix.if && matrix.type == 'app-store' && github.event_name == 'workflow_dispatch' && github.ref == 'refs/heads/testing' + if: matrix.if && matrix.name != 'macOS-standalone' && github.event_name == 'workflow_dispatch' && github.ref =='refs/heads/testing' run: |- - arguments=(publish_testflight "${{ matrix.platform }}") - if [[ -n "$TEST_MESSAGE" ]]; then - arguments+=("$TEST_MESSAGE") - fi - go run -v ./cmd/internal/app_store_connect "${arguments[@]}" - env: - TEST_MESSAGE: ${{ inputs.test_message }} - - name: Build standalone packages - if: matrix.if && matrix.type == 'standalone' + go run -v ./cmd/internal/app_store_connect publish_testflight ${{ matrix.platform }} + - name: Build image + if: matrix.if && matrix.name == 'macOS-standalone' && github.event_name == 'workflow_dispatch' run: |- - make -C clients/apple build_macos_pkg_${{ matrix.variant }} - env: - DISABLE_SWIFTLINT: "1" - XCODEBUILD_FLAGS: -clonedSourcePackagesDirPath ${{ runner.temp }}/SourcePackages - - name: Notarize standalone packages - if: matrix.if && matrix.type == 'standalone' && github.event_name == 'workflow_dispatch' - run: |- - make -C clients/apple notarize_macos_pkg_${{ matrix.variant }} - - name: Prepare standalone packages - if: matrix.if && matrix.type == 'standalone' - run: |- - mkdir -p dist - for label in Apple Intel Universal; do - cp "clients/apple/build/SFM-${label}.pkg" "dist/SFM-${VERSION}-${label}.pkg" - done - pushd clients/apple/build/SFM.System-universal.xcarchive + 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 - cp clients/apple/build/SFM.System-universal.xcarchive/SFM.dSYMs.zip "dist/SFM-${VERSION}.dSYMs.zip" - - name: Build jailbreak package - if: matrix.if && matrix.type == 'jailbreak' - run: |- - make -C clients/apple build_ios_deb + mkdir -p dist - cp "clients/apple/build/jailbreak/SFI-${VERSION}-iphoneos-arm64.deb" dist - env: - XCODEBUILD_CLONED_SOURCE_PACKAGES_DIR_PATH: ${{ runner.temp }}/SourcePackages - - name: Report cache directory sizes - if: matrix.if - run: |- - du -sh "$RUNNER_TEMP/SourcePackages"/* 2>/dev/null || true - du -sh "$RUNNER_TEMP/DerivedData" 2>/dev/null || true - du -sh clients/apple/build/jailbreak/DerivedData 2>/dev/null || true - du -sh clients/apple/build/SFM.System-*.dd 2>/dev/null || true - - name: Prune Swift package repositories - if: matrix.if - run: rm -rf "$RUNNER_TEMP/SourcePackages/repositories" - - name: Upload release artifact - if: matrix.if && (matrix.type == 'standalone' || matrix.type == 'jailbreak') + cp clients/apple/SFM.dmg "dist/SFM-${VERSION}-universal.dmg" + cp "clients/apple/${{ matrix.archive }}/SFM.dSYMs.zip" "dist/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-apple-${{ matrix.type }}${{ matrix.variant && format('-{0}', matrix.variant) || '' }} + name: binary-macos-dmg path: 'dist' - build_apple_app_store: - name: Build Apple App Store clients - runs-on: ${{ matrix.if && 'macos-26' || 'ubuntu-latest' }} - if: github.event_name == 'workflow_dispatch' && (inputs.build == 'All' || inputs.build == 'Apple' || inputs.build == 'iOS' || inputs.build == 'macOS' || inputs.build == 'tvOS') - needs: - - calculate_version - - build_apple_library - strategy: - fail-fast: false - matrix: - include: - - name: iOS - if: ${{ inputs.build == 'All' || inputs.build == 'Apple' || inputs.build == 'iOS' }} - type: app-store - platform: ios - scheme: SFI - destination: 'generic/platform=iOS' - archive: build/SFI.xcarchive - upload: SFI/Upload.plist - variant: '' - library_arch: '' - - name: macOS - if: ${{ inputs.build == 'All' || inputs.build == 'Apple' || inputs.build == 'macOS' }} - type: app-store - platform: macos - scheme: SFM - destination: 'generic/platform=macOS' - archive: build/SFM.xcarchive - upload: SFI/Upload.plist - variant: '' - library_arch: '' - - name: tvOS - if: ${{ inputs.build == 'All' || inputs.build == 'Apple' || inputs.build == 'tvOS' }} - type: app-store - platform: tvos - scheme: SFT - destination: 'generic/platform=tvOS' - archive: build/SFT.xcarchive - upload: SFI/Upload.plist - variant: '' - library_arch: '' - steps: *build_apple_steps upload: name: Upload builds - if: "!failure() && github.event_name == 'workflow_dispatch' && (inputs.build == 'All' || inputs.build == 'Binary' || inputs.build == 'Android' || inputs.build == 'Apple-release' || inputs.build == 'macOS-standalone')" + if: "!failure() && github.event_name == 'workflow_dispatch' && (inputs.build == 'All' || inputs.build == 'Binary' || inputs.build == 'Android' || inputs.build == 'Windows' || inputs.build == 'Apple' || inputs.build == 'macOS-standalone')" runs-on: ubuntu-latest - permissions: - contents: write - id-token: write - attestations: write - artifact-metadata: write needs: - calculate_version - build - build_darwin - build_windows - - build_android_library - build_android - - build_apple_library + - build_windows_client - build_apple steps: - name: Checkout uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5 with: - fetch-depth: 1 + fetch-depth: 0 - name: Cache ghr uses: actions/cache@v4 id: cache-ghr @@ -1384,13 +1105,8 @@ jobs: - name: Download builds uses: actions/download-artifact@v5 with: - pattern: binary-* path: dist merge-multiple: true - - name: Attest build provenance - uses: actions/attest@f7c74d28b9d84cb8768d0b8ca14a4bac6ef463e6 # v4.2.0 - with: - subject-path: 'dist/**' - name: Upload builds if: ${{ env.PUBLISHED == 'false' }} run: |- @@ -1405,70 +1121,3 @@ jobs: ghr --replace -p 5 "v${VERSION}" dist env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - cache_gc: - name: Cache GC - if: always() - runs-on: ubuntu-latest - permissions: - actions: write - needs: - - build - - build_darwin - - build_windows - - build_android_library - - build_android - - build_apple_library - - build_apple - - build_apple_app_store - - upload - steps: - - name: Delete superseded caches - env: - GH_TOKEN: ${{ github.token }} - run: |- - set -euo pipefail - gh api "repos/$GITHUB_REPOSITORY/actions/caches?per_page=100" --paginate \ - --jq '.actions_caches[] | [.id, .ref, .created_at, .last_accessed_at, .size_in_bytes, .key] | @tsv' > "$RUNNER_TEMP/caches.tsv" - python3 - "$GITHUB_REPOSITORY" "$RUNNER_TEMP/caches.tsv" <<'EOF' - import re - import subprocess - import sys - - repository, list_path = sys.argv[1], sys.argv[2] - budget = 9 * 1024 * 1024 * 1024 - rows = [] - with open(list_path) as list_file: - for line in list_file: - cache_id, ref, created_at, accessed_at, size, key = line.rstrip("\n").split("\t") - family = key - while True: - stripped = re.sub(r"-[0-9a-f]{16,}$", "", family) - if stripped == family: - break - family = stripped - rows.append({ - "id": cache_id, "ref": ref, "created": created_at, - "accessed": accessed_at, "size": int(size), "key": key, - "family": family, - }) - newest = {} - for row in rows: - group = (row["ref"], row["family"]) - if group not in newest or row["created"] > newest[group]["created"]: - newest[group] = row - doomed = [row for row in rows if newest[(row["ref"], row["family"])] is not row] - kept = [row for row in rows if newest[(row["ref"], row["family"])] is row] - total = sum(row["size"] for row in kept) - for row in sorted(kept, key=lambda entry: entry["accessed"]): - if total <= budget: - break - doomed.append(row) - total -= row["size"] - for row in doomed: - print(f"delete {row['size'] >> 20}MB {row['key']} ({row['ref']})") - subprocess.run( - ["gh", "api", "-X", "DELETE", f"repos/{repository}/actions/caches/{row['id']}"], - check=False, - ) - print(f"kept {len(rows) - len(doomed)} caches, {total >> 20}MB total") - EOF diff --git a/.gitmodules b/.gitmodules index 45ffb563..cc95289e 100644 --- a/.gitmodules +++ b/.gitmodules @@ -4,3 +4,6 @@ [submodule "clients/android"] path = clients/android url = https://github.com/SagerNet/sing-box-for-android.git +[submodule "clients/desktop"] + path = clients/desktop + url = https://github.com/SagerNet/sing-box-for-desktop.git diff --git a/clients/desktop b/clients/desktop new file mode 160000 index 00000000..cebee0d5 --- /dev/null +++ b/clients/desktop @@ -0,0 +1 @@ +Subproject commit cebee0d527c4e5d5500f971553628e0dfa8bae0f diff --git a/docs/clients/desktop/features.md b/docs/clients/desktop/features.md new file mode 100644 index 00000000..bb399a03 --- /dev/null +++ b/docs/clients/desktop/features.md @@ -0,0 +1,9 @@ +# :material-decagram: Features + +#### Service + +SFW runs sing-box as a system service, so no administrator elevation is required for daily use. + +### Chore + +* The working directory is located at `C:\ProgramData\sing-box-daemon` diff --git a/docs/clients/desktop/index.md b/docs/clients/desktop/index.md new file mode 100644 index 00000000..332636eb --- /dev/null +++ b/docs/clients/desktop/index.md @@ -0,0 +1,22 @@ +--- +icon: material/laptop +--- + +# sing-box for Desktop + +SFW allows users to manage and run local or remote sing-box configuration files, and provides +platform-specific function implementation, such as TUN transparent proxy implementation. + +Linux support is on the way. + +## :material-graph: Requirements + +* Windows 10+ (x64 / x86 / arm64) + +## :material-download: Download + +* [GitHub Releases](https://github.com/SagerNet/sing-box/releases) + +## :material-source-repository: Source code + +* [GitHub](https://github.com/SagerNet/sing-box-for-desktop) diff --git a/docs/clients/index.md b/docs/clients/index.md index 45d2c9a9..c1232407 100644 --- a/docs/clients/index.md +++ b/docs/clients/index.md @@ -6,7 +6,8 @@ Maintained by Project S to provide a unified experience and platform-specific fu |---------------------------------------|------------------------------------------| | :material-android: Android | [sing-box for Android](./android/) | | :material-apple: iOS/macOS/Apple tvOS | [sing-box for Apple platforms](./apple/) | -| :material-laptop: Desktop | Working in progress | +| :material-microsoft-windows: Windows | [sing-box for Desktop](./desktop/) | +| :material-linux: Linux | Working in progress | Some third-party projects that claim to use sing-box or use sing-box as a selling point are not listed here. The core motivation of the maintainers of such projects is to acquire more users, and even though they provide friendly VPN diff --git a/docs/clients/index.zh.md b/docs/clients/index.zh.md index 736b42ea..d2fbb4d4 100644 --- a/docs/clients/index.zh.md +++ b/docs/clients/index.zh.md @@ -6,7 +6,8 @@ |---------------------------------------|------------------------------------------| | :material-android: Android | [sing-box for Android](./android/) | | :material-apple: iOS/macOS/Apple tvOS | [sing-box for Apple platforms](./apple/) | -| :material-laptop: Desktop | 施工中 | +| :material-microsoft-windows: Windows | [sing-box for Desktop](./desktop/) | +| :material-linux: Linux | 施工中 | 此处没有列出一些声称使用或以 sing-box 为卖点的第三方项目。此类项目维护者的动机是获得更多用户,即使它们提供友好的商业 VPN 客户端功能, 但代码质量很差且包含广告。 diff --git a/mkdocs.yml b/mkdocs.yml index 6b724da1..f8b6318a 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -62,6 +62,9 @@ nav: - Apple platforms: - clients/apple/index.md - Features: clients/apple/features.md + - Desktop: + - clients/desktop/index.md + - Features: clients/desktop/features.md - General: clients/general.md - Privacy policy: clients/privacy.md - Manual: