name: Build the flutter version of the RustDesk

on:
  workflow_call:
    inputs:
      upload-artifact:
        type: boolean
        default: true
      upload-tag:
        type: string
        default: "nightly"

# NOTE: F-Droid builder script 'flutter/build_fdroid.sh' reads environment
# variables from this workflow!
#
# It does NOT read build steps, however, so please fix 'flutter/build_fdroid.sh
# whenever you add changes to Android CI build action ('build-rustdesk-android')
# in this file!

env:
  SCITER_RUST_VERSION: "1.75" # https://github.com/rustdesk/rustdesk/discussions/7503, also 1.78 has ABI change which causes our sciter version not working, https://blog.rust-lang.org/2024/03/30/i128-layout-update.html
  RUST_VERSION: "1.75" # sciter failed on m1 with 1.78 because of https://blog.rust-lang.org/2024/03/30/i128-layout-update.html
  MAC_RUST_VERSION: "1.81" # 1.81 is requred for macos, because of https://github.com/yury/cidre requires 1.81
  CARGO_NDK_VERSION: "3.1.2"
  SCITER_ARMV7_CMAKE_VERSION: "3.29.7"
  SCITER_NASM_DEBVERSION: "2.15.05-1"
  LLVM_VERSION: "15.0.6"
  FLUTTER_VERSION: "3.24.5"
  ANDROID_FLUTTER_VERSION: "3.24.5"
  # for arm64 linux because official Dart SDK does not work
  FLUTTER_ELINUX_VERSION: "3.16.9"
  TAG_NAME: "${{ inputs.upload-tag }}"
  VCPKG_BINARY_SOURCES: "clear;x-gha,readwrite"
  # vcpkg version: 2025.08.27
  # If we change the `VCPKG COMMIT_ID`, please remember:
  # 1. Call `$VCPKG_ROOT/vcpkg x-update-baseline` to update the baseline in `vcpkg.json`.
  #  Or we may face build issue like 
  #  https://github.com/rustdesk/rustdesk/actions/runs/14414119794/job/40427970174
  # 2. Update the `VCPKG_COMMIT_ID` in `ci.yml` and `playground.yml`.
  VCPKG_COMMIT_ID: "120deac3062162151622ca4860575a33844ba10b"
  ARMV7_VCPKG_COMMIT_ID: "6f29f12e82a8293156836ad81cc9bf5af41fe836" # 2025.01.13, got "/opt/artifacts/vcpkg/vcpkg: No such file or directory" with latest version
  VERSION: "1.4.6"
  NDK_VERSION: "r27c"
  #signing keys env variable checks
  ANDROID_SIGNING_KEY: "${{ secrets.ANDROID_SIGNING_KEY }}"
  MACOS_P12_BASE64: "${{ secrets.MACOS_P12_BASE64 }}"
  UPLOAD_ARTIFACT: "${{ inputs.upload-artifact }}"
  SIGN_BASE_URL: "${{ secrets.SIGN_BASE_URL }}-2"

jobs:
  generate-bridge:
    uses: ./.github/workflows/bridge.yml

  build-RustDeskTempTopMostWindow:
    uses: ./.github/workflows/third-party-RustDeskTempTopMostWindow.yml
    with:
      upload-artifact: ${{ inputs.upload-artifact }}
      target: windows-2022
      configuration: Release
      platform: x64
      target_version: Windows10
    strategy:
      fail-fast: false

  build-for-windows-flutter:
    name: ${{ matrix.job.target }}
    needs: [build-RustDeskTempTopMostWindow, generate-bridge]
    runs-on: ${{ matrix.job.os }}
    strategy:
      fail-fast: false
      matrix:
        job:
          # - { target: i686-pc-windows-msvc        , os: windows-2022                  }
          # - { target: x86_64-pc-windows-gnu       , os: windows-2022                  }
          - {
              target: x86_64-pc-windows-msvc,
              os: windows-2022,
              arch: x86_64,
              vcpkg-triplet: x64-windows-static,
            }
          # - { target: aarch64-pc-windows-msvc, os: windows-2022, arch: aarch64 }
    steps:
      - name: Export GitHub Actions cache environment variables
        uses: actions/github-script@v6
        with:
          script: |
            core.exportVariable('ACTIONS_CACHE_URL', process.env.ACTIONS_CACHE_URL || '');
            core.exportVariable('ACTIONS_RUNTIME_TOKEN', process.env.ACTIONS_RUNTIME_TOKEN || '');

      - name: Checkout source code
        uses: actions/checkout@v4
        with:
          submodules: recursive

      - name: Restore bridge files
        uses: actions/download-artifact@master
        with:
          name: bridge-artifact
          path: ./

      - name: Install LLVM and Clang
        uses: KyleMayes/install-llvm-action@v1
        with:
          version: ${{ env.LLVM_VERSION }}

      - name: Install flutter
        uses: subosito/flutter-action@v2.12.0 #https://github.com/subosito/flutter-action/issues/277
        with:
          channel: "stable"
          flutter-version: ${{ env.FLUTTER_VERSION }}

      # https://github.com/flutter/flutter/issues/155685
      - name: Replace engine with rustdesk custom flutter engine
        run: |
          flutter doctor -v
          flutter precache --windows
          Invoke-WebRequest -Uri https://github.com/rustdesk/engine/releases/download/main/windows-x64-release.zip -OutFile windows-x64-release.zip
          Expand-Archive -Path windows-x64-release.zip -DestinationPath windows-x64-release
          mv -Force windows-x64-release/*  C:/hostedtoolcache/windows/flutter/stable-${{ env.FLUTTER_VERSION }}-x64/bin/cache/artifacts/engine/windows-x64-release/

      - name: Patch flutter
        shell: bash
        run: |
          cp .github/patches/flutter_3.24.4_dropdown_menu_enableFilter.diff $(dirname $(dirname $(which flutter)))
          cd $(dirname $(dirname $(which flutter)))
          [[ "3.24.5" == ${{env.FLUTTER_VERSION}} ]] && git apply flutter_3.24.4_dropdown_menu_enableFilter.diff

      - name: Install Rust toolchain
        uses: dtolnay/rust-toolchain@v1
        with:
          toolchain: ${{ env.SCITER_RUST_VERSION }}
          targets: ${{ matrix.job.target }}
          components: "rustfmt"

      - uses: Swatinem/rust-cache@v2
        with:
          prefix-key: ${{ matrix.job.os }}

      - name: Setup vcpkg with Github Actions binary cache
        uses: lukka/run-vcpkg@v11
        with:
          vcpkgDirectory: C:\vcpkg
          vcpkgGitCommitId: ${{ env.VCPKG_COMMIT_ID }}
          doNotCache: false

      - name: Install vcpkg dependencies
        env:
          VCPKG_DEFAULT_HOST_TRIPLET: ${{ matrix.job.vcpkg-triplet }}
        run: |
          if ! $VCPKG_ROOT/vcpkg \
            install \
            --triplet ${{ matrix.job.vcpkg-triplet }} \
            --x-install-root="$VCPKG_ROOT/installed"; then
            find "${VCPKG_ROOT}/" -name "*.log" | while read -r _1; do
              echo "$_1:"
              echo "======"
              cat "$_1"
              echo "======"
              echo ""
            done
            exit 1
          fi
          head -n 100 "${VCPKG_ROOT}/buildtrees/ffmpeg/build-${{ matrix.job.vcpkg-triplet }}-rel-out.log" || true
        shell: bash

      - name: Build rustdesk
        run: |
          # Windows: build RustDesk
          python3 .\build.py --portable --hwcodec --flutter --vram --skip-portable-pack
          mv ./flutter/build/windows/x64/runner/Release ./rustdesk

          # Download usbmmidd_v2.zip and extract it to ./rustdesk
          Invoke-WebRequest -Uri https://github.com/rustdesk-org/rdev/releases/download/usbmmidd_v2/usbmmidd_v2.zip -OutFile usbmmidd_v2.zip
          Expand-Archive usbmmidd_v2.zip -DestinationPath .
          Remove-Item -Path usbmmidd_v2\Win32 -Recurse
          Remove-Item -Path "usbmmidd_v2\deviceinstaller64.exe", "usbmmidd_v2\deviceinstaller.exe", "usbmmidd_v2\usbmmidd.bat"
          mv -Force .\usbmmidd_v2 ./rustdesk

          # Download printer driver files and extract them to ./rustdesk
          try {
            Invoke-WebRequest -Uri https://github.com/rustdesk/hbb_common/releases/download/driver/rustdesk_printer_driver_v4-1.4.zip -OutFile rustdesk_printer_driver_v4-1.4.zip
            Invoke-WebRequest -Uri https://github.com/rustdesk/hbb_common/releases/download/driver/printer_driver_adapter.zip -OutFile printer_driver_adapter.zip
            Invoke-WebRequest -Uri https://github.com/rustdesk/hbb_common/releases/download/driver/sha256sums -OutFile sha256sums

            # Check and move the files
            $checksum_driver = (Select-String -Path .\sha256sums -Pattern '^([a-fA-F0-9]{64}) \*rustdesk_printer_driver_v4-1.4\.zip$').Matches.Groups[1].Value
            $downloadsum_driver = Get-FileHash -Path rustdesk_printer_driver_v4-1.4.zip -Algorithm SHA256
            $checksum_adapter = (Select-String -Path .\sha256sums -Pattern '^([a-fA-F0-9]{64}) \*printer_driver_adapter\.zip$').Matches.Groups[1].Value
            $downloadsum_adapter = Get-FileHash -Path printer_driver_adapter.zip -Algorithm SHA256
            if ($checksum_driver -eq $downloadsum_driver.Hash -and $checksum_adapter -eq $downloadsum_adapter.Hash) {
                Write-Output "rustdesk_printer_driver_v4-1.4, checksums match, extract the file."
                Expand-Archive rustdesk_printer_driver_v4-1.4.zip -DestinationPath .
                mkdir ./rustdesk/drivers
                mv -Force .\rustdesk_printer_driver_v4-1.4 ./rustdesk/drivers/RustDeskPrinterDriver
                Expand-Archive printer_driver_adapter.zip -DestinationPath .
                mv -Force .\printer_driver_adapter.dll ./rustdesk
            } elseif ($checksum_driver -ne $downloadsum_driver.Hash) {
                Write-Output "rustdesk_printer_driver_v4-1.4, checksums do not match, ignore the file."
            } else {
                Write-Output "printer_driver_adapter.dll, checksums do not match, ignore the file."
            }
          } catch {
              Write-Host "Ingore the printer driver error."
          }

      - name: find Runner.res
        # Windows: find Runner.res (compiled from ./flutter/windows/runner/Runner.rc), copy to ./Runner.res
        # Runner.rc does not contain actual version, but Runner.res does
        continue-on-error: true
        shell: bash
        run: |
          runner_res=$(find . -name "Runner.res");
          if [ "$runner_res" == "" ]; then
            echo "Runner.res: not found";
          else
            echo "Runner.res: $runner_res";
            cp $runner_res ./libs/portable/Runner.res;
            echo "list ./libs/portable/Runner.res";
            ls -l ./libs/portable/Runner.res;
          fi

      - name: Download RustDeskTempTopMostWindow artifacts
        uses: actions/download-artifact@master
        if: ${{ inputs.upload-artifact }}
        with:
          name: topmostwindow-artifacts
          path: "./rustdesk"

      - name: Upload unsigned
        if: env.UPLOAD_ARTIFACT == 'true'
        uses: actions/upload-artifact@master
        with:
          name: rustdesk-unsigned-windows-${{ matrix.job.arch }}
          path: rustdesk

      - name: Sign rustdesk files
        if: env.UPLOAD_ARTIFACT == 'true' && env.SIGN_BASE_URL != '-2'
        shell: bash
        run: |
          pip3 install requests argparse
          BASE_URL=${{ env.SIGN_BASE_URL }} SECRET_KEY=${{ secrets.SIGN_SECRET_KEY }} python3 res/job.py sign_files ./rustdesk/

      - name: Build self-extracted executable
        shell: bash
        if: env.UPLOAD_ARTIFACT == 'true'
        run: |
          sed -i '/dpiAware/d' res/manifest.xml
          pushd ./libs/portable
          pip3 install -r requirements.txt
          python3 ./generate.py -f ../../rustdesk/ -o . -e ../../rustdesk/rustdesk.exe
          popd
          mkdir -p ./SignOutput
          mv ./target/release/rustdesk-portable-packer.exe ./SignOutput/rustdesk-${{ env.VERSION }}-${{ matrix.job.arch }}.exe

      - name: Add MSBuild to PATH
        uses: microsoft/setup-msbuild@v2

      - name: Build msi
        if: env.UPLOAD_ARTIFACT == 'true'
        run: |
          pushd ./res/msi
          python preprocess.py --arp -d ../../rustdesk
          nuget restore msi.sln
          msbuild msi.sln -p:Configuration=Release -p:Platform=x64 /p:TargetVersion=Windows10
          mv ./Package/bin/x64/Release/en-us/Package.msi ../../SignOutput/rustdesk-${{ env.VERSION }}-${{ matrix.job.arch }}.msi
          sha256sum ../../SignOutput/rustdesk-*.msi

      - name: Sign rustdesk self-extracted file
        if: env.UPLOAD_ARTIFACT == 'true' && env.SIGN_BASE_URL != '-2'
        shell: bash
        run: |
          BASE_URL=${{ env.SIGN_BASE_URL }} SECRET_KEY=${{ secrets.SIGN_SECRET_KEY }} python3 res/job.py sign_files ./SignOutput

      - name: Publish Release
        uses: softprops/action-gh-release@v1
        if: env.UPLOAD_ARTIFACT == 'true'
        with:
          prerelease: true
          tag_name: ${{ env.TAG_NAME }}
          files: |
            ./SignOutput/rustdesk-*.msi
            ./SignOutput/rustdesk-*.exe

  # The fallback for the flutter version, we use Sciter for 32bit Windows.
  build-for-windows-sciter:
    name: ${{ matrix.job.target }} (${{ matrix.job.os }})
    runs-on: ${{ matrix.job.os }}
    # Temporarily disable this action due to additional test is needed.
    # if: false
    strategy:
      fail-fast: false
      matrix:
        job:
          # - { target: i686-pc-windows-msvc        , os: windows-2022                  }
          # - { target: x86_64-pc-windows-gnu       , os: windows-2022                  }
          - {
              target: i686-pc-windows-msvc,
              os: windows-2022,
              arch: x86,
              vcpkg-triplet: x86-windows-static,
            }
          # - { target: aarch64-pc-windows-msvc, os: windows-2022 }
    steps:
      - name: Export GitHub Actions cache environment variables
        uses: actions/github-script@v6
        with:
          script: |
            core.exportVariable('ACTIONS_CACHE_URL', process.env.ACTIONS_CACHE_URL || '');
            core.exportVariable('ACTIONS_RUNTIME_TOKEN', process.env.ACTIONS_RUNTIME_TOKEN || '');

      - name: Checkout source code
        uses: actions/checkout@v4
        with:
          submodules: recursive

      - name: Install LLVM and Clang
        uses: rustdesk-org/install-llvm-action-32bit@master
        with:
          version: ${{ env.LLVM_VERSION }}

      - name: Install Rust toolchain
        uses: dtolnay/rust-toolchain@v1
        with:
          toolchain: nightly-2023-10-13-${{ matrix.job.target }} # must use nightly here, because of abi_thiscall feature required
          targets: ${{ matrix.job.target }}
          components: "rustfmt"

      - uses: Swatinem/rust-cache@v2
        with:
          prefix-key: ${{ matrix.job.os }}-sciter

      - name: Setup vcpkg with Github Actions binary cache
        uses: lukka/run-vcpkg@v11
        with:
          vcpkgDirectory: C:\vcpkg
          vcpkgGitCommitId: ${{ env.VCPKG_COMMIT_ID }}
          doNotCache: false

      - name: Install vcpkg dependencies
        env:
          VCPKG_DEFAULT_HOST_TRIPLET: ${{ matrix.job.vcpkg-triplet }}
        run: |
          if ! $VCPKG_ROOT/vcpkg \
            install \
            --triplet ${{ matrix.job.vcpkg-triplet }} \
            --x-install-root="$VCPKG_ROOT/installed"; then
            find "${VCPKG_ROOT}/" -name "*.log" | while read -r _1; do
              echo "$_1:"
              echo "======"
              cat "$_1"
              echo "======"
              echo ""
            done
            exit 1
          fi
          head -n 100 "${VCPKG_ROOT}/buildtrees/ffmpeg/build-${{ matrix.job.vcpkg-triplet }}-rel-out.log" || true
        shell: bash

      - name: Build rustdesk
        id: build
        shell: bash
        run: |
          python3 res/inline-sciter.py
          # Patch sciter x86
          sed -i 's/branch = "dyn"/branch = "dyn_x86"/g' ./Cargo.toml
          cargo build --features inline,vram,hwcodec --release --bins
          mkdir -p ./Release
          mv ./target/release/rustdesk.exe ./Release/rustdesk.exe
          curl -LJ -o ./Release/sciter.dll https://github.com/c-smile/sciter-sdk/raw/master/bin.win/x32/sciter.dll
          echo "output_folder=./Release" >> $GITHUB_OUTPUT
          curl -LJ -o ./usbmmidd_v2.zip https://github.com/rustdesk-org/rdev/releases/download/usbmmidd_v2/usbmmidd_v2.zip
          unzip usbmmidd_v2.zip
          # Do not remove x64 files, because the user may run the 32bit version on a 64bit system.
          # Do not remove ./usbmmidd_v2/deviceinstaller64.exe, as x86 exe cannot install and uninstall drivers when running on x64,
          # we need the x64 exe to install and uninstall the driver.
          rm -rf ./usbmmidd_v2/deviceinstaller.exe ./usbmmidd_v2/usbmmidd.bat
          mv ./usbmmidd_v2 ./Release || true

      - name: find Runner.res
        # Windows: find Runner.res (compiled from ./flutter/windows/runner/Runner.rc), copy to ./Runner.res
        # Runner.rc does not contain actual version, but Runner.res does
        continue-on-error: true
        shell: bash
        run: |
          runner_res=$(find . -name "Runner.res");
          if [ "$runner_res" == "" ]; then
            echo "Runner.res: not found";
          else
            echo "Runner.res: $runner_res";
            cp $runner_res ./libs/portable/Runner.res;
            echo "list ./libs/portable/Runner.res";
            ls -l ./libs/portable/Runner.res;
          fi

      - name: Upload unsigned
        if: env.UPLOAD_ARTIFACT == 'true'
        uses: actions/upload-artifact@master
        with:
          name: rustdesk-unsigned-windows-${{ matrix.job.arch }}
          path: Release

      - name: Sign rustdesk files
        if: env.UPLOAD_ARTIFACT == 'true' && env.SIGN_BASE_URL != '-2'
        shell: bash
        run: |
          pip3 install requests argparse
          BASE_URL=${{ env.SIGN_BASE_URL }} SECRET_KEY=${{ secrets.SIGN_SECRET_KEY }} python3 res/job.py sign_files ./Release/

      - name: Build self-extracted executable
        shell: bash
        run: |
          sed -i '/dpiAware/d' res/manifest.xml
          pushd ./libs/portable
          pip3 install -r requirements.txt
          python3 ./generate.py -f ../../Release/ -o . -e ../../Release/rustdesk.exe
          popd
          mkdir -p ./SignOutput
          mv ./target/release/rustdesk-portable-packer.exe ./SignOutput/rustdesk-${{ env.VERSION }}-${{ matrix.job.arch }}-sciter.exe

      - name: Sign rustdesk self-extracted file
        if: env.UPLOAD_ARTIFACT == 'true' && env.SIGN_BASE_URL != '-2'
        shell: bash
        run: |
          BASE_URL=${{ env.SIGN_BASE_URL }} SECRET_KEY=${{ secrets.SIGN_SECRET_KEY }} python3 res/job.py sign_files ./SignOutput/

      - name: Publish Release
        uses: softprops/action-gh-release@v1
        if: env.UPLOAD_ARTIFACT == 'true'
        with:
          prerelease: true
          tag_name: ${{ env.TAG_NAME }}
          files: |
            ./SignOutput/rustdesk-*.exe

  build-rustdesk-ios:
    if: ${{ inputs.upload-artifact }}
    name: build rustdesk ios ipa
    runs-on: ${{ matrix.job.os }}
    needs: [generate-bridge]
    strategy:
      fail-fast: false
      matrix:
        job:
          - {
              arch: aarch64,
              target: aarch64-apple-ios,
              os: macos-latest,
              vcpkg-triplet: arm64-ios,
            }
    steps:
      - name: Export GitHub Actions cache environment variables
        uses: actions/github-script@v6
        with:
          script: |
            core.exportVariable('ACTIONS_CACHE_URL', process.env.ACTIONS_CACHE_URL || '');
            core.exportVariable('ACTIONS_RUNTIME_TOKEN', process.env.ACTIONS_RUNTIME_TOKEN || '');

      - name: Install dependencies
        run: |
          brew install nasm yasm
      - name: Checkout source code
        uses: actions/checkout@v4
        with:
          submodules: recursive

      - name: Install flutter
        uses: subosito/flutter-action@v2
        with:
          channel: "stable"
          flutter-version: ${{ env.FLUTTER_VERSION }}

      - name: Patch flutter
        run: |
          cd $(dirname $(dirname $(which flutter)))
          [[ "3.24.5" == ${{env.FLUTTER_VERSION}} ]] && git apply ${{ github.workspace }}/.github/patches/flutter_3.24.4_dropdown_menu_enableFilter.diff

      - name: Setup vcpkg with Github Actions binary cache
        uses: lukka/run-vcpkg@v11
        with:
          vcpkgGitCommitId: ${{ env.VCPKG_COMMIT_ID }}
          doNotCache: false

      - name: Install vcpkg dependencies
        run: |
          if ! $VCPKG_ROOT/vcpkg \
            install \
            --triplet ${{ matrix.job.vcpkg-triplet }} \
            --x-install-root="$VCPKG_ROOT/installed"; then
            find "${VCPKG_ROOT}/" -name "*.log" | while read -r _1; do
              echo "$_1:"
              echo "======"
              cat "$_1"
              echo "======"
              echo ""
            done
            exit 1
          fi
          head -n 100 "${VCPKG_ROOT}/buildtrees/ffmpeg/build-${{ matrix.job.vcpkg-triplet }}-rel-out.log" || true
        shell: bash

      - name: Install Rust toolchain
        uses: dtolnay/rust-toolchain@v1
        with:
          toolchain: ${{ env.RUST_VERSION }}
          targets: ${{ matrix.job.target }}
          components: "rustfmt"

      - uses: Swatinem/rust-cache@v2
        with:
          prefix-key: rustdesk-lib-cache-ios
          key: ${{ matrix.job.target }}

      - name: Restore bridge files
        uses: actions/download-artifact@master
        with:
          name: bridge-artifact
          path: ./

      - name: Build rustdesk lib
        run: |
          rustup target add ${{ matrix.job.target }}
          cargo build --features flutter,hwcodec --release --target aarch64-apple-ios --lib
          
      - name: Upload liblibrustdesk.a Artifacts
        uses: actions/upload-artifact@master
        with:
          name: liblibrustdesk.a
          path: target/aarch64-apple-ios/release/liblibrustdesk.a

      - name: Build rustdesk
        shell: bash
        run: |
          pushd flutter
          # flutter build ipa --release --obfuscate --split-debug-info=./split-debug-info --no-codesign
          # for easy debugging
          flutter build ipa --release --no-codesign

      # - name: Upload Artifacts
      #   # if: env.ANDROID_SIGNING_KEY != null && env.UPLOAD_ARTIFACT == 'true'
      #   uses: actions/upload-artifact@master
      #   with:
      #     name: rustdesk-${{ env.VERSION }}-${{ matrix.job.arch }}.apk
      #     path: flutter/build/ios/ipa/*.ipa

      # - name: Publish ipa package
      #   # if: env.ANDROID_SIGNING_KEY != null && env.UPLOAD_ARTIFACT == 'true'
      #   uses: softprops/action-gh-release@v1
      #   with:
      #     prerelease: true
      #     tag_name: ${{ env.TAG_NAME }}
      #     files: |
      #       flutter/build/ios/ipa/*.ipa


  build-for-macOS:
    name: ${{ matrix.job.target }}
    runs-on: ${{ matrix.job.os }}
    needs: [generate-bridge]
    strategy:
      fail-fast: false
      matrix:
        job:
          - {
              target: x86_64-apple-darwin,
              os: macos-15-intel, #macos-latest or macos-14 use M1 now, https://docs.github.com/en/actions/using-github-hosted-runners/about-github-hosted-runners/about-github-hosted-runners#:~:text=14%20GB-,macos%2Dlatest%20or%20macos%2D14,-The%20macos%2Dlatestlabel
              extra-build-args: "",
              arch: x86_64,
              vcpkg-triplet: x64-osx,
            }
          - {
              target: aarch64-apple-darwin,
              os: macos-14,
              # extra-build-args: "--disable-flutter-texture-render", # disable this for mac, because we see a lot of users reporting flickering both on arm and x64, and we can not confirm if texture rendering has better performance if htere is no vram, https://github.com/rustdesk/rustdesk/issues/6296
              extra-build-args: "--screencapturekit",
              arch: aarch64,
              vcpkg-triplet: arm64-osx,
            }
    steps:
      - name: Export GitHub Actions cache environment variables
        uses: actions/github-script@v6
        with:
          script: |
            core.exportVariable('ACTIONS_CACHE_URL', process.env.ACTIONS_CACHE_URL || '');
            core.exportVariable('ACTIONS_RUNTIME_TOKEN', process.env.ACTIONS_RUNTIME_TOKEN || '');

      - name: Checkout source code
        uses: actions/checkout@v4
        with:
          submodules: recursive

      - name: Import the codesign cert
        if: env.MACOS_P12_BASE64 != null
        uses: apple-actions/import-codesign-certs@v1
        with:
          p12-file-base64: ${{ secrets.MACOS_P12_BASE64 }}
          p12-password: ${{ secrets.MACOS_P12_PASSWORD }}
          keychain: rustdesk

      - name: Check sign and import sign key
        if: env.MACOS_P12_BASE64 != null
        run: |
          security default-keychain -s rustdesk.keychain
          security find-identity -v

      - name: Import notarize key
        if: env.MACOS_P12_BASE64 != null
        uses: timheuer/base64-to-file@v1.2
        with:
          # https://gregoryszorc.com/docs/apple-codesign/stable/apple_codesign_rcodesign.html#notarizing-and-stapling
          fileName: rustdesk.json
          fileDir: ${{ github.workspace }}
          encodedString: ${{ secrets.MACOS_NOTARIZE_JSON }}

      - name: Install rcodesign tool
        if: env.MACOS_P12_BASE64 != null
        shell: bash
        run: |
          pushd /tmp
          wget https://github.com/indygreg/apple-platform-rs/releases/download/apple-codesign%2F0.22.0/apple-codesign-0.22.0-macos-universal.tar.gz
          tar -zxvf apple-codesign-0.22.0-macos-universal.tar.gz
          mv apple-codesign-0.22.0-macos-universal/rcodesign /usr/local/bin
          popd

      - name: Install build runtime
        run: |
          brew install llvm create-dmg
          # pkg-config is handled in a separate step, because it may be already installed by `macos-latest`(14.7.1) runner
          if command -v pkg-config &>/dev/null; then
              echo "pkg-config is already installed"
          else
              brew install pkg-config
          fi

      - name: Install NASM
        run: |
          # Install NASM 2.16.x from official release.
          # Do NOT use `brew install nasm` which installs NASM 3.x.
          # NASM 3.x is a complete rewrite with incompatible CLI options and removed features.
          # aom and other multimedia libraries require NASM 2.x for x86/x86_64 assembly.
          wget https://www.nasm.us/pub/nasm/releasebuilds/2.16.03/macosx/nasm-2.16.03-macosx.zip
          unzip nasm-2.16.03-macosx.zip
          sudo cp nasm-2.16.03/nasm /usr/local/bin/nasm
          nasm --version

      - name: Install flutter
        uses: subosito/flutter-action@v2
        with:
          channel: "stable"
          flutter-version: ${{ env.FLUTTER_VERSION }}

      - name: Patch flutter
        run: |
          cd $(dirname $(dirname $(which flutter)))
          [[ "3.24.5" == ${{env.FLUTTER_VERSION}} ]] && git apply ${{ github.workspace }}/.github/patches/flutter_3.24.4_dropdown_menu_enableFilter.diff

      - name: Workaround for flutter issue
        shell: bash
        run: |
          cd "$(dirname "$(which flutter)")"
          # https://github.com/flutter/flutter/issues/133533
          sed -i -e 's/_setFramesEnabledState(false);/\/\/_setFramesEnabledState(false);/g' ../packages/flutter/lib/src/scheduler/binding.dart
          grep -n '_setFramesEnabledState(false);' ../packages/flutter/lib/src/scheduler/binding.dart

      - name: Install Rust toolchain
        uses: dtolnay/rust-toolchain@v1
        with:
          toolchain: ${{ env.MAC_RUST_VERSION }}
          targets: ${{ matrix.job.target }}
          components: "rustfmt"

      - uses: Swatinem/rust-cache@v2
        with:
          prefix-key: ${{ matrix.job.os }}

      - name: Restore bridge files
        uses: actions/download-artifact@master
        with:
          name: bridge-artifact
          path: ./

      - name: Setup vcpkg with Github Actions binary cache
        uses: lukka/run-vcpkg@v11
        with:
          vcpkgGitCommitId: ${{ env.VCPKG_COMMIT_ID }}
          doNotCache: false

      - name: Install vcpkg dependencies
        run: |
          if ! $VCPKG_ROOT/vcpkg \
            install \
            --x-install-root="$VCPKG_ROOT/installed"; then
            find "${VCPKG_ROOT}/" -name "*.log" | while read -r _1; do
              echo "$_1:"
              echo "======"
              cat "$_1"
              echo "======"
              echo ""
            done
            exit 1
          fi
          head -n 100 "${VCPKG_ROOT}/buildtrees/ffmpeg/build-${{ matrix.job.vcpkg-triplet }}-rel-out.log" || true

      - name: Show version information (Rust, cargo, Clang)
        shell: bash
        run: |
          clang --version || true
          rustup -V
          rustup toolchain list
          rustup default
          cargo -V
          rustc -V

      - name: Build rustdesk
        run: |
          if [ "${{ matrix.job.target }}" = "aarch64-apple-darwin" ]; then
            MIN_MACOS_VERSION="12.3"
            sed -i -e "s/MACOSX_DEPLOYMENT_TARGET\=[0-9]*.[0-9]*/MACOSX_DEPLOYMENT_TARGET=${MIN_MACOS_VERSION}/" build.py
            sed -i -e "s/platform :osx, '.*'/platform :osx, '${MIN_MACOS_VERSION}'/" flutter/macos/Podfile
            sed -i -e "s/osx_minimum_system_version = \"[0-9]*.[0-9]*\"/osx_minimum_system_version = \"${MIN_MACOS_VERSION}\"/" Cargo.toml
            sed -i -e "s/MACOSX_DEPLOYMENT_TARGET = [0-9]*.[0-9]*;/MACOSX_DEPLOYMENT_TARGET = ${MIN_MACOS_VERSION};/" flutter/macos/Runner.xcodeproj/project.pbxproj
          fi
          ./build.py --flutter --hwcodec --unix-file-copy-paste ${{ matrix.job.extra-build-args }}

      - name: create unsigned dmg
        if: env.UPLOAD_ARTIFACT == 'true'
        run: |
          CREATE_DMG="$(command -v create-dmg)"
          CREATE_DMG="$(readlink -f "$CREATE_DMG")"
          sed -i -e 's/MAXIMUM_UNMOUNTING_ATTEMPTS=3/MAXIMUM_UNMOUNTING_ATTEMPTS=7/' "$CREATE_DMG"
          create-dmg --icon "RustDesk.app" 200 190 --hide-extension "RustDesk.app" --window-size 800 400 --app-drop-link 600 185 rustdesk-${{ env.VERSION }}-${{ matrix.job.arch }}.dmg ./flutter/build/macos/Build/Products/Release/RustDesk.app

      - name: Upload unsigned macOS app
        if: env.UPLOAD_ARTIFACT == 'true'
        uses: actions/upload-artifact@master
        with:
          name: rustdesk-unsigned-macos-${{ matrix.job.arch }}
          path: rustdesk-${{ env.VERSION }}-${{ matrix.job.arch }}.dmg # can not upload the directory directly or tar.gz, which destroy the link structure, causing the codesign failed

      - name: Codesign app and create signed dmg
        if: env.MACOS_P12_BASE64 != null && env.UPLOAD_ARTIFACT == 'true'
        run: |
          # Patch create-dmg to give more attempts to unmount image
          CREATE_DMG="$(command -v create-dmg)"
          CREATE_DMG="$(readlink -f "$CREATE_DMG")"
          sed -i -e 's/MAXIMUM_UNMOUNTING_ATTEMPTS=3/MAXIMUM_UNMOUNTING_ATTEMPTS=7/' "$CREATE_DMG"
          # Unlock keychain
          security default-keychain -s rustdesk.keychain
          security unlock-keychain -p ${{ secrets.MACOS_P12_PASSWORD }} rustdesk.keychain
          # start sign the rustdesk.app and dmg
          rm -rf *.dmg || true
          codesign --force --options runtime -s ${{ secrets.MACOS_CODESIGN_IDENTITY }} --deep --strict ./flutter/build/macos/Build/Products/Release/RustDesk.app -vvv
          create-dmg --icon "RustDesk.app" 200 190 --hide-extension "RustDesk.app" --window-size 800 400 --app-drop-link 600 185 rustdesk-${{ env.VERSION }}.dmg ./flutter/build/macos/Build/Products/Release/RustDesk.app
          codesign --force --options runtime -s ${{ secrets.MACOS_CODESIGN_IDENTITY }} --deep --strict rustdesk-${{ env.VERSION }}.dmg -vvv
          # notarize the rustdesk-${{ env.VERSION }}.dmg
          rcodesign notary-submit --api-key-path ${{ github.workspace }}/rustdesk.json  --staple rustdesk-${{ env.VERSION }}.dmg

      - name: Rename rustdesk
        if: env.UPLOAD_ARTIFACT == 'true'
        run: |
          for name in rustdesk*??.dmg; do
              mv "$name" "${name%%.dmg}-${{ matrix.job.arch }}.dmg"
          done

      - name: Publish DMG package
        if: env.UPLOAD_ARTIFACT == 'true'
        uses: softprops/action-gh-release@v1
        with:
          prerelease: true
          tag_name: ${{ env.TAG_NAME }}
          files: |
            rustdesk*-${{ matrix.job.arch }}.dmg

  publish_unsigned:
    needs:
      - build-for-macOS
      - build-for-windows-flutter
      - build-for-windows-sciter
    runs-on: ubuntu-latest
    if: ${{ inputs.upload-artifact }}
    steps:
      - name: Download artifacts
        uses: actions/download-artifact@master
        with:
          name: rustdesk-unsigned-macos-x86_64
          path: ./

      - name: Download Artifacts
        uses: actions/download-artifact@master
        with:
          name: rustdesk-unsigned-macos-aarch64
          path: ./

      - name: Download Artifacts
        uses: actions/download-artifact@master
        with:
          name: rustdesk-unsigned-windows-x86_64
          path: ./windows-x86_64/

      - name: Download Artifacts
        uses: actions/download-artifact@master
        with:
          name: rustdesk-unsigned-windows-x86
          path: ./windows-x86/

      - name: Combine unsigned app
        run: |
          tar czf rustdesk-${{ env.VERSION }}-unsigned.tar.gz *.dmg windows-x86_64 windows-x86

      - name: Publish unsigned app
        uses: softprops/action-gh-release@v1
        with:
          prerelease: true
          tag_name: ${{ env.TAG_NAME }}
          files: rustdesk-${{ env.VERSION }}-unsigned.tar.gz

  build-rustdesk-android:
    needs: [generate-bridge]
    name: build rustdesk android apk ${{ matrix.job.target }}
    runs-on: ${{ matrix.job.os }}
    strategy:
      fail-fast: false
      matrix:
        job:
          - {
              arch: aarch64,
              target: aarch64-linux-android,
              os: ubuntu-24.04,
              reltype: release,
              suffix: "",
            }
          - {
              arch: armv7,
              target: armv7-linux-androideabi,
              os: ubuntu-24.04,
              reltype: release,
              suffix: "",
            }
          - {
              arch: x86_64,
              target: x86_64-linux-android,
              os: ubuntu-24.04,
              reltype: release,
              suffix: "",
            }
    steps:
      - name: Free Disk Space (Ubuntu)
        uses: jlumbroso/free-disk-space@main
        with:
          tool-cache: false
          android: false
          dotnet: true
          haskell: true
          large-packages: false
          docker-images: true
          swap-storage: false

      - name: Export GitHub Actions cache environment variables
        uses: actions/github-script@v6
        with:
          script: |
            core.exportVariable('ACTIONS_CACHE_URL', process.env.ACTIONS_CACHE_URL || '');
            core.exportVariable('ACTIONS_RUNTIME_TOKEN', process.env.ACTIONS_RUNTIME_TOKEN || '');

      - name: Install dependencies
        run: |
          sudo apt-get update
          sudo apt-get install -y \
               clang \
               cmake \
               curl \
               gcc-multilib \
               git \
               g++ \
               g++-multilib \
               libayatana-appindicator3-dev \
               libasound2-dev \
               libc6-dev \
               libclang-dev \
               libunwind-dev \
               libgstreamer1.0-dev \
               libgstreamer-plugins-base1.0-dev \
               libgtk-3-dev \
               libpam0g-dev \
               libpulse-dev \
               libva-dev \
               libxcb-randr0-dev \
               libxcb-shape0-dev \
               libxcb-xfixes0-dev \
               libxdo-dev \
               libxfixes-dev \
               llvm-dev \
               nasm \
               ninja-build \
               openjdk-17-jdk-headless \
               pkg-config \
               tree \
               wget

      - name: Checkout source code
        uses: actions/checkout@v4
        with:
          submodules: recursive

      - name: Install flutter
        uses: subosito/flutter-action@v2
        with:
          channel: "stable"
          flutter-version: ${{ env.ANDROID_FLUTTER_VERSION }}

      - name: Patch flutter
        run: |
          cd $(dirname $(dirname $(which flutter)))
          [[ "3.24.5" == ${{env.ANDROID_FLUTTER_VERSION}} ]] && git apply ${{ github.workspace }}/.github/patches/flutter_3.24.4_dropdown_menu_enableFilter.diff

      - uses: nttld/setup-ndk@v1
        id: setup-ndk
        with:
          ndk-version: ${{ env.NDK_VERSION }}
          add-to-path: true

      - name: Setup vcpkg with Github Actions binary cache
        uses: lukka/run-vcpkg@v11
        with:
          vcpkgDirectory: /opt/artifacts/vcpkg
          vcpkgGitCommitId: ${{ env.VCPKG_COMMIT_ID }}
          doNotCache: false

      - name: Install vcpkg dependencies
        run: |
          case ${{ matrix.job.target }} in
            aarch64-linux-android)
              ANDROID_TARGET=arm64-v8a
            ;;
            armv7-linux-androideabi)
              ANDROID_TARGET=armeabi-v7a
            ;;
            x86_64-linux-android)
              ANDROID_TARGET=x86_64
            ;;
            i686-linux-android)
              ANDROID_TARGET=x86
            ;;
          esac
          if ! ./flutter/build_android_deps.sh "${ANDROID_TARGET}"; then
            find "${VCPKG_ROOT}/" -name "*.log" | while read -r _1; do
              echo "$_1:"
              echo "======"
              cat "$_1"
              echo "======"
              echo ""
            done
            exit 1
          fi
        shell: bash

      - name: Restore bridge files
        uses: actions/download-artifact@master
        with:
          name: bridge-artifact
          path: ./

      - name: Install Rust toolchain
        uses: dtolnay/rust-toolchain@v1
        with:
          toolchain: ${{ env.RUST_VERSION }}
          components: "rustfmt"

      - uses: Swatinem/rust-cache@v2
        with:
          prefix-key: rustdesk-lib-cache-android # TODO: drop '-android' part after caches are invalidated
          key: ${{ matrix.job.target }}

      - name: Build rustdesk lib
        env:
          ANDROID_NDK_HOME: ${{ steps.setup-ndk.outputs.ndk-path }}
          ANDROID_NDK_ROOT: ${{ steps.setup-ndk.outputs.ndk-path }}
        run: |
          rustup target add ${{ matrix.job.target }}
          cargo install cargo-ndk --version ${{ env.CARGO_NDK_VERSION }} --locked
          case ${{ matrix.job.target }} in
            aarch64-linux-android)
              ./flutter/ndk_arm64.sh
              mkdir -p ./flutter/android/app/src/main/jniLibs/arm64-v8a
              cp ./target/${{ matrix.job.target }}/release/liblibrustdesk.so ./flutter/android/app/src/main/jniLibs/arm64-v8a/librustdesk.so
            ;;
            armv7-linux-androideabi)
              ./flutter/ndk_arm.sh
              mkdir -p ./flutter/android/app/src/main/jniLibs/armeabi-v7a
              cp ./target/${{ matrix.job.target }}/release/liblibrustdesk.so ./flutter/android/app/src/main/jniLibs/armeabi-v7a/librustdesk.so
            ;;
            x86_64-linux-android)
              ./flutter/ndk_x64.sh
              mkdir -p ./flutter/android/app/src/main/jniLibs/x86_64
              cp ./target/${{ matrix.job.target }}/release/liblibrustdesk.so ./flutter/android/app/src/main/jniLibs/x86_64/librustdesk.so
            ;;
            i686-linux-android)
              ./flutter/ndk_x86.sh
              mkdir -p ./flutter/android/app/src/main/jniLibs/x86
              cp ./target/${{ matrix.job.target }}/release/liblibrustdesk.so ./flutter/android/app/src/main/jniLibs/x86/librustdesk.so
            ;;
          esac

      - name: Upload Rustdesk library to Artifacts
        uses: actions/upload-artifact@master
        with:
          name: librustdesk.so.${{ matrix.job.target }}
          path: ./target/${{ matrix.job.target }}/release/liblibrustdesk.so

      - name: Build rustdesk
        shell: bash
        env:
          JAVA_HOME: /usr/lib/jvm/java-17-openjdk-amd64
        run: |
          export PATH=/usr/lib/jvm/java-17-openjdk-amd64/bin:$PATH
          # Increase Gradle JVM memory for CI builds
          sed -i "s/org.gradle.jvmargs=-Xmx1024M/org.gradle.jvmargs=-Xmx2g/g" ./flutter/android/gradle.properties
          # temporary use debug sign config
          sed -i "s/signingConfigs.release/signingConfigs.debug/g" ./flutter/android/app/build.gradle
          case ${{ matrix.job.target }} in
            aarch64-linux-android)
              mkdir -p ./flutter/android/app/src/main/jniLibs/arm64-v8a
              cp ${ANDROID_NDK_HOME}/toolchains/llvm/prebuilt/linux-x86_64/sysroot/usr/lib/aarch64-linux-android/libc++_shared.so ./flutter/android/app/src/main/jniLibs/arm64-v8a/
              cp ./target/${{ matrix.job.target }}/release/liblibrustdesk.so ./flutter/android/app/src/main/jniLibs/arm64-v8a/librustdesk.so
              # build flutter
              pushd flutter
              flutter build apk "--${{ matrix.job.reltype }}" --target-platform android-arm64 --split-per-abi
              mv build/app/outputs/flutter-apk/app-arm64-v8a-${{ matrix.job.reltype }}.apk ../rustdesk-${{ env.VERSION }}-${{ matrix.job.arch }}${{ matrix.job.suffix }}.apk
            ;;
            armv7-linux-androideabi)
              mkdir -p ./flutter/android/app/src/main/jniLibs/armeabi-v7a
              cp ${ANDROID_NDK_HOME}/toolchains/llvm/prebuilt/linux-x86_64/sysroot/usr/lib/arm-linux-androideabi/libc++_shared.so ./flutter/android/app/src/main/jniLibs/armeabi-v7a/
              cp ./target/${{ matrix.job.target }}/release/liblibrustdesk.so ./flutter/android/app/src/main/jniLibs/armeabi-v7a/librustdesk.so
              # build flutter
              pushd flutter
              flutter build apk "--${{ matrix.job.reltype }}" --target-platform android-arm --split-per-abi
              mv build/app/outputs/flutter-apk/app-armeabi-v7a-${{ matrix.job.reltype }}.apk ../rustdesk-${{ env.VERSION }}-${{ matrix.job.arch }}${{ matrix.job.suffix }}.apk
            ;;
            x86_64-linux-android)
              mkdir -p ./flutter/android/app/src/main/jniLibs/x86_64
              cp ${ANDROID_NDK_HOME}/toolchains/llvm/prebuilt/linux-x86_64/sysroot/usr/lib/x86_64-linux-android/libc++_shared.so ./flutter/android/app/src/main/jniLibs/x86_64/
              cp ./target/${{ matrix.job.target }}/release/liblibrustdesk.so ./flutter/android/app/src/main/jniLibs/x86_64/librustdesk.so
              # build flutter
              pushd flutter
              flutter build apk "--${{ matrix.job.reltype }}" --target-platform android-x64 --split-per-abi
              mv build/app/outputs/flutter-apk/app-x86_64-${{ matrix.job.reltype }}.apk ../rustdesk-${{ env.VERSION }}-${{ matrix.job.arch }}${{ matrix.job.suffix }}.apk
            ;;
            i686-linux-android)
              mkdir -p ./flutter/android/app/src/main/jniLibs/x86
              cp ${ANDROID_NDK_HOME}/toolchains/llvm/prebuilt/linux-x86_64/sysroot/usr/lib/i686-linux-android/libc++_shared.so ./flutter/android/app/src/main/jniLibs/x86/
              cp ./target/${{ matrix.job.target }}/release/liblibrustdesk.so ./flutter/android/app/src/main/jniLibs/x86/librustdesk.so
              # build flutter
              pushd flutter
              flutter build apk "--${{ matrix.job.reltype }}" --target-platform android-x86 --split-per-abi
              mv build/app/outputs/flutter-apk/app-x86-${{ matrix.job.reltype }}.apk ../rustdesk-${{ env.VERSION }}-${{ matrix.job.arch }}${{ matrix.job.suffix }}.apk
            ;;
          esac
          popd
          mkdir -p signed-apk; pushd signed-apk
          mv ../rustdesk-${{ env.VERSION }}-${{ matrix.job.arch }}${{ matrix.job.suffix }}.apk .

      # https://github.com/r0adkll/sign-android-release/issues/84#issuecomment-1889636075
      - name: Setup sign tool version variable
        shell: bash
        run: |
          BUILD_TOOL_VERSION=$(ls /usr/local/lib/android/sdk/build-tools/ | tail -n 1)
          echo "ANDROID_SIGN_TOOL_VERSION=$BUILD_TOOL_VERSION" >> $GITHUB_ENV
          echo Last build tool version is: $BUILD_TOOL_VERSION

      - uses: r0adkll/sign-android-release@v1
        name: Sign app APK
        if: env.ANDROID_SIGNING_KEY != null
        id: sign-rustdesk
        with:
          releaseDirectory: ./signed-apk
          signingKeyBase64: ${{ secrets.ANDROID_SIGNING_KEY }}
          alias: ${{ secrets.ANDROID_ALIAS }}
          keyStorePassword: ${{ secrets.ANDROID_KEY_STORE_PASSWORD }}
          keyPassword: ${{ secrets.ANDROID_KEY_PASSWORD }}
        env:
          # env.ANDROID_SIGN_TOOL_VERSION is set by Step "Setup sign tool version variable"
          BUILD_TOOLS_VERSION: ${{ env.ANDROID_SIGN_TOOL_VERSION }}

      - name: Upload Artifacts
        if: env.ANDROID_SIGNING_KEY != null && env.UPLOAD_ARTIFACT == 'true'
        uses: actions/upload-artifact@master
        with:
          name: rustdesk-${{ env.VERSION }}-${{ matrix.job.arch }}.apk
          path: ${{steps.sign-rustdesk.outputs.signedReleaseFile}}

      - name: Publish signed apk package
        if: env.ANDROID_SIGNING_KEY != null && env.UPLOAD_ARTIFACT == 'true'
        uses: softprops/action-gh-release@v1
        with:
          prerelease: true
          tag_name: ${{ env.TAG_NAME }}
          files: |
            ${{steps.sign-rustdesk.outputs.signedReleaseFile}}

      - name: Publish unsigned apk package
        if: env.ANDROID_SIGNING_KEY == null && env.UPLOAD_ARTIFACT == 'true'
        uses: softprops/action-gh-release@v1
        with:
          prerelease: true
          tag_name: ${{ env.TAG_NAME }}
          files: |
            signed-apk/rustdesk-${{ env.VERSION }}-${{ matrix.job.arch }}.apk

  build-rustdesk-android-universal:
    needs: [build-rustdesk-android]
    name: build rustdesk android universal apk
    if: ${{ inputs.upload-artifact }}
    runs-on: ubuntu-24.04
    env:
      reltype: release
      x86_target: "" # can be ",android-x86"
      suffix: ""
    steps:
      - name: Free Disk Space (Ubuntu)
        uses: jlumbroso/free-disk-space@main
        with:
          tool-cache: false
          android: false
          dotnet: true
          haskell: true
          large-packages: false
          docker-images: true
          swap-storage: false

      - name: Export GitHub Actions cache environment variables
        uses: actions/github-script@v6
        with:
          script: |
            core.exportVariable('ACTIONS_CACHE_URL', process.env.ACTIONS_CACHE_URL || '');
            core.exportVariable('ACTIONS_RUNTIME_TOKEN', process.env.ACTIONS_RUNTIME_TOKEN || '');

      - name: Install dependencies
        run: |
          sudo apt-get update
          sudo apt-get install -y \
               clang \
               cmake \
               curl \
               gcc-multilib \
               git \
               g++ \
               g++-multilib \
               libayatana-appindicator3-dev \
               libasound2-dev \
               libc6-dev \
               libclang-dev \
               libunwind-dev \
               libgstreamer1.0-dev \
               libgstreamer-plugins-base1.0-dev \
               libgtk-3-dev \
               libpam0g-dev \
               libpulse-dev \
               libva-dev \
               libxcb-randr0-dev \
               libxcb-shape0-dev \
               libxcb-xfixes0-dev \
               libxdo-dev \
               libxfixes-dev \
               llvm-dev \
               nasm \
               ninja-build \
               openjdk-17-jdk-headless \
               pkg-config \
               tree \
               wget

      - name: Checkout source code
        uses: actions/checkout@v4
        with:
          submodules: recursive

      - name: Install flutter
        uses: subosito/flutter-action@v2
        with:
          channel: "stable"
          flutter-version: ${{ env.ANDROID_FLUTTER_VERSION }}

      - name: Patch flutter
        run: |
          cd $(dirname $(dirname $(which flutter)))
          [[ "3.24.5" == ${{env.ANDROID_FLUTTER_VERSION}} ]] && git apply ${{ github.workspace }}/.github/patches/flutter_3.24.4_dropdown_menu_enableFilter.diff

      - name: Restore bridge files
        uses: actions/download-artifact@master
        with:
          name: bridge-artifact
          path: ./

      - name: Download Rustdesk library from Artifacts
        uses: actions/download-artifact@master
        with:
          name: librustdesk.so.aarch64-linux-android
          path: ./flutter/android/app/src/main/jniLibs/arm64-v8a

      - name: Download Rustdesk library from Artifacts
        uses: actions/download-artifact@master
        with:
          name: librustdesk.so.armv7-linux-androideabi
          path: ./flutter/android/app/src/main/jniLibs/armeabi-v7a

      - name: Download Rustdesk library from Artifacts
        uses: actions/download-artifact@master
        with:
          name: librustdesk.so.x86_64-linux-android
          path: ./flutter/android/app/src/main/jniLibs/x86_64

      - name: Download Rustdesk library from Artifacts
        if: ${{ env.reltype == 'debug' }}
        uses: actions/download-artifact@master
        with:
          name: librustdesk.so.i686-linux-android
          path: ./flutter/android/app/src/main/jniLibs/x86

      - name: Build rustdesk
        shell: bash
        env:
          JAVA_HOME: /usr/lib/jvm/java-17-openjdk-amd64
        run: |
          export PATH=/usr/lib/jvm/java-17-openjdk-amd64/bin:$PATH
          # Increase Gradle JVM memory for CI builds
          sed -i "s/org.gradle.jvmargs=-Xmx1024M/org.gradle.jvmargs=-Xmx2g/g" ./flutter/android/gradle.properties
          # temporary use debug sign config
          sed -i "s/signingConfigs.release/signingConfigs.debug/g" ./flutter/android/app/build.gradle
          mv ./flutter/android/app/src/main/jniLibs/arm64-v8a/liblibrustdesk.so ./flutter/android/app/src/main/jniLibs/arm64-v8a/librustdesk.so
          cp ${ANDROID_NDK_HOME}/toolchains/llvm/prebuilt/linux-x86_64/sysroot/usr/lib/aarch64-linux-android/libc++_shared.so ./flutter/android/app/src/main/jniLibs/arm64-v8a/
          mv ./flutter/android/app/src/main/jniLibs/armeabi-v7a/liblibrustdesk.so ./flutter/android/app/src/main/jniLibs/armeabi-v7a/librustdesk.so
          cp ${ANDROID_NDK_HOME}/toolchains/llvm/prebuilt/linux-x86_64/sysroot/usr/lib/arm-linux-androideabi/libc++_shared.so ./flutter/android/app/src/main/jniLibs/armeabi-v7a/
          mv ./flutter/android/app/src/main/jniLibs/x86_64/liblibrustdesk.so ./flutter/android/app/src/main/jniLibs/x86_64/librustdesk.so
          cp ${ANDROID_NDK_HOME}/toolchains/llvm/prebuilt/linux-x86_64/sysroot/usr/lib/x86_64-linux-android/libc++_shared.so ./flutter/android/app/src/main/jniLibs/x86_64/
          if [ "${{ env.reltype }}" = "debug" ]; then
            mv ./flutter/android/app/src/main/jniLibs/x86/liblibrustdesk.so ./flutter/android/app/src/main/jniLibs/x86/librustdesk.so
            cp ${ANDROID_NDK_HOME}/toolchains/llvm/prebuilt/linux-x86_64/sysroot/usr/lib/i686-linux-android/libc++_shared.so ./flutter/android/app/src/main/jniLibs/x86/
          fi
          # build flutter
          pushd flutter
          flutter build apk "--${{ env.reltype }}" --target-platform android-arm64,android-arm,android-x64${{ env.x86_target }}
          popd
          mkdir -p signed-apk
          mv ./flutter/build/app/outputs/flutter-apk/app-${{ env.reltype }}.apk signed-apk/rustdesk-${{ env.VERSION }}-universal${{ env.suffix }}.apk

      # https://github.com/r0adkll/sign-android-release/issues/84#issuecomment-1889636075
      - name: Setup sign tool version variable
        shell: bash
        run: |
          BUILD_TOOL_VERSION=$(ls /usr/local/lib/android/sdk/build-tools/ | tail -n 1)
          echo "ANDROID_SIGN_TOOL_VERSION=$BUILD_TOOL_VERSION" >> $GITHUB_ENV
          echo Last build tool version is: $BUILD_TOOL_VERSION

      - uses: r0adkll/sign-android-release@v1
        name: Sign app APK
        if: env.ANDROID_SIGNING_KEY != null
        id: sign-rustdesk
        with:
          releaseDirectory: ./signed-apk
          signingKeyBase64: ${{ secrets.ANDROID_SIGNING_KEY }}
          alias: ${{ secrets.ANDROID_ALIAS }}
          keyStorePassword: ${{ secrets.ANDROID_KEY_STORE_PASSWORD }}
          keyPassword: ${{ secrets.ANDROID_KEY_PASSWORD }}
        env:
          # env.ANDROID_SIGN_TOOL_VERSION is set by Step "Setup sign tool version variable"
          BUILD_TOOLS_VERSION: ${{ env.ANDROID_SIGN_TOOL_VERSION }}

      - name: Upload Artifacts
        if: env.ANDROID_SIGNING_KEY != null && env.UPLOAD_ARTIFACT == 'true'
        uses: actions/upload-artifact@master
        with:
          name: rustdesk-${{ env.VERSION }}-${{ matrix.job.arch }}.apk
          path: ${{steps.sign-rustdesk.outputs.signedReleaseFile}}

      - name: Publish signed apk package
        if: env.ANDROID_SIGNING_KEY != null && env.UPLOAD_ARTIFACT == 'true'
        uses: softprops/action-gh-release@v1
        with:
          prerelease: true
          tag_name: ${{ env.TAG_NAME }}
          files: |
            ${{steps.sign-rustdesk.outputs.signedReleaseFile}}

      - name: Publish unsigned apk package
        if: env.ANDROID_SIGNING_KEY == null && env.UPLOAD_ARTIFACT == 'true'
        uses: softprops/action-gh-release@v1
        with:
          prerelease: true
          tag_name: ${{ env.TAG_NAME }}
          files: |
            signed-apk/rustdesk-${{ env.VERSION }}-universal${{ env.suffix }}.apk

  build-rustdesk-linux:
    needs: [generate-bridge]
    name: build rustdesk linux ${{ matrix.job.target }}
    runs-on: ${{ matrix.job.on }}
    strategy:
      fail-fast: false
      matrix:
        # use a high level qemu-user-static
        job:
          - {
              arch: x86_64,
              target: x86_64-unknown-linux-gnu,
              distro: ubuntu18.04,
              on: ubuntu-22.04,
              deb_arch: amd64,
              vcpkg-triplet: x64-linux,
            }
          - {
              arch: aarch64,
              target: aarch64-unknown-linux-gnu,
              distro: ubuntu18.04,
              on: ubuntu-22.04-arm,
              deb_arch: arm64,
              vcpkg-triplet: arm64-linux,
            }
    steps:
      - name: Export GitHub Actions cache environment variables
        uses: actions/github-script@v6
        with:
          script: |
            core.exportVariable('ACTIONS_CACHE_URL', process.env.ACTIONS_CACHE_URL || '');
            core.exportVariable('ACTIONS_RUNTIME_TOKEN', process.env.ACTIONS_RUNTIME_TOKEN || '');

      - name: Maximize build space
        run: |
          sudo rm -rf /opt/ghc
          sudo rm -rf /usr/local/lib/android
          sudo rm -rf /usr/share/dotnet
          sudo apt-get update -y
          sudo apt-get install -y nasm
          if [[ "${{ matrix.job.arch }}" == "x86_64" ]]; then
            sudo apt-get install -y qemu-user-static
          fi

      - name: Checkout source code
        uses: actions/checkout@v4
        with:
          submodules: recursive

      - name: Set Swap Space
        if: ${{ matrix.job.arch == 'x86_64' }}
        uses: pierotofy/set-swap-space@master
        with:
          swap-size-gb: 12

      - name: Free Space
        run: |
          df -h
          free -m

      - name: Install Rust toolchain
        uses: dtolnay/rust-toolchain@v1
        if: matrix.job.arch == 'x86_64' || env.UPLOAD_ARTIFACT == 'true'
        with:
          toolchain: ${{ env.RUST_VERSION }}
          targets: ${{ matrix.job.target }}
          components: "rustfmt"

      - name: Save Rust toolchain version
        run: |
          RUST_TOOLCHAIN_VERSION=$(cargo --version | awk '{print $2}')
          echo "RUST_TOOLCHAIN_VERSION=$RUST_TOOLCHAIN_VERSION" >> $GITHUB_ENV

      - name: Disable rust bridge build
        run: |
          # only build cdylib
          sed -i  "s/\[\"cdylib\", \"staticlib\", \"rlib\"\]/\[\"cdylib\"\]/g" Cargo.toml

      - name: Restore bridge files
        if: matrix.job.arch == 'x86_64' || env.UPLOAD_ARTIFACT == 'true'
        uses: actions/download-artifact@master
        with:
          name: bridge-artifact
          path: ./

      - name: Setup vcpkg with Github Actions binary cache
        if: matrix.job.arch == 'x86_64' || env.UPLOAD_ARTIFACT == 'true'
        uses: lukka/run-vcpkg@v11
        with:
          vcpkgDirectory: /opt/artifacts/vcpkg
          vcpkgGitCommitId: ${{ env.VCPKG_COMMIT_ID }}
          doNotCache: false

      - name: Install vcpkg dependencies
        if: matrix.job.arch == 'x86_64' || env.UPLOAD_ARTIFACT == 'true'
        run: |
          sudo apt install -y libva-dev && apt show libva-dev
          if ! $VCPKG_ROOT/vcpkg \
            install \
            --triplet ${{ matrix.job.vcpkg-triplet }} \
            --x-install-root="$VCPKG_ROOT/installed"; then
            find "${VCPKG_ROOT}/" -name "*.log" | while read -r _1; do
              echo "$_1:"
              echo "======"
              cat "$_1"
              echo "======"
              echo ""
            done
            exit 1
          fi
          head -n 100 "${VCPKG_ROOT}/buildtrees/ffmpeg/build-${{ matrix.job.vcpkg-triplet }}-rel-out.log" || true
        shell: bash

      - name: Restore bridge files
        if: matrix.job.arch == 'x86_64' || env.UPLOAD_ARTIFACT == 'true'
        uses: actions/download-artifact@master
        with:
          name: bridge-artifact
          path: ./

      - uses: rustdesk-org/run-on-arch-action@amd64-support
        name: Build rustdesk
        id: vcpkg
        if: matrix.job.arch == 'x86_64' || env.UPLOAD_ARTIFACT == 'true'
        with:
          arch: ${{ matrix.job.arch }}
          distro: ${{ matrix.job.distro }}
          githubToken: ${{ github.token }}
          setup: |
            ls -l "${PWD}"
            ls -l /opt/artifacts/vcpkg/installed
          dockerRunArgs: |
            --volume "${PWD}:/workspace"
            --volume "/opt/artifacts:/opt/artifacts"
          shell: /bin/bash
          install: |
            apt-get update -y
            echo -e "installing deps"
            apt-get install -y \
               build-essential \
               clang \
               cmake \
               curl \
               gcc \
               git \
               g++ \
               libayatana-appindicator3-dev \
               libasound2-dev \
               libclang-10-dev \
               libgstreamer1.0-dev \
               libgstreamer-plugins-base1.0-dev \
               libgtk-3-dev \
               libpam0g-dev \
               libpulse-dev \
               libva-dev \
               libxcb-randr0-dev \
               libxcb-shape0-dev \
               libxcb-xfixes0-dev \
               libxdo-dev \
               libxfixes-dev \
               llvm-10-dev \
               nasm \
               ninja-build \
               pkg-config \
               tree \
               python3 \
               rpm \
               unzip \
               wget \
               xz-utils \
               libssl-dev
            # we have libopus compiled by us.
            apt-get remove -y libopus-dev || true
            # output devs
            ls -l ./
            tree -L 3 /opt/artifacts/vcpkg/installed
          run: |
            # disable git safe.directory
            git config --global --add safe.directory "*"
            # rust
            pushd /opt
            # do not use rustup, because memory overflow in qemu
            wget -O rust.tar.gz https://static.rust-lang.org/dist/rust-${{env.RUST_TOOLCHAIN_VERSION}}-${{ matrix.job.target }}.tar.gz
            tar -zxvf rust.tar.gz > /dev/null && rm rust.tar.gz
            cd rust-${{env.RUST_TOOLCHAIN_VERSION}}-${{ matrix.job.target }} && ./install.sh
            rm -rf rust-${{env.RUST_TOOLCHAIN_VERSION}}-${{ matrix.job.target }}
            # edit config
            mkdir -p ~/.cargo/
            echo """
              [source.crates-io]
              registry = 'https://github.com/rust-lang/crates.io-index'
            """ > ~/.cargo/config
            cat ~/.cargo/config
            # start build
            pushd /workspace
            export VCPKG_ROOT=/opt/artifacts/vcpkg
            if [[ "${{ matrix.job.arch }}" == "aarch64" ]]; then
              export JOBS="--jobs 3"
            else
              export JOBS=""
            fi
            echo $JOBS
            cargo build --lib $JOBS --features hwcodec,flutter,unix-file-copy-paste --release
            rm -rf target/release/deps target/release/build
            rm -rf ~/.cargo

            # Setup Flutter
            # disable git safe.directory
            git config --global --add safe.directory "*"
            pushd /workspace
            case ${{ matrix.job.arch }} in
              aarch64)
                export PATH=/opt/flutter-elinux/bin:$PATH
                sed -i "s/flutter build linux --release/flutter-elinux build linux --verbose/g" ./build.py
                sed -i "s/x64\/release/arm64\/release/g" ./build.py
              ;;
              x86_64)
                export PATH=/opt/flutter/bin:$PATH
              ;;
            esac
            popd
            pushd /opt
            wget https://storage.googleapis.com/flutter_infra_release/releases/stable/linux/flutter_linux_${{ env.FLUTTER_VERSION }}-stable.tar.xz
            tar xf flutter_linux_${{ env.FLUTTER_VERSION }}-stable.tar.xz
            case ${{ matrix.job.arch }} in
              aarch64)
                # clone repo and reset to flutter ${{ env.FLUTTER_VERSION }}
                git clone https://github.com/sony/flutter-elinux.git || true
                pushd flutter-elinux
                  git fetch
                  git reset --hard ${{ env.FLUTTER_VERSION }}
                  bin/flutter-elinux doctor -v
                  bin/flutter-elinux precache --linux
                popd
                cp -R flutter/bin/cache/artifacts/engine/linux-x64/shader_lib flutter-elinux/flutter/bin/cache/artifacts/engine/linux-arm64
                rm -rf flutter
              ;;
              x86_64)
                flutter doctor -v
              ;;
            esac

            if [[ "3.24.5" == ${{ env.FLUTTER_VERSION }} ]]; then
              case ${{ matrix.job.arch }} in
                aarch64)
                  pushd /opt/flutter-elinux/flutter
                ;;
                x86_64)
                  pushd /opt/flutter
                ;;
              esac
              git apply ${{ github.workspace }}/.github/patches/flutter_3.24.4_dropdown_menu_enableFilter.diff
              popd
            fi

            # build flutter
            pushd /workspace
            export CARGO_INCREMENTAL=0
            export DEB_ARCH=${{ matrix.job.deb_arch }}
            python3 ./build.py --flutter --skip-cargo
            for name in rustdesk*??.deb; do
              mv "$name" "${name%%.deb}-${{ matrix.job.arch }}.deb"
            done

            # rpm package
            echo -e "start packaging fedora package"
            pushd /workspace
            case ${{ matrix.job.arch }} in
              aarch64)
                sed -i "s/linux\/x64/linux\/arm64/g" ./res/rpm-flutter.spec
                ;;
            esac
            HBB=`pwd` rpmbuild ./res/rpm-flutter.spec -bb
            pushd ~/rpmbuild/RPMS/${{ matrix.job.arch }}
            for name in rustdesk*??.rpm; do
                mv "$name" /workspace/"${name%%.rpm}.rpm"
            done

            # rpm suse package
            echo -e "start packaging suse package"
            pushd /workspace
            case ${{ matrix.job.arch }} in
              aarch64)
                sed -i "s/linux\/x64/linux\/arm64/g" ./res/rpm-flutter-suse.spec
                ;;
            esac
            HBB=`pwd` rpmbuild ./res/rpm-flutter-suse.spec -bb
            pushd ~/rpmbuild/RPMS/${{ matrix.job.arch }}
            for name in rustdesk*??.rpm; do
                mv "$name" /workspace/"${name%%.rpm}-suse.rpm"
            done

      - name: Publish debian/rpm package
        if: env.UPLOAD_ARTIFACT == 'true'
        uses: softprops/action-gh-release@v1
        with:
          prerelease: true
          tag_name: ${{ env.TAG_NAME }}
          files: |
            rustdesk-*.deb
            rustdesk-*.rpm

      - name: Upload deb
        uses: actions/upload-artifact@master
        if: env.UPLOAD_ARTIFACT == 'true'
        with:
          name: rustdesk-${{ env.VERSION }}-${{ matrix.job.arch }}.deb
          path: rustdesk-${{ env.VERSION }}-${{ matrix.job.arch }}.deb

      # only x86_64 for arch since we can not find newest arm64 docker image to build
      # old arch image does not make sense for arch since it is "arch" which always update to date
      # and failed to makepkg arm64 on x86_64
      - name: Patch archlinux PKGBUILD
        if: matrix.job.arch == 'x86_64' && env.UPLOAD_ARTIFACT == 'true'
        run: |
          sed -i "s/x86_64/${{ matrix.job.arch }}/g" res/PKGBUILD
          if [[ "${{ matrix.job.arch }}" == "aarch64" ]]; then
            sed -i "s/x86_64/aarch64/g" ./res/PKGBUILD
          fi

      - name: Build archlinux package
        if: matrix.job.arch == 'x86_64' && env.UPLOAD_ARTIFACT == 'true'
        uses: rustdesk-org/arch-makepkg-action@master
        with:
          packages:
          scripts: |
            cd res && HBB=`pwd`/.. FLUTTER=1 makepkg -f

      - name: Publish archlinux package
        if: matrix.job.arch == 'x86_64' && env.UPLOAD_ARTIFACT == 'true'
        uses: softprops/action-gh-release@v1
        with:
          prerelease: true
          tag_name: ${{ env.TAG_NAME }}
          files: |
            res/rustdesk-${{ env.VERSION }}*.zst

  build-rustdesk-linux-sciter:
    if: ${{ inputs.upload-artifact }}
    runs-on: ${{ matrix.job.on }}
    name: build-rustdesk-linux-sciter ${{ matrix.job.target }}
    strategy:
      fail-fast: false
      matrix:
        # use a high level qemu-user-static
        job:
          - {
              arch: x86_64,
              target: x86_64-unknown-linux-gnu,
              on: ubuntu-22.04,
              distro: ubuntu18.04,
              deb_arch: amd64,
              sciter_arch: x64,
              vcpkg-triplet: x64-linux,
              extra_features: ",hwcodec,unix-file-copy-paste",
            }
          - {
              arch: armv7,
              target: armv7-unknown-linux-gnueabihf,
              on: ubuntu-22.04-arm,
              distro: ubuntu18.04-rustdesk,
              deb_arch: armhf,
              sciter_arch: arm32,
              vcpkg-triplet: arm-linux,
              extra_features: ",unix-file-copy-paste",
            }
    steps:
      - name: Export GitHub Actions cache environment variables
        uses: actions/github-script@v6
        with:
          script: |
            core.exportVariable('ACTIONS_CACHE_URL', process.env.ACTIONS_CACHE_URL || '');
            core.exportVariable('ACTIONS_RUNTIME_TOKEN', process.env.ACTIONS_RUNTIME_TOKEN || '');

      - name: Checkout source code
        uses: actions/checkout@v4
        with:
          submodules: recursive

      - name: Modify vcpkg.json for armv7
        if: matrix.job.vcpkg-triplet == 'arm-linux'
        run: |
          # Replace the baseline in vcpkg.json with ARMV7_VCPKG_COMMIT_ID for armv7 builds
          sed -i 's/"baseline": ".*"/"baseline": "${{ env.ARMV7_VCPKG_COMMIT_ID }}"/' vcpkg.json
          echo "Modified vcpkg.json for armv7 build:"
          grep -A 2 -B 2 '"baseline"' vcpkg.json

      - name: Free Space
        run: |
          df -h
          free -m

      - name: Install Rust toolchain
        uses: dtolnay/rust-toolchain@v1
        with:
          toolchain: ${{ env.SCITER_RUST_VERSION }}
          targets: ${{ matrix.job.target }}
          components: "rustfmt"

      - name: Save Rust toolchain version
        run: |
          RUST_TOOLCHAIN_VERSION=$(cargo --version | awk '{print $2}')
          echo "RUST_TOOLCHAIN_VERSION=$RUST_TOOLCHAIN_VERSION" >> $GITHUB_ENV

      - uses: rustdesk-org/run-on-arch-action@amd64-support
        name: Build rustdesk sciter binary for ${{ matrix.job.arch }}
        id: vcpkg
        with:
          arch: ${{ matrix.job.arch }}
          distro: ${{ matrix.job.distro }}
          githubToken: ${{ github.token }}
          setup: |
            ls -l "${PWD}"
          dockerRunArgs: |
            --volume "${PWD}:/workspace"
          shell: /bin/bash
          install: |
            apt-get update
            apt-get install -y \
               build-essential \
               clang \
               curl \
               gcc \
               git \
               g++ \
               libayatana-appindicator3-dev \
               libasound2-dev \
               libclang-dev \
               libdbus-1-dev \
               libglib2.0-dev \
               libgstreamer1.0-dev \
               libgstreamer-plugins-base1.0-dev \
               libgtk-3-dev \
               liblzma-dev \
               libpam0g-dev \
               libpulse-dev \
               libva-dev \
               libxcb-randr0-dev \
               libxcb-shape0-dev \
               libxcb-xfixes0-dev \
               libxdo-dev \
               libxfixes-dev \
               ninja-build \
               pkg-config \
               python3 \
               python3.7 \
               rpm \
               unzip \
               wget \
               xz-utils \
               zip \
               libssl-dev
            # arm-linux needs CMake and vcokg built from source as there
            # are no prebuilts available from Kitware and Microsoft
            if [ "${{ matrix.job.vcpkg-triplet }}" = "arm-linux" ]; then
              # install gcc/g++ 8 for vcpkg and OpenSSL headers for CMake
              apt-get install -y gcc-8 g++-8
              # bootstrap CMake amd add it to PATH
              git clone --depth 1 https://github.com/kitware/cmake -b "v${{ env.SCITER_ARMV7_CMAKE_VERSION }}" /tmp/cmake
              pushd /tmp/cmake
              ./bootstrap --generator='Unix Makefiles' "--prefix=/opt/cmake-${{ env.SCITER_ARMV7_CMAKE_VERSION }}-linux-armhf"
              make -j1 install
              popd
              rm -rf /tmp/cmake
              export PATH="/opt/cmake-${{ env.SCITER_ARMV7_CMAKE_VERSION }}-linux-armhf/bin:$PATH"
            fi
            # bootstrap vcpkg and set VCPKG_ROOT
            export VCPKG_ROOT=/opt/artifacts/vcpkg
            mkdir -p /opt/artifacts
            pushd /opt/artifacts
            rm -rf vcpkg
            git clone https://github.com/microsoft/vcpkg
            pushd vcpkg
            # build vcpkg helper executable with gcc-8 for arm-linux but use prebuilt one on x64-linux
            if [ "${{ matrix.job.vcpkg-triplet }}" = "arm-linux" ]; then
              git reset --hard ${{ env.ARMV7_VCPKG_COMMIT_ID }}
              CC=/usr/bin/gcc-8 CXX=/usr/bin/g++-8 sh bootstrap-vcpkg.sh -disableMetrics
            else
              git reset --hard ${{ env.VCPKG_COMMIT_ID }}
              sh bootstrap-vcpkg.sh -disableMetrics
            fi
            popd
            popd
            # rust
            pushd /opt
            # do not use rustup, because memory overflow in qemu
            wget --output-document rust.tar.gz https://static.rust-lang.org/dist/rust-${{env.RUST_TOOLCHAIN_VERSION}}-${{ matrix.job.target }}.tar.gz
            tar -zxvf rust.tar.gz > /dev/null && rm rust.tar.gz
            pushd rust-${{env.RUST_TOOLCHAIN_VERSION}}-${{ matrix.job.target }}
            ./install.sh
            popd
            rm -rf rust-${{env.RUST_TOOLCHAIN_VERSION}}-${{ matrix.job.target }}
            popd
            # install newer nasm for aom
            wget --output-document nasm.deb "http://ftp.us.debian.org/debian/pool/main/n/nasm/nasm_${{ env.SCITER_NASM_DEBVERSION }}_${{ matrix.job.deb_arch }}.deb"
            dpkg -i nasm.deb
            rm -f nasm.deb
          run: |
            # disable git safe.directory
            git config --global --add safe.directory "*"
            # set python3.7 as default python3
            update-alternatives --install /usr/bin/python3 python3 /usr/bin/python3.7 1
            # add built CMake to PATH and set VCPKG_FORCE_SYSTEM_BINARIES Afor arm-linux
            if [ "${{ matrix.job.vcpkg-triplet }}" = "arm-linux" ]; then
              export PATH="/opt/cmake-${{ env.SCITER_ARMV7_CMAKE_VERSION }}-linux-armhf/bin:$PATH"
              export VCPKG_FORCE_SYSTEM_BINARIES=1
            fi
            # edit cargo config
            mkdir -p ~/.cargo/
            echo """
              [source.crates-io]
              registry = 'https://github.com/rust-lang/crates.io-index'
            """ > ~/.cargo/config
            cat ~/.cargo/config
            # install dependencies from vcpkg
            export VCPKG_ROOT=/opt/artifacts/vcpkg
            # remove this when support higher version
            export USE_AOM_391=1
            if ! $VCPKG_ROOT/vcpkg install --triplet ${{ matrix.job.vcpkg-triplet }} --x-install-root="$VCPKG_ROOT/installed"; then
              find "${VCPKG_ROOT}/" -name "*.log" | while read -r _1; do
                echo "$_1:"
                echo "======"
                cat "$_1"
                echo "======"
                echo ""
              done
              exit 1
            fi
            head -n 100 "${VCPKG_ROOT}/buildtrees/ffmpeg/build-${{ matrix.job.vcpkg-triplet }}-rel-out.log" || true
            # build rustdesk
            python3 ./res/inline-sciter.py
            export CARGO_INCREMENTAL=0
            cargo build --features inline${{ matrix.job.extra_features }} --release --bins --jobs 1
            # make debian package
            mkdir -p ./Release
            mv ./target/release/rustdesk ./Release/rustdesk
            wget -O ./Release/libsciter-gtk.so https://github.com/c-smile/sciter-sdk/raw/master/bin.lnx/${{ matrix.job.sciter_arch }}/libsciter-gtk.so
            export DEB_ARCH=${{ matrix.job.deb_arch }}
            ./build.py --package ./Release

      - name: Rename rustdesk
        shell: bash
        run: |
          for name in rustdesk*??.deb; do
              # use cp to duplicate deb files to fit other packages.
              cp "$name" "${name%%.deb}-${{ matrix.job.arch }}-sciter.deb"
          done

      - name: Publish debian package
        if: env.UPLOAD_ARTIFACT == 'true'
        uses: softprops/action-gh-release@v1
        with:
          prerelease: true
          tag_name: ${{ env.TAG_NAME }}
          files: |
            rustdesk-${{ env.VERSION }}-${{ matrix.job.arch }}-sciter.deb

      - name: Upload deb
        uses: actions/upload-artifact@master
        if: env.UPLOAD_ARTIFACT == 'true'
        with:
          name: rustdesk-${{ env.VERSION }}-${{ matrix.job.arch }}-sciter.deb
          path: rustdesk-${{ env.VERSION }}-${{ matrix.job.arch }}-sciter.deb

  build-appimage:
    name: Build appimage ${{ matrix.job.target }}
    needs: [build-rustdesk-linux]
    runs-on: ubuntu-22.04
    if: ${{ inputs.upload-artifact }}
    strategy:
      fail-fast: false
      matrix:
        job:
          - { target: x86_64-unknown-linux-gnu, arch: x86_64 }
          - { target: aarch64-unknown-linux-gnu, arch: aarch64 }
    steps:
      - name: Checkout source code
        uses: actions/checkout@v4
        with:
          submodules: recursive

      - name: Download Binary
        uses: actions/download-artifact@master
        with:
          name: rustdesk-${{ env.VERSION }}-${{ matrix.job.arch }}.deb
          path: .

      - name: Rename Binary
        run: |
          mv rustdesk-${{ env.VERSION }}-${{ matrix.job.arch }}.deb appimage/rustdesk.deb

      - name: Build appimage package
        shell: bash
        run: |
          # install libarchive-tools for bsdtar command used in AppImageBuilder.yml
          sudo apt-get update -y
          # https://github.com/AppImage/AppImageKit/wiki/FUSE
          sudo apt-get install -y libarchive-tools libfuse2
          # set-up appimage-builder
          # https://github.com/AppImage/AppImageKit/issues/1395
          sudo pip3 install git+https://github.com/rustdesk-org/appimage-builder.git
          # run appimage-builder
          pushd appimage
          sudo appimage-builder --skip-tests --recipe ./AppImageBuilder-${{ matrix.job.arch }}.yml

      - name: Publish appimage package
        if: env.UPLOAD_ARTIFACT == 'true'
        uses: softprops/action-gh-release@v1
        with:
          prerelease: true
          tag_name: ${{ env.TAG_NAME }}
          files: |
            ./appimage/rustdesk-${{ env.VERSION }}-*.AppImage

  build-flatpak:
    name: Build flatpak ${{ matrix.job.target }}${{ matrix.job.suffix }}
    needs:
      - build-rustdesk-linux
      - build-rustdesk-linux-sciter
    runs-on: ${{ matrix.job.on }}
    if: ${{ inputs.upload-artifact }}
    strategy:
      fail-fast: false
      matrix:
        job:
          - {
              target: x86_64-unknown-linux-gnu,
              # https://github.com/ostreedev/ostree/commit/4bac96a8c817beda37448f9b8c662162bb619981
              distro: ubuntu22.04,
              on: ubuntu-22.04,
              arch: x86_64,
              suffix: "",
            }
          - {
              target: x86_64-unknown-linux-gnu,
              distro: ubuntu22.04,
              on: ubuntu-22.04,
              arch: x86_64,
              suffix: "-sciter",
            }
          - {
              target: aarch64-unknown-linux-gnu,
              # try out newer flatpak since error of "error: Nothing matches org.freedesktop.Platform in remote flathub"
              distro: ubuntu22.04,
              on: ubuntu-22.04-arm,
              arch: aarch64,
              suffix: "",
            }
    steps:
      - name: Checkout source code
        uses: actions/checkout@v4
        with:
          submodules: recursive

      - name: Download Binary
        uses: actions/download-artifact@master
        with:
          name: rustdesk-${{ env.VERSION }}-${{ matrix.job.arch }}${{ matrix.job.suffix }}.deb
          path: .

      - name: Rename Binary
        run: |
          mv rustdesk-${{ env.VERSION }}-${{ matrix.job.arch }}${{ matrix.job.suffix }}.deb flatpak/rustdesk.deb

      - uses: rustdesk-org/run-on-arch-action@amd64-support
        name: Build rustdesk flatpak package for ${{ matrix.job.arch }}
        id: flatpak
        with:
          arch: ${{ matrix.job.arch }}
          distro: ${{ matrix.job.distro }}
          githubToken: ${{ github.token }}
          setup: |
            ls -l "${PWD}"
          dockerRunArgs: |
            --volume "${PWD}:/workspace"
          shell: /bin/bash
          install: |
            apt-get update -y
            apt-get install -y git flatpak flatpak-builder
          run: |
            # disable git safe.directory
            git config --global --add safe.directory "*"
            pushd /workspace
            # flatpak deps
            flatpak --user remote-add --if-not-exists flathub https://dl.flathub.org/repo/flathub.flatpakrepo
            # package
            pushd flatpak
            git clone https://github.com/flathub/shared-modules.git --depth=1
            flatpak-builder --user --install-deps-from=flathub -y --force-clean --repo=repo ./build ./rustdesk.json
            flatpak build-bundle ./repo rustdesk-${{ env.VERSION }}-${{ matrix.job.arch }}${{ matrix.job.suffix }}.flatpak com.rustdesk.RustDesk

      - name: Publish flatpak package
        uses: softprops/action-gh-release@v1
        with:
          prerelease: true
          tag_name: ${{ env.TAG_NAME }}
          files: |
            flatpak/rustdesk-${{ env.VERSION }}-${{ matrix.job.arch }}${{ matrix.job.suffix }}.flatpak

  build-rustdesk-web:
    if: False
    name: build-rustdesk-web
    runs-on: ubuntu-22.04
    permissions:
      contents: read
    strategy:
      fail-fast: false
    env:
      RELEASE_NAME: web-basic
    steps:
      - name: Checkout source code
        uses: actions/checkout@v4
        with:
          submodules: recursive

      - name: Prepare env
        run: |
          sudo apt-get update -y
          sudo apt-get install -y wget npm

      - name: Install flutter
        uses: subosito/flutter-action@v2.12.0 #https://github.com/subosito/flutter-action/issues/277
        with:
          channel: "stable"
          flutter-version: ${{ env.FLUTTER_VERSION }}

      - name: Patch flutter
        shell: bash
        run: |
          cd $(dirname $(dirname $(which flutter)))
          [[ "3.24.5" == ${{env.FLUTTER_VERSION}} ]] && git apply ${{ github.workspace }}/.github/patches/flutter_3.24.4_dropdown_menu_enableFilter.diff

      # https://rustdesk.com/docs/en/dev/build/web/
      - name: Build web
        shell: bash
        run: |
          pushd flutter/web/js
          npm install yarn -g
          npm install typescript -g
          npm install protoc -g
          # Install protoc first, see: https://google.github.io/proto-lens/installing-protoc.html
          npm install ts-proto
          # Only works with vite <= 2.8, see: https://github.com/vitejs/vite/blob/main/docs/guide/build.md#chunking-strategy
          npm install vite@2.8
          yarn install && yarn build
          popd

          pushd flutter/web
          wget https://github.com/rustdesk/doc.rustdesk.com/releases/download/console/web_deps.tar.gz
          tar xzf web_deps.tar.gz
          popd

          pushd flutter
          flutter build web --release
          cd build
          cp ../web/README.md web
          # TODO: Remove the following line when the web is almost complete.
          echo -e "\n\nThis build is for preview and not full functionality." >> web/README.md
          dir_name="rustdesk-${{ env.VERSION }}-${{ env.RELEASE_NAME }}"
          mv web "${dir_name}" && tar czf "${dir_name}".tar.gz "${dir_name}"
          sha256sum "${dir_name}".tar.gz
          popd

      - name: Publish web
        if: env.UPLOAD_ARTIFACT == 'true'
        uses: softprops/action-gh-release@v1
        with:
          prerelease: true
          tag_name: ${{ env.TAG_NAME }}
          files: |
            flutter/build/rustdesk-${{ env.VERSION }}-${{ env.RELEASE_NAME }}.tar.gz
