name: playground

on:
  #schedule:
    # schedule build every night
  #  - cron: "0/6 * * * *"
  workflow_dispatch:

env:
  RUST_VERSION: "1.75" # https://github.com/rustdesk/rustdesk/discussions/7503
  CARGO_NDK_VERSION: "3.1.2"
  LLVM_VERSION: "15.0.6"
  FLUTTER_VERSION: "3.22.2"
  FLUTTER_RUST_BRIDGE_VERSION: "1.80.1"
  # for arm64 linux because official Dart SDK does not work
  FLUTTER_ELINUX_VERSION: "3.16.9"
  TAG_NAME: "nightly"
  VCPKG_BINARY_SOURCES: "clear;x-gha,readwrite"
  VCPKG_COMMIT_ID: "6f29f12e82a8293156836ad81cc9bf5af41fe836"
  VERSION: "1.4.0"
  NDK_VERSION: "r26d"
  #signing keys env variable checks
  ANDROID_SIGNING_KEY: "${{ secrets.ANDROID_SIGNING_KEY }}"
  MACOS_P12_BASE64: "${{ secrets.MACOS_P12_BASE64 }}"
  # To make a custom build with your own servers set the below secret values
  RS_PUB_KEY: "${{ secrets.RS_PUB_KEY }}"
  RENDEZVOUS_SERVER: "${{ secrets.RENDEZVOUS_SERVER }}"
  API_SERVER: "${{ secrets.API_SERVER }}"
  UPLOAD_ARTIFACT: "${{ inputs.upload-artifact }}"
  SIGN_BASE_URL: "${{ secrets.SIGN_BASE_URL }}"

jobs:
  build-for-macOS:
    name: ${{ matrix.job.target }}
    runs-on: ${{ matrix.job.os }}
    strategy:
      fail-fast: false
      matrix:
        job:
          - {
              target: x86_64-apple-darwin,
              os: macos-13, #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,
              flutter: "3.13.9",
              ref: "f6509e3fd6917aa976bad2fc684182601ebf2434",
              bridge: "1.80.1",
              date: "20231219"
            }
          - {
              target: x86_64-apple-darwin,
              os: macos-13, #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,
              flutter: "3.10.6",
              ref: "f6509e3fd6917aa976bad2fc684182601ebf2434",
              bridge: "1.80.1",
              date: "20231219"
            }
          - {
              target: x86_64-apple-darwin,
              os: macos-13, #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,
              flutter: "3.10.6",
              ref: "85ddfc0739f052cab0029c46b899b959ee94eeb8",
              bridge: "1.80.1",
              date: "20231119"
            }
          - {
              target: x86_64-apple-darwin,
              os: macos-13, #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,
              flutter: "3.13.9",
              ref: "85ddfc0739f052cab0029c46b899b959ee94eeb8",
              bridge: "1.80.1",
              date: "20231119"
            }
    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@v3
        with:
          ref: ${{ matrix.job.ref }}
          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 nasm cmake gcc wget ninja pkg-config

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

      - 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: ${{ matrix.job.os }}

      - name: Install flutter rust bridge deps
        shell: bash
        run: |
          sed -i '' 's/3.1.0/2.17.0/g' flutter/pubspec.yaml;
          cargo install flutter_rust_bridge_codegen --version ${{ matrix.job.bridge }} --features "uuid" --locked
          # below works for mac to make buildable on 3.13.9
          # pushd flutter/lib; find . -name "*.dart" | xargs -I{} sed -i '' 's/textScaler: TextScaler.linear(\(.*\)),/textScaleFactor: \1,/g' {}; popd;
          pushd flutter && flutter pub get && popd
          ~/.cargo/bin/flutter_rust_bridge_codegen --rust-input ./src/flutter_ffi.rs --dart-output ./flutter/lib/generated_bridge.dart --c-output ./flutter/macos/Runner/bridge_generated.h

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

      - name: Install vcpkg dependencies
        run: |
          $VCPKG_ROOT/vcpkg install --x-install-root="$VCPKG_ROOT/installed"
          
      - name: Restore from cache and install vcpkg
        uses: lukka/run-vcpkg@v7
        if: false
        with:
          setupOnly: true
          vcpkgGitCommitId: ${{ env.VCPKG_COMMIT_ID }}

      - name: Install vcpkg dependencies
        if: false
        run: |
          $VCPKG_ROOT/vcpkg install libvpx libyuv opus aom

      - 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: |
          ./build.py --flutter ${{ matrix.job.extra-build-args }}

      - name: create unsigned dmg
        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: Codesign app and create signed dmg
        if: env.MACOS_P12_BASE64 != null
        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
        run: |
          for name in rustdesk*??.dmg; do
              mv "$name" "${name%%.dmg}-${{ matrix.job.arch }}-flutter${{ matrix.job.flutter }}-flutter${{ matrix.job.date }}.dmg"
          done

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


  build-rustdesk-android:
    if: false
    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-22.04,
              openssl-arch: android-arm64,
              ref: master, # latest
            }
    steps:
      - name: Checkout source code
        uses: actions/checkout@v3
        with:
          ref: ${{ matrix.job.ref }}
          submodules: recursive

      - 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-11-dev \
               libunwind-dev \
               libgstreamer1.0-dev \
               libgstreamer-plugins-base1.0-dev \
               libgtk-3-dev \
               libpam0g-dev \
               libpulse-dev \
               libva-dev \
               libvdpau-dev \
               libxcb-randr0-dev \
               libxcb-shape0-dev \
               libxcb-xfixes0-dev \
               libxdo-dev \
               libxfixes-dev \
               llvm-11-dev \
               nasm \
               yasm \
               ninja-build \
               openjdk-11-jdk-headless \
               pkg-config \
               tree \
               wget

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

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

      - name: Install flutter rust bridge deps
        run: |
          git config --global core.longpaths true
          cargo install flutter_rust_bridge_codegen --version ${{ env.FLUTTER_RUST_BRIDGE_VERSION }} --features "uuid" --locked
          sed -i 's/uni_links_desktop/#uni_links_desktop/g' flutter/pubspec.yaml
          pushd flutter/lib;  find . | grep dart | xargs sed -i 's/textScaler: TextScaler.linear(\(.*\)),/textScaleFactor: \1,/g'; popd;
          pushd flutter ; flutter pub get ; popd
          ~/.cargo/bin/flutter_rust_bridge_codegen --rust-input ./src/flutter_ffi.rs --dart-output ./flutter/lib/generated_bridge.dart

      - 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 }}

      - name: Install vcpkg dependencies
        run: |
          case ${{ matrix.job.target }} in
            aarch64-linux-android)
              ./flutter/build_android_deps.sh arm64-v8a
            ;;
            armv7-linux-androideabi)
              ./flutter/build_android_deps.sh armeabi-v7a
            ;;
          esac
        shell: bash

      - name: Clone deps
        shell: bash
        run: |
          pushd /opt
          git clone https://github.com/rustdesk-org/rustdesk_thirdparty_lib.git --depth=1
          ls -ls /opt/artifacts/vcpkg/installed/arm64-android/lib/
          # cp -rf /opt/rustdesk_thirdparty_lib/vcpkg/* /opt/artifacts/vcpkg/
          ls -ls /opt/artifacts/vcpkg/installed/arm64-android/lib/

      - 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
            ;;
          esac

      - name: Build rustdesk
        shell: bash
        env:
          JAVA_HOME: /usr/lib/jvm/java-11-openjdk-amd64
        run: |
          export PATH=/usr/lib/jvm/java-11-openjdk-amd64/bin:$PATH
          # 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 --release --target-platform android-arm64 --split-per-abi
              mv build/app/outputs/flutter-apk/app-arm64-v8a-release.apk ../rustdesk-${{ env.VERSION }}-${{ matrix.job.arch }}.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 --release --target-platform android-arm --split-per-abi
              mv build/app/outputs/flutter-apk/app-armeabi-v7a-release.apk ../rustdesk-${{ env.VERSION }}-${{ matrix.job.arch }}.apk
            ;;
          esac
          popd
          mkdir -p signed-apk; pushd signed-apk
          mv ../rustdesk-${{ env.VERSION }}-${{ matrix.job.arch }}.apk ./rustdesk-test-${{ matrix.job.ref }}-${{ matrix.job.ndk }}.apk

      - 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:
          # override default build-tools version (29.0.3) -- optional
          BUILD_TOOLS_VERSION: "30.0.2"

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