name: R-package

on:
  push:
    branches:
      - master
  pull_request:
    branches:
      - master

# automatically cancel in-progress builds if another commit is pushed
concurrency:
  group: ${{ github.workflow }}-${{ github.ref }}
  cancel-in-progress: true

env:
  # in CMake-driven builds, parallelize compilation
  CMAKE_BUILD_PARALLEL_LEVEL: 4
  # on Debian-based images, avoid interactive prompts
  DEBIAN_FRONTEND: noninteractive
  # Fix issues like the following that can show up running 'R CMD check' on
  # specific clang versions:
  #
  #   * checking for detritus in the temp directory ... NOTE
  #   Found the following files/directories:
  #     ‘dsymutil-63923a’ ‘dsymutil-9aa721’ ‘dsymutil-b7e1bb’
  #
  # These are unlikely to show up in CRAN's checks. They come from
  # 'dsymutil ---gen-reproducer' being run (not something LightGBM explicitly does).
  #
  # ref:
  #   - https://github.com/llvm/llvm-project/issues/61920
  #   - https://github.com/golang/go/issues/59026#issuecomment-1520487072
  DSYMUTIL_REPRODUCER_PATH: /dev/null
  # parallelize compilation (extra important for Linux, where CRAN doesn't supply pre-compiled binaries)
  MAKEFLAGS: "-j4"
  # hack to get around this:
  # https://stat.ethz.ch/pipermail/r-package-devel/2020q3/005930.html
  _R_CHECK_SYSTEM_CLOCK_: 0
  # ignore R CMD CHECK NOTE checking how long it has
  # been since the last submission
  _R_CHECK_CRAN_INCOMING_REMOTE_: 0
  # CRAN ignores the "installed size is too large" NOTE,
  # so our CI can too. Setting to a large value here just
  # to catch extreme problems
  _R_CHECK_PKG_SIZES_THRESHOLD_: 100

jobs:
  test:
    # yamllint disable-line rule:line-length
    name: ${{ matrix.task }} (${{ matrix.os }}, ${{ matrix.compiler }}, R ${{ matrix.r_version }}, ${{ matrix.build_type }})
    runs-on: ${{ matrix.os }}
    container: ${{ matrix.container }}
    timeout-minutes: 60
    strategy:
      fail-fast: false
      matrix:
        include:
          ################
          # CMake builds #
          ################
          - os: ubuntu-latest
            task: r-package
            compiler: gcc
            r_version: 4.3
            build_type: cmake
            container: 'ubuntu:22.04'
          - os: ubuntu-latest
            task: r-package
            compiler: clang
            r_version: 4.3
            build_type: cmake
            container: 'ubuntu:22.04'
          - os: macos-13
            task: r-package
            compiler: gcc
            r_version: 4.3
            build_type: cmake
            container: null
          - os: macos-13
            task: r-package
            compiler: clang
            r_version: 4.3
            build_type: cmake
            container: null
          - os: windows-latest
            task: r-package
            compiler: MINGW
            toolchain: MINGW
            r_version: 3.6
            build_type: cmake
            container: null
          - os: windows-latest
            task: r-package
            compiler: MINGW
            toolchain: MSYS
            r_version: 4.3
            build_type: cmake
            container: null
          # Visual Studio 2022
          - os: windows-2022
            task: r-package
            compiler: MSVC
            toolchain: MSVC
            r_version: 3.6
            build_type: cmake
            container: null
          - os: windows-2022
            task: r-package
            compiler: MSVC
            toolchain: MSVC
            r_version: 4.3
            build_type: cmake
            container: null
          ###############
          # CRAN builds #
          ###############
          - os: windows-latest
            task: r-package
            compiler: MINGW
            toolchain: MINGW
            r_version: 3.6
            build_type: cran
            container: null
          - os: windows-latest
            task: r-package
            compiler: MINGW
            toolchain: MSYS
            r_version: 4.3
            build_type: cran
            container: null
          - os: ubuntu-latest
            task: r-package
            compiler: gcc
            r_version: 4.3
            build_type: cran
            container: 'ubuntu:22.04'
          - os: macos-13
            task: r-package
            compiler: clang
            r_version: 4.3
            build_type: cran
            container: null
          # macos-14 = arm64
          - os: macos-14
            task: r-package
            compiler: clang
            r_version: 4.3
            build_type: cran
            container: null
    steps:
      - name: Prevent conversion of line endings on Windows
        if: startsWith(matrix.os, 'windows')
        shell: pwsh
        run: git config --global core.autocrlf false
      - name: Install packages used by third-party actions
        if: startsWith(matrix.os, 'ubuntu')
        shell: bash
        run: |
          apt-get update -y
          apt-get install --no-install-recommends -y \
            ca-certificates \
            dirmngr \
            gpg \
            gpg-agent \
            software-properties-common \
            sudo
          # install newest version of git
          # ref:
          #     - https://unix.stackexchange.com/a/170831/550004
          #     - https://git-scm.com/download/linux
          add-apt-repository ppa:git-core/ppa -y
          apt-get update -y
          apt-get install --no-install-recommends -y \
            git
      - name: Trust git cloning LightGBM
        if: startsWith(matrix.os, 'ubuntu')
        run: |
          git config --global --add safe.directory "${GITHUB_WORKSPACE}"
      - name: Checkout repository
        uses: actions/checkout@v4
        with:
          fetch-depth: 5
          submodules: true
      - name: Install pandoc
        uses: r-lib/actions/setup-pandoc@v2
      - name: Install tinytex
        if: startsWith(matrix.os, 'windows')
        uses: r-lib/actions/setup-tinytex@v2
        env:
          CTAN_MIRROR: https://ctan.math.illinois.edu/systems/win32/miktex
          TINYTEX_INSTALLER: TinyTeX
      - name: Setup and run tests on Linux and macOS
        if: startsWith(matrix.os, 'macos') || startsWith(matrix.os, 'ubuntu')
        shell: bash
        run: |
          export TASK="${{ matrix.task }}"
          export COMPILER="${{ matrix.compiler }}"
          if [[ "${{ matrix.os }}" =~ ^macos ]]; then
              export OS_NAME="macos"
          elif [[ "${{ matrix.os }}" == "ubuntu-latest" ]]; then
              export OS_NAME="linux"
              export IN_UBUNTU_BASE_CONTAINER="true"
          fi
          export BUILD_DIRECTORY="$GITHUB_WORKSPACE"
          export R_VERSION="${{ matrix.r_version }}"
          export R_BUILD_TYPE="${{ matrix.build_type }}"
          $GITHUB_WORKSPACE/.ci/setup.sh
          $GITHUB_WORKSPACE/.ci/test.sh
      - name: Setup and run tests on Windows
        if: startsWith(matrix.os, 'windows')
        shell: pwsh -command ". {0}"
        run: |
          $env:BUILD_SOURCESDIRECTORY = $env:GITHUB_WORKSPACE
          $env:LGB_VER = (Get-Content -TotalCount 1 $env:BUILD_SOURCESDIRECTORY\VERSION.txt).trim().replace('rc', '-')
          $env:TOOLCHAIN = "${{ matrix.toolchain }}"
          $env:R_VERSION = "${{ matrix.r_version }}"
          $env:R_BUILD_TYPE = "${{ matrix.build_type }}"
          $env:COMPILER = "${{ matrix.compiler }}"
          $env:TASK = "${{ matrix.task }}"
          & "$env:GITHUB_WORKSPACE/.ci/test-windows.ps1"
  test-r-sanitizers:
    name: r-sanitizers (ubuntu-latest, R-devel, ${{ matrix.compiler }} ASAN/UBSAN)
    timeout-minutes: 60
    runs-on: ubuntu-latest
    container: wch1/r-debug
    strategy:
      fail-fast: false
      matrix:
        include:
          - r_customization: san
            compiler: gcc
          - r_customization: csan
            compiler: clang
    steps:
      - name: Trust git cloning LightGBM
        run: |
          git config --global --add safe.directory "${GITHUB_WORKSPACE}"
      - name: Checkout repository
        uses: actions/checkout@v4
        with:
          fetch-depth: 5
          submodules: true
      - name: Install packages
        shell: bash
        run: |
          RDscript${{ matrix.r_customization }} ./.ci/install-r-deps.R --build --test
          sh build-cran-package.sh --r-executable=RD${{ matrix.r_customization }}
          RD${{ matrix.r_customization }} CMD INSTALL lightgbm_*.tar.gz || exit 1
      - name: Run tests with sanitizers
        shell: bash
        run: |
          cd R-package/tests
          exit_code=0
          RDscript${{ matrix.r_customization }} testthat.R >> tests.log 2>&1 || exit_code=-1
          cat ./tests.log
          exit ${exit_code}
  test-r-extra-checks:
    name: r-package (${{ matrix.image }}, R-devel)
    timeout-minutes: 60
    strategy:
      fail-fast: false
      matrix:
        # references:
        #   * CRAN "additional checks": https://cran.r-project.org/web/checks/check_issue_kinds.html
        #   * images: https://r-hub.github.io/containers/containers.html
        image:
          - clang16
          - clang17
          - clang18
          - clang19
          - clang20
          - gcc14
          - intel
          - rchk
    runs-on: ubuntu-latest
    container: ghcr.io/r-hub/containers/${{ matrix.image }}:latest
    steps:
      - name: Checkout repository
        uses: actions/checkout@v4
        with:
          fetch-depth: 5
          submodules: true
      - name: Install pandoc
        uses: r-lib/actions/setup-pandoc@v2
      - name: Install LaTeX
        shell: bash
        run: |
          if type -f apt 2>&1 > /dev/null; then
            apt-get update
            apt-get install --no-install-recommends -y \
                devscripts \
                texinfo \
                texlive-latex-extra \
                texlive-latex-recommended \
                texlive-fonts-recommended \
                texlive-fonts-extra \
                tidy \
                qpdf
          else
            yum update -y
            yum install -y \
                devscripts \
                qpdf \
                texinfo \
                texinfo-tex \
                texlive-latex \
                tidy
          fi
      - name: Install packages and run tests
        shell: bash
        run: |
          Rscript ./.ci/install-r-deps.R --build --test --exclude=testthat
          sh build-cran-package.sh

          # 'rchk' isn't run through 'R CMD check', use the approach documented at
          # https://r-hub.github.io/containers/local.html
          if [[ "${{ matrix.image }}" =~ "rchk" ]]; then
            r-check "$(pwd)" \
            | tee ./rchk-logs.txt 2>&1

            # the '-v' exceptions below are from R/rchk itself and not LightGBM:
            # https://github.com/kalibera/rchk/issues/22#issuecomment-656036156
            if grep -E '\[PB\]|ERROR' ./rchk-logs.txt \
               | grep -v 'too many states' \
               > /dev/null; \
            then
                echo "rchk found issues"
                exit 1
            else
                echo "rchk did not find any issues"
                exit 0
            fi
          fi

          # 'testthat' is not needed by 'rchk', so avoid installing it until here
          Rscript -e "install.packages('testthat', repos = 'https://cran.rstudio.com', Ncpus = parallel::detectCores())"

          if [[ "${{ matrix.image }}" =~ "clang" ]]; then
            # allowing the following NOTEs (produced by default in the clang images):
            #
            #   * checking compilation flags used ... NOTE
            #       Compilation used the following non-portable flag(s):
            #       ‘-Wp,-D_FORTIFY_SOURCE=3’
            #
            # even though CRAN itself sets that:
            # https://www.stats.ox.ac.uk/pub/bdr/Rconfig/r-devel-linux-x86_64-fedora-clang
            #
            declare -i allowed_notes=1
          else
            declare -i allowed_notes=0
          fi

          bash .ci/run-r-cmd-check.sh \
            "$(echo lightgbm_$(head -1 VERSION.txt).tar.gz)" \
            "${allowed_notes}"
  all-r-package-jobs-successful:
    if: always()
    runs-on: ubuntu-latest
    needs: [test, test-r-sanitizers, test-r-extra-checks]
    steps:
      - name: Note that all tests succeeded
        uses: re-actors/alls-green@v1.2.2
        with:
          jobs: ${{ toJSON(needs) }}
