clash-verge-rev/clash-verge-rev

13 workflows · maturity 67% · 10 patterns · GitHub ↗

Security 17.31/100

Practices

✓ Matrix✓ Permissions○ Security scan○ AI review✓ Cache✓ Concurrency✓ Reusable workflows

Detected patterns

Security dimensions

permissions
17.3
security scan
0
supply chain
0
secret handling
0
harden runner
0

Workflows (13)

autobuild matrix perms .github/workflows/autobuild.yml
Triggers
workflow_dispatch, schedule
Runs on
ubuntu-latest, ${{ matrix.os }}, ${{ matrix.os }}, ${{ matrix.os }}, ubuntu-latest
Jobs
check_commit, update_tag, clean_old_assets, autobuild-x86-windows-macos-linux, autobuild-arm-linux, autobuild-x86-arm-windows_webview2, notify-telegram
Matrix
include, include.arch, include.os, include.target→ aarch64-apple-darwin, aarch64-pc-windows-msvc, aarch64-unknown-linux-gnu, arm64, armhf, armv7-unknown-linux-gnueabihf, macos-latest, ubuntu-22.04, windows-latest, x64, x86_64-apple-darwin, x86_64-pc-windows-msvc, x86_64-unknown-linux-gnu
Actions
pnpm/action-setup, softprops/action-gh-release, dtolnay/rust-toolchain, Swatinem/rust-cache, pnpm/action-setup, tauri-apps/tauri-action, dtolnay/rust-toolchain, Swatinem/rust-cache, pnpm/action-setup, softprops/action-gh-release, Swatinem/rust-cache, pnpm/action-setup, tauri-apps/tauri-action, softprops/action-gh-release, pnpm/action-setup
Commands
  • bash ./scripts/extract_update_logs.sh
  • pnpm install --frozen-lockfile
  • pnpm release-version autobuild-latest
  • echo "BUILDTIME=$(TZ=Asia/Shanghai date)" >> $GITHUB_ENV VERSION=$(jq -r .version package.json) echo "VERSION=$VERSION" >> $GITHUB_ENV echo "DOWNLOAD_URL=https://github.com/clash-verge-rev/clash-verge-rev/releases/download/autobuild" >> $GITHUB_ENV
  • if [ -z "$UPDATE_LOGS" ]; then echo "No update logs found, using default message" UPDATE_LOGS="More new features are now supported. Check for detailed changelog soon." else echo "Using found update logs" fi cat > release.txt << EOF $UPDATE_LOGS ## 下载地址 ### Windows (不再支持Win7) #### 正常版本(推荐) - [64位(常用)](${{ env.DOWNLOAD_URL }}/Clash.Verge_${{ env.VERSION }}_x64-setup.exe) | [ARM64(不常用)](${{ env.DOWNLOAD_URL }}/Clash.Verge_${{ env.VERSION }}_arm64-setup.exe) #### 内置Webview2版(体积较大,仅在企业版系统或无法安装webview2时使用) - [64位](${{ env.DOWNLOAD_URL }}/Clash.Verge_${{ env.VERSION }}_x64_fixed_webview2-setup.exe) | [ARM64](${{ env.DOWNLOAD_URL }}/Clash.Verge_${{ env.VERSION }}_arm64_fixed_webview2-setup.exe) ### macOS - [Apple M芯片](${{ env.DOWNLOAD_URL }}/Clash.Verge_${{ env.VERSION }}_aarch64.dmg) | [Intel芯片](${{ env.DOWNLOAD_URL }}/Clash.Verge_${{ env.VERSION }}_x64.dmg) ### Linux #### DEB包(Debian系) 使用 apt ./路径 安装 - [64位](${{ env.DOWNLOAD_URL }}/Clash.Verge_${{ env.VERSION }}_amd64.deb) | [ARM64](${{ env.DOWNLOAD_URL }}/Clash.Verge_${{ env.VERSION }}_arm64.deb) | [ARMv7](${{ env.DOWNLOAD_URL }}/Clash.Verge_${{ env.VERSION }}_armhf.deb) #### RPM包(Redhat系) 使用 dnf ./路径 安装 - [64位](${{ env.DOWNLOAD_URL }}/Clash.Verge-${{ env.VERSION }}-1.x86_64.rpm) | [ARM64](${{ env.DOWNLOAD_URL }}/Clash.Verge-${{ env.VERSION }}-1.aarch64.rpm) | [ARMv7](${{ env.DOWNLOAD_URL }}/Clash.Verge-${{ env.VERSION }}-1.armhfp.rpm) ### FAQ - [常见问题](https://clash-verge-rev.github.io/faq/windows.html) ### 稳定机场VPN推荐 - [狗狗加速](https://verge.dginv.click/#/register?code=oaxsAGo6) Created at ${{ env.BUILDTIME }}. EOF
  • rustup target add ${{ matrix.target }}
  • sudo apt-get update sudo apt-get install -y libxslt1.1 libwebkit2gtk-4.1-dev libayatana-appindicator3-dev librsvg2-dev patchelf
  • arch -x86_64 brew install openssl@3 echo "OPENSSL_DIR=$(brew --prefix openssl@3)" >> $GITHUB_ENV echo "OPENSSL_INCLUDE_DIR=$(brew --prefix openssl@3)/include" >> $GITHUB_ENV echo "OPENSSL_LIB_DIR=$(brew --prefix openssl@3)/lib" >> $GITHUB_ENV echo "PKG_CONFIG_PATH=$(brew --prefix openssl@3)/lib/pkgconfig" >> $GITHUB_ENV
View raw YAML
name: Auto Build

on:
  workflow_dispatch:
  schedule:
    # UTC+8 12:00, 18:00 -> UTC 4:00, 10:00
    - cron: '0 4,10 * * *'
permissions: write-all
env:
  TAG_NAME: autobuild
  TAG_CHANNEL: AutoBuild
  CARGO_INCREMENTAL: 0
  RUST_BACKTRACE: short
  HUSKY: 0
concurrency:
  group: '${{ github.workflow }} - ${{ github.head_ref || github.ref }}'
  cancel-in-progress: ${{ github.ref != 'refs/heads/main' }}

jobs:
  check_commit:
    name: Check Commit Needs Build
    uses: clash-verge-rev/clash-verge-rev/.github/workflows/check-commit-needs-build.yml@dev
    with:
      tag_name: autobuild
      force_build: ${{ github.event_name == 'workflow_dispatch' }}

  update_tag:
    name: Update tag
    runs-on: ubuntu-latest
    needs: check_commit
    if: ${{ needs.check_commit.outputs.should_run == 'true' }}
    steps:
      - name: Checkout repository
        uses: actions/checkout@v6

      - name: Fetch UPDATE logs
        id: fetch_update_logs
        run: bash ./scripts/extract_update_logs.sh
        shell: bash

      - uses: pnpm/action-setup@v5.0.0
        name: Install pnpm
        with:
          run_install: false

      - name: Install Node
        uses: actions/setup-node@v6
        with:
          node-version: '24.14.1'

      - name: Install dependencies
        run: pnpm install --frozen-lockfile

      - name: Release AutoBuild Version
        run: pnpm release-version autobuild-latest

      - name: Set Env
        run: |
          echo "BUILDTIME=$(TZ=Asia/Shanghai date)" >> $GITHUB_ENV
          VERSION=$(jq -r .version package.json)
          echo "VERSION=$VERSION" >> $GITHUB_ENV
          echo "DOWNLOAD_URL=https://github.com/clash-verge-rev/clash-verge-rev/releases/download/autobuild" >> $GITHUB_ENV
        shell: bash

      - run: |
          if [ -z "$UPDATE_LOGS" ]; then
            echo "No update logs found, using default message"
            UPDATE_LOGS="More new features are now supported. Check for detailed changelog soon."
          else
            echo "Using found update logs"
          fi

          cat > release.txt << EOF
          $UPDATE_LOGS

          ## 下载地址

          ### Windows (不再支持Win7)
          #### 正常版本(推荐)
          - [64位(常用)](${{ env.DOWNLOAD_URL }}/Clash.Verge_${{ env.VERSION }}_x64-setup.exe) | [ARM64(不常用)](${{ env.DOWNLOAD_URL }}/Clash.Verge_${{ env.VERSION }}_arm64-setup.exe)

          #### 内置Webview2版(体积较大,仅在企业版系统或无法安装webview2时使用)
          - [64位](${{ env.DOWNLOAD_URL }}/Clash.Verge_${{ env.VERSION }}_x64_fixed_webview2-setup.exe) | [ARM64](${{ env.DOWNLOAD_URL }}/Clash.Verge_${{ env.VERSION }}_arm64_fixed_webview2-setup.exe)

          ### macOS
          - [Apple M芯片](${{ env.DOWNLOAD_URL }}/Clash.Verge_${{ env.VERSION }}_aarch64.dmg) | [Intel芯片](${{ env.DOWNLOAD_URL }}/Clash.Verge_${{ env.VERSION }}_x64.dmg)

          ### Linux
          #### DEB包(Debian系) 使用 apt ./路径 安装
          - [64位](${{ env.DOWNLOAD_URL }}/Clash.Verge_${{ env.VERSION }}_amd64.deb) | [ARM64](${{ env.DOWNLOAD_URL }}/Clash.Verge_${{ env.VERSION }}_arm64.deb) | [ARMv7](${{ env.DOWNLOAD_URL }}/Clash.Verge_${{ env.VERSION }}_armhf.deb)

          #### RPM包(Redhat系) 使用 dnf ./路径 安装
          - [64位](${{ env.DOWNLOAD_URL }}/Clash.Verge-${{ env.VERSION }}-1.x86_64.rpm) | [ARM64](${{ env.DOWNLOAD_URL }}/Clash.Verge-${{ env.VERSION }}-1.aarch64.rpm) | [ARMv7](${{ env.DOWNLOAD_URL }}/Clash.Verge-${{ env.VERSION }}-1.armhfp.rpm)

          ### FAQ
          - [常见问题](https://clash-verge-rev.github.io/faq/windows.html)

          ### 稳定机场VPN推荐
          - [狗狗加速](https://verge.dginv.click/#/register?code=oaxsAGo6)

          Created at ${{ env.BUILDTIME }}.
          EOF

      - name: Upload Release
        uses: softprops/action-gh-release@v2
        with:
          tag_name: ${{ env.TAG_NAME }}
          name: 'Clash Verge Rev ${{ env.TAG_CHANNEL }}'
          body_path: release.txt
          prerelease: true
          token: ${{ secrets.GITHUB_TOKEN }}
          generate_release_notes: false

  clean_old_assets:
    name: Clean Old Release Assets
    needs: [check_commit, update_tag]
    if: ${{ needs.check_commit.outputs.should_run == 'true' && needs.update_tag.result == 'success' }}

    uses: clash-verge-rev/clash-verge-rev/.github/workflows/clean-old-assets.yml@dev
    with:
      tag_name: autobuild
      dry_run: false

  autobuild-x86-windows-macos-linux:
    name: Autobuild x86 Windows, MacOS and Linux
    needs: [check_commit, update_tag]
    if: ${{ needs.check_commit.outputs.should_run == 'true' }}
    strategy:
      fail-fast: false
      matrix:
        include:
          - os: windows-latest
            target: x86_64-pc-windows-msvc
          - os: windows-latest
            target: aarch64-pc-windows-msvc
          - os: macos-latest
            target: aarch64-apple-darwin
          - os: macos-latest
            target: x86_64-apple-darwin
          - os: ubuntu-22.04
            target: x86_64-unknown-linux-gnu
    runs-on: ${{ matrix.os }}
    steps:
      - name: Checkout Repository
        uses: actions/checkout@v6

      - name: Install Rust Stable
        uses: dtolnay/rust-toolchain@master
        with:
          toolchain: '1.91.0'
          targets: ${{ matrix.target }}

      - name: Add Rust Target
        run: rustup target add ${{ matrix.target }}

      - name: Rust Cache
        uses: Swatinem/rust-cache@v2
        with:
          save-if: ${{ github.ref == 'refs/heads/dev' }}
          prefix-key: 'v1-rust'
          key: 'rust-shared-stable-${{ matrix.os }}-${{ matrix.target }}'
          workspaces: |
            . -> target
          cache-all-crates: true
          cache-workspace-crates: true

      - name: Install dependencies (ubuntu only)
        if: matrix.os == 'ubuntu-22.04'
        run: |
          sudo apt-get update
          sudo apt-get install -y libxslt1.1 libwebkit2gtk-4.1-dev libayatana-appindicator3-dev librsvg2-dev patchelf

      - name: Install x86 OpenSSL (macOS only)
        if: matrix.target == 'x86_64-apple-darwin'
        run: |
          arch -x86_64 brew install openssl@3
          echo "OPENSSL_DIR=$(brew --prefix openssl@3)" >> $GITHUB_ENV
          echo "OPENSSL_INCLUDE_DIR=$(brew --prefix openssl@3)/include" >> $GITHUB_ENV
          echo "OPENSSL_LIB_DIR=$(brew --prefix openssl@3)/lib" >> $GITHUB_ENV
          echo "PKG_CONFIG_PATH=$(brew --prefix openssl@3)/lib/pkgconfig" >> $GITHUB_ENV

      - uses: pnpm/action-setup@v5.0.0
        name: Install pnpm
        with:
          run_install: false

      - name: Install Node
        uses: actions/setup-node@v6
        with:
          node-version: '24.14.1'
          cache: 'pnpm'

      - name: Pnpm Cache
        uses: actions/cache@v5
        with:
          path: ~/.pnpm-store
          key: 'pnpm-shared-stable-${{ matrix.os }}-${{ matrix.target }}'
          restore-keys: |
            pnpm-shared-stable-${{ matrix.os }}-${{ matrix.target }}

      - name: Pnpm install and check
        run: |
          pnpm i
          pnpm run prebuild ${{ matrix.target }}

      - name: Release ${{ env.TAG_CHANNEL }} Version
        run: pnpm release-version autobuild-latest

      - name: Add Rust Target
        run: |
          # Ensure cross target is installed for the pinned toolchain; fallback without explicit toolchain if needed
          rustup target add ${{ matrix.target }} --toolchain 1.91.0 || rustup target add ${{ matrix.target }}
          rustup target list --installed
          echo "Rust target ${{ matrix.target }} installed."

      - name: Tauri build for Windows-macOS-Linux
        uses: tauri-apps/tauri-action@v0
        env:
          NODE_OPTIONS: '--max_old_space_size=4096'
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_PRIVATE_KEY }}
          TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_KEY_PASSWORD }}
          APPLE_CERTIFICATE: ${{ secrets.APPLE_CERTIFICATE }}
          APPLE_CERTIFICATE_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }}
          APPLE_SIGNING_IDENTITY: ${{ secrets.APPLE_SIGNING_IDENTITY }}
          APPLE_ID: ${{ secrets.APPLE_ID }}
          APPLE_PASSWORD: ${{ secrets.APPLE_PASSWORD }}
          APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
        with:
          tagName: ${{ env.TAG_NAME }}
          releaseName: 'Clash Verge Rev ${{ env.TAG_CHANNEL }}'
          releaseBody: 'More new features are now supported.'
          releaseDraft: false
          prerelease: true
          tauriScript: pnpm
          args: --target ${{ matrix.target }}
          # includeUpdaterJson: true

  autobuild-arm-linux:
    name: Autobuild ARM Linux
    needs: [check_commit, update_tag]
    if: ${{ needs.check_commit.outputs.should_run == 'true' }}
    strategy:
      fail-fast: false
      matrix:
        include:
          # It should be ubuntu-22.04 to match the cross-compilation environment
          # ortherwise it is hard to resolve the dependencies
          - os: ubuntu-22.04
            target: aarch64-unknown-linux-gnu
            arch: arm64
          - os: ubuntu-22.04
            target: armv7-unknown-linux-gnueabihf
            arch: armhf
    runs-on: ${{ matrix.os }}
    steps:
      - name: Checkout Repository
        uses: actions/checkout@v6

      - name: Install Rust Stable
        uses: dtolnay/rust-toolchain@master
        with:
          toolchain: '1.91.0'
          targets: ${{ matrix.target }}

      - name: Add Rust Target
        run: rustup target add ${{ matrix.target }}

      - name: Rust Cache
        uses: Swatinem/rust-cache@v2
        with:
          save-if: ${{ github.ref == 'refs/heads/dev' }}
          prefix-key: 'v1-rust'
          key: 'rust-shared-stable-${{ matrix.os }}-${{ matrix.target }}'
          workspaces: |
            . -> target
          cache-all-crates: true
          cache-workspace-crates: true

      - name: Install pnpm
        uses: pnpm/action-setup@v5.0.0
        with:
          run_install: false

      - name: Install Node
        uses: actions/setup-node@v6
        with:
          node-version: '24.14.1'
          cache: 'pnpm'

      - name: Pnpm Cache
        uses: actions/cache@v5
        with:
          path: ~/.pnpm-store
          key: 'pnpm-shared-stable-${{ matrix.os }}-${{ matrix.target }}'
          restore-keys: |
            pnpm-shared-stable-${{ matrix.os }}-${{ matrix.target }}

      - name: Pnpm install and check
        run: |
          pnpm i
          pnpm run prebuild ${{ matrix.target }}

      - name: Release ${{ env.TAG_CHANNEL }} Version
        run: pnpm release-version autobuild-latest

      - name: 'Setup for linux'
        run: |-
          sudo ls -lR /etc/apt/

          cat > /tmp/sources.list << EOF
          deb [arch=amd64,i386] http://archive.ubuntu.com/ubuntu jammy main multiverse universe restricted
          deb [arch=amd64,i386] http://archive.ubuntu.com/ubuntu jammy-security main multiverse universe restricted
          deb [arch=amd64,i386] http://archive.ubuntu.com/ubuntu jammy-updates main multiverse universe restricted
          deb [arch=amd64,i386] http://archive.ubuntu.com/ubuntu jammy-backports main multiverse universe restricted

          deb [arch=armhf,arm64] http://ports.ubuntu.com/ubuntu-ports jammy main multiverse universe restricted
          deb [arch=armhf,arm64] http://ports.ubuntu.com/ubuntu-ports jammy-security main multiverse universe restricted
          deb [arch=armhf,arm64] http://ports.ubuntu.com/ubuntu-ports jammy-updates main multiverse universe restricted
          deb [arch=armhf,arm64] http://ports.ubuntu.com/ubuntu-ports jammy-backports main multiverse universe restricted
          EOF

          sudo mv /etc/apt/sources.list /etc/apt/sources.list.default
          sudo mv /tmp/sources.list /etc/apt/sources.list

          sudo dpkg --add-architecture ${{ matrix.arch }}
          sudo apt update

          sudo apt install -y \
            libxslt1.1:${{ matrix.arch }} \
            libwebkit2gtk-4.1-dev:${{ matrix.arch }} \
            libayatana-appindicator3-dev:${{ matrix.arch }} \
            libssl-dev:${{ matrix.arch }} \
            patchelf:${{ matrix.arch }} \
            librsvg2-dev:${{ matrix.arch }}

      - name: Install aarch64 tools
        if: matrix.target == 'aarch64-unknown-linux-gnu'
        run: |
          sudo apt install -y \
            gcc-aarch64-linux-gnu \
            g++-aarch64-linux-gnu

      - name: Install armv7 tools
        if: matrix.target == 'armv7-unknown-linux-gnueabihf'
        run: |
          sudo apt install -y \
            gcc-arm-linux-gnueabihf \
            g++-arm-linux-gnueabihf

      - name: Add Rust Target
        run: |
          # Ensure cross target is installed for the pinned toolchain; fallback without explicit toolchain if needed
          rustup target add ${{ matrix.target }} --toolchain 1.91.0 || rustup target add ${{ matrix.target }}
          rustup target list --installed
          echo "Rust target ${{ matrix.target }} installed."

      - name: Tauri Build for Linux
        run: |
          export PKG_CONFIG_ALLOW_CROSS=1
          if [ "${{ matrix.target }}" == "aarch64-unknown-linux-gnu" ]; then
            export PKG_CONFIG_PATH=/usr/lib/aarch64-linux-gnu/pkgconfig/:$PKG_CONFIG_PATH
            export PKG_CONFIG_SYSROOT_DIR=/usr/aarch64-linux-gnu/
          elif [ "${{ matrix.target }}" == "armv7-unknown-linux-gnueabihf" ]; then
            export PKG_CONFIG_PATH=/usr/lib/arm-linux-gnueabihf/pkgconfig/:$PKG_CONFIG_PATH
            export PKG_CONFIG_SYSROOT_DIR=/usr/arm-linux-gnueabihf/
          fi
          pnpm build --target ${{ matrix.target }}
        env:
          NODE_OPTIONS: '--max_old_space_size=4096'
          TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_PRIVATE_KEY }}
          TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_KEY_PASSWORD }}

      - name: Get Version
        run: |
          sudo apt-get update
          sudo apt-get install jq
          echo "VERSION=$(cat package.json | jq '.version' | tr -d '"')" >> $GITHUB_ENV
          echo "BUILDTIME=$(TZ=Asia/Shanghai date)" >> $GITHUB_ENV

      - name: Upload Release
        uses: softprops/action-gh-release@v2
        with:
          tag_name: ${{ env.TAG_NAME }}
          name: 'Clash Verge Rev ${{ env.TAG_CHANNEL }}'
          prerelease: true
          token: ${{ secrets.GITHUB_TOKEN }}
          files: |
            target/${{ matrix.target }}/release/bundle/deb/*.deb
            target/${{ matrix.target }}/release/bundle/rpm/*.rpm

  autobuild-x86-arm-windows_webview2:
    name: Autobuild x86 and ARM Windows with WebView2
    needs: [check_commit, update_tag]
    if: ${{ needs.check_commit.outputs.should_run == 'true' }}
    strategy:
      fail-fast: false
      matrix:
        include:
          - os: windows-latest
            target: x86_64-pc-windows-msvc
            arch: x64
          - os: windows-latest
            target: aarch64-pc-windows-msvc
            arch: arm64
    runs-on: ${{ matrix.os }}
    steps:
      - name: Checkout Repository
        uses: actions/checkout@v6

      - name: Add Rust Target
        run: rustup target add ${{ matrix.target }}

      - name: Rust Cache
        uses: Swatinem/rust-cache@v2
        with:
          save-if: ${{ github.ref == 'refs/heads/dev' }}
          prefix-key: 'v1-rust'
          key: 'rust-shared-stable-${{ matrix.os }}-${{ matrix.target }}'
          workspaces: |
            . -> target
          cache-all-crates: true
          cache-workspace-crates: true

      - name: Install pnpm
        uses: pnpm/action-setup@v5.0.0
        with:
          run_install: false

      - name: Install Node
        uses: actions/setup-node@v6
        with:
          node-version: '24.14.1'
          cache: 'pnpm'

      - name: Pnpm Cache
        uses: actions/cache@v5
        with:
          path: ~/.pnpm-store
          key: 'pnpm-shared-stable-${{ matrix.os }}-${{ matrix.target }}'
          restore-keys: |
            pnpm-shared-stable-${{ matrix.os }}-${{ matrix.target }}

      - name: Pnpm install and check
        run: |
          pnpm i
          pnpm run prebuild ${{ matrix.target }}

      - name: Release ${{ env.TAG_CHANNEL }} Version
        run: pnpm release-version autobuild-latest

      - name: Download WebView2 Runtime
        run: |
          invoke-webrequest -uri https://github.com/westinyang/WebView2RuntimeArchive/releases/download/133.0.3065.92/Microsoft.WebView2.FixedVersionRuntime.133.0.3065.92.${{ matrix.arch }}.cab -outfile Microsoft.WebView2.FixedVersionRuntime.133.0.3065.92.${{ matrix.arch }}.cab
          Expand .\Microsoft.WebView2.FixedVersionRuntime.133.0.3065.92.${{ matrix.arch }}.cab -F:* ./src-tauri
          Remove-Item .\src-tauri\tauri.windows.conf.json
          Rename-Item .\src-tauri\webview2.${{ matrix.arch }}.json tauri.windows.conf.json

      - name: Add Rust Target
        run: |
          # Ensure cross target is installed for the pinned toolchain; fallback without explicit toolchain if needed
          rustup target add ${{ matrix.target }} --toolchain 1.91.0 || rustup target add ${{ matrix.target }}
          rustup target list --installed
          echo "Rust target ${{ matrix.target }} installed."

      - name: Tauri build for Windows
        id: build
        uses: tauri-apps/tauri-action@v0
        env:
          NODE_OPTIONS: '--max_old_space_size=4096'
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_PRIVATE_KEY }}
          TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_KEY_PASSWORD }}
        with:
          tauriScript: pnpm
          args: --target ${{ matrix.target }}
          # includeUpdaterJson: true

      - name: Rename
        run: |
          $files = Get-ChildItem ".\target\${{ matrix.target }}\release\bundle\nsis\*-setup.exe"
          foreach ($file in $files) {
            $newName = $file.Name -replace "-setup\.exe$", "_fixed_webview2-setup.exe"
            Rename-Item $file.FullName $newName
          }

          $files = Get-ChildItem ".\target\${{ matrix.target }}\release\bundle\nsis\*.nsis.zip"
          foreach ($file in $files) {
            $newName = $file.Name -replace "-setup\.nsis\.zip$", "_fixed_webview2-setup.nsis.zip"
            Rename-Item $file.FullName $newName
          }

          $files = Get-ChildItem ".\target\${{ matrix.target }}\release\bundle\nsis\*-setup.exe.sig"
          foreach ($file in $files) {
            $newName = $file.Name -replace "-setup\.exe\.sig$", "_fixed_webview2-setup.exe.sig"
            Rename-Item $file.FullName $newName
          }

      - name: Upload Release
        uses: softprops/action-gh-release@v2
        with:
          tag_name: ${{ env.TAG_NAME }}
          name: 'Clash Verge Rev ${{ env.TAG_CHANNEL }}'
          prerelease: true
          token: ${{ secrets.GITHUB_TOKEN }}
          files: target/${{ matrix.target }}/release/bundle/nsis/*setup*

      - name: Portable Bundle
        run: pnpm portable-fixed-webview2 ${{ matrix.target }} --${{ env.TAG_NAME }}
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

  notify-telegram:
    name: Notify Telegram
    runs-on: ubuntu-latest
    needs:
      [
        update_tag,
        autobuild-x86-windows-macos-linux,
        autobuild-arm-linux,
        autobuild-x86-arm-windows_webview2,
      ]
    steps:
      - name: Checkout repository
        uses: actions/checkout@v6

      - name: Fetch UPDATE logs
        id: fetch_update_logs
        run: bash ./scripts/extract_update_logs.sh
        shell: bash

      - name: Install Node
        uses: actions/setup-node@v6
        with:
          node-version: '24.14.1'

      - uses: pnpm/action-setup@v5.0.0
        name: Install pnpm
        with:
          run_install: false

      - name: Install dependencies
        run: pnpm install --frozen-lockfile

      - name: Release AutoBuild Version
        run: pnpm release-version autobuild-latest

      - name: Get Version and Release Info
        run: |
          sudo apt-get update
          sudo apt-get install jq
          echo "VERSION=$(cat package.json | jq '.version' | tr -d '"')" >> $GITHUB_ENV
          echo "DOWNLOAD_URL=https://github.com/clash-verge-rev/clash-verge-rev/releases/download/autobuild" >> $GITHUB_ENV
          echo "BUILDTIME=$(TZ=Asia/Shanghai date)" >> $GITHUB_ENV

      - name: Generate release.txt
        run: |
          if [ -z "$UPDATE_LOGS" ]; then
            echo "No update logs found, using default message"
            UPDATE_LOGS="More new features are now supported. Check for detailed changelog soon."
          else
            echo "Using found update logs"
          fi

          cat > release.txt << EOF
          $UPDATE_LOGS

          ## 下载地址

          ### Windows (不再支持Win7)
          #### 正常版本(推荐)
          - [64位(常用)](${{ env.DOWNLOAD_URL }}/Clash.Verge_${{ env.VERSION }}_x64-setup.exe) | [ARM64(不常用)](${{ env.DOWNLOAD_URL }}/Clash.Verge_${{ env.VERSION }}_arm64-setup.exe)

          #### 内置Webview2版(体积较大,仅在企业版系统或无法安装webview2时使用)
          - [64位](${{ env.DOWNLOAD_URL }}/Clash.Verge_${{ env.VERSION }}_x64_fixed_webview2-setup.exe) | [ARM64](${{ env.DOWNLOAD_URL }}/Clash.Verge_${{ env.VERSION }}_arm64_fixed_webview2-setup.exe)

          ### macOS
          - [Apple M芯片](${{ env.DOWNLOAD_URL }}/Clash.Verge_${{ env.VERSION }}_aarch64.dmg) | [Intel芯片](${{ env.DOWNLOAD_URL }}/Clash.Verge_${{ env.VERSION }}_x64.dmg)

          ### Linux
          #### DEB包(Debian系) 使用 apt ./路径 安装
          - [64位](${{ env.DOWNLOAD_URL }}/Clash.Verge_${{ env.VERSION }}_amd64.deb) | [ARM64](${{ env.DOWNLOAD_URL }}/Clash.Verge_${{ env.VERSION }}_arm64.deb) | [ARMv7](${{ env.DOWNLOAD_URL }}/Clash.Verge_${{ env.VERSION }}_armhf.deb)

          #### RPM包(Redhat系) 使用 dnf ./路径 安装
          - [64位](${{ env.DOWNLOAD_URL }}/Clash.Verge-${{ env.VERSION }}-1.x86_64.rpm) | [ARM64](${{ env.DOWNLOAD_URL }}/Clash.Verge-${{ env.VERSION }}-1.aarch64.rpm) | [ARMv7](${{ env.DOWNLOAD_URL }}/Clash.Verge-${{ env.VERSION }}-1.armhfp.rpm)

          ### FAQ
          - [常见问题](https://clash-verge-rev.github.io/faq/windows.html)

          ### 稳定机场VPN推荐
          - [狗狗加速](https://verge.dginv.click/#/register?code=oaxsAGo6)

          Created at ${{ env.BUILDTIME }}.
          EOF

      - name: Send Telegram Notification
        run: node scripts/telegram.mjs
        env:
          TELEGRAM_BOT_TOKEN: ${{ secrets.TELEGRAM_BOT_TOKEN }}
          BUILD_TYPE: autobuild
          VERSION: ${{ env.VERSION }}
          DOWNLOAD_URL: ${{ env.DOWNLOAD_URL }}
autobuild-check-test .github/workflows/autobuild-check-test.yml
Triggers
workflow_dispatch
Runs on
ubuntu-latest
Jobs
check_autobuild_logic
Commands
  • # # 仅用于测试逻辑,手动触发自动跳过 # if [ "${{ github.event_name }}" == "workflow_dispatch" ]; then # echo "should_run=skip" >> $GITHUB_OUTPUT # echo "🟡 手动触发,跳过 should_run 检查" # exit 0 # fi # 确保有 HEAD~1 if ! git rev-parse HEAD~1 > /dev/null 2>&1; then echo "should_run=true" >> $GITHUB_OUTPUT echo "🟢 没有前一个提交,默认需要构建" exit 0 fi # 版本号变更判断 CURRENT_VERSION=$(jq -r '.version' package.json) PREVIOUS_VERSION=$(git show HEAD~1:package.json | jq -r '.version' 2>/dev/null || echo "") if [ "$CURRENT_VERSION" != "$PREVIOUS_VERSION" ]; then echo "should_run=true" >> $GITHUB_OUTPUT echo "🟢 版本号变更: $PREVIOUS_VERSION → $CURRENT_VERSION" exit 0 fi # 检查 src 变更(排除常见产物与缓存) SRC_DIFF=$(git diff --name-only HEAD~1 HEAD -- src/ | grep -Ev '^src/(dist|build|node_modules|\.next|\.cache)' || true) TAURI_DIFF=$(git diff --name-only HEAD~1 HEAD -- src-tauri/ | grep -Ev '^src-tauri/(target|node_modules|dist|\.cache)' || true) if [ -n "$SRC_DIFF" ] || [ -n "$TAURI_DIFF" ]; then echo "should_run=true" >> $GITHUB_OUTPUT echo "🟢 源码变更 detected" exit 0 fi # 找到最后一个修改 Tauri 相关文件的 commit echo "🔍 查找最后一个 Tauri 相关变更的 commit..." LAST_TAURI_COMMIT="" for commit in $(git rev-list HEAD --max-count=50); do # 检查此 commit 是否修改了 Tauri 相关文件 CHANGED_FILES=$(git show --name-only --pretty=format: $commit | tr '\n' ' ') HAS_TAURI_CHANGES=false # 检查各个模式 if echo "$CHANGED_FILES" | grep -q "src/" && echo "$CHANGED_FILES" | grep -qvE "src/(dist|build|node_modules|\.next|\.cache)"; then HAS_TAURI_CHANGES=true elif echo "$CHANGED_FILES" | grep -qE "src-tauri/(src|Cargo\.(toml|lock)|tauri\..*\.conf\.json|build\.rs|capabilities)"; then HAS_TAURI_CHANGES=true fi if [ "$HAS_TAURI_CHANGES" = true ]; then LAST_TAURI_COMMIT=$(git rev-parse --short $commit) break fi done if [ -z "$LAST_TAURI_COMMIT" ]; then echo "⚠️ 最近的 commits 中未找到 Tauri 相关变更,使用当前 commit" LAST_TAURI_COMMIT=$(git rev-parse --short HEAD) fi CURRENT_COMMIT=$(git rev-parse --short HEAD) echo "📝 最后 Tauri 相关 commit: $LAST_TAURI_COMMIT" echo "📝 当前 commit: $CURRENT_COMMIT" # 检查 autobuild release 是否存在 AUTOBUILD_RELEASE_EXISTS=$(gh release view "autobuild" --json id -q '.id' 2>/dev/null || echo "") if [ -z "$AUTOBUILD_RELEASE_EXISTS" ]; then echo "should_run=true" >> $GITHUB_OUTPUT echo "🟢 没有 autobuild release,需构建" else # 检查 latest.json 是否存在 LATEST_JSON_EXISTS=$(gh release view "autobuild" --json assets -q '.assets[] | select(.name == "latest.json") | .name' 2>/dev/null || echo "") if [ -z "$LATEST_JSON_EXISTS" ]; then echo "should_run=true" >> $GITHUB_OUTPUT echo "🟢 没有 latest.json,需构建" else # 下载并解析 latest.json 检查版本和 commit hash echo "📥 下载 latest.json 检查版本..." LATEST_JSON_URL=$(gh release view "autobuild" --json assets -q '.assets[] | select(.name == "latest.json") | .browser_download_url' 2>/dev/null) if [ -n "$LATEST_JSON_URL" ]; then LATEST_JSON_CONTENT=$(curl -s "$LATEST_JSON_URL" 2>/dev/null || echo "") if [ -n "$LATEST_JSON_CONTENT" ]; then LATEST_VERSION=$(echo "$LATEST_JSON_CONTENT" | jq -r '.version' 2>/dev/null || echo "") echo "📦 最新 autobuild 版本: $LATEST_VERSION" # 从版本字符串中提取 commit hash (格式: X.Y.Z+autobuild.MMDD.commit) LATEST_COMMIT=$(echo "$LATEST_VERSION" | sed -n 's/.*+autobuild\.[0-9]\{4\}\.\([a-f0-9]*\)$/\1/p' || echo "") echo "📝 最新 autobuild commit: $LATEST_COMMIT" if [ "$LAST_TAURI_COMMIT" != "$LATEST_COMMIT" ]; then echo "should_run=true" >> $GITHUB_OUTPUT echo "🟢 Tauri commit hash 不匹配 ($LAST_TAURI_COMMIT != $LATEST_COMMIT),需构建" else echo "should_run=false" >> $GITHUB_OUTPUT echo "🔴 相同 Tauri commit hash ($LAST_TAURI_COMMIT),不需构建" fi else echo "should_run=true" >> $GITHUB_OUTPUT echo "⚠️ 无法下载或解析 latest.json,需构建" fi else echo "should_run=true" >> $GITHUB_OUTPUT echo "⚠️ 无法获取 latest.json 下载 URL,需构建" fi fi fi
  • echo "Result: ${{ steps.check.outputs.should_run }}"
View raw YAML
name: Autobuild Check Logic Test

on:
  workflow_dispatch:

jobs:
  check_autobuild_logic:
    name: Check Autobuild Should Run Logic
    runs-on: ubuntu-latest
    steps:
      - name: Checkout repository
        uses: actions/checkout@v6
        with:
          fetch-depth: 2

      - name: Check if version or source changed, or assets already exist
        id: check
        run: |
          # # 仅用于测试逻辑,手动触发自动跳过
          # if [ "${{ github.event_name }}" == "workflow_dispatch" ]; then
          #   echo "should_run=skip" >> $GITHUB_OUTPUT
          #   echo "🟡 手动触发,跳过 should_run 检查"
          #   exit 0
          # fi

          # 确保有 HEAD~1
          if ! git rev-parse HEAD~1 > /dev/null 2>&1; then
            echo "should_run=true" >> $GITHUB_OUTPUT
            echo "🟢 没有前一个提交,默认需要构建"
            exit 0
          fi

          # 版本号变更判断
          CURRENT_VERSION=$(jq -r '.version' package.json)
          PREVIOUS_VERSION=$(git show HEAD~1:package.json | jq -r '.version' 2>/dev/null || echo "")

          if [ "$CURRENT_VERSION" != "$PREVIOUS_VERSION" ]; then
            echo "should_run=true" >> $GITHUB_OUTPUT
            echo "🟢 版本号变更: $PREVIOUS_VERSION → $CURRENT_VERSION"
            exit 0
          fi

          # 检查 src 变更(排除常见产物与缓存)
          SRC_DIFF=$(git diff --name-only HEAD~1 HEAD -- src/ | grep -Ev '^src/(dist|build|node_modules|\.next|\.cache)' || true)
          TAURI_DIFF=$(git diff --name-only HEAD~1 HEAD -- src-tauri/ | grep -Ev '^src-tauri/(target|node_modules|dist|\.cache)' || true)

          if [ -n "$SRC_DIFF" ] || [ -n "$TAURI_DIFF" ]; then
            echo "should_run=true" >> $GITHUB_OUTPUT
            echo "🟢 源码变更 detected"
            exit 0
          fi

          # 找到最后一个修改 Tauri 相关文件的 commit
          echo "🔍 查找最后一个 Tauri 相关变更的 commit..."

          LAST_TAURI_COMMIT=""
          for commit in $(git rev-list HEAD --max-count=50); do
            # 检查此 commit 是否修改了 Tauri 相关文件
            CHANGED_FILES=$(git show --name-only --pretty=format: $commit | tr '\n' ' ')
            HAS_TAURI_CHANGES=false
            
            # 检查各个模式
            if echo "$CHANGED_FILES" | grep -q "src/" && echo "$CHANGED_FILES" | grep -qvE "src/(dist|build|node_modules|\.next|\.cache)"; then
              HAS_TAURI_CHANGES=true
            elif echo "$CHANGED_FILES" | grep -qE "src-tauri/(src|Cargo\.(toml|lock)|tauri\..*\.conf\.json|build\.rs|capabilities)"; then
              HAS_TAURI_CHANGES=true
            fi
            
            if [ "$HAS_TAURI_CHANGES" = true ]; then
              LAST_TAURI_COMMIT=$(git rev-parse --short $commit)
              break
            fi
          done

          if [ -z "$LAST_TAURI_COMMIT" ]; then
            echo "⚠️  最近的 commits 中未找到 Tauri 相关变更,使用当前 commit"
            LAST_TAURI_COMMIT=$(git rev-parse --short HEAD)
          fi

          CURRENT_COMMIT=$(git rev-parse --short HEAD)
          echo "📝 最后 Tauri 相关 commit: $LAST_TAURI_COMMIT"
          echo "📝 当前 commit: $CURRENT_COMMIT"

          # 检查 autobuild release 是否存在
          AUTOBUILD_RELEASE_EXISTS=$(gh release view "autobuild" --json id -q '.id' 2>/dev/null || echo "")

          if [ -z "$AUTOBUILD_RELEASE_EXISTS" ]; then
            echo "should_run=true" >> $GITHUB_OUTPUT
            echo "🟢 没有 autobuild release,需构建"
          else
            # 检查 latest.json 是否存在
            LATEST_JSON_EXISTS=$(gh release view "autobuild" --json assets -q '.assets[] | select(.name == "latest.json") | .name' 2>/dev/null || echo "")
            
            if [ -z "$LATEST_JSON_EXISTS" ]; then
              echo "should_run=true" >> $GITHUB_OUTPUT
              echo "🟢 没有 latest.json,需构建"
            else
              # 下载并解析 latest.json 检查版本和 commit hash
              echo "📥 下载 latest.json 检查版本..."
              LATEST_JSON_URL=$(gh release view "autobuild" --json assets -q '.assets[] | select(.name == "latest.json") | .browser_download_url' 2>/dev/null)
              
              if [ -n "$LATEST_JSON_URL" ]; then
                LATEST_JSON_CONTENT=$(curl -s "$LATEST_JSON_URL" 2>/dev/null || echo "")
                
                if [ -n "$LATEST_JSON_CONTENT" ]; then
                  LATEST_VERSION=$(echo "$LATEST_JSON_CONTENT" | jq -r '.version' 2>/dev/null || echo "")
                  echo "📦 最新 autobuild 版本: $LATEST_VERSION"
                  
                  # 从版本字符串中提取 commit hash (格式: X.Y.Z+autobuild.MMDD.commit)
                  LATEST_COMMIT=$(echo "$LATEST_VERSION" | sed -n 's/.*+autobuild\.[0-9]\{4\}\.\([a-f0-9]*\)$/\1/p' || echo "")
                  echo "📝 最新 autobuild commit: $LATEST_COMMIT"
                  
                  if [ "$LAST_TAURI_COMMIT" != "$LATEST_COMMIT" ]; then
                    echo "should_run=true" >> $GITHUB_OUTPUT
                    echo "🟢 Tauri commit hash 不匹配 ($LAST_TAURI_COMMIT != $LATEST_COMMIT),需构建"
                  else
                    echo "should_run=false" >> $GITHUB_OUTPUT
                    echo "🔴 相同 Tauri commit hash ($LAST_TAURI_COMMIT),不需构建"
                  fi
                else
                  echo "should_run=true" >> $GITHUB_OUTPUT
                  echo "⚠️  无法下载或解析 latest.json,需构建"
                fi
              else
                echo "should_run=true" >> $GITHUB_OUTPUT
                echo "⚠️  无法获取 latest.json 下载 URL,需构建"
              fi
            fi
          fi

      - name: Output should_run result
        run: |
          echo "Result: ${{ steps.check.outputs.should_run }}"
check-commit-needs-build perms .github/workflows/check-commit-needs-build.yml
Triggers
workflow_dispatch, workflow_call
Runs on
ubuntu-latest
Jobs
check_commit
Commands
  • # Force build if requested if [ "${{ inputs.force_build }}" == "true" ]; then echo "🚀 Force build requested" echo "should_run=true" >> $GITHUB_OUTPUT exit 0 fi CURRENT_VERSION=$(cat package.json | jq -r '.version') echo "📦 Current version: $CURRENT_VERSION" git checkout HEAD~1 package.json PREVIOUS_VERSION=$(cat package.json | jq -r '.version') echo "📦 Previous version: $PREVIOUS_VERSION" git checkout HEAD package.json if [ "$CURRENT_VERSION" != "$PREVIOUS_VERSION" ]; then echo "✅ Version changed from $PREVIOUS_VERSION to $CURRENT_VERSION" echo "should_run=true" >> $GITHUB_OUTPUT exit 0 fi # Use get_latest_tauri_commit.bash to find the latest Tauri-related commit echo "🔍 Finding last commit with Tauri-related changes using script..." # Make script executable chmod +x scripts-workflow/get_latest_tauri_commit.bash # Get the latest Tauri-related commit hash (full hash) LAST_TAURI_COMMIT_FULL=$(./scripts-workflow/get_latest_tauri_commit.bash) if [[ $? -ne 0 ]] || [[ -z "$LAST_TAURI_COMMIT_FULL" ]]; then echo "❌ Failed to get Tauri-related commit, using current commit" LAST_TAURI_COMMIT_FULL=$(git rev-parse HEAD) fi # Get short hash for display and version tagging LAST_TAURI_COMMIT=$(git rev-parse --short "$LAST_TAURI_COMMIT_FULL") echo "📝 Last Tauri-related commit: $LAST_TAURI_COMMIT" # Generate autobuild version using autobuild-latest format CURRENT_BASE_VERSION=$(echo "$CURRENT_VERSION" | sed -E 's/-(alpha|beta|rc)(\.[0-9]+)?//g' | sed -E 's/\+[a-zA-Z0-9.-]+//g') MONTH=$(TZ=Asia/Shanghai date +%m) DAY=$(TZ=Asia/Shanghai date +%d) AUTOBUILD_VERSION="${CURRENT_BASE_VERSION}+autobuild.${MONTH}${DAY}.${LAST_TAURI_COMMIT}" echo "🏷️ Autobuild version: $AUTOBUILD_VERSION" echo "📝 Last Tauri commit: $LAST_TAURI_COMMIT" # Set outputs for other jobs to use echo "last_tauri_commit=$LAST_TAURI_COMMIT" >> $GITHUB_OUTPUT echo "autobuild_version=$AUTOBUILD_VERSION" >> $GITHUB_OUTPUT # Check if autobuild release exists echo "🔍 Checking autobuild release and latest.json..." AUTOBUILD_RELEASE_EXISTS=$(gh release view "${{ env.TAG_NAME }}" --json id -q '.id' 2>/dev/null || echo "") if [ -z "$AUTOBUILD_RELEASE_EXISTS" ]; then echo "✅ No autobuild release exists, build needed" echo "should_run=true" >> $GITHUB_OUTPUT else # Check if latest.json exists in the release LATEST_JSON_EXISTS=$(gh release view "${{ env.TAG_NAME }}" --json assets -q '.assets[] | select(.name == "latest.json") | .name' 2>/dev/null || echo "") if [ -z "$LATEST_JSON_EXISTS" ]; then echo "✅ No latest.json found in autobuild release, build needed" echo "should_run=true" >> $GITHUB_OUTPUT else # Download and parse latest.json to check version and commit hash echo "📥 Downloading latest.json to check version..." LATEST_JSON_URL="https://github.com/clash-verge-rev/clash-verge-rev/releases/download/autobuild/latest.json" LATEST_JSON_CONTENT=$(curl -sL "$LATEST_JSON_URL" 2>/dev/null || echo "") if [ -n "$LATEST_JSON_CONTENT" ]; then LATEST_VERSION=$(echo "$LATEST_JSON_CONTENT" | jq -r '.version' 2>/dev/null || echo "") echo "📦 Latest autobuild version: $LATEST_VERSION" # Extract commit hash from version string (format: X.Y.Z+autobuild.MMDD.commit) LATEST_COMMIT=$(echo "$LATEST_VERSION" | sed -n 's/.*+autobuild\.[0-9]\{4\}\.\([a-f0-9]*\)$/\1/p' || echo "") echo "📝 Latest autobuild commit: $LATEST_COMMIT" if [ "$LAST_TAURI_COMMIT" != "$LATEST_COMMIT" ]; then echo "✅ Tauri commit hash mismatch ($LAST_TAURI_COMMIT != $LATEST_COMMIT), build needed" echo "should_run=true" >> $GITHUB_OUTPUT else echo "❌ Same Tauri commit hash ($LAST_TAURI_COMMIT), no build needed" echo "should_run=false" >> $GITHUB_OUTPUT fi else echo "⚠️ Failed to download or parse latest.json, build needed" echo "should_run=true" >> $GITHUB_OUTPUT fi fi fi
View raw YAML
name: Check Commit Needs Build

on:
  workflow_dispatch:
    inputs:
      tag_name:
        description: 'Release tag name to check against (default: autobuild)'
        required: false
        default: 'autobuild'
        type: string
      force_build:
        description: 'Force build regardless of checks'
        required: false
        default: false
        type: boolean
  workflow_call:
    inputs:
      tag_name:
        description: 'Release tag name to check against (default: autobuild)'
        required: false
        default: 'autobuild'
        type: string
      force_build:
        description: 'Force build regardless of checks'
        required: false
        default: false
        type: boolean
    outputs:
      should_run:
        description: 'Whether the build should run'
        value: ${{ jobs.check_commit.outputs.should_run }}
      last_tauri_commit:
        description: 'The last commit hash with Tauri-related changes'
        value: ${{ jobs.check_commit.outputs.last_tauri_commit }}
      autobuild_version:
        description: 'The generated autobuild version string'
        value: ${{ jobs.check_commit.outputs.autobuild_version }}

permissions:
  contents: read
  actions: read

env:
  TAG_NAME: ${{ inputs.tag_name || 'autobuild' }}

jobs:
  check_commit:
    name: Check Commit Needs Build
    runs-on: ubuntu-latest
    outputs:
      should_run: ${{ steps.check.outputs.should_run }}
      last_tauri_commit: ${{ steps.check.outputs.last_tauri_commit }}
      autobuild_version: ${{ steps.check.outputs.autobuild_version }}
    steps:
      - name: Checkout repository
        uses: actions/checkout@v6
        with:
          fetch-depth: 50

      - name: Check if version changed or src changed
        id: check
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        run: |
          # Force build if requested
          if [ "${{ inputs.force_build }}" == "true" ]; then
            echo "🚀 Force build requested"
            echo "should_run=true" >> $GITHUB_OUTPUT
            exit 0
          fi

          CURRENT_VERSION=$(cat package.json | jq -r '.version')
          echo "📦 Current version: $CURRENT_VERSION"

          git checkout HEAD~1 package.json
          PREVIOUS_VERSION=$(cat package.json | jq -r '.version')
          echo "📦 Previous version: $PREVIOUS_VERSION"

          git checkout HEAD package.json

          if [ "$CURRENT_VERSION" != "$PREVIOUS_VERSION" ]; then
            echo "✅ Version changed from $PREVIOUS_VERSION to $CURRENT_VERSION"
            echo "should_run=true" >> $GITHUB_OUTPUT
            exit 0
          fi

          # Use get_latest_tauri_commit.bash to find the latest Tauri-related commit
          echo "🔍 Finding last commit with Tauri-related changes using script..."

          # Make script executable
          chmod +x scripts-workflow/get_latest_tauri_commit.bash

          # Get the latest Tauri-related commit hash (full hash)
          LAST_TAURI_COMMIT_FULL=$(./scripts-workflow/get_latest_tauri_commit.bash)
          if [[ $? -ne 0 ]] || [[ -z "$LAST_TAURI_COMMIT_FULL" ]]; then
            echo "❌ Failed to get Tauri-related commit, using current commit"
            LAST_TAURI_COMMIT_FULL=$(git rev-parse HEAD)
          fi

          # Get short hash for display and version tagging
          LAST_TAURI_COMMIT=$(git rev-parse --short "$LAST_TAURI_COMMIT_FULL")

          echo "📝 Last Tauri-related commit: $LAST_TAURI_COMMIT"

          # Generate autobuild version using autobuild-latest format
          CURRENT_BASE_VERSION=$(echo "$CURRENT_VERSION" | sed -E 's/-(alpha|beta|rc)(\.[0-9]+)?//g' | sed -E 's/\+[a-zA-Z0-9.-]+//g')
          MONTH=$(TZ=Asia/Shanghai date +%m)
          DAY=$(TZ=Asia/Shanghai date +%d)
          AUTOBUILD_VERSION="${CURRENT_BASE_VERSION}+autobuild.${MONTH}${DAY}.${LAST_TAURI_COMMIT}"

          echo "🏷️  Autobuild version: $AUTOBUILD_VERSION"
          echo "📝 Last Tauri commit: $LAST_TAURI_COMMIT"

          # Set outputs for other jobs to use
          echo "last_tauri_commit=$LAST_TAURI_COMMIT" >> $GITHUB_OUTPUT
          echo "autobuild_version=$AUTOBUILD_VERSION" >> $GITHUB_OUTPUT

          # Check if autobuild release exists
          echo "🔍 Checking autobuild release and latest.json..."
          AUTOBUILD_RELEASE_EXISTS=$(gh release view "${{ env.TAG_NAME }}" --json id -q '.id' 2>/dev/null || echo "")

          if [ -z "$AUTOBUILD_RELEASE_EXISTS" ]; then
            echo "✅ No autobuild release exists, build needed"
            echo "should_run=true" >> $GITHUB_OUTPUT
          else
            # Check if latest.json exists in the release
            LATEST_JSON_EXISTS=$(gh release view "${{ env.TAG_NAME }}" --json assets -q '.assets[] | select(.name == "latest.json") | .name' 2>/dev/null || echo "")
            
            if [ -z "$LATEST_JSON_EXISTS" ]; then
              echo "✅ No latest.json found in autobuild release, build needed"
              echo "should_run=true" >> $GITHUB_OUTPUT
            else
              # Download and parse latest.json to check version and commit hash
              echo "📥 Downloading latest.json to check version..."
              LATEST_JSON_URL="https://github.com/clash-verge-rev/clash-verge-rev/releases/download/autobuild/latest.json"
              
              LATEST_JSON_CONTENT=$(curl -sL "$LATEST_JSON_URL" 2>/dev/null || echo "")
              
              if [ -n "$LATEST_JSON_CONTENT" ]; then
                LATEST_VERSION=$(echo "$LATEST_JSON_CONTENT" | jq -r '.version' 2>/dev/null || echo "")
                echo "📦 Latest autobuild version: $LATEST_VERSION"
                
                # Extract commit hash from version string (format: X.Y.Z+autobuild.MMDD.commit)
                LATEST_COMMIT=$(echo "$LATEST_VERSION" | sed -n 's/.*+autobuild\.[0-9]\{4\}\.\([a-f0-9]*\)$/\1/p' || echo "")
                echo "📝 Latest autobuild commit: $LATEST_COMMIT"
                
                if [ "$LAST_TAURI_COMMIT" != "$LATEST_COMMIT" ]; then
                  echo "✅ Tauri commit hash mismatch ($LAST_TAURI_COMMIT != $LATEST_COMMIT), build needed"
                  echo "should_run=true" >> $GITHUB_OUTPUT
                else
                  echo "❌ Same Tauri commit hash ($LAST_TAURI_COMMIT), no build needed"
                  echo "should_run=false" >> $GITHUB_OUTPUT
                fi
              else
                echo "⚠️  Failed to download or parse latest.json, build needed"
                echo "should_run=true" >> $GITHUB_OUTPUT
              fi
            fi
          fi
clean-old-assets perms .github/workflows/clean-old-assets.yml
Triggers
workflow_dispatch, workflow_call
Runs on
ubuntu-latest, ubuntu-latest
Jobs
check_current_version, clean_old_assets
Commands
  • CURRENT_VERSION=$(cat package.json | jq -r '.version') echo "📦 Current version: $CURRENT_VERSION" # Find the last commit that changed Tauri-related files echo "🔍 Finding last commit with Tauri-related changes..." # Define patterns for Tauri-related files TAURI_PATTERNS="src/ src-tauri/src src-tauri/Cargo.toml Cargo.lock src-tauri/tauri.*.conf.json src-tauri/build.rs src-tauri/capabilities" # Get the last commit that changed any of these patterns (excluding build artifacts) LAST_TAURI_COMMIT="" for commit in $(git rev-list HEAD --max-count=50); do # Check if this commit changed any Tauri-related files CHANGED_FILES=$(git show --name-only --pretty=format: $commit | tr '\n' ' ') HAS_TAURI_CHANGES=false # Check each pattern if echo "$CHANGED_FILES" | grep -q "src/" && echo "$CHANGED_FILES" | grep -qvE "src/(dist|build|node_modules|\.next|\.cache)"; then HAS_TAURI_CHANGES=true elif echo "$CHANGED_FILES" | grep -qE "src-tauri/(src|Cargo\.(toml|lock)|tauri\..*\.conf\.json|build\.rs|capabilities)"; then HAS_TAURI_CHANGES=true fi if [ "$HAS_TAURI_CHANGES" = true ]; then LAST_TAURI_COMMIT=$(git rev-parse --short $commit) break fi done if [ -z "$LAST_TAURI_COMMIT" ]; then echo "⚠️ No Tauri-related changes found in recent commits, using current commit" LAST_TAURI_COMMIT=$(git rev-parse --short HEAD) fi echo "📝 Last Tauri-related commit: $LAST_TAURI_COMMIT" echo "📝 Current commit: $(git rev-parse --short HEAD)" # Generate autobuild version for consistency CURRENT_BASE_VERSION=$(echo "$CURRENT_VERSION" | sed -E 's/-(alpha|beta|rc)(\.[0-9]+)?//g' | sed -E 's/\+[a-zA-Z0-9.-]+//g') MONTH=$(TZ=Asia/Shanghai date +%m) DAY=$(TZ=Asia/Shanghai date +%d) AUTOBUILD_VERSION="${CURRENT_BASE_VERSION}+autobuild.${MONTH}${DAY}.${LAST_TAURI_COMMIT}" echo "🏷️ Current autobuild version: $AUTOBUILD_VERSION" # Set outputs for other jobs to use echo "current_version=$CURRENT_VERSION" >> $GITHUB_OUTPUT echo "last_tauri_commit=$LAST_TAURI_COMMIT" >> $GITHUB_OUTPUT echo "autobuild_version=$AUTOBUILD_VERSION" >> $GITHUB_OUTPUT
  • # Use values from check_current_version job CURRENT_AUTOBUILD_VERSION="${{ needs.check_current_version.outputs.autobuild_version }}" LAST_TAURI_COMMIT="${{ needs.check_current_version.outputs.last_tauri_commit }}" CURRENT_VERSION="${{ needs.check_current_version.outputs.current_version }}" echo "📦 Current version: $CURRENT_VERSION" echo "📦 Current autobuild version: $CURRENT_AUTOBUILD_VERSION" echo "📝 Last Tauri commit: $LAST_TAURI_COMMIT" echo "🏷️ Target tag: $TAG_NAME" echo "🔍 Dry run mode: $DRY_RUN" # Check if release exists RELEASE_EXISTS=$(gh release view "$TAG_NAME" --json id -q '.id' 2>/dev/null || echo "") if [ -z "$RELEASE_EXISTS" ]; then echo "❌ Release '$TAG_NAME' not found" exit 1 fi echo "✅ Found release '$TAG_NAME'" # Get all assets echo "📋 Getting list of all assets..." assets=$(gh release view "$TAG_NAME" --json assets -q '.assets[].name' || true) if [ -z "$assets" ]; then echo "ℹ️ No assets found in release '$TAG_NAME'" exit 0 fi echo "📋 Found assets:" echo "$assets" | sed 's/^/ - /' # Count assets to keep and delete ASSETS_TO_KEEP="" ASSETS_TO_DELETE="" for asset in $assets; do # Keep assets that match current autobuild version or are non-versioned files (like latest.json) if [[ "$asset" == *"$CURRENT_AUTOBUILD_VERSION"* ]] || [[ "$asset" == "latest.json" ]]; then ASSETS_TO_KEEP="$ASSETS_TO_KEEP$asset\n" else ASSETS_TO_DELETE="$ASSETS_TO_DELETE$asset\n" fi done echo "" echo "🔒 Assets to keep (current version: $CURRENT_AUTOBUILD_VERSION):" if [ -n "$ASSETS_TO_KEEP" ]; then echo -e "$ASSETS_TO_KEEP" | grep -v '^$' | sed 's/^/ - /' else echo " - None" fi echo "" echo "🗑️ Assets to delete:" if [ -n "$ASSETS_TO_DELETE" ]; then echo -e "$ASSETS_TO_DELETE" | grep -v '^$' | sed 's/^/ - /' else echo " - None" echo "ℹ️ No old assets to clean" exit 0 fi if [ "$DRY_RUN" = "true" ]; then echo "" echo "🔍 DRY RUN MODE: No assets will actually be deleted" echo " To actually delete these assets, run this workflow again with dry_run=false" else echo "" echo "🗑️ Deleting old assets..." DELETED_COUNT=0 FAILED_COUNT=0 for asset in $assets; do # Skip assets that should be kept if [[ "$asset" == *"$CURRENT_AUTOBUILD_VERSION"* ]] || [[ "$asset" == "latest.json" ]]; then continue fi echo " Deleting: $asset" if gh release delete-asset "$TAG_NAME" "$asset" -y 2>/dev/null; then DELETED_COUNT=$((DELETED_COUNT + 1)) else echo " ⚠️ Failed to delete $asset" FAILED_COUNT=$((FAILED_COUNT + 1)) fi done echo "" echo "📊 Cleanup summary:" echo " - Deleted: $DELETED_COUNT assets" if [ $FAILED_COUNT -gt 0 ]; then echo " - Failed: $FAILED_COUNT assets" fi echo " - Kept: $(echo -e "$ASSETS_TO_KEEP" | grep -v '^$' | wc -l) assets" if [ $FAILED_COUNT -gt 0 ]; then echo "⚠️ Some assets failed to delete. Please check the logs above." exit 1 else echo "✅ Cleanup completed successfully!" fi fi
View raw YAML
name: Clean Old Assets

on:
  workflow_dispatch:
    inputs:
      tag_name:
        description: 'Release tag name to clean (default: autobuild)'
        required: false
        default: 'autobuild'
        type: string
      dry_run:
        description: 'Dry run mode (only show what would be deleted)'
        required: false
        default: false
        type: boolean
  workflow_call:
    inputs:
      tag_name:
        description: 'Release tag name to clean (default: autobuild)'
        required: false
        default: 'autobuild'
        type: string
      dry_run:
        description: 'Dry run mode (only show what would be deleted)'
        required: false
        default: false
        type: boolean

permissions: write-all

env:
  TAG_NAME: ${{ inputs.tag_name || 'autobuild' }}
  TAG_CHANNEL: AutoBuild

jobs:
  check_current_version:
    name: Check Current Version and Commit
    runs-on: ubuntu-latest
    outputs:
      current_version: ${{ steps.check.outputs.current_version }}
      last_tauri_commit: ${{ steps.check.outputs.last_tauri_commit }}
      autobuild_version: ${{ steps.check.outputs.autobuild_version }}
    steps:
      - name: Checkout repository
        uses: actions/checkout@v6
        with:
          fetch-depth: 50

      - name: Get current version and find last Tauri commit
        id: check
        run: |
          CURRENT_VERSION=$(cat package.json | jq -r '.version')
          echo "📦 Current version: $CURRENT_VERSION"

          # Find the last commit that changed Tauri-related files
          echo "🔍 Finding last commit with Tauri-related changes..."

          # Define patterns for Tauri-related files
          TAURI_PATTERNS="src/ src-tauri/src src-tauri/Cargo.toml Cargo.lock src-tauri/tauri.*.conf.json src-tauri/build.rs src-tauri/capabilities"

          # Get the last commit that changed any of these patterns (excluding build artifacts)
          LAST_TAURI_COMMIT=""
          for commit in $(git rev-list HEAD --max-count=50); do
            # Check if this commit changed any Tauri-related files
            CHANGED_FILES=$(git show --name-only --pretty=format: $commit | tr '\n' ' ')
            HAS_TAURI_CHANGES=false
            
            # Check each pattern
            if echo "$CHANGED_FILES" | grep -q "src/" && echo "$CHANGED_FILES" | grep -qvE "src/(dist|build|node_modules|\.next|\.cache)"; then
              HAS_TAURI_CHANGES=true
            elif echo "$CHANGED_FILES" | grep -qE "src-tauri/(src|Cargo\.(toml|lock)|tauri\..*\.conf\.json|build\.rs|capabilities)"; then
              HAS_TAURI_CHANGES=true
            fi
            
            if [ "$HAS_TAURI_CHANGES" = true ]; then
              LAST_TAURI_COMMIT=$(git rev-parse --short $commit)
              break
            fi
          done

          if [ -z "$LAST_TAURI_COMMIT" ]; then
            echo "⚠️  No Tauri-related changes found in recent commits, using current commit"
            LAST_TAURI_COMMIT=$(git rev-parse --short HEAD)
          fi

          echo "📝 Last Tauri-related commit: $LAST_TAURI_COMMIT"
          echo "📝 Current commit: $(git rev-parse --short HEAD)"

          # Generate autobuild version for consistency
          CURRENT_BASE_VERSION=$(echo "$CURRENT_VERSION" | sed -E 's/-(alpha|beta|rc)(\.[0-9]+)?//g' | sed -E 's/\+[a-zA-Z0-9.-]+//g')
          MONTH=$(TZ=Asia/Shanghai date +%m)
          DAY=$(TZ=Asia/Shanghai date +%d)
          AUTOBUILD_VERSION="${CURRENT_BASE_VERSION}+autobuild.${MONTH}${DAY}.${LAST_TAURI_COMMIT}"

          echo "🏷️  Current autobuild version: $AUTOBUILD_VERSION"

          # Set outputs for other jobs to use
          echo "current_version=$CURRENT_VERSION" >> $GITHUB_OUTPUT
          echo "last_tauri_commit=$LAST_TAURI_COMMIT" >> $GITHUB_OUTPUT
          echo "autobuild_version=$AUTOBUILD_VERSION" >> $GITHUB_OUTPUT

  clean_old_assets:
    name: Clean Old Release Assets
    runs-on: ubuntu-latest
    needs: check_current_version
    steps:
      - name: Checkout repository
        uses: actions/checkout@v6

      - name: Clean old assets from release
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          TAG_NAME: ${{ env.TAG_NAME }}
          DRY_RUN: ${{ inputs.dry_run }}
        run: |
          # Use values from check_current_version job
          CURRENT_AUTOBUILD_VERSION="${{ needs.check_current_version.outputs.autobuild_version }}"
          LAST_TAURI_COMMIT="${{ needs.check_current_version.outputs.last_tauri_commit }}"
          CURRENT_VERSION="${{ needs.check_current_version.outputs.current_version }}"

          echo "📦 Current version: $CURRENT_VERSION"
          echo "📦 Current autobuild version: $CURRENT_AUTOBUILD_VERSION"
          echo "📝 Last Tauri commit: $LAST_TAURI_COMMIT"
          echo "🏷️  Target tag: $TAG_NAME"
          echo "🔍 Dry run mode: $DRY_RUN"

          # Check if release exists
          RELEASE_EXISTS=$(gh release view "$TAG_NAME" --json id -q '.id' 2>/dev/null || echo "")

          if [ -z "$RELEASE_EXISTS" ]; then
            echo "❌ Release '$TAG_NAME' not found"
            exit 1
          fi

          echo "✅ Found release '$TAG_NAME'"

          # Get all assets
          echo "📋 Getting list of all assets..."
          assets=$(gh release view "$TAG_NAME" --json assets -q '.assets[].name' || true)

          if [ -z "$assets" ]; then
            echo "ℹ️  No assets found in release '$TAG_NAME'"
            exit 0
          fi

          echo "📋 Found assets:"
          echo "$assets" | sed 's/^/  - /'

          # Count assets to keep and delete
          ASSETS_TO_KEEP=""
          ASSETS_TO_DELETE=""

          for asset in $assets; do
            # Keep assets that match current autobuild version or are non-versioned files (like latest.json)
            if [[ "$asset" == *"$CURRENT_AUTOBUILD_VERSION"* ]] || [[ "$asset" == "latest.json" ]]; then
              ASSETS_TO_KEEP="$ASSETS_TO_KEEP$asset\n"
            else
              ASSETS_TO_DELETE="$ASSETS_TO_DELETE$asset\n"
            fi
          done

          echo ""
          echo "🔒 Assets to keep (current version: $CURRENT_AUTOBUILD_VERSION):"
          if [ -n "$ASSETS_TO_KEEP" ]; then
            echo -e "$ASSETS_TO_KEEP" | grep -v '^$' | sed 's/^/  - /'
          else
            echo "  - None"
          fi

          echo ""
          echo "🗑️  Assets to delete:"
          if [ -n "$ASSETS_TO_DELETE" ]; then
            echo -e "$ASSETS_TO_DELETE" | grep -v '^$' | sed 's/^/  - /'
          else
            echo "  - None"
            echo "ℹ️  No old assets to clean"
            exit 0
          fi

          if [ "$DRY_RUN" = "true" ]; then
            echo ""
            echo "🔍 DRY RUN MODE: No assets will actually be deleted"
            echo "   To actually delete these assets, run this workflow again with dry_run=false"
          else
            echo ""
            echo "🗑️  Deleting old assets..."
            
            DELETED_COUNT=0
            FAILED_COUNT=0
            
            for asset in $assets; do
              # Skip assets that should be kept
              if [[ "$asset" == *"$CURRENT_AUTOBUILD_VERSION"* ]] || [[ "$asset" == "latest.json" ]]; then
                continue
              fi
              
              echo "  Deleting: $asset"
              if gh release delete-asset "$TAG_NAME" "$asset" -y 2>/dev/null; then
                DELETED_COUNT=$((DELETED_COUNT + 1))
              else
                echo "    ⚠️  Failed to delete $asset"
                FAILED_COUNT=$((FAILED_COUNT + 1))
              fi
            done

            echo ""
            echo "📊 Cleanup summary:"
            echo "  - Deleted: $DELETED_COUNT assets"
            if [ $FAILED_COUNT -gt 0 ]; then
              echo "  - Failed: $FAILED_COUNT assets"
            fi
            echo "  - Kept: $(echo -e "$ASSETS_TO_KEEP" | grep -v '^$' | wc -l) assets"
            
            if [ $FAILED_COUNT -gt 0 ]; then
              echo "⚠️  Some assets failed to delete. Please check the logs above."
              exit 1
            else
              echo "✅ Cleanup completed successfully!"
            fi
          fi
cross_check matrix perms .github/workflows/cross_check.yaml
Triggers
workflow_dispatch
Runs on
${{ matrix.os }}
Jobs
cargo-check
Matrix
include, include.os, include.target→ aarch64-apple-darwin, macos-latest, ubuntu-latest, windows-latest, x86_64-pc-windows-msvc, x86_64-unknown-linux-gnu
Actions
dtolnay/rust-toolchain, pnpm/action-setup, Swatinem/rust-cache
Commands
  • rustup target add ${{ matrix.target }}
  • pnpm i pnpm run prebuild ${{ matrix.target }}
  • cargo check --target ${{ matrix.target }} --workspace --all-features
View raw YAML
name: Cross Platform Cargo Check

on:
  workflow_dispatch:
#   pull_request:
#   push:
# branches: [main, dev]

permissions:
  contents: read

env:
  HUSKY: 0

jobs:
  cargo-check:
    # Treat all Rust compiler warnings as errors
    env:
      RUSTFLAGS: '-D warnings'
    strategy:
      fail-fast: false
      matrix:
        include:
          - os: macos-latest
            target: aarch64-apple-darwin
          - os: windows-latest
            target: x86_64-pc-windows-msvc
          - os: ubuntu-latest
            target: x86_64-unknown-linux-gnu
    runs-on: ${{ matrix.os }}
    steps:
      - name: Checkout Repository
        uses: actions/checkout@v6

      - name: Install Rust Stable
        uses: dtolnay/rust-toolchain@stable
        with:
          targets: ${{ matrix.target }}

      - name: Add Rust Target
        run: rustup target add ${{ matrix.target }}

      - name: Install Node
        uses: actions/setup-node@v6
        with:
          node-version: '24.14.1'

      - uses: pnpm/action-setup@v5
        name: Install pnpm
        with:
          run_install: false

      - name: Pnpm install and check
        run: |
          pnpm i
          pnpm run prebuild ${{ matrix.target }}

      - name: Rust Cache
        uses: Swatinem/rust-cache@v2
        with:
          workspaces: src-tauri
          save-if: false

      - name: Cargo Check (deny warnings)
        working-directory: src-tauri
        run: |
          cargo check --target ${{ matrix.target }} --workspace --all-features
dev matrix perms .github/workflows/dev.yml
Triggers
workflow_dispatch
Runs on
${{ matrix.os }}
Jobs
dev
Matrix
include, include.bundle, include.id, include.input, include.os, include.target→ aarch64-apple-darwin, aarch64-pc-windows-msvc, deb, dmg, linux-amd64, macos-aarch64, macos-latest, nsis, run_linux_amd64, run_macos_aarch64, run_windows, run_windows_arm64, ubuntu-22.04, windows, windows-arm64, windows-latest, x86_64-pc-windows-msvc, x86_64-unknown-linux-gnu
Actions
dtolnay/rust-toolchain, Swatinem/rust-cache, pnpm/action-setup, tauri-apps/tauri-action
Commands
  • echo "Job ${{ matrix.id }} skipped as requested"
  • sudo apt-get update sudo apt-get install -y libxslt1.1 libwebkit2gtk-4.1-dev libayatana-appindicator3-dev librsvg2-dev patchelf
  • pnpm i pnpm run prebuild ${{ matrix.target }}
  • pnpm release-version ${{ env.TAG_NAME }}
  • # Ensure cross target is installed for the pinned toolchain; fallback without explicit toolchain if needed rustup target add ${{ matrix.target }} --toolchain 1.91.0 || rustup target add ${{ matrix.target }} rustup target list --installed echo "Rust target ${{ matrix.target }} installed."
View raw YAML
name: Development Test

on:
  workflow_dispatch:
    inputs:
      run_windows:
        description: '运行 Windows'
        required: false
        type: boolean
        default: true
      run_macos_aarch64:
        description: '运行 macOS aarch64'
        required: false
        type: boolean
        default: true
      run_windows_arm64:
        description: '运行 Windows ARM64'
        required: false
        type: boolean
        default: true
      run_linux_amd64:
        description: '运行 Linux amd64'
        required: false
        type: boolean
        default: true

permissions: write-all
env:
  TAG_NAME: deploytest
  TAG_CHANNEL: DeployTest
  CARGO_INCREMENTAL: 0
  RUST_BACKTRACE: short
  HUSKY: 0
concurrency:
  group: '${{ github.workflow }} - ${{ github.head_ref || github.ref }}'
  cancel-in-progress: ${{ github.ref != 'refs/heads/main' }}

jobs:
  dev:
    strategy:
      fail-fast: false
      matrix:
        include:
          - os: windows-latest
            target: x86_64-pc-windows-msvc
            bundle: nsis
            id: windows
            input: run_windows
          - os: macos-latest
            target: aarch64-apple-darwin
            bundle: dmg
            id: macos-aarch64
            input: run_macos_aarch64
          - os: windows-latest
            target: aarch64-pc-windows-msvc
            bundle: nsis
            id: windows-arm64
            input: run_windows_arm64
          - os: ubuntu-22.04
            target: x86_64-unknown-linux-gnu
            bundle: deb
            id: linux-amd64
            input: run_linux_amd64

    runs-on: ${{ matrix.os }}
    steps:
      - name: Skip job if not selected
        if: github.event.inputs[matrix.input] != 'true'
        run: echo "Job ${{ matrix.id }} skipped as requested"

      - name: Checkout Repository
        if: github.event.inputs[matrix.input] == 'true'
        uses: actions/checkout@v6

      - name: Install Rust Stable
        if: github.event.inputs[matrix.input] == 'true'
        uses: dtolnay/rust-toolchain@1.91.0

      - name: Rust Cache
        uses: Swatinem/rust-cache@v2
        with:
          save-if: ${{ github.ref == 'refs/heads/dev' }}
          prefix-key: 'v1-rust'
          key: 'rust-shared-stable-${{ matrix.os }}-${{ matrix.target }}'
          workspaces: |
            . -> target
          cache-all-crates: true
          cache-workspace-crates: true

      - name: Install dependencies (ubuntu only)
        if: matrix.os == 'ubuntu-22.04' && github.event.inputs[matrix.input] == 'true'
        run: |
          sudo apt-get update
          sudo apt-get install -y libxslt1.1 libwebkit2gtk-4.1-dev libayatana-appindicator3-dev librsvg2-dev patchelf

      - uses: pnpm/action-setup@v5
        name: Install pnpm
        if: github.event.inputs[matrix.input] == 'true'
        with:
          run_install: false

      - name: Install Node
        if: github.event.inputs[matrix.input] == 'true'
        uses: actions/setup-node@v6
        with:
          node-version: '24.14.1'
          cache: 'pnpm'

      - name: Pnpm Cache
        uses: actions/cache@v5
        with:
          path: ~/.pnpm-store
          key: 'pnpm-shared-stable-${{ matrix.os }}-${{ matrix.target }}'
          restore-keys: |
            pnpm-shared-stable-${{ matrix.os }}-${{ matrix.target }}
          lookup-only: true

      - name: Pnpm install and check
        if: github.event.inputs[matrix.input] == 'true'
        run: |
          pnpm i
          pnpm run prebuild ${{ matrix.target }}

      - name: Release ${{ env.TAG_CHANNEL }} Version
        if: github.event.inputs[matrix.input] == 'true'
        run: pnpm release-version ${{ env.TAG_NAME }}

      - name: Add Rust Target
        if: github.event.inputs[matrix.input] == 'true'
        run: |
          # Ensure cross target is installed for the pinned toolchain; fallback without explicit toolchain if needed
          rustup target add ${{ matrix.target }} --toolchain 1.91.0 || rustup target add ${{ matrix.target }}
          rustup target list --installed
          echo "Rust target ${{ matrix.target }} installed."

      - name: Tauri build
        if: github.event.inputs[matrix.input] == 'true'
        uses: tauri-apps/tauri-action@v0
        env:
          NODE_OPTIONS: '--max_old_space_size=4096'
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_PRIVATE_KEY }}
          TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_KEY_PASSWORD }}
          APPLE_CERTIFICATE: ${{ secrets.APPLE_CERTIFICATE }}
          APPLE_CERTIFICATE_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }}
          APPLE_SIGNING_IDENTITY: ${{ secrets.APPLE_SIGNING_IDENTITY }}
          APPLE_ID: ${{ secrets.APPLE_ID }}
          APPLE_PASSWORD: ${{ secrets.APPLE_PASSWORD }}
          APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
        with:
          tauriScript: pnpm
          args: --target ${{ matrix.target }} -b ${{ matrix.bundle }}

      - name: Upload Artifacts (macOS)
        if: matrix.os == 'macos-latest' && github.event.inputs[matrix.input] == 'true'
        uses: actions/upload-artifact@v7
        with:
          archive: false
          path: target/${{ matrix.target }}/release/bundle/dmg/*.dmg
          if-no-files-found: error

      - name: Upload Artifacts (Windows)
        if: matrix.os == 'windows-latest' && github.event.inputs[matrix.input] == 'true'
        uses: actions/upload-artifact@v7
        with:
          archive: false
          path: target/${{ matrix.target }}/release/bundle/nsis/*.exe
          if-no-files-found: error

      - name: Upload Artifacts (Linux)
        if: matrix.os == 'ubuntu-22.04' && github.event.inputs[matrix.input] == 'true'
        uses: actions/upload-artifact@v7
        with:
          archive: false
          path: target/${{ matrix.target }}/release/bundle/deb/*.deb
          if-no-files-found: error
frontend-check .github/workflows/frontend-check.yml
Triggers
pull_request, workflow_dispatch
Runs on
ubuntu-latest
Jobs
frontend
Actions
dorny/paths-filter, pnpm/action-setup
Commands
  • echo "No frontend changes, skipping frontend checks."
  • pnpm install --frozen-lockfile
  • pnpm format:check
  • pnpm lint
  • pnpm typecheck
View raw YAML
name: Frontend Check

on:
  pull_request:
  workflow_dispatch:

env:
  HUSKY: 0

jobs:
  frontend:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v6

      - name: Check frontend changes
        id: check_frontend
        uses: dorny/paths-filter@v4
        with:
          filters: |
            frontend:
              - 'src/**'
              - '**/*.js'
              - '**/*.ts'
              - '**/*.tsx'
              - '**/*.css'
              - '**/*.scss'
              - '**/*.json'
              - '**/*.md'
              - 'package.json'
              - 'pnpm-lock.yaml'
              - 'pnpm-workspace.yaml'
              - 'eslint.config.ts'
              - 'tsconfig.json'
              - 'vite.config.*'

      - name: Skip if no frontend changes
        if: steps.check_frontend.outputs.frontend != 'true'
        run: echo "No frontend changes, skipping frontend checks."

      - name: Install pnpm
        if: steps.check_frontend.outputs.frontend == 'true'
        uses: pnpm/action-setup@v5
        with:
          run_install: false

      - uses: actions/setup-node@v6
        if: steps.check_frontend.outputs.frontend == 'true'
        with:
          node-version: '24.14.1'
          cache: 'pnpm'

      - name: Restore pnpm cache
        if: steps.check_frontend.outputs.frontend == 'true'
        uses: actions/cache@v5
        with:
          path: ~/.pnpm-store
          key: "pnpm-shared-stable-${{ runner.os }}-${{ hashFiles('pnpm-lock.yaml') }}"
          restore-keys: |
            pnpm-shared-stable-${{ runner.os }}-

      - run: pnpm install --frozen-lockfile
        if: steps.check_frontend.outputs.frontend == 'true'

      - name: Run Prettier check
        if: steps.check_frontend.outputs.frontend == 'true'
        run: pnpm format:check

      - name: Run ESLint
        if: steps.check_frontend.outputs.frontend == 'true'
        run: pnpm lint

      - name: Run TypeScript typecheck
        if: steps.check_frontend.outputs.frontend == 'true'
        run: pnpm typecheck
lint-clippy matrix .github/workflows/lint-clippy.yml
Triggers
pull_request, workflow_dispatch
Runs on
${{ matrix.os }}
Jobs
clippy
Matrix
include, include.os, include.target→ aarch64-apple-darwin, macos-latest, ubuntu-22.04, windows-latest, x86_64-pc-windows-msvc, x86_64-unknown-linux-gnu
Actions
dorny/paths-filter, dtolnay/rust-toolchain, Swatinem/rust-cache
Commands
  • echo "No src-tauri changes, skipping clippy lint."
  • echo "src-tauri changed, running clippy lint."
  • echo "Manual trigger detected: skipping changes check and running clippy."
  • rustup target add ${{ matrix.target }}
  • sudo apt-get update sudo apt-get install -y libxslt1.1 libwebkit2gtk-4.1-dev libayatana-appindicator3-dev librsvg2-dev patchelf
  • cargo clippy-all
  • cargo install --git https://github.com/clash-verge-rev/clash-verge-logging-check.git clash-verge-logging-check
View raw YAML
name: Clippy Lint

on:
  pull_request:
  workflow_dispatch:
env:
  HUSKY: 0

jobs:
  clippy:
    strategy:
      fail-fast: false
      matrix:
        include:
          - os: windows-latest
            target: x86_64-pc-windows-msvc
          - os: macos-latest
            target: aarch64-apple-darwin
          - os: ubuntu-22.04
            target: x86_64-unknown-linux-gnu

    runs-on: ${{ matrix.os }}
    steps:
      - name: Check src-tauri changes
        if: github.event_name != 'workflow_dispatch'
        id: check_changes
        uses: dorny/paths-filter@v4
        with:
          filters: |
            rust:
              - 'src-tauri/**'

      - name: Skip if src-tauri not changed
        if: github.event_name != 'workflow_dispatch' && steps.check_changes.outputs.rust != 'true'
        run: echo "No src-tauri changes, skipping clippy lint."

      - name: Continue if src-tauri changed
        if: github.event_name != 'workflow_dispatch' && steps.check_changes.outputs.rust == 'true'
        run: echo "src-tauri changed, running clippy lint."

      - name: Manual trigger - always run
        if: github.event_name == 'workflow_dispatch'
        run: |
          echo "Manual trigger detected: skipping changes check and running clippy."

      - name: Checkout Repository
        uses: actions/checkout@v6

      - name: Install Rust Stable
        uses: dtolnay/rust-toolchain@master
        with:
          toolchain: stable
          components: clippy

      - name: Add Rust Target
        run: rustup target add ${{ matrix.target }}

      - name: Rust Cache
        uses: Swatinem/rust-cache@v2
        with:
          save-if: ${{ github.ref == 'refs/heads/dev' }}
          prefix-key: 'v1-rust'
          key: 'rust-shared-stable-${{ matrix.os }}-${{ matrix.target }}'
          workspaces: |
            . -> target
          cache-all-crates: true
          cache-workspace-crates: true

      - name: Install dependencies (ubuntu only)
        if: matrix.os == 'ubuntu-22.04'
        run: |
          sudo apt-get update
          sudo apt-get install -y libxslt1.1 libwebkit2gtk-4.1-dev libayatana-appindicator3-dev librsvg2-dev patchelf

      - name: Run Clippy
        working-directory: ./src-tauri
        run: cargo clippy-all

      - name: Run Logging Check
        working-directory: ./src-tauri
        shell: bash
        run: |
          cargo install --git https://github.com/clash-verge-rev/clash-verge-logging-check.git
          clash-verge-logging-check
pr-ai-slop-review.lock perms .github/workflows/pr-ai-slop-review.lock.yml
Triggers
pull_request_target, workflow_dispatch
Runs on
ubuntu-slim, ubuntu-latest, ubuntu-slim, ubuntu-latest, ubuntu-slim
Jobs
activation, agent, conclusion, detection, safe_outputs
Actions
github/gh-aw-actions/setup, github/gh-aw-actions/setup, github/gh-aw-actions/setup, github/gh-aw-actions/setup, github/gh-aw-actions/setup
Commands
  • ${RUNNER_TEMP}/gh-aw/actions/validate_multi_secret.sh COPILOT_GITHUB_TOKEN 'GitHub Copilot CLI' https://github.github.com/gh-aw/reference/engines/#github-copilot-default
  • bash ${RUNNER_TEMP}/gh-aw/actions/create_prompt_first.sh { cat << 'GH_AW_PROMPT_EOF' <system> GH_AW_PROMPT_EOF cat "${RUNNER_TEMP}/gh-aw/prompts/xpia.md" cat "${RUNNER_TEMP}/gh-aw/prompts/temp_folder_prompt.md" cat "${RUNNER_TEMP}/gh-aw/prompts/markdown.md" cat "${RUNNER_TEMP}/gh-aw/prompts/safe_outputs_prompt.md" cat << 'GH_AW_PROMPT_EOF' <safe-output-tools> Tools: add_comment, add_labels, remove_labels(max:2), missing_tool, missing_data, noop </safe-output-tools> <github-context> The following GitHub context information is available for this workflow: {{#if __GH_AW_GITHUB_ACTOR__ }} - **actor**: __GH_AW_GITHUB_ACTOR__ {{/if}} {{#if __GH_AW_GITHUB_REPOSITORY__ }} - **repository**: __GH_AW_GITHUB_REPOSITORY__ {{/if}} {{#if __GH_AW_GITHUB_WORKSPACE__ }} - **workspace**: __GH_AW_GITHUB_WORKSPACE__ {{/if}} {{#if __GH_AW_GITHUB_EVENT_ISSUE_NUMBER__ }} - **issue-number**: #__GH_AW_GITHUB_EVENT_ISSUE_NUMBER__ {{/if}} {{#if __GH_AW_GITHUB_EVENT_DISCUSSION_NUMBER__ }} - **discussion-number**: #__GH_AW_GITHUB_EVENT_DISCUSSION_NUMBER__ {{/if}} {{#if __GH_AW_GITHUB_EVENT_PULL_REQUEST_NUMBER__ }} - **pull-request-number**: #__GH_AW_GITHUB_EVENT_PULL_REQUEST_NUMBER__ {{/if}} {{#if __GH_AW_GITHUB_EVENT_COMMENT_ID__ }} - **comment-id**: __GH_AW_GITHUB_EVENT_COMMENT_ID__ {{/if}} {{#if __GH_AW_GITHUB_RUN_ID__ }} - **workflow-run-id**: __GH_AW_GITHUB_RUN_ID__ {{/if}} </github-context> GH_AW_PROMPT_EOF cat "${RUNNER_TEMP}/gh-aw/prompts/github_mcp_tools_with_safeoutputs_prompt.md" cat << 'GH_AW_PROMPT_EOF' </system> GH_AW_PROMPT_EOF cat << 'GH_AW_PROMPT_EOF' {{#runtime-import .github/workflows/pr-ai-slop-review.md}} GH_AW_PROMPT_EOF } > "$GH_AW_PROMPT"
  • bash ${RUNNER_TEMP}/gh-aw/actions/validate_prompt_placeholders.sh
  • bash ${RUNNER_TEMP}/gh-aw/actions/print_prompt_summary.sh
  • echo "GH_AW_SAFE_OUTPUTS=${RUNNER_TEMP}/gh-aw/safeoutputs/outputs.jsonl" >> "$GITHUB_OUTPUT" echo "GH_AW_SAFE_OUTPUTS_CONFIG_PATH=${RUNNER_TEMP}/gh-aw/safeoutputs/config.json" >> "$GITHUB_OUTPUT" echo "GH_AW_SAFE_OUTPUTS_TOOLS_PATH=${RUNNER_TEMP}/gh-aw/safeoutputs/tools.json" >> "$GITHUB_OUTPUT"
  • bash ${RUNNER_TEMP}/gh-aw/actions/create_gh_aw_tmp_dir.sh
  • bash ${RUNNER_TEMP}/gh-aw/actions/configure_gh_for_ghe.sh
  • git config --global user.email "github-actions[bot]@users.noreply.github.com" git config --global user.name "github-actions[bot]" git config --global am.keepcr true # Re-authenticate git with GitHub token SERVER_URL_STRIPPED="${SERVER_URL#https://}" git remote set-url origin "https://x-access-token:${{ github.token }}@${SERVER_URL_STRIPPED}/${REPO_NAME}.git" echo "Git configured with standard GitHub Actions identity"
View raw YAML
#    ___                   _   _      
#   / _ \                 | | (_)     
#  | |_| | __ _  ___ _ __ | |_ _  ___ 
#  |  _  |/ _` |/ _ \ '_ \| __| |/ __|
#  | | | | (_| |  __/ | | | |_| | (__ 
#  \_| |_/\__, |\___|_| |_|\__|_|\___|
#          __/ |
#  _    _ |___/ 
# | |  | |                / _| |
# | |  | | ___ _ __ _  __| |_| | _____      ____
# | |/\| |/ _ \ '__| |/ /|  _| |/ _ \ \ /\ / / ___|
# \  /\  / (_) | | | | ( | | | | (_) \ V  V /\__ \
#  \/  \/ \___/|_| |_|\_\|_| |_|\___/ \_/\_/ |___/
#
# This file was automatically generated by gh-aw (v0.64.0). DO NOT EDIT.
#
# To update this file, edit the corresponding .md file and run:
#   gh aw compile
# Not all edits will cause changes to this file.
#
# For more information: https://github.github.com/gh-aw/introduction/overview/
#
# Reviews incoming pull requests for missing issue linkage and high-confidence
# signs of one-shot AI-generated changes, then posts a maintainer-focused
# comment when the risk is high enough to warrant follow-up.
#
# gh-aw-metadata: {"schema_version":"v3","frontmatter_hash":"6b65c1d60eaf3a611755c7def95a13c3673df2f25ff609508e99d59c77d57d8d","compiler_version":"v0.64.0","strict":true,"agent_id":"copilot"}

name: "PR AI Slop Review"
"on":
  pull_request_target:
    types:
    - opened
    - synchronize
  # roles: all # Roles processed as role check in pre-activation job
  workflow_dispatch:
    inputs:
      aw_context:
        default: ""
        description: Agent caller context (used internally by Agentic Workflows).
        required: false
        type: string

permissions: {}

concurrency:
  group: "gh-aw-${{ github.workflow }}-${{ github.event.pull_request.number || github.ref || github.run_id }}"
  cancel-in-progress: true

run-name: "PR AI Slop Review"

jobs:
  activation:
    runs-on: ubuntu-slim
    permissions:
      contents: read
    outputs:
      body: ${{ steps.sanitized.outputs.body }}
      comment_id: ""
      comment_repo: ""
      lockdown_check_failed: ${{ steps.generate_aw_info.outputs.lockdown_check_failed == 'true' }}
      model: ${{ steps.generate_aw_info.outputs.model }}
      secret_verification_result: ${{ steps.validate-secret.outputs.verification_result }}
      text: ${{ steps.sanitized.outputs.text }}
      title: ${{ steps.sanitized.outputs.title }}
    steps:
      - name: Setup Scripts
        uses: github/gh-aw-actions/setup@0048fdad270986610dd71c966d15f7abd7de037a # v0.64.2
        with:
          destination: ${{ runner.temp }}/gh-aw/actions
      - name: Generate agentic run info
        id: generate_aw_info
        env:
          GH_AW_INFO_ENGINE_ID: "copilot"
          GH_AW_INFO_ENGINE_NAME: "GitHub Copilot CLI"
          GH_AW_INFO_MODEL: ${{ vars.GH_AW_MODEL_AGENT_COPILOT || 'auto' }}
          GH_AW_INFO_VERSION: "latest"
          GH_AW_INFO_AGENT_VERSION: "latest"
          GH_AW_INFO_CLI_VERSION: "v0.64.0"
          GH_AW_INFO_WORKFLOW_NAME: "PR AI Slop Review"
          GH_AW_INFO_EXPERIMENTAL: "false"
          GH_AW_INFO_SUPPORTS_TOOLS_ALLOWLIST: "true"
          GH_AW_INFO_STAGED: "false"
          GH_AW_INFO_ALLOWED_DOMAINS: '["defaults"]'
          GH_AW_INFO_FIREWALL_ENABLED: "true"
          GH_AW_INFO_AWF_VERSION: "v0.25.0"
          GH_AW_INFO_AWMG_VERSION: ""
          GH_AW_INFO_FIREWALL_TYPE: "squid"
          GH_AW_COMPILED_STRICT: "true"
        uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
        with:
          script: |
            const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs');
            setupGlobals(core, github, context, exec, io);
            const { main } = require('${{ runner.temp }}/gh-aw/actions/generate_aw_info.cjs');
            await main(core, context);
      - name: Validate COPILOT_GITHUB_TOKEN secret
        id: validate-secret
        run: ${RUNNER_TEMP}/gh-aw/actions/validate_multi_secret.sh COPILOT_GITHUB_TOKEN 'GitHub Copilot CLI' https://github.github.com/gh-aw/reference/engines/#github-copilot-default
        env:
          COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }}
      - name: Checkout .github and .agents folders
        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
        with:
          persist-credentials: false
          sparse-checkout: |
            .github
            .agents
          sparse-checkout-cone-mode: true
          fetch-depth: 1
      - name: Check workflow file timestamps
        uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
        env:
          GH_AW_WORKFLOW_FILE: "pr-ai-slop-review.lock.yml"
        with:
          script: |
            const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs');
            setupGlobals(core, github, context, exec, io);
            const { main } = require('${{ runner.temp }}/gh-aw/actions/check_workflow_timestamp_api.cjs');
            await main();
      - name: Compute current body text
        id: sanitized
        uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
        with:
          script: |
            const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs');
            setupGlobals(core, github, context, exec, io);
            const { main } = require('${{ runner.temp }}/gh-aw/actions/compute_text.cjs');
            await main();
      - name: Create prompt with built-in context
        env:
          GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
          GH_AW_SAFE_OUTPUTS: ${{ runner.temp }}/gh-aw/safeoutputs/outputs.jsonl
          GH_AW_GITHUB_ACTOR: ${{ github.actor }}
          GH_AW_GITHUB_EVENT_COMMENT_ID: ${{ github.event.comment.id }}
          GH_AW_GITHUB_EVENT_DISCUSSION_NUMBER: ${{ github.event.discussion.number }}
          GH_AW_GITHUB_EVENT_ISSUE_NUMBER: ${{ github.event.issue.number }}
          GH_AW_GITHUB_EVENT_PULL_REQUEST_NUMBER: ${{ github.event.pull_request.number }}
          GH_AW_GITHUB_REPOSITORY: ${{ github.repository }}
          GH_AW_GITHUB_RUN_ID: ${{ github.run_id }}
          GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
        # poutine:ignore untrusted_checkout_exec
        run: |
          bash ${RUNNER_TEMP}/gh-aw/actions/create_prompt_first.sh
          {
          cat << 'GH_AW_PROMPT_EOF'
          <system>
          GH_AW_PROMPT_EOF
          cat "${RUNNER_TEMP}/gh-aw/prompts/xpia.md"
          cat "${RUNNER_TEMP}/gh-aw/prompts/temp_folder_prompt.md"
          cat "${RUNNER_TEMP}/gh-aw/prompts/markdown.md"
          cat "${RUNNER_TEMP}/gh-aw/prompts/safe_outputs_prompt.md"
          cat << 'GH_AW_PROMPT_EOF'
          <safe-output-tools>
          Tools: add_comment, add_labels, remove_labels(max:2), missing_tool, missing_data, noop
          </safe-output-tools>
          <github-context>
          The following GitHub context information is available for this workflow:
          {{#if __GH_AW_GITHUB_ACTOR__ }}
          - **actor**: __GH_AW_GITHUB_ACTOR__
          {{/if}}
          {{#if __GH_AW_GITHUB_REPOSITORY__ }}
          - **repository**: __GH_AW_GITHUB_REPOSITORY__
          {{/if}}
          {{#if __GH_AW_GITHUB_WORKSPACE__ }}
          - **workspace**: __GH_AW_GITHUB_WORKSPACE__
          {{/if}}
          {{#if __GH_AW_GITHUB_EVENT_ISSUE_NUMBER__ }}
          - **issue-number**: #__GH_AW_GITHUB_EVENT_ISSUE_NUMBER__
          {{/if}}
          {{#if __GH_AW_GITHUB_EVENT_DISCUSSION_NUMBER__ }}
          - **discussion-number**: #__GH_AW_GITHUB_EVENT_DISCUSSION_NUMBER__
          {{/if}}
          {{#if __GH_AW_GITHUB_EVENT_PULL_REQUEST_NUMBER__ }}
          - **pull-request-number**: #__GH_AW_GITHUB_EVENT_PULL_REQUEST_NUMBER__
          {{/if}}
          {{#if __GH_AW_GITHUB_EVENT_COMMENT_ID__ }}
          - **comment-id**: __GH_AW_GITHUB_EVENT_COMMENT_ID__
          {{/if}}
          {{#if __GH_AW_GITHUB_RUN_ID__ }}
          - **workflow-run-id**: __GH_AW_GITHUB_RUN_ID__
          {{/if}}
          </github-context>
          
          GH_AW_PROMPT_EOF
          cat "${RUNNER_TEMP}/gh-aw/prompts/github_mcp_tools_with_safeoutputs_prompt.md"
          cat << 'GH_AW_PROMPT_EOF'
          </system>
          GH_AW_PROMPT_EOF
          cat << 'GH_AW_PROMPT_EOF'
          {{#runtime-import .github/workflows/pr-ai-slop-review.md}}
          GH_AW_PROMPT_EOF
          } > "$GH_AW_PROMPT"
      - name: Interpolate variables and render templates
        uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
        env:
          GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
        with:
          script: |
            const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs');
            setupGlobals(core, github, context, exec, io);
            const { main } = require('${{ runner.temp }}/gh-aw/actions/interpolate_prompt.cjs');
            await main();
      - name: Substitute placeholders
        uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
        env:
          GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
          GH_AW_GITHUB_ACTOR: ${{ github.actor }}
          GH_AW_GITHUB_EVENT_COMMENT_ID: ${{ github.event.comment.id }}
          GH_AW_GITHUB_EVENT_DISCUSSION_NUMBER: ${{ github.event.discussion.number }}
          GH_AW_GITHUB_EVENT_ISSUE_NUMBER: ${{ github.event.issue.number }}
          GH_AW_GITHUB_EVENT_PULL_REQUEST_NUMBER: ${{ github.event.pull_request.number }}
          GH_AW_GITHUB_REPOSITORY: ${{ github.repository }}
          GH_AW_GITHUB_RUN_ID: ${{ github.run_id }}
          GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
        with:
          script: |
            const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs');
            setupGlobals(core, github, context, exec, io);
            
            const substitutePlaceholders = require('${{ runner.temp }}/gh-aw/actions/substitute_placeholders.cjs');
            
            // Call the substitution function
            return await substitutePlaceholders({
              file: process.env.GH_AW_PROMPT,
              substitutions: {
                GH_AW_GITHUB_ACTOR: process.env.GH_AW_GITHUB_ACTOR,
                GH_AW_GITHUB_EVENT_COMMENT_ID: process.env.GH_AW_GITHUB_EVENT_COMMENT_ID,
                GH_AW_GITHUB_EVENT_DISCUSSION_NUMBER: process.env.GH_AW_GITHUB_EVENT_DISCUSSION_NUMBER,
                GH_AW_GITHUB_EVENT_ISSUE_NUMBER: process.env.GH_AW_GITHUB_EVENT_ISSUE_NUMBER,
                GH_AW_GITHUB_EVENT_PULL_REQUEST_NUMBER: process.env.GH_AW_GITHUB_EVENT_PULL_REQUEST_NUMBER,
                GH_AW_GITHUB_REPOSITORY: process.env.GH_AW_GITHUB_REPOSITORY,
                GH_AW_GITHUB_RUN_ID: process.env.GH_AW_GITHUB_RUN_ID,
                GH_AW_GITHUB_WORKSPACE: process.env.GH_AW_GITHUB_WORKSPACE
              }
            });
      - name: Validate prompt placeholders
        env:
          GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
        # poutine:ignore untrusted_checkout_exec
        run: bash ${RUNNER_TEMP}/gh-aw/actions/validate_prompt_placeholders.sh
      - name: Print prompt
        env:
          GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
        # poutine:ignore untrusted_checkout_exec
        run: bash ${RUNNER_TEMP}/gh-aw/actions/print_prompt_summary.sh
      - name: Upload activation artifact
        if: success()
        uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7
        with:
          name: activation
          path: |
            /tmp/gh-aw/aw_info.json
            /tmp/gh-aw/aw-prompts/prompt.txt
          retention-days: 1

  agent:
    needs: activation
    runs-on: ubuntu-latest
    permissions:
      contents: read
      issues: read
      pull-requests: read
    env:
      DEFAULT_BRANCH: ${{ github.event.repository.default_branch }}
      GH_AW_ASSETS_ALLOWED_EXTS: ""
      GH_AW_ASSETS_BRANCH: ""
      GH_AW_ASSETS_MAX_SIZE_KB: 0
      GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
      GH_AW_WORKFLOW_ID_SANITIZED: praislopreview
    outputs:
      checkout_pr_success: ${{ steps.checkout-pr.outputs.checkout_pr_success || 'true' }}
      has_patch: ${{ steps.collect_output.outputs.has_patch }}
      inference_access_error: ${{ steps.detect-inference-error.outputs.inference_access_error || 'false' }}
      model: ${{ needs.activation.outputs.model }}
      output: ${{ steps.collect_output.outputs.output }}
      output_types: ${{ steps.collect_output.outputs.output_types }}
    steps:
      - name: Setup Scripts
        uses: github/gh-aw-actions/setup@0048fdad270986610dd71c966d15f7abd7de037a # v0.64.2
        with:
          destination: ${{ runner.temp }}/gh-aw/actions
      - name: Set runtime paths
        id: set-runtime-paths
        run: |
          echo "GH_AW_SAFE_OUTPUTS=${RUNNER_TEMP}/gh-aw/safeoutputs/outputs.jsonl" >> "$GITHUB_OUTPUT"
          echo "GH_AW_SAFE_OUTPUTS_CONFIG_PATH=${RUNNER_TEMP}/gh-aw/safeoutputs/config.json" >> "$GITHUB_OUTPUT"
          echo "GH_AW_SAFE_OUTPUTS_TOOLS_PATH=${RUNNER_TEMP}/gh-aw/safeoutputs/tools.json" >> "$GITHUB_OUTPUT"
      - name: Checkout repository
        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
        with:
          persist-credentials: false
      - name: Create gh-aw temp directory
        run: bash ${RUNNER_TEMP}/gh-aw/actions/create_gh_aw_tmp_dir.sh
      - name: Configure gh CLI for GitHub Enterprise
        run: bash ${RUNNER_TEMP}/gh-aw/actions/configure_gh_for_ghe.sh
        env:
          GH_TOKEN: ${{ github.token }}
      - name: Configure Git credentials
        env:
          REPO_NAME: ${{ github.repository }}
          SERVER_URL: ${{ github.server_url }}
        run: |
          git config --global user.email "github-actions[bot]@users.noreply.github.com"
          git config --global user.name "github-actions[bot]"
          git config --global am.keepcr true
          # Re-authenticate git with GitHub token
          SERVER_URL_STRIPPED="${SERVER_URL#https://}"
          git remote set-url origin "https://x-access-token:${{ github.token }}@${SERVER_URL_STRIPPED}/${REPO_NAME}.git"
          echo "Git configured with standard GitHub Actions identity"
      - name: Checkout PR branch
        id: checkout-pr
        if: |
          github.event.pull_request || github.event.issue.pull_request
        uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
        env:
          GH_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
        with:
          github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
          script: |
            const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs');
            setupGlobals(core, github, context, exec, io);
            const { main } = require('${{ runner.temp }}/gh-aw/actions/checkout_pr_branch.cjs');
            await main();
      - name: Install GitHub Copilot CLI
        run: ${RUNNER_TEMP}/gh-aw/actions/install_copilot_cli.sh latest
        env:
          GH_HOST: github.com
      - name: Install AWF binary
        run: bash ${RUNNER_TEMP}/gh-aw/actions/install_awf_binary.sh v0.25.0
      - name: Parse integrity filter lists
        id: parse-guard-vars
        env:
          GH_AW_BLOCKED_USERS_VAR: ${{ vars.GH_AW_GITHUB_BLOCKED_USERS || '' }}
          GH_AW_APPROVAL_LABELS_VAR: ${{ vars.GH_AW_GITHUB_APPROVAL_LABELS || '' }}
        run: bash ${RUNNER_TEMP}/gh-aw/actions/parse_guard_list.sh
      - name: Download container images
        run: bash ${RUNNER_TEMP}/gh-aw/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.25.0 ghcr.io/github/gh-aw-firewall/api-proxy:0.25.0 ghcr.io/github/gh-aw-firewall/squid:0.25.0 ghcr.io/github/gh-aw-mcpg:v0.2.6 ghcr.io/github/github-mcp-server:v0.32.0 node:lts-alpine
      - name: Write Safe Outputs Config
        run: |
          mkdir -p ${RUNNER_TEMP}/gh-aw/safeoutputs
          mkdir -p /tmp/gh-aw/safeoutputs
          mkdir -p /tmp/gh-aw/mcp-logs/safeoutputs
          cat > ${RUNNER_TEMP}/gh-aw/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
          {"add_comment":{"hide_older_comments":true,"max":1},"add_labels":{"allowed":["ai-slop:high","ai-slop:med"],"max":1},"mentions":{"enabled":false},"missing_data":{},"missing_tool":{},"noop":{"max":1,"report-as-issue":"true"},"remove_labels":{"allowed":["ai-slop:high","ai-slop:med"],"max":2}}
          GH_AW_SAFE_OUTPUTS_CONFIG_EOF
      - name: Write Safe Outputs Tools
        run: |
          cat > ${RUNNER_TEMP}/gh-aw/safeoutputs/tools_meta.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF'
          {
            "description_suffixes": {
              "add_comment": " CONSTRAINTS: Maximum 1 comment(s) can be added.",
              "add_labels": " CONSTRAINTS: Maximum 1 label(s) can be added. Only these labels are allowed: [\"ai-slop:high\" \"ai-slop:med\"].",
              "remove_labels": " CONSTRAINTS: Maximum 2 label(s) can be removed. Only these labels can be removed: [ai-slop:high ai-slop:med]."
            },
            "repo_params": {},
            "dynamic_tools": []
          }
          GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF
          cat > ${RUNNER_TEMP}/gh-aw/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
          {
            "add_comment": {
              "defaultMax": 1,
              "fields": {
                "body": {
                  "required": true,
                  "type": "string",
                  "sanitize": true,
                  "maxLength": 65000
                },
                "item_number": {
                  "issueOrPRNumber": true
                },
                "repo": {
                  "type": "string",
                  "maxLength": 256
                }
              }
            },
            "add_labels": {
              "defaultMax": 5,
              "fields": {
                "item_number": {
                  "issueNumberOrTemporaryId": true
                },
                "labels": {
                  "required": true,
                  "type": "array",
                  "itemType": "string",
                  "itemSanitize": true,
                  "itemMaxLength": 128
                },
                "repo": {
                  "type": "string",
                  "maxLength": 256
                }
              }
            },
            "missing_data": {
              "defaultMax": 20,
              "fields": {
                "alternatives": {
                  "type": "string",
                  "sanitize": true,
                  "maxLength": 256
                },
                "context": {
                  "type": "string",
                  "sanitize": true,
                  "maxLength": 256
                },
                "data_type": {
                  "type": "string",
                  "sanitize": true,
                  "maxLength": 128
                },
                "reason": {
                  "type": "string",
                  "sanitize": true,
                  "maxLength": 256
                }
              }
            },
            "missing_tool": {
              "defaultMax": 20,
              "fields": {
                "alternatives": {
                  "type": "string",
                  "sanitize": true,
                  "maxLength": 512
                },
                "reason": {
                  "required": true,
                  "type": "string",
                  "sanitize": true,
                  "maxLength": 256
                },
                "tool": {
                  "type": "string",
                  "sanitize": true,
                  "maxLength": 128
                }
              }
            },
            "noop": {
              "defaultMax": 1,
              "fields": {
                "message": {
                  "required": true,
                  "type": "string",
                  "sanitize": true,
                  "maxLength": 65000
                }
              }
            },
            "remove_labels": {
              "defaultMax": 5,
              "fields": {
                "item_number": {
                  "issueNumberOrTemporaryId": true
                },
                "labels": {
                  "required": true,
                  "type": "array",
                  "itemType": "string",
                  "itemSanitize": true,
                  "itemMaxLength": 128
                },
                "repo": {
                  "type": "string",
                  "maxLength": 256
                }
              }
            }
          }
          GH_AW_SAFE_OUTPUTS_VALIDATION_EOF
          node ${RUNNER_TEMP}/gh-aw/actions/generate_safe_outputs_tools.cjs
      - name: Generate Safe Outputs MCP Server Config
        id: safe-outputs-config
        run: |
          # Generate a secure random API key (360 bits of entropy, 40+ chars)
          # Mask immediately to prevent timing vulnerabilities
          API_KEY=$(openssl rand -base64 45 | tr -d '/+=')
          echo "::add-mask::${API_KEY}"
          
          PORT=3001
          
          # Set outputs for next steps
          {
            echo "safe_outputs_api_key=${API_KEY}"
            echo "safe_outputs_port=${PORT}"
          } >> "$GITHUB_OUTPUT"
          
          echo "Safe Outputs MCP server will run on port ${PORT}"
          
      - name: Start Safe Outputs MCP HTTP Server
        id: safe-outputs-start
        env:
          DEBUG: '*'
          GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-config.outputs.safe_outputs_port }}
          GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-config.outputs.safe_outputs_api_key }}
          GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ runner.temp }}/gh-aw/safeoutputs/tools.json
          GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ runner.temp }}/gh-aw/safeoutputs/config.json
          GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
        run: |
          # Environment variables are set above to prevent template injection
          export DEBUG
          export GH_AW_SAFE_OUTPUTS_PORT
          export GH_AW_SAFE_OUTPUTS_API_KEY
          export GH_AW_SAFE_OUTPUTS_TOOLS_PATH
          export GH_AW_SAFE_OUTPUTS_CONFIG_PATH
          export GH_AW_MCP_LOG_DIR
          
          bash ${RUNNER_TEMP}/gh-aw/actions/start_safe_outputs_server.sh
          
      - name: Start MCP Gateway
        id: start-mcp-gateway
        env:
          GH_AW_SAFE_OUTPUTS: ${{ steps.set-runtime-paths.outputs.GH_AW_SAFE_OUTPUTS }}
          GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-start.outputs.api_key }}
          GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-start.outputs.port }}
          GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
        run: |
          set -eo pipefail
          mkdir -p /tmp/gh-aw/mcp-config
          
          # Export gateway environment variables for MCP config and gateway script
          export MCP_GATEWAY_PORT="80"
          export MCP_GATEWAY_DOMAIN="host.docker.internal"
          MCP_GATEWAY_API_KEY=$(openssl rand -base64 45 | tr -d '/+=')
          echo "::add-mask::${MCP_GATEWAY_API_KEY}"
          export MCP_GATEWAY_API_KEY
          export MCP_GATEWAY_PAYLOAD_DIR="/tmp/gh-aw/mcp-payloads"
          mkdir -p "${MCP_GATEWAY_PAYLOAD_DIR}"
          export MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD="524288"
          export DEBUG="*"
          
          export GH_AW_ENGINE="copilot"
          export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_GUARD_MIN_INTEGRITY -e GITHUB_MCP_GUARD_REPOS -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.2.6'
          
          mkdir -p /home/runner/.copilot
          cat << GH_AW_MCP_CONFIG_EOF | bash ${RUNNER_TEMP}/gh-aw/actions/start_mcp_gateway.sh
          {
            "mcpServers": {
              "github": {
                "type": "stdio",
                "container": "ghcr.io/github/github-mcp-server:v0.32.0",
                "env": {
                  "GITHUB_HOST": "\${GITHUB_SERVER_URL}",
                  "GITHUB_PERSONAL_ACCESS_TOKEN": "\${GITHUB_MCP_SERVER_TOKEN}",
                  "GITHUB_READ_ONLY": "1",
                  "GITHUB_TOOLSETS": "context,repos,issues,pull_requests"
                },
                "guard-policies": {
                  "allow-only": {
                    "approval-labels": ${{ steps.parse-guard-vars.outputs.approval_labels }},
                    "blocked-users": ${{ steps.parse-guard-vars.outputs.blocked_users }},
                    "min-integrity": "unapproved",
                    "repos": "all"
                  }
                }
              },
              "safeoutputs": {
                "type": "http",
                "url": "http://host.docker.internal:$GH_AW_SAFE_OUTPUTS_PORT",
                "headers": {
                  "Authorization": "\${GH_AW_SAFE_OUTPUTS_API_KEY}"
                },
                "guard-policies": {
                  "write-sink": {
                    "accept": [
                      "*"
                    ]
                  }
                }
              }
            },
            "gateway": {
              "port": $MCP_GATEWAY_PORT,
              "domain": "${MCP_GATEWAY_DOMAIN}",
              "apiKey": "${MCP_GATEWAY_API_KEY}",
              "payloadDir": "${MCP_GATEWAY_PAYLOAD_DIR}"
            }
          }
          GH_AW_MCP_CONFIG_EOF
      - name: Download activation artifact
        uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
        with:
          name: activation
          path: /tmp/gh-aw
      - name: Clean git credentials
        continue-on-error: true
        run: bash ${RUNNER_TEMP}/gh-aw/actions/clean_git_credentials.sh
      - name: Execute GitHub Copilot CLI
        id: agentic_execution
        # Copilot CLI tool arguments (sorted):
        timeout-minutes: 20
        run: |
          set -o pipefail
          touch /tmp/gh-aw/agent-step-summary.md
          # shellcheck disable=SC1003
          sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --mount "${RUNNER_TEMP}/gh-aw:${RUNNER_TEMP}/gh-aw:ro" --mount "${RUNNER_TEMP}/gh-aw:/host${RUNNER_TEMP}/gh-aw:ro" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,ppa.launchpad.net,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.googleapis.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --audit-dir /tmp/gh-aw/sandbox/firewall/audit --enable-host-access --image-tag 0.25.0 --skip-pull --enable-api-proxy \
            -- /bin/bash -c '/usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --add-dir "${GITHUB_WORKSPACE}" --disable-builtin-mcps --allow-all-tools --allow-all-paths --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"' 2>&1 | tee -a /tmp/gh-aw/agent-stdio.log
        env:
          COPILOT_AGENT_RUNNER_TYPE: STANDALONE
          COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }}
          COPILOT_MODEL: ${{ vars.GH_AW_MODEL_AGENT_COPILOT || '' }}
          GH_AW_MCP_CONFIG: /home/runner/.copilot/mcp-config.json
          GH_AW_PHASE: agent
          GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
          GH_AW_SAFE_OUTPUTS: ${{ steps.set-runtime-paths.outputs.GH_AW_SAFE_OUTPUTS }}
          GH_AW_VERSION: v0.64.0
          GITHUB_API_URL: ${{ github.api_url }}
          GITHUB_AW: true
          GITHUB_HEAD_REF: ${{ github.head_ref }}
          GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
          GITHUB_REF_NAME: ${{ github.ref_name }}
          GITHUB_SERVER_URL: ${{ github.server_url }}
          GITHUB_STEP_SUMMARY: /tmp/gh-aw/agent-step-summary.md
          GITHUB_WORKSPACE: ${{ github.workspace }}
          GIT_AUTHOR_EMAIL: github-actions[bot]@users.noreply.github.com
          GIT_AUTHOR_NAME: github-actions[bot]
          GIT_COMMITTER_EMAIL: github-actions[bot]@users.noreply.github.com
          GIT_COMMITTER_NAME: github-actions[bot]
          XDG_CONFIG_HOME: /home/runner
      - name: Detect inference access error
        id: detect-inference-error
        if: always()
        continue-on-error: true
        run: bash ${RUNNER_TEMP}/gh-aw/actions/detect_inference_access_error.sh
      - name: Configure Git credentials
        env:
          REPO_NAME: ${{ github.repository }}
          SERVER_URL: ${{ github.server_url }}
        run: |
          git config --global user.email "github-actions[bot]@users.noreply.github.com"
          git config --global user.name "github-actions[bot]"
          git config --global am.keepcr true
          # Re-authenticate git with GitHub token
          SERVER_URL_STRIPPED="${SERVER_URL#https://}"
          git remote set-url origin "https://x-access-token:${{ github.token }}@${SERVER_URL_STRIPPED}/${REPO_NAME}.git"
          echo "Git configured with standard GitHub Actions identity"
      - name: Copy Copilot session state files to logs
        if: always()
        continue-on-error: true
        run: |
          # Copy Copilot session state files to logs folder for artifact collection
          # This ensures they are in /tmp/gh-aw/ where secret redaction can scan them
          SESSION_STATE_DIR="$HOME/.copilot/session-state"
          LOGS_DIR="/tmp/gh-aw/sandbox/agent/logs"
          
          if [ -d "$SESSION_STATE_DIR" ]; then
            echo "Copying Copilot session state files from $SESSION_STATE_DIR to $LOGS_DIR"
            mkdir -p "$LOGS_DIR"
            cp -v "$SESSION_STATE_DIR"/*.jsonl "$LOGS_DIR/" 2>/dev/null || true
            echo "Session state files copied successfully"
          else
            echo "No session-state directory found at $SESSION_STATE_DIR"
          fi
      - name: Stop MCP Gateway
        if: always()
        continue-on-error: true
        env:
          MCP_GATEWAY_PORT: ${{ steps.start-mcp-gateway.outputs.gateway-port }}
          MCP_GATEWAY_API_KEY: ${{ steps.start-mcp-gateway.outputs.gateway-api-key }}
          GATEWAY_PID: ${{ steps.start-mcp-gateway.outputs.gateway-pid }}
        run: |
          bash ${RUNNER_TEMP}/gh-aw/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
      - name: Redact secrets in logs
        if: always()
        uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
        with:
          script: |
            const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs');
            setupGlobals(core, github, context, exec, io);
            const { main } = require('${{ runner.temp }}/gh-aw/actions/redact_secrets.cjs');
            await main();
        env:
          GH_AW_SECRET_NAMES: 'COPILOT_GITHUB_TOKEN,GH_AW_GITHUB_MCP_SERVER_TOKEN,GH_AW_GITHUB_TOKEN,GITHUB_TOKEN'
          SECRET_COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }}
          SECRET_GH_AW_GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN }}
          SECRET_GH_AW_GITHUB_TOKEN: ${{ secrets.GH_AW_GITHUB_TOKEN }}
          SECRET_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
      - name: Append agent step summary
        if: always()
        run: bash ${RUNNER_TEMP}/gh-aw/actions/append_agent_step_summary.sh
      - name: Copy Safe Outputs
        if: always()
        env:
          GH_AW_SAFE_OUTPUTS: ${{ steps.set-runtime-paths.outputs.GH_AW_SAFE_OUTPUTS }}
        run: |
          mkdir -p /tmp/gh-aw
          cp "$GH_AW_SAFE_OUTPUTS" /tmp/gh-aw/safeoutputs.jsonl 2>/dev/null || true
      - name: Ingest agent output
        id: collect_output
        if: always()
        uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
        env:
          GH_AW_SAFE_OUTPUTS: ${{ steps.set-runtime-paths.outputs.GH_AW_SAFE_OUTPUTS }}
          GH_AW_ALLOWED_DOMAINS: "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,ppa.launchpad.net,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.googleapis.com"
          GH_AW_ALLOWED_GITHUB_REFS: ""
          GITHUB_SERVER_URL: ${{ github.server_url }}
          GITHUB_API_URL: ${{ github.api_url }}
        with:
          script: |
            const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs');
            setupGlobals(core, github, context, exec, io);
            const { main } = require('${{ runner.temp }}/gh-aw/actions/collect_ndjson_output.cjs');
            await main();
      - name: Parse agent logs for step summary
        if: always()
        uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
        env:
          GH_AW_AGENT_OUTPUT: /tmp/gh-aw/sandbox/agent/logs/
        with:
          script: |
            const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs');
            setupGlobals(core, github, context, exec, io);
            const { main } = require('${{ runner.temp }}/gh-aw/actions/parse_copilot_log.cjs');
            await main();
      - name: Parse MCP Gateway logs for step summary
        if: always()
        uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
        with:
          script: |
            const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs');
            setupGlobals(core, github, context, exec, io);
            const { main } = require('${{ runner.temp }}/gh-aw/actions/parse_mcp_gateway_log.cjs');
            await main();
      - name: Print firewall logs
        if: always()
        continue-on-error: true
        env:
          AWF_LOGS_DIR: /tmp/gh-aw/sandbox/firewall/logs
        run: |
          # Fix permissions on firewall logs so they can be uploaded as artifacts
          # AWF runs with sudo, creating files owned by root
          sudo chmod -R a+r /tmp/gh-aw/sandbox/firewall/logs 2>/dev/null || true
          # Only run awf logs summary if awf command exists (it may not be installed if workflow failed before install step)
          if command -v awf &> /dev/null; then
            awf logs summary | tee -a "$GITHUB_STEP_SUMMARY"
          else
            echo 'AWF binary not installed, skipping firewall log summary'
          fi
      - name: Write agent output placeholder if missing
        if: always()
        run: |
          if [ ! -f /tmp/gh-aw/agent_output.json ]; then
            echo '{"items":[]}' > /tmp/gh-aw/agent_output.json
          fi
      - name: Upload agent artifacts
        if: always()
        continue-on-error: true
        uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7
        with:
          name: agent
          path: |
            /tmp/gh-aw/aw-prompts/prompt.txt
            /tmp/gh-aw/sandbox/agent/logs/
            /tmp/gh-aw/redacted-urls.log
            /tmp/gh-aw/mcp-logs/
            /tmp/gh-aw/proxy-logs/
            !/tmp/gh-aw/proxy-logs/proxy-tls/
            /tmp/gh-aw/agent-stdio.log
            /tmp/gh-aw/agent/
            /tmp/gh-aw/safeoutputs.jsonl
            /tmp/gh-aw/agent_output.json
            /tmp/gh-aw/aw-*.patch
          if-no-files-found: ignore
      - name: Upload firewall audit logs
        if: always()
        continue-on-error: true
        uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7
        with:
          name: firewall-audit-logs
          path: |
            /tmp/gh-aw/sandbox/firewall/logs/
            /tmp/gh-aw/sandbox/firewall/audit/
          if-no-files-found: ignore

  conclusion:
    needs:
      - activation
      - agent
      - detection
      - safe_outputs
    if: always() && (needs.agent.result != 'skipped' || needs.activation.outputs.lockdown_check_failed == 'true')
    runs-on: ubuntu-slim
    permissions:
      contents: read
      discussions: write
      issues: write
      pull-requests: write
    concurrency:
      group: "gh-aw-conclusion-pr-ai-slop-review"
      cancel-in-progress: false
    outputs:
      noop_message: ${{ steps.noop.outputs.noop_message }}
      tools_reported: ${{ steps.missing_tool.outputs.tools_reported }}
      total_count: ${{ steps.missing_tool.outputs.total_count }}
    steps:
      - name: Setup Scripts
        uses: github/gh-aw-actions/setup@0048fdad270986610dd71c966d15f7abd7de037a # v0.64.2
        with:
          destination: ${{ runner.temp }}/gh-aw/actions
      - name: Download agent output artifact
        id: download-agent-output
        continue-on-error: true
        uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
        with:
          name: agent
          path: /tmp/gh-aw/
      - name: Setup agent output environment variable
        id: setup-agent-output-env
        if: steps.download-agent-output.outcome == 'success'
        run: |
          mkdir -p /tmp/gh-aw/
          find "/tmp/gh-aw/" -type f -print
          echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/agent_output.json" >> "$GITHUB_OUTPUT"
      - name: Process No-Op Messages
        id: noop
        uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
        env:
          GH_AW_AGENT_OUTPUT: ${{ steps.setup-agent-output-env.outputs.GH_AW_AGENT_OUTPUT }}
          GH_AW_NOOP_MAX: "1"
          GH_AW_WORKFLOW_NAME: "PR AI Slop Review"
        with:
          github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
          script: |
            const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs');
            setupGlobals(core, github, context, exec, io);
            const { main } = require('${{ runner.temp }}/gh-aw/actions/noop.cjs');
            await main();
      - name: Record Missing Tool
        id: missing_tool
        uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
        env:
          GH_AW_AGENT_OUTPUT: ${{ steps.setup-agent-output-env.outputs.GH_AW_AGENT_OUTPUT }}
          GH_AW_WORKFLOW_NAME: "PR AI Slop Review"
        with:
          github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
          script: |
            const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs');
            setupGlobals(core, github, context, exec, io);
            const { main } = require('${{ runner.temp }}/gh-aw/actions/missing_tool.cjs');
            await main();
      - name: Handle Agent Failure
        id: handle_agent_failure
        if: always()
        uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
        env:
          GH_AW_AGENT_OUTPUT: ${{ steps.setup-agent-output-env.outputs.GH_AW_AGENT_OUTPUT }}
          GH_AW_WORKFLOW_NAME: "PR AI Slop Review"
          GH_AW_RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
          GH_AW_AGENT_CONCLUSION: ${{ needs.agent.result }}
          GH_AW_WORKFLOW_ID: "pr-ai-slop-review"
          GH_AW_SECRET_VERIFICATION_RESULT: ${{ needs.activation.outputs.secret_verification_result }}
          GH_AW_CHECKOUT_PR_SUCCESS: ${{ needs.agent.outputs.checkout_pr_success }}
          GH_AW_INFERENCE_ACCESS_ERROR: ${{ needs.agent.outputs.inference_access_error }}
          GH_AW_LOCKDOWN_CHECK_FAILED: ${{ needs.activation.outputs.lockdown_check_failed }}
          GH_AW_GROUP_REPORTS: "false"
          GH_AW_FAILURE_REPORT_AS_ISSUE: "true"
          GH_AW_TIMEOUT_MINUTES: "20"
        with:
          github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
          script: |
            const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs');
            setupGlobals(core, github, context, exec, io);
            const { main } = require('${{ runner.temp }}/gh-aw/actions/handle_agent_failure.cjs');
            await main();
      - name: Handle No-Op Message
        id: handle_noop_message
        uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
        env:
          GH_AW_AGENT_OUTPUT: ${{ steps.setup-agent-output-env.outputs.GH_AW_AGENT_OUTPUT }}
          GH_AW_WORKFLOW_NAME: "PR AI Slop Review"
          GH_AW_RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
          GH_AW_AGENT_CONCLUSION: ${{ needs.agent.result }}
          GH_AW_NOOP_MESSAGE: ${{ steps.noop.outputs.noop_message }}
          GH_AW_NOOP_REPORT_AS_ISSUE: "true"
        with:
          github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
          script: |
            const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs');
            setupGlobals(core, github, context, exec, io);
            const { main } = require('${{ runner.temp }}/gh-aw/actions/handle_noop_message.cjs');
            await main();

  detection:
    needs: agent
    if: always() && needs.agent.result != 'skipped'
    runs-on: ubuntu-latest
    outputs:
      detection_conclusion: ${{ steps.detection_conclusion.outputs.conclusion }}
      detection_success: ${{ steps.detection_conclusion.outputs.success }}
    steps:
      - name: Setup Scripts
        uses: github/gh-aw-actions/setup@0048fdad270986610dd71c966d15f7abd7de037a # v0.64.2
        with:
          destination: ${{ runner.temp }}/gh-aw/actions
      - name: Download agent output artifact
        id: download-agent-output
        continue-on-error: true
        uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
        with:
          name: agent
          path: /tmp/gh-aw/
      - name: Setup agent output environment variable
        id: setup-agent-output-env
        if: steps.download-agent-output.outcome == 'success'
        run: |
          mkdir -p /tmp/gh-aw/
          find "/tmp/gh-aw/" -type f -print
          echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/agent_output.json" >> "$GITHUB_OUTPUT"
      # --- Threat Detection ---
      - name: Download container images
        run: bash ${RUNNER_TEMP}/gh-aw/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.25.0 ghcr.io/github/gh-aw-firewall/api-proxy:0.25.0 ghcr.io/github/gh-aw-firewall/squid:0.25.0
      - name: Check if detection needed
        id: detection_guard
        if: always()
        env:
          OUTPUT_TYPES: ${{ needs.agent.outputs.output_types }}
          HAS_PATCH: ${{ needs.agent.outputs.has_patch }}
        run: |
          if [[ -n "$OUTPUT_TYPES" || "$HAS_PATCH" == "true" ]]; then
            echo "run_detection=true" >> "$GITHUB_OUTPUT"
            echo "Detection will run: output_types=$OUTPUT_TYPES, has_patch=$HAS_PATCH"
          else
            echo "run_detection=false" >> "$GITHUB_OUTPUT"
            echo "Detection skipped: no agent outputs or patches to analyze"
          fi
      - name: Clear MCP configuration for detection
        if: always() && steps.detection_guard.outputs.run_detection == 'true'
        run: |
          rm -f /tmp/gh-aw/mcp-config/mcp-servers.json
          rm -f /home/runner/.copilot/mcp-config.json
          rm -f "$GITHUB_WORKSPACE/.gemini/settings.json"
      - name: Prepare threat detection files
        if: always() && steps.detection_guard.outputs.run_detection == 'true'
        run: |
          mkdir -p /tmp/gh-aw/threat-detection/aw-prompts
          cp /tmp/gh-aw/aw-prompts/prompt.txt /tmp/gh-aw/threat-detection/aw-prompts/prompt.txt 2>/dev/null || true
          cp /tmp/gh-aw/agent_output.json /tmp/gh-aw/threat-detection/agent_output.json 2>/dev/null || true
          for f in /tmp/gh-aw/aw-*.patch; do
            [ -f "$f" ] && cp "$f" /tmp/gh-aw/threat-detection/ 2>/dev/null || true
          done
          echo "Prepared threat detection files:"
          ls -la /tmp/gh-aw/threat-detection/ 2>/dev/null || true
      - name: Setup threat detection
        if: always() && steps.detection_guard.outputs.run_detection == 'true'
        uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
        env:
          WORKFLOW_NAME: "PR AI Slop Review"
          WORKFLOW_DESCRIPTION: "Reviews incoming pull requests for missing issue linkage and high-confidence\nsigns of one-shot AI-generated changes, then posts a maintainer-focused\ncomment when the risk is high enough to warrant follow-up."
          HAS_PATCH: ${{ needs.agent.outputs.has_patch }}
        with:
          script: |
            const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs');
            setupGlobals(core, github, context, exec, io);
            const { main } = require('${{ runner.temp }}/gh-aw/actions/setup_threat_detection.cjs');
            await main();
      - name: Ensure threat-detection directory and log
        if: always() && steps.detection_guard.outputs.run_detection == 'true'
        run: |
          mkdir -p /tmp/gh-aw/threat-detection
          touch /tmp/gh-aw/threat-detection/detection.log
      - name: Install GitHub Copilot CLI
        run: ${RUNNER_TEMP}/gh-aw/actions/install_copilot_cli.sh latest
        env:
          GH_HOST: github.com
      - name: Install AWF binary
        run: bash ${RUNNER_TEMP}/gh-aw/actions/install_awf_binary.sh v0.25.0
      - name: Execute GitHub Copilot CLI
        if: always() && steps.detection_guard.outputs.run_detection == 'true'
        id: detection_agentic_execution
        # Copilot CLI tool arguments (sorted):
        # --allow-tool shell(cat)
        # --allow-tool shell(grep)
        # --allow-tool shell(head)
        # --allow-tool shell(jq)
        # --allow-tool shell(ls)
        # --allow-tool shell(tail)
        # --allow-tool shell(wc)
        timeout-minutes: 20
        run: |
          set -o pipefail
          touch /tmp/gh-aw/agent-step-summary.md
          # shellcheck disable=SC1003
          sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --mount "${RUNNER_TEMP}/gh-aw:${RUNNER_TEMP}/gh-aw:ro" --mount "${RUNNER_TEMP}/gh-aw:/host${RUNNER_TEMP}/gh-aw:ro" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,github.com,host.docker.internal,raw.githubusercontent.com,registry.npmjs.org,telemetry.enterprise.githubcopilot.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --audit-dir /tmp/gh-aw/sandbox/firewall/audit --enable-host-access --image-tag 0.25.0 --skip-pull --enable-api-proxy \
            -- /bin/bash -c '/usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --add-dir "${GITHUB_WORKSPACE}" --disable-builtin-mcps --allow-tool '\''shell(cat)'\'' --allow-tool '\''shell(grep)'\'' --allow-tool '\''shell(head)'\'' --allow-tool '\''shell(jq)'\'' --allow-tool '\''shell(ls)'\'' --allow-tool '\''shell(tail)'\'' --allow-tool '\''shell(wc)'\'' --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"' 2>&1 | tee -a /tmp/gh-aw/threat-detection/detection.log
        env:
          COPILOT_AGENT_RUNNER_TYPE: STANDALONE
          COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }}
          COPILOT_MODEL: gpt-5.1-codex-mini
          GH_AW_PHASE: detection
          GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
          GH_AW_VERSION: v0.64.0
          GITHUB_API_URL: ${{ github.api_url }}
          GITHUB_AW: true
          GITHUB_HEAD_REF: ${{ github.head_ref }}
          GITHUB_REF_NAME: ${{ github.ref_name }}
          GITHUB_SERVER_URL: ${{ github.server_url }}
          GITHUB_STEP_SUMMARY: /tmp/gh-aw/agent-step-summary.md
          GITHUB_WORKSPACE: ${{ github.workspace }}
          GIT_AUTHOR_EMAIL: github-actions[bot]@users.noreply.github.com
          GIT_AUTHOR_NAME: github-actions[bot]
          GIT_COMMITTER_EMAIL: github-actions[bot]@users.noreply.github.com
          GIT_COMMITTER_NAME: github-actions[bot]
          XDG_CONFIG_HOME: /home/runner
      - name: Upload threat detection log
        if: always() && steps.detection_guard.outputs.run_detection == 'true'
        uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7
        with:
          name: detection
          path: /tmp/gh-aw/threat-detection/detection.log
          if-no-files-found: ignore
      - name: Parse and conclude threat detection
        id: detection_conclusion
        if: always()
        uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
        env:
          RUN_DETECTION: ${{ steps.detection_guard.outputs.run_detection }}
        with:
          script: |
            const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs');
            setupGlobals(core, github, context, exec, io);
            const { main } = require('${{ runner.temp }}/gh-aw/actions/parse_threat_detection_results.cjs');
            await main();

  safe_outputs:
    needs:
      - agent
      - detection
    if: (!cancelled()) && needs.agent.result != 'skipped' && needs.detection.result == 'success'
    runs-on: ubuntu-slim
    permissions:
      contents: read
      discussions: write
      issues: write
      pull-requests: write
    timeout-minutes: 15
    env:
      GH_AW_CALLER_WORKFLOW_ID: "${{ github.repository }}/pr-ai-slop-review"
      GH_AW_ENGINE_ID: "copilot"
      GH_AW_ENGINE_MODEL: ${{ needs.agent.outputs.model }}
      GH_AW_WORKFLOW_ID: "pr-ai-slop-review"
      GH_AW_WORKFLOW_NAME: "PR AI Slop Review"
    outputs:
      code_push_failure_count: ${{ steps.process_safe_outputs.outputs.code_push_failure_count }}
      code_push_failure_errors: ${{ steps.process_safe_outputs.outputs.code_push_failure_errors }}
      comment_id: ${{ steps.process_safe_outputs.outputs.comment_id }}
      comment_url: ${{ steps.process_safe_outputs.outputs.comment_url }}
      create_discussion_error_count: ${{ steps.process_safe_outputs.outputs.create_discussion_error_count }}
      create_discussion_errors: ${{ steps.process_safe_outputs.outputs.create_discussion_errors }}
      process_safe_outputs_processed_count: ${{ steps.process_safe_outputs.outputs.processed_count }}
      process_safe_outputs_temporary_id_map: ${{ steps.process_safe_outputs.outputs.temporary_id_map }}
    steps:
      - name: Setup Scripts
        uses: github/gh-aw-actions/setup@0048fdad270986610dd71c966d15f7abd7de037a # v0.64.2
        with:
          destination: ${{ runner.temp }}/gh-aw/actions
      - name: Download agent output artifact
        id: download-agent-output
        continue-on-error: true
        uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
        with:
          name: agent
          path: /tmp/gh-aw/
      - name: Setup agent output environment variable
        id: setup-agent-output-env
        if: steps.download-agent-output.outcome == 'success'
        run: |
          mkdir -p /tmp/gh-aw/
          find "/tmp/gh-aw/" -type f -print
          echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/agent_output.json" >> "$GITHUB_OUTPUT"
      - name: Configure GH_HOST for enterprise compatibility
        id: ghes-host-config
        shell: bash
        run: |
          # Derive GH_HOST from GITHUB_SERVER_URL so the gh CLI targets the correct
          # GitHub instance (GHES/GHEC). On github.com this is a harmless no-op.
          GH_HOST="${GITHUB_SERVER_URL#https://}"
          GH_HOST="${GH_HOST#http://}"
          echo "GH_HOST=${GH_HOST}" >> "$GITHUB_OUTPUT"
      - name: Process Safe Outputs
        id: process_safe_outputs
        uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
        env:
          GH_AW_AGENT_OUTPUT: ${{ steps.setup-agent-output-env.outputs.GH_AW_AGENT_OUTPUT }}
          GH_AW_ALLOWED_DOMAINS: "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,ppa.launchpad.net,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.googleapis.com"
          GITHUB_SERVER_URL: ${{ github.server_url }}
          GITHUB_API_URL: ${{ github.api_url }}
          GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"add_comment\":{\"hide_older_comments\":true,\"max\":1},\"add_labels\":{\"allowed\":[\"ai-slop:high\",\"ai-slop:med\"],\"max\":1},\"missing_data\":{},\"missing_tool\":{},\"noop\":{\"max\":1,\"report-as-issue\":\"true\"},\"remove_labels\":{\"allowed\":[\"ai-slop:high\",\"ai-slop:med\"],\"max\":2}}"
        with:
          github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
          script: |
            const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs');
            setupGlobals(core, github, context, exec, io);
            const { main } = require('${{ runner.temp }}/gh-aw/actions/safe_output_handler_manager.cjs');
            await main();
      - name: Upload Safe Output Items
        if: always()
        uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7
        with:
          name: safe-output-items
          path: /tmp/gh-aw/safe-output-items.jsonl
          if-no-files-found: ignore

release matrix perms .github/workflows/release.yml
Triggers
push
Runs on
ubuntu-latest, ubuntu-latest, ${{ matrix.os }}, ${{ matrix.os }}, ${{ matrix.os }}, ubuntu-latest, ubuntu-latest, ubuntu-latest, ubuntu-latest
Jobs
check_tag_version, update_tag, release, release-for-linux-arm, release-for-fixed-webview2, release-update, release-update-for-fixed-webview2, submit-to-winget, notify-telegram
Matrix
include, include.arch, include.os, include.target→ aarch64-apple-darwin, aarch64-pc-windows-msvc, aarch64-unknown-linux-gnu, arm64, armhf, armv7-unknown-linux-gnueabihf, macos-latest, ubuntu-22.04, windows-latest, x64, x86_64-apple-darwin, x86_64-pc-windows-msvc, x86_64-unknown-linux-gnu
Actions
softprops/action-gh-release, dtolnay/rust-toolchain, Swatinem/rust-cache, pnpm/action-setup, tauri-apps/tauri-action, dtolnay/rust-toolchain, Swatinem/rust-cache, pnpm/action-setup, softprops/action-gh-release, dtolnay/rust-toolchain, Swatinem/rust-cache, pnpm/action-setup, tauri-apps/tauri-action, softprops/action-gh-release, pnpm/action-setup, pnpm/action-setup, vedantmgoyal9/winget-releaser, pnpm/action-setup
Commands
  • TAG_REF="${GITHUB_REF##*/}" echo "Checking if tag $TAG_REF is from main branch..." TAG_COMMIT=$(git rev-list -n 1 $TAG_REF) MAIN_COMMITS=$(git rev-list origin/main) if echo "$MAIN_COMMITS" | grep -q "$TAG_COMMIT"; then echo "✅ Tag $TAG_REF is from main branch" else echo "❌ Tag $TAG_REF is not from main branch" echo "This release workflow only accepts tags from main branch." exit 1 fi
  • TAG_REF="${GITHUB_REF_NAME:-${GITHUB_REF##*/}}" echo "Current tag: $TAG_REF" PKG_VERSION=$(jq -r .version package.json) echo "package.json version: $PKG_VERSION" EXPECTED_TAG="v$PKG_VERSION" if [[ "$TAG_REF" != "$EXPECTED_TAG" ]]; then echo "❌ Version mismatch:" echo " Git tag : $TAG_REF" echo " package.json : $EXPECTED_TAG" exit 1 fi echo "✅ Tag and package.json version are consistent."
  • bash ./scripts/extract_update_logs.sh
  • echo "BUILDTIME=$(TZ=Asia/Shanghai date)" >> $GITHUB_ENV TAG_REF="${GITHUB_REF##*/}" echo "TAG_NAME=$TAG_REF" >> $GITHUB_ENV VERSION=$(echo "$TAG_REF" | sed 's/^v//') echo "VERSION=$VERSION" >> $GITHUB_ENV echo "DOWNLOAD_URL=https://github.com/clash-verge-rev/clash-verge-rev/releases/download/$TAG_REF" >> $GITHUB_ENV
  • if [ -z "$UPDATE_LOGS" ]; then echo "No update logs found, using default message" UPDATE_LOGS="More new features are now supported. Check for detailed changelog soon." else echo "Using found update logs" fi cat > release.txt << EOF $UPDATE_LOGS ## 下载地址 ### Windows (不再支持Win7) #### 正常版本(推荐) - [64位(常用)](${{ env.DOWNLOAD_URL }}/Clash.Verge_${{ env.VERSION }}_x64-setup.exe) | [ARM64(不常用)](${{ env.DOWNLOAD_URL }}/Clash.Verge_${{ env.VERSION }}_arm64-setup.exe) #### 内置Webview2版(体积较大,仅在企业版系统或无法安装webview2时使用) - [64位](${{ env.DOWNLOAD_URL }}/Clash.Verge_${{ env.VERSION }}_x64_fixed_webview2-setup.exe) | [ARM64](${{ env.DOWNLOAD_URL }}/Clash.Verge_${{ env.VERSION }}_arm64_fixed_webview2-setup.exe) ### macOS - [Apple M芯片](${{ env.DOWNLOAD_URL }}/Clash.Verge_${{ env.VERSION }}_aarch64.dmg) | [Intel芯片](${{ env.DOWNLOAD_URL }}/Clash.Verge_${{ env.VERSION }}_x64.dmg) ### Linux #### DEB包(Debian系) 使用 apt ./路径 安装 - [64位](${{ env.DOWNLOAD_URL }}/Clash.Verge_${{ env.VERSION }}_amd64.deb) | [ARM64](${{ env.DOWNLOAD_URL }}/Clash.Verge_${{ env.VERSION }}_arm64.deb) | [ARMv7](${{ env.DOWNLOAD_URL }}/Clash.Verge_${{ env.VERSION }}_armhf.deb) #### RPM包(Redhat系) 使用 dnf ./路径 安装 - [64位](${{ env.DOWNLOAD_URL }}/Clash.Verge-${{ env.VERSION }}-1.x86_64.rpm) | [ARM64](${{ env.DOWNLOAD_URL }}/Clash.Verge-${{ env.VERSION }}-1.aarch64.rpm) | [ARMv7](${{ env.DOWNLOAD_URL }}/Clash.Verge-${{ env.VERSION }}-1.armhfp.rpm) ### FAQ - [常见问题](https://clash-verge-rev.github.io/faq/windows.html) ### 稳定机场VPN推荐 - [狗狗加速](https://verge.dginv.click/#/register?code=oaxsAGo6) Created at ${{ env.BUILDTIME }}. EOF
  • rustup target add ${{ matrix.target }}
  • sudo apt-get update sudo apt-get install -y libxslt1.1 libwebkit2gtk-4.1-dev libayatana-appindicator3-dev librsvg2-dev patchelf
  • arch -x86_64 brew install openssl@3 echo "OPENSSL_DIR=$(brew --prefix openssl@3)" >> $GITHUB_ENV echo "OPENSSL_INCLUDE_DIR=$(brew --prefix openssl@3)/include" >> $GITHUB_ENV echo "OPENSSL_LIB_DIR=$(brew --prefix openssl@3)/lib" >> $GITHUB_ENV echo "PKG_CONFIG_PATH=$(brew --prefix openssl@3)/lib/pkgconfig" >> $GITHUB_ENV
View raw YAML
name: Release Build

on:
  # ! 为了避免重复发布版本,应当通过独特 git tag 触发。
  # ! 不再使用 workflow_dispatch 触发。
  # workflow_dispatch:
  push:
    # -rc tag 时预览发布, 跳过 telegram 通知、跳过 winget 提交、跳过 latest.json 文件更新
    tags:
      - 'v*.*.*'
permissions: write-all
env:
  CARGO_INCREMENTAL: 0
  RUST_BACKTRACE: short
  HUSKY: 0
concurrency:
  # only allow per workflow per commit (and not pr) to run at a time
  group: '${{ github.workflow }} - ${{ github.head_ref || github.ref }}'
  cancel-in-progress: ${{ github.ref != 'refs/heads/main' }}

jobs:
  check_tag_version:
    name: Check Release Tag and package.json Version Consistency
    runs-on: ubuntu-latest
    steps:
      - name: Checkout repository
        uses: actions/checkout@v6
        with:
          fetch-depth: 0

      - name: Check if tag is from main branch
        run: |
          TAG_REF="${GITHUB_REF##*/}"
          echo "Checking if tag $TAG_REF is from main branch..."

          TAG_COMMIT=$(git rev-list -n 1 $TAG_REF)
          MAIN_COMMITS=$(git rev-list origin/main)

          if echo "$MAIN_COMMITS" | grep -q "$TAG_COMMIT"; then
            echo "✅ Tag $TAG_REF is from main branch"
          else
            echo "❌ Tag $TAG_REF is not from main branch"
            echo "This release workflow only accepts tags from main branch."
            exit 1
          fi

      - name: Check tag and package.json version
        run: |
          TAG_REF="${GITHUB_REF_NAME:-${GITHUB_REF##*/}}"
          echo "Current tag: $TAG_REF"

          PKG_VERSION=$(jq -r .version package.json)
          echo "package.json version: $PKG_VERSION"

          EXPECTED_TAG="v$PKG_VERSION"

          if [[ "$TAG_REF" != "$EXPECTED_TAG" ]]; then
            echo "❌ Version mismatch:"
            echo "   Git tag       : $TAG_REF"
            echo "   package.json  : $EXPECTED_TAG"
            exit 1
          fi

          echo "✅ Tag and package.json version are consistent."

  update_tag:
    name: Update tag
    runs-on: ubuntu-latest
    needs: [release, release-for-linux-arm, release-for-fixed-webview2]
    steps:
      - name: Checkout repository
        uses: actions/checkout@v6

      - name: Fetch UPDATE logs
        id: fetch_update_logs
        run: bash ./scripts/extract_update_logs.sh
        shell: bash

      - name: Set Env
        run: |
          echo "BUILDTIME=$(TZ=Asia/Shanghai date)" >> $GITHUB_ENV
          TAG_REF="${GITHUB_REF##*/}"
          echo "TAG_NAME=$TAG_REF" >> $GITHUB_ENV
          VERSION=$(echo "$TAG_REF" | sed 's/^v//')
          echo "VERSION=$VERSION" >> $GITHUB_ENV
          echo "DOWNLOAD_URL=https://github.com/clash-verge-rev/clash-verge-rev/releases/download/$TAG_REF" >> $GITHUB_ENV
        shell: bash

      - run: |
          if [ -z "$UPDATE_LOGS" ]; then
            echo "No update logs found, using default message"
            UPDATE_LOGS="More new features are now supported. Check for detailed changelog soon."
          else
            echo "Using found update logs"
          fi

          cat > release.txt << EOF
          $UPDATE_LOGS

          ## 下载地址

          ### Windows (不再支持Win7)
          #### 正常版本(推荐)
          - [64位(常用)](${{ env.DOWNLOAD_URL }}/Clash.Verge_${{ env.VERSION }}_x64-setup.exe) | [ARM64(不常用)](${{ env.DOWNLOAD_URL }}/Clash.Verge_${{ env.VERSION }}_arm64-setup.exe)

          #### 内置Webview2版(体积较大,仅在企业版系统或无法安装webview2时使用)
          - [64位](${{ env.DOWNLOAD_URL }}/Clash.Verge_${{ env.VERSION }}_x64_fixed_webview2-setup.exe) | [ARM64](${{ env.DOWNLOAD_URL }}/Clash.Verge_${{ env.VERSION }}_arm64_fixed_webview2-setup.exe)

          ### macOS
          - [Apple M芯片](${{ env.DOWNLOAD_URL }}/Clash.Verge_${{ env.VERSION }}_aarch64.dmg) | [Intel芯片](${{ env.DOWNLOAD_URL }}/Clash.Verge_${{ env.VERSION }}_x64.dmg)

          ### Linux
          #### DEB包(Debian系) 使用 apt ./路径 安装
          - [64位](${{ env.DOWNLOAD_URL }}/Clash.Verge_${{ env.VERSION }}_amd64.deb) | [ARM64](${{ env.DOWNLOAD_URL }}/Clash.Verge_${{ env.VERSION }}_arm64.deb) | [ARMv7](${{ env.DOWNLOAD_URL }}/Clash.Verge_${{ env.VERSION }}_armhf.deb)

          #### RPM包(Redhat系) 使用 dnf ./路径 安装
          - [64位](${{ env.DOWNLOAD_URL }}/Clash.Verge-${{ env.VERSION }}-1.x86_64.rpm) | [ARM64](${{ env.DOWNLOAD_URL }}/Clash.Verge-${{ env.VERSION }}-1.aarch64.rpm) | [ARMv7](${{ env.DOWNLOAD_URL }}/Clash.Verge-${{ env.VERSION }}-1.armhfp.rpm)

          ### FAQ
          - [常见问题](https://clash-verge-rev.github.io/faq/windows.html)

          ### 稳定机场VPN推荐
          - [狗狗加速](https://verge.dginv.click/#/register?code=oaxsAGo6)

          Created at ${{ env.BUILDTIME }}.
          EOF

      - name: Upload Release
        uses: softprops/action-gh-release@v2
        with:
          tag_name: ${{ env.TAG_NAME }}
          name: 'Clash Verge Rev ${{ env.TAG_NAME }}'
          body_path: release.txt
          draft: false
          prerelease: ${{ contains(github.ref_name, '-rc') }}
          token: ${{ secrets.GITHUB_TOKEN }}
          # generate_release_notes: true

  release:
    name: Release Build
    needs: [check_tag_version]
    strategy:
      fail-fast: false
      matrix:
        include:
          - os: windows-latest
            target: x86_64-pc-windows-msvc
          - os: windows-latest
            target: aarch64-pc-windows-msvc
          - os: macos-latest
            target: aarch64-apple-darwin
          - os: macos-latest
            target: x86_64-apple-darwin
          - os: ubuntu-22.04
            target: x86_64-unknown-linux-gnu

    runs-on: ${{ matrix.os }}
    steps:
      - name: Checkout Repository
        uses: actions/checkout@v6

      - name: Install Rust Stable
        uses: dtolnay/rust-toolchain@master
        with:
          toolchain: '1.91.0'
          targets: ${{ matrix.target }}

      - name: Add Rust Target
        run: rustup target add ${{ matrix.target }}

      - name: Rust Cache
        uses: Swatinem/rust-cache@v2
        with:
          save-if: ${{ github.ref == 'refs/heads/dev' }}
          prefix-key: 'v1-rust'
          key: 'rust-shared-stable-${{ matrix.os }}-${{ matrix.target }}'
          workspaces: |
            . -> target
          cache-all-crates: true
          cache-workspace-crates: true

      - name: Install dependencies (ubuntu only)
        if: matrix.os == 'ubuntu-22.04'
        run: |
          sudo apt-get update
          sudo apt-get install -y libxslt1.1 libwebkit2gtk-4.1-dev libayatana-appindicator3-dev librsvg2-dev patchelf

      - name: Install x86 OpenSSL (macOS only)
        if: matrix.target == 'x86_64-apple-darwin'
        run: |
          arch -x86_64 brew install openssl@3
          echo "OPENSSL_DIR=$(brew --prefix openssl@3)" >> $GITHUB_ENV
          echo "OPENSSL_INCLUDE_DIR=$(brew --prefix openssl@3)/include" >> $GITHUB_ENV
          echo "OPENSSL_LIB_DIR=$(brew --prefix openssl@3)/lib" >> $GITHUB_ENV
          echo "PKG_CONFIG_PATH=$(brew --prefix openssl@3)/lib/pkgconfig" >> $GITHUB_ENV

      - name: Install Node
        uses: actions/setup-node@v6
        with:
          node-version: '24.14.1'

      - uses: pnpm/action-setup@v5
        name: Install pnpm
        with:
          run_install: false

      - name: Pnpm install and check
        run: |
          pnpm i
          pnpm run prebuild ${{ matrix.target }}

      - name: Add Rust Target
        run: |
          # Ensure cross target is installed for the pinned toolchain; fallback without explicit toolchain if needed
          rustup target add ${{ matrix.target }} --toolchain 1.91.0 || rustup target add ${{ matrix.target }}
          rustup target list --installed
          echo "Rust target ${{ matrix.target }} installed."

      - name: Tauri build
        # 上游 5.24 修改了 latest.json 的生成逻辑,且依赖 tauri-plugin-update 2.10.0 暂未发布,故锁定在 0.5.23 版本
        uses: tauri-apps/tauri-action@v0.6.2
        env:
          NODE_OPTIONS: '--max_old_space_size=4096'
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_PRIVATE_KEY }}
          TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_KEY_PASSWORD }}
          APPLE_CERTIFICATE: ${{ secrets.APPLE_CERTIFICATE }}
          APPLE_CERTIFICATE_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }}
          APPLE_SIGNING_IDENTITY: ${{ secrets.APPLE_SIGNING_IDENTITY }}
          APPLE_ID: ${{ secrets.APPLE_ID }}
          APPLE_PASSWORD: ${{ secrets.APPLE_PASSWORD }}
          APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
        with:
          tagName: ${{ github.ref_name }}
          releaseName: 'Clash Verge Rev ${{ github.ref_name }}'
          releaseBody: 'Draft release, will be updated later.'
          releaseDraft: true
          prerelease: ${{ contains(github.ref_name, '-rc') }}
          tauriScript: pnpm
          args: --target ${{ matrix.target }}
          includeUpdaterJson: true

  release-for-linux-arm:
    name: Release Build for Linux ARM
    needs: [check_tag_version]
    strategy:
      fail-fast: false
      matrix:
        include:
          - os: ubuntu-22.04
            target: aarch64-unknown-linux-gnu
            arch: arm64
          - os: ubuntu-22.04
            target: armv7-unknown-linux-gnueabihf
            arch: armhf
    runs-on: ${{ matrix.os }}
    steps:
      - name: Checkout Repository
        uses: actions/checkout@v6

      - name: Install Rust Stable
        uses: dtolnay/rust-toolchain@master
        with:
          toolchain: '1.91.0'
          targets: ${{ matrix.target }}

      - name: Add Rust Target
        run: rustup target add ${{ matrix.target }}

      - name: Rust Cache
        uses: Swatinem/rust-cache@v2
        with:
          save-if: ${{ github.ref == 'refs/heads/dev' }}
          prefix-key: 'v1-rust'
          key: 'rust-shared-stable-${{ matrix.os }}-${{ matrix.target }}'
          workspaces: |
            . -> target
          cache-all-crates: true
          cache-workspace-crates: true

      - name: Install Node
        uses: actions/setup-node@v6
        with:
          node-version: '24.14.1'

      - name: Install pnpm
        uses: pnpm/action-setup@v5
        with:
          run_install: false

      - name: Pnpm install and check
        run: |
          pnpm i
          pnpm run prebuild ${{ matrix.target }}

      - name: 'Setup for linux'
        run: |-
          sudo ls -lR /etc/apt/

          cat > /tmp/sources.list << EOF
          deb [arch=amd64,i386] http://archive.ubuntu.com/ubuntu jammy main multiverse universe restricted
          deb [arch=amd64,i386] http://archive.ubuntu.com/ubuntu jammy-security main multiverse universe restricted
          deb [arch=amd64,i386] http://archive.ubuntu.com/ubuntu jammy-updates main multiverse universe restricted
          deb [arch=amd64,i386] http://archive.ubuntu.com/ubuntu jammy-backports main multiverse universe restricted

          deb [arch=armhf,arm64] http://ports.ubuntu.com/ubuntu-ports jammy main multiverse universe restricted
          deb [arch=armhf,arm64] http://ports.ubuntu.com/ubuntu-ports jammy-security main multiverse universe restricted
          deb [arch=armhf,arm64] http://ports.ubuntu.com/ubuntu-ports jammy-updates main multiverse universe restricted
          deb [arch=armhf,arm64] http://ports.ubuntu.com/ubuntu-ports jammy-backports main multiverse universe restricted
          EOF

          sudo mv /etc/apt/sources.list /etc/apt/sources.list.default
          sudo mv /tmp/sources.list /etc/apt/sources.list

          sudo dpkg --add-architecture ${{ matrix.arch }}
          sudo apt update

          sudo apt install -y \
            libxslt1.1:${{ matrix.arch }} \
            libwebkit2gtk-4.1-dev:${{ matrix.arch }} \
            libayatana-appindicator3-dev:${{ matrix.arch }} \
            libssl-dev:${{ matrix.arch }} \
            patchelf:${{ matrix.arch }} \
            librsvg2-dev:${{ matrix.arch }}

      - name: 'Install aarch64 tools'
        if: matrix.target == 'aarch64-unknown-linux-gnu'
        run: |
          sudo apt install -y \
            gcc-aarch64-linux-gnu \
            g++-aarch64-linux-gnu

      - name: 'Install armv7 tools'
        if: matrix.target == 'armv7-unknown-linux-gnueabihf'
        run: |
          sudo apt install -y \
            gcc-arm-linux-gnueabihf \
            g++-arm-linux-gnueabihf

      - name: Add Rust Target
        run: |
          # Ensure cross target is installed for the pinned toolchain; fallback without explicit toolchain if needed
          rustup target add ${{ matrix.target }} --toolchain 1.91.0 || rustup target add ${{ matrix.target }}
          rustup target list --installed
          echo "Rust target ${{ matrix.target }} installed."

      - name: Build for Linux
        run: |
          export PKG_CONFIG_ALLOW_CROSS=1
          if [ "${{ matrix.target }}" == "aarch64-unknown-linux-gnu" ]; then
            export PKG_CONFIG_PATH=/usr/lib/aarch64-linux-gnu/pkgconfig/:$PKG_CONFIG_PATH
            export PKG_CONFIG_SYSROOT_DIR=/usr/aarch64-linux-gnu/
          elif [ "${{ matrix.target }}" == "armv7-unknown-linux-gnueabihf" ]; then
            export PKG_CONFIG_PATH=/usr/lib/arm-linux-gnueabihf/pkgconfig/:$PKG_CONFIG_PATH
            export PKG_CONFIG_SYSROOT_DIR=/usr/arm-linux-gnueabihf/
          fi
          pnpm build --target ${{ matrix.target }}
        env:
          NODE_OPTIONS: '--max_old_space_size=4096'
          TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_PRIVATE_KEY }}
          TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_KEY_PASSWORD }}

      - name: Get Version
        run: |
          sudo apt-get update
          sudo apt-get install jq
          echo "VERSION=$(cat package.json | jq '.version' | tr -d '"')" >> $GITHUB_ENV
          echo "BUILDTIME=$(TZ=Asia/Shanghai date)" >> $GITHUB_ENV

      - name: Upload Release
        uses: softprops/action-gh-release@v2
        with:
          tag_name: v${{env.VERSION}}
          name: 'Clash Verge Rev v${{env.VERSION}}'
          body: 'See release notes for detailed changelog.'
          token: ${{ secrets.GITHUB_TOKEN }}
          prerelease: ${{ contains(github.ref_name, '-rc') }}
          files: |
            target/${{ matrix.target }}/release/bundle/deb/*.deb
            target/${{ matrix.target }}/release/bundle/rpm/*.rpm

  release-for-fixed-webview2:
    name: Release Build for Fixed WebView2
    needs: [check_tag_version]
    strategy:
      fail-fast: false
      matrix:
        include:
          - os: windows-latest
            target: x86_64-pc-windows-msvc
            arch: x64
          - os: windows-latest
            target: aarch64-pc-windows-msvc
            arch: arm64
    runs-on: ${{ matrix.os }}
    steps:
      - name: Checkout Repository
        uses: actions/checkout@v6

      - name: Install Rust Stable
        uses: dtolnay/rust-toolchain@master
        with:
          toolchain: '1.91.0'
          targets: ${{ matrix.target }}

      - name: Add Rust Target
        run: rustup target add ${{ matrix.target }}

      - name: Rust Cache
        uses: Swatinem/rust-cache@v2
        with:
          save-if: ${{ github.ref == 'refs/heads/dev' }}
          prefix-key: 'v1-rust'
          key: 'rust-shared-stable-${{ matrix.os }}-${{ matrix.target }}'
          workspaces: |
            . -> target
          cache-all-crates: true
          cache-workspace-crates: true

      - name: Install Node
        uses: actions/setup-node@v6
        with:
          node-version: '24.14.1'

      - uses: pnpm/action-setup@v5
        name: Install pnpm
        with:
          run_install: false

      - name: Pnpm install and check
        run: |
          pnpm i
          pnpm run prebuild ${{ matrix.target }}

      - name: Download WebView2 Runtime
        run: |
          invoke-webrequest -uri https://github.com/westinyang/WebView2RuntimeArchive/releases/download/133.0.3065.92/Microsoft.WebView2.FixedVersionRuntime.133.0.3065.92.${{ matrix.arch }}.cab -outfile Microsoft.WebView2.FixedVersionRuntime.133.0.3065.92.${{ matrix.arch }}.cab
          Expand .\Microsoft.WebView2.FixedVersionRuntime.133.0.3065.92.${{ matrix.arch }}.cab -F:* ./src-tauri
          Remove-Item .\src-tauri\tauri.windows.conf.json
          Rename-Item .\src-tauri\webview2.${{ matrix.arch }}.json tauri.windows.conf.json

      - name: Add Rust Target
        run: |
          # Ensure cross target is installed for the pinned toolchain; fallback without explicit toolchain if needed
          rustup target add ${{ matrix.target }} --toolchain 1.91.0 || rustup target add ${{ matrix.target }}
          rustup target list --installed
          echo "Rust target ${{ matrix.target }} installed."

      - name: Tauri build
        id: build
        uses: tauri-apps/tauri-action@v0.6.2
        env:
          NODE_OPTIONS: '--max_old_space_size=4096'
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_PRIVATE_KEY }}
          TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_KEY_PASSWORD }}
        with:
          tauriScript: pnpm
          args: --target ${{ matrix.target }}

      - name: Rename
        run: |
          $files = Get-ChildItem ".\target\${{ matrix.target }}\release\bundle\nsis\*-setup.exe"
          foreach ($file in $files) {
            $newName = $file.Name -replace "-setup\.exe$", "_fixed_webview2-setup.exe"
            Rename-Item $file.FullName $newName
          }

          $files = Get-ChildItem ".\target\${{ matrix.target }}\release\bundle\nsis\*.nsis.zip"
          foreach ($file in $files) {
            $newName = $file.Name -replace "-setup\.nsis\.zip$", "_fixed_webview2-setup.nsis.zip"
            Rename-Item $file.FullName $newName
          }

          $files = Get-ChildItem ".\target\${{ matrix.target }}\release\bundle\nsis\*-setup.exe.sig"
          foreach ($file in $files) {
            $newName = $file.Name -replace "-setup\.exe\.sig$", "_fixed_webview2-setup.exe.sig"
            Rename-Item $file.FullName $newName
          }

      - name: Upload Release
        uses: softprops/action-gh-release@v2
        with:
          tag_name: v${{steps.build.outputs.appVersion}}
          name: 'Clash Verge Rev v${{steps.build.outputs.appVersion}}'
          body: 'See release notes for detailed changelog.'
          token: ${{ secrets.GITHUB_TOKEN }}
          prerelease: ${{ contains(github.ref_name, '-rc') }}
          files: target/${{ matrix.target }}/release/bundle/nsis/*setup*

      - name: Portable Bundle
        run: pnpm portable-fixed-webview2 ${{ matrix.target }}
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

  release-update:
    if: ${{ !contains(github.ref_name, '-rc') }}
    name: Release Update
    runs-on: ubuntu-latest
    needs: [update_tag]
    steps:
      - name: Checkout repository
        uses: actions/checkout@v6

      - name: Install Node
        uses: actions/setup-node@v6
        with:
          node-version: '24.14.1'

      - uses: pnpm/action-setup@v5
        name: Install pnpm
        with:
          run_install: false

      - name: Pnpm install
        run: pnpm i

      - name: Release updater file
        run: pnpm updater
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

  release-update-for-fixed-webview2:
    if: ${{ !contains(github.ref_name, '-rc') }}
    runs-on: ubuntu-latest
    needs: [update_tag]
    steps:
      - name: Checkout repository
        uses: actions/checkout@v6

      - name: Install Node
        uses: actions/setup-node@v6
        with:
          node-version: '24.14.1'

      - uses: pnpm/action-setup@v5
        name: Install pnpm
        with:
          run_install: false

      - name: Pnpm install
        run: pnpm i

      - name: Release updater file
        run: pnpm updater-fixed-webview2
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

  submit-to-winget:
    if: ${{ !contains(github.ref_name, '-rc') }}
    name: Submit to Winget
    runs-on: ubuntu-latest
    needs: [update_tag, release-update]
    steps:
      - name: Checkout repository
        uses: actions/checkout@v6
        with:
          fetch-depth: 0
      - name: Get Version
        run: |
          sudo apt-get update
          sudo apt-get install jq
          echo "VERSION=$(cat package.json | jq '.version' | tr -d '"')" >> $GITHUB_ENV
      - name: Submit to Winget
        uses: vedantmgoyal9/winget-releaser@main
        with:
          identifier: ClashVergeRev.ClashVergeRev
          version: ${{env.VERSION}}
          release-tag: v${{env.VERSION}}
          installers-regex: '_(arm64|x64|x86)-setup\.exe$'
          token: ${{ secrets.WINGET_TOKEN  }}

  notify-telegram:
    if: ${{ !contains(github.ref_name, '-rc') }}
    name: Notify Telegram
    runs-on: ubuntu-latest
    needs:
      [
        update_tag,
        release-update,
        release-update-for-fixed-webview2,
        submit-to-winget,
      ]
    steps:
      - name: Checkout repository
        uses: actions/checkout@v6

      - name: Fetch UPDATE logs
        id: fetch_update_logs
        run: bash ./scripts/extract_update_logs.sh
        shell: bash

      - name: Install Node
        uses: actions/setup-node@v6
        with:
          node-version: '24.14.1'

      - uses: pnpm/action-setup@v5
        name: Install pnpm
        with:
          run_install: false

      - name: Install dependencies
        run: pnpm install --frozen-lockfile

      - name: Get Version and Release Info
        run: |
          sudo apt-get update
          sudo apt-get install jq
          echo "VERSION=$(cat package.json | jq '.version' | tr -d '"')" >> $GITHUB_ENV
          echo "DOWNLOAD_URL=https://github.com/clash-verge-rev/clash-verge-rev/releases/download/v$(cat package.json | jq '.version' | tr -d '"')" >> $GITHUB_ENV
          echo "BUILDTIME=$(TZ=Asia/Shanghai date)" >> $GITHUB_ENV

      - name: Generate release.txt
        run: |
          if [ -z "$UPDATE_LOGS" ]; then
            echo "No update logs found, using default message"
            UPDATE_LOGS="More new features are now supported. Check for detailed changelog soon."
          else
            echo "Using found update logs"
          fi

          cat > release.txt << EOF
          $UPDATE_LOGS

          ## 下载地址

          ### Windows (不再支持Win7)
          #### 正常版本(推荐)
          - [64位(常用)](${{ env.DOWNLOAD_URL }}/Clash.Verge_${{ env.VERSION }}_x64-setup.exe) | [ARM64(不常用)](${{ env.DOWNLOAD_URL }}/Clash.Verge_${{ env.VERSION }}_arm64-setup.exe)

          #### 内置Webview2版(体积较大,仅在企业版系统或无法安装webview2时使用)
          - [64位](${{ env.DOWNLOAD_URL }}/Clash.Verge_${{ env.VERSION }}_x64_fixed_webview2-setup.exe) | [ARM64](${{ env.DOWNLOAD_URL }}/Clash.Verge_${{ env.VERSION }}_arm64_fixed_webview2-setup.exe)

          ### macOS
          - [Apple M芯片](${{ env.DOWNLOAD_URL }}/Clash.Verge_${{ env.VERSION }}_aarch64.dmg) | [Intel芯片](${{ env.DOWNLOAD_URL }}/Clash.Verge_${{ env.VERSION }}_x64.dmg)

          ### Linux
          #### DEB包(Debian系) 使用 apt ./路径 安装
          - [64位](${{ env.DOWNLOAD_URL }}/Clash.Verge_${{ env.VERSION }}_amd64.deb) | [ARM64](${{ env.DOWNLOAD_URL }}/Clash.Verge_${{ env.VERSION }}_arm64.deb) | [ARMv7](${{ env.DOWNLOAD_URL }}/Clash.Verge_${{ env.VERSION }}_armhf.deb)

          #### RPM包(Redhat系) 使用 dnf ./路径 安装
          - [64位](${{ env.DOWNLOAD_URL }}/Clash.Verge-${{ env.VERSION }}-1.x86_64.rpm) | [ARM64](${{ env.DOWNLOAD_URL }}/Clash.Verge-${{ env.VERSION }}-1.aarch64.rpm) | [ARMv7](${{ env.DOWNLOAD_URL }}/Clash.Verge-${{ env.VERSION }}-1.armhfp.rpm)

          ### FAQ
          - [常见问题](https://clash-verge-rev.github.io/faq/windows.html)

          ### 稳定机场VPN推荐
          - [狗狗加速](https://verge.dginv.click/#/register?code=oaxsAGo6)

          Created at ${{ env.BUILDTIME }}.
          EOF

      - name: Send Telegram Notification
        run: node scripts/telegram.mjs
        env:
          TELEGRAM_BOT_TOKEN: ${{ secrets.TELEGRAM_BOT_TOKEN }}
          BUILD_TYPE: release
          VERSION: ${{ env.VERSION }}
          DOWNLOAD_URL: ${{ env.DOWNLOAD_URL }}
rustfmt .github/workflows/rustfmt.yml
Triggers
pull_request
Runs on
ubuntu-latest
Jobs
rustfmt
Actions
dorny/paths-filter, dtolnay/rust-toolchain
Commands
  • echo "No Rust changes, skipping rustfmt."
  • cargo fmt --manifest-path ./src-tauri/Cargo.toml --all -- --check
View raw YAML
# Copyright 2019-2024 Tauri Programme within The Commons Conservancy
# SPDX-License-Identifier: Apache-2.0
# SPDX-License-Identifier: MIT

name: Check Formatting

on:
  pull_request:

env:
  HUSKY: 0

jobs:
  rustfmt:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v6

      - name: Check Rust changes
        id: check_rust
        uses: dorny/paths-filter@v4
        with:
          filters: |
            rust:
              - 'src-tauri/**'
              - '**/*.rs'

      - name: Skip if no Rust changes
        if: steps.check_rust.outputs.rust != 'true'
        run: echo "No Rust changes, skipping rustfmt."

      - name: install Rust stable and rustfmt
        if: steps.check_rust.outputs.rust == 'true'
        uses: dtolnay/rust-toolchain@stable
        with:
          components: rustfmt

      - name: run cargo fmt
        if: steps.check_rust.outputs.rust == 'true'
        run: cargo fmt --manifest-path ./src-tauri/Cargo.toml --all -- --check

  # taplo:
  #   name: taplo (.toml files)
  #   runs-on: ubuntu-latest
  #   steps:
  #     - uses: actions/checkout@v6

  #     - name: install Rust stable
  #       uses: dtolnay/rust-toolchain@stable

  #     - name: install taplo-cli
  #       uses: taiki-e/install-action@v2.68.8
  #       with:
  #         tool: taplo-cli

  #     - run: taplo fmt --check --diff
telegram-notify perms .github/workflows/telegram-notify.yml
Triggers
workflow_dispatch
Runs on
ubuntu-latest
Jobs
notify-telegram
Actions
pnpm/action-setup
Commands
  • bash ./scripts/extract_update_logs.sh
  • pnpm install --frozen-lockfile
  • if [ -n "${{ inputs.version }}" ]; then VERSION="${{ inputs.version }}" else VERSION=$(jq -r '.version' package.json) fi echo "VERSION=$VERSION" >> $GITHUB_ENV echo "DOWNLOAD_URL=https://github.com/clash-verge-rev/clash-verge-rev/releases/download/v${VERSION}" >> $GITHUB_ENV echo "BUILDTIME=$(TZ=Asia/Shanghai date)" >> $GITHUB_ENV
  • if [ -z "$UPDATE_LOGS" ]; then echo "No update logs found, using default message" UPDATE_LOGS="More new features are now supported. Check for detailed changelog soon." else echo "Using found update logs" fi cat > release.txt << EOF $UPDATE_LOGS ## 下载地址 ### Windows (不再支持Win7) #### 正常版本(推荐) - [64位(常用)](${{ env.DOWNLOAD_URL }}/Clash.Verge_${{ env.VERSION }}_x64-setup.exe) | [ARM64(不常用)](${{ env.DOWNLOAD_URL }}/Clash.Verge_${{ env.VERSION }}_arm64-setup.exe) #### 内置Webview2版(体积较大,仅在企业版系统或无法安装webview2时使用) - [64位](${{ env.DOWNLOAD_URL }}/Clash.Verge_${{ env.VERSION }}_x64_fixed_webview2-setup.exe) | [ARM64](${{ env.DOWNLOAD_URL }}/Clash.Verge_${{ env.VERSION }}_arm64_fixed_webview2-setup.exe) ### macOS - [Apple M芯片](${{ env.DOWNLOAD_URL }}/Clash.Verge_${{ env.VERSION }}_aarch64.dmg) | [Intel芯片](${{ env.DOWNLOAD_URL }}/Clash.Verge_${{ env.VERSION }}_x64.dmg) ### Linux #### DEB包(Debian系) 使用 apt ./路径 安装 - [64位](${{ env.DOWNLOAD_URL }}/Clash.Verge_${{ env.VERSION }}_amd64.deb) | [ARM64](${{ env.DOWNLOAD_URL }}/Clash.Verge_${{ env.VERSION }}_arm64.deb) | [ARMv7](${{ env.DOWNLOAD_URL }}/Clash.Verge_${{ env.VERSION }}_armhf.deb) #### RPM包(Redhat系) 使用 dnf ./路径 安装 - [64位](${{ env.DOWNLOAD_URL }}/Clash.Verge-${{ env.VERSION }}-1.x86_64.rpm) | [ARM64](${{ env.DOWNLOAD_URL }}/Clash.Verge-${{ env.VERSION }}-1.aarch64.rpm) | [ARMv7](${{ env.DOWNLOAD_URL }}/Clash.Verge-${{ env.VERSION }}-1.armhfp.rpm) ### FAQ - [常见问题](https://clash-verge-rev.github.io/faq/windows.html) ### 稳定机场VPN推荐 - [狗狗加速](https://verge.dginv.click/#/register?code=oaxsAGo6) Created at ${{ env.BUILDTIME }}. EOF
  • node scripts/telegram.mjs
View raw YAML
name: Telegram Notify

on:
  workflow_dispatch:
    inputs:
      version:
        description: 'Version to notify (e.g. 2.4.7), defaults to package.json version'
        required: false
        type: string
      build_type:
        description: 'Build type'
        required: false
        default: 'release'
        type: choice
        options:
          - release
          - autobuild

permissions: {}

jobs:
  notify-telegram:
    name: Notify Telegram
    runs-on: ubuntu-latest
    steps:
      - name: Checkout repository
        uses: actions/checkout@v6

      - name: Fetch UPDATE logs
        id: fetch_update_logs
        run: bash ./scripts/extract_update_logs.sh
        shell: bash

      - name: Install Node
        uses: actions/setup-node@v6
        with:
          node-version: '24.14.1'

      - uses: pnpm/action-setup@v5
        name: Install pnpm
        with:
          run_install: false

      - name: Install dependencies
        run: pnpm install --frozen-lockfile

      - name: Get Version and Release Info
        run: |
          if [ -n "${{ inputs.version }}" ]; then
            VERSION="${{ inputs.version }}"
          else
            VERSION=$(jq -r '.version' package.json)
          fi
          echo "VERSION=$VERSION" >> $GITHUB_ENV
          echo "DOWNLOAD_URL=https://github.com/clash-verge-rev/clash-verge-rev/releases/download/v${VERSION}" >> $GITHUB_ENV
          echo "BUILDTIME=$(TZ=Asia/Shanghai date)" >> $GITHUB_ENV

      - name: Generate release.txt
        run: |
          if [ -z "$UPDATE_LOGS" ]; then
            echo "No update logs found, using default message"
            UPDATE_LOGS="More new features are now supported. Check for detailed changelog soon."
          else
            echo "Using found update logs"
          fi

          cat > release.txt << EOF
          $UPDATE_LOGS

          ## 下载地址

          ### Windows (不再支持Win7)
          #### 正常版本(推荐)
          - [64位(常用)](${{ env.DOWNLOAD_URL }}/Clash.Verge_${{ env.VERSION }}_x64-setup.exe) | [ARM64(不常用)](${{ env.DOWNLOAD_URL }}/Clash.Verge_${{ env.VERSION }}_arm64-setup.exe)

          #### 内置Webview2版(体积较大,仅在企业版系统或无法安装webview2时使用)
          - [64位](${{ env.DOWNLOAD_URL }}/Clash.Verge_${{ env.VERSION }}_x64_fixed_webview2-setup.exe) | [ARM64](${{ env.DOWNLOAD_URL }}/Clash.Verge_${{ env.VERSION }}_arm64_fixed_webview2-setup.exe)

          ### macOS
          - [Apple M芯片](${{ env.DOWNLOAD_URL }}/Clash.Verge_${{ env.VERSION }}_aarch64.dmg) | [Intel芯片](${{ env.DOWNLOAD_URL }}/Clash.Verge_${{ env.VERSION }}_x64.dmg)

          ### Linux
          #### DEB包(Debian系) 使用 apt ./路径 安装
          - [64位](${{ env.DOWNLOAD_URL }}/Clash.Verge_${{ env.VERSION }}_amd64.deb) | [ARM64](${{ env.DOWNLOAD_URL }}/Clash.Verge_${{ env.VERSION }}_arm64.deb) | [ARMv7](${{ env.DOWNLOAD_URL }}/Clash.Verge_${{ env.VERSION }}_armhf.deb)

          #### RPM包(Redhat系) 使用 dnf ./路径 安装
          - [64位](${{ env.DOWNLOAD_URL }}/Clash.Verge-${{ env.VERSION }}-1.x86_64.rpm) | [ARM64](${{ env.DOWNLOAD_URL }}/Clash.Verge-${{ env.VERSION }}-1.aarch64.rpm) | [ARMv7](${{ env.DOWNLOAD_URL }}/Clash.Verge-${{ env.VERSION }}-1.armhfp.rpm)

          ### FAQ
          - [常见问题](https://clash-verge-rev.github.io/faq/windows.html)

          ### 稳定机场VPN推荐
          - [狗狗加速](https://verge.dginv.click/#/register?code=oaxsAGo6)

          Created at ${{ env.BUILDTIME }}.
          EOF

      - name: Send Telegram Notification
        run: node scripts/telegram.mjs
        env:
          TELEGRAM_BOT_TOKEN: ${{ secrets.TELEGRAM_BOT_TOKEN }}
          BUILD_TYPE: ${{ inputs.build_type }}
          VERSION: ${{ env.VERSION }}
          DOWNLOAD_URL: ${{ env.DOWNLOAD_URL }}
updater perms .github/workflows/updater.yml
Triggers
workflow_dispatch
Runs on
ubuntu-latest, ubuntu-latest
Jobs
release-update, release-update-for-fixed-webview2
Actions
pnpm/action-setup, pnpm/action-setup
Commands
  • pnpm i
  • pnpm updater
  • pnpm i
  • pnpm updater-fixed-webview2
View raw YAML
name: Updater CI

on: workflow_dispatch
permissions: write-all
env:
  HUSKY: 0

jobs:
  release-update:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout repository
        uses: actions/checkout@v6

      - name: Install Node
        uses: actions/setup-node@v6
        with:
          node-version: '24.14.1'

      - uses: pnpm/action-setup@v5
        name: Install pnpm
        with:
          run_install: false

      - name: Pnpm install
        run: pnpm i

      - name: Release updater file
        run: pnpm updater
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

  release-update-for-fixed-webview2:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout repository
        uses: actions/checkout@v6

      - name: Install Node
        uses: actions/setup-node@v6
        with:
          node-version: '24.14.1'

      - uses: pnpm/action-setup@v5
        name: Install pnpm
        with:
          run_install: false

      - name: Pnpm install
        run: pnpm i

      - name: Release updater file
        run: pnpm updater-fixed-webview2
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}