diff --git a/.github/revdepcheck/run.R b/.github/revdepcheck/run.R new file mode 100644 index 00000000..6d4a880d --- /dev/null +++ b/.github/revdepcheck/run.R @@ -0,0 +1,13 @@ +options(repos = c(CRAN = "https://cloud.r-project.org")) + +if (Sys.getenv("PREVIOUS_RUN_ID") == "") { + revdepcheck::revdep_reset() +} + +revdepcheck::revdep_check( + num_workers = 2, + timeout = as.difftime( + as.integer(Sys.getenv("TIMEOUT_MINUTES")), + units = "mins" + ) +) diff --git a/.github/revdepcheck/run.sh b/.github/revdepcheck/run.sh new file mode 100644 index 00000000..a8d44941 --- /dev/null +++ b/.github/revdepcheck/run.sh @@ -0,0 +1,22 @@ +#!/usr/bin/env bash +set -euo pipefail + +set +e +timeout -s TERM -k 5m 350m Rscript .github/revdepcheck/run.R +code=$? +set -e + +case "$code" in + 124|130|137|143) + echo "again=true" >> "$GITHUB_OUTPUT" + exit 0 + ;; + 0) + echo "again=false" >> "$GITHUB_OUTPUT" + exit 0 + ;; + *) + echo "again=false" >> "$GITHUB_OUTPUT" + exit "$code" + ;; +esac diff --git a/.github/workflows/revdepcheck.yaml b/.github/workflows/revdepcheck.yaml new file mode 100644 index 00000000..4741cea1 --- /dev/null +++ b/.github/workflows/revdepcheck.yaml @@ -0,0 +1,122 @@ +name: revdepcheck + +on: + workflow_dispatch: + inputs: + timeout_minutes: + description: "Timeout per reverse dependency" + required: false + default: "60" + previous_run_id: + description: "Previous run ID" + required: false + default: "" + +permissions: + contents: read + actions: write + +concurrency: + group: revdepcheck-${{ github.ref }} + cancel-in-progress: false + +jobs: + revdepcheck: + runs-on: ubuntu-latest + timeout-minutes: 360 + + env: + GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} + NOT_CRAN: "true" + TIMEOUT_MINUTES: ${{ inputs.timeout_minutes }} + PREVIOUS_RUN_ID: ${{ inputs.previous_run_id }} + + steps: + - uses: actions/checkout@v7 + + - uses: actions/download-artifact@v8 + if: ${{ inputs.previous_run_id != '' }} + with: + name: revdep-state + path: revdep + run-id: ${{ inputs.previous_run_id }} + github-token: ${{ secrets.GITHUB_TOKEN }} + + - uses: r-lib/actions/setup-r@v2 + with: + use-public-rspm: true + + - uses: r-lib/actions/setup-r-dependencies@v2 + with: + extra-packages: | + r-lib/revdepcheck + any::callr + any::rstantools + any::systemfonts + any::textshaping + any::ragg + any::Cairo + any::rjags + any::runjags + any::R2jags + any::gsl + any::Rmpfr + any::rJava + any::xlsx + any::xlsxjars + any::OpenCL + any::Rmpi + needs: check + + - name: Run revdepcheck + id: revdep + run: bash .github/revdepcheck/run.sh + + - uses: actions/upload-artifact@v7 + if: always() + with: + name: revdep-state + path: revdep/ + retention-days: 14 + if-no-files-found: ignore + + - name: Continue + if: ${{ steps.revdep.outputs.again == 'true' }} + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + gh workflow run revdepcheck.yaml \ + --ref "${{ github.ref_name }}" \ + -f timeout_minutes="${{ inputs.timeout_minutes }}" \ + -f previous_run_id="${{ github.run_id }}" + + - name: Fail on broken revdeps + if: ${{ steps.revdep.outputs.again == 'false' }} + shell: Rscript {0} + run: | + status <- revdepcheck:::report_status(".") + if (status$broken > 0) { + stop(status$broken, " reverse dependencies have new failures.") + } + + - name: Write final revdep report + if: ${{ always() && steps.revdep.outputs.again != 'true' }} + shell: Rscript {0} + run: | + try( + revdepcheck::revdep_report(pkg = ".", all = TRUE), + silent = TRUE + ) + + - name: Upload final revdep report + if: ${{ always() && steps.revdep.outputs.again != 'true' }} + uses: actions/upload-artifact@v7 + with: + name: revdep-report-${{ github.run_id }}-${{ github.run_attempt }} + path: | + revdep/README.md + revdep/problems.md + revdep/failures.md + revdep/cran.md + retention-days: 30 + if-no-files-found: warn