Upstream watch #4
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Upstream watch | |
| # Daily sweep: queries the Go module proxy for the latest tags of the | |
| # three core deps (mihomo, sing-box, xray-core). If any have moved | |
| # beyond what's pinned in go/go.mod, bump them, rebuild the aar, tag a | |
| # v<YYYY.MM.DD> release, and publish the aar as a GitHub Release | |
| # asset. Multiple bumps on the same day are batched into one release. | |
| # Same-day re-runs (manual force) append .1, .2… to the tag. | |
| on: | |
| schedule: | |
| - cron: '0 8 * * *' # 08:00 UTC daily | |
| workflow_dispatch: | |
| inputs: | |
| force_release: | |
| description: "Cut a release even if no upstream change is detected" | |
| type: boolean | |
| default: false | |
| concurrency: | |
| group: upstream-watch | |
| cancel-in-progress: false | |
| permissions: | |
| contents: write | |
| jobs: | |
| watch-and-release: | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 60 | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v4 | |
| with: | |
| token: ${{ secrets.GITHUB_TOKEN }} | |
| fetch-depth: 0 | |
| persist-credentials: true | |
| - name: Set up Go | |
| uses: actions/setup-go@v5 | |
| with: | |
| go-version-file: go/go.mod | |
| cache: true | |
| cache-dependency-path: go/go.sum | |
| - name: Point gomobile at the runner's NDK | |
| run: | | |
| set -euo pipefail | |
| # ubuntu-latest images ship an Android SDK + NDK; gomobile | |
| # resolves the NDK from ANDROID_NDK_HOME. | |
| echo "ANDROID_NDK_HOME=$ANDROID_NDK_LATEST_HOME" >> "$GITHUB_ENV" | |
| - name: Install gomobile | |
| run: | | |
| set -euo pipefail | |
| go install golang.org/x/mobile/cmd/gomobile@latest | |
| go install golang.org/x/mobile/cmd/gobind@latest | |
| echo "$(go env GOPATH)/bin" >> "$GITHUB_PATH" | |
| - name: Detect upstream versions | |
| id: versions | |
| working-directory: go | |
| run: | | |
| set -euo pipefail | |
| # The Go proxy's /@latest endpoint is defined to return the | |
| # newest *stable* tag (no pre-releases) for a module. We | |
| # query it directly rather than `go list -m -versions`, which | |
| # would happily include `v1.14.0-alpha.23` in its output. | |
| # For xray-core this returns the Go-module-compatible | |
| # v1.YYMMDD.0 tag, not the human v26.x.x. | |
| latest() { | |
| curl -sSf "https://proxy.golang.org/$1/@latest" | jq -r '.Version' | |
| } | |
| # Current pin = the version go.mod's require block resolves to. | |
| current() { go list -m -f '{{.Version}}' "$1"; } | |
| mihomo_latest=$(latest github.com/metacubex/mihomo) | |
| singbox_latest=$(latest github.com/sagernet/sing-box) | |
| xray_latest=$(latest github.com/xtls/xray-core) | |
| mihomo_current=$(current github.com/metacubex/mihomo) | |
| singbox_current=$(current github.com/sagernet/sing-box) | |
| xray_current=$(current github.com/xtls/xray-core) | |
| { | |
| echo "### Upstream pin check" | |
| echo | |
| echo "| module | current | latest | bump? |" | |
| echo "|---|---|---|---|" | |
| for trio in \ | |
| "mihomo|$mihomo_current|$mihomo_latest" \ | |
| "sing-box|$singbox_current|$singbox_latest" \ | |
| "xray-core|$xray_current|$xray_latest"; do | |
| IFS='|' read -r name cur lat <<<"$trio" | |
| [[ "$cur" == "$lat" ]] && marker="—" || marker="✓" | |
| echo "| $name | $cur | $lat | $marker |" | |
| done | |
| } >> "$GITHUB_STEP_SUMMARY" | |
| changed=false | |
| [[ "$mihomo_current" != "$mihomo_latest" ]] && changed=true | |
| [[ "$singbox_current" != "$singbox_latest" ]] && changed=true | |
| [[ "$xray_current" != "$xray_latest" ]] && changed=true | |
| { | |
| echo "mihomo_latest=$mihomo_latest" | |
| echo "singbox_latest=$singbox_latest" | |
| echo "xray_latest=$xray_latest" | |
| echo "mihomo_current=$mihomo_current" | |
| echo "singbox_current=$singbox_current" | |
| echo "xray_current=$xray_current" | |
| echo "changed=$changed" | |
| } >> "$GITHUB_OUTPUT" | |
| - name: Decide whether to proceed | |
| id: proceed | |
| run: | | |
| if [[ "${{ steps.versions.outputs.changed }}" == "true" \ | |
| || "${{ inputs.force_release }}" == "true" ]]; then | |
| echo "go=true" >> "$GITHUB_OUTPUT" | |
| else | |
| echo "go=false" >> "$GITHUB_OUTPUT" | |
| echo "::notice::No upstream changes; skipping release." | |
| fi | |
| - name: Bump go.mod | |
| if: steps.proceed.outputs.go == 'true' | |
| working-directory: go | |
| run: | | |
| set -euo pipefail | |
| go get github.com/metacubex/mihomo@${{ steps.versions.outputs.mihomo_latest }} | |
| go get github.com/sagernet/sing-box@${{ steps.versions.outputs.singbox_latest }} | |
| go get github.com/xtls/xray-core@${{ steps.versions.outputs.xray_latest }} | |
| go mod tidy | |
| - name: Build aar | |
| if: steps.proceed.outputs.go == 'true' | |
| run: Scripts/build.sh | |
| - name: Compute release tag | |
| if: steps.proceed.outputs.go == 'true' | |
| id: tag | |
| run: | | |
| set -euo pipefail | |
| base="v$(date -u +%Y.%m.%d)" | |
| tag="$base" | |
| n=1 | |
| # Already taken? Append .1, .2, … (manual force on the same day). | |
| while git rev-parse --verify --quiet "refs/tags/$tag" >/dev/null; do | |
| n=$((n+1)) | |
| tag="$base.$n" | |
| done | |
| echo "tag=$tag" >> "$GITHUB_OUTPUT" | |
| - name: Checksum | |
| if: steps.proceed.outputs.go == 'true' | |
| id: artifact | |
| run: | | |
| set -euo pipefail | |
| aar_name="EverywhereCore-${{ steps.tag.outputs.tag }}.aar" | |
| mv EverywhereCore.aar "$aar_name" | |
| sha256sum "$aar_name" > "$aar_name.sha256" | |
| echo "aar=$aar_name" >> "$GITHUB_OUTPUT" | |
| - name: Commit + tag + push | |
| if: steps.proceed.outputs.go == 'true' | |
| run: | | |
| set -euo pipefail | |
| git config user.name "github-actions[bot]" | |
| git config user.email "41898282+github-actions[bot]@users.noreply.github.com" | |
| git add go/go.mod go/go.sum | |
| git commit -m "release ${{ steps.tag.outputs.tag }} | |
| mihomo: ${{ steps.versions.outputs.mihomo_current }} → ${{ steps.versions.outputs.mihomo_latest }} | |
| sing-box: ${{ steps.versions.outputs.singbox_current }} → ${{ steps.versions.outputs.singbox_latest }} | |
| xray-core: ${{ steps.versions.outputs.xray_current }} → ${{ steps.versions.outputs.xray_latest }}" | |
| git tag -a "${{ steps.tag.outputs.tag }}" -m "${{ steps.tag.outputs.tag }}" | |
| # Tag + main in a single push. HEAD is detached at the | |
| # checked-out SHA; HEAD:main pushes it to the main branch. | |
| git push origin "HEAD:${{ github.event.repository.default_branch }}" "${{ steps.tag.outputs.tag }}" | |
| - name: Create GitHub Release | |
| if: steps.proceed.outputs.go == 'true' | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| run: | | |
| set -euo pipefail | |
| notes=$(cat <<EOF | |
| ### Upstream bumps | |
| - **mihomo:** \`${{ steps.versions.outputs.mihomo_current }}\` → \`${{ steps.versions.outputs.mihomo_latest }}\` | |
| - **sing-box:** \`${{ steps.versions.outputs.singbox_current }}\` → \`${{ steps.versions.outputs.singbox_latest }}\` | |
| - **xray-core:** \`${{ steps.versions.outputs.xray_current }}\` → \`${{ steps.versions.outputs.xray_latest }}\` | |
| ### Consume | |
| Download \`${{ steps.artifact.outputs.aar }}\`, drop it into | |
| \`app/libs/\`, and add \`implementation(files("libs/${{ steps.artifact.outputs.aar }}"))\`. | |
| EOF | |
| ) | |
| gh release create "${{ steps.tag.outputs.tag }}" \ | |
| "${{ steps.artifact.outputs.aar }}" \ | |
| "${{ steps.artifact.outputs.aar }}.sha256" \ | |
| --title "${{ steps.tag.outputs.tag }}" \ | |
| --notes "$notes" |