|
| 1 | +# NOTE: This script is a modification of the r-lib/actions standard cehcking script and the public-private sync script, on which it piggybacks to detect when it's in a private repo and can therefore skip some or even all tests. It also builds and uploads binaries for Windows and MacOS. |
| 2 | + |
| 3 | +# For help debugging build failures open an issue on the RStudio community with the 'github-actions' tag. |
| 4 | +# https://community.rstudio.com/new-topic?category=Package%20development&tags=github-actions |
| 5 | + |
| 6 | +# Each entry in the config matrix must have the following keys: |
| 7 | +# |
| 8 | +# os: windows-latest, macOS-latest, ubuntu-20.04, etc. (as of this writing) |
| 9 | +# |
| 10 | +# r: devel or release |
| 11 | +# |
| 12 | +# rspm: Linux only, just leave it alone. |
| 13 | +# |
| 14 | +# timeout: in minutes |
| 15 | +# |
| 16 | +# flags: a comma (or some other aesthetically pleasing and |
| 17 | +# non-forbidden symbol) separated list of flags. Currently |
| 18 | +# supported: |
| 19 | +# |
| 20 | +# binaries: build and upload as an artefact a binary version (particularly for MacOS and Windows). |
| 21 | +# |
| 22 | +# full: run with ENABLE_statnet_TESTS. |
| 23 | +# |
| 24 | +# vignettes: build vignettes and manuals; otherwise ignore. |
| 25 | +# |
| 26 | +# ubsan: compile with -fsanitize=undefined and fail on any errors identified. |
| 27 | +# |
| 28 | +# debug: compile package with -DDEBUG and -UNDEBUG |
| 29 | +# |
| 30 | +# covr: run in covr mode may be used with 'full' but probably not with others. |
| 31 | + |
| 32 | +on: [push, pull_request] |
| 33 | + |
| 34 | +name: R-CMD-check |
| 35 | + |
| 36 | +# Set public and private repositories (i.e., USER/PKG). Leave blank to autodetect. |
| 37 | +env: |
| 38 | + PUBLIC: '' |
| 39 | + PRIVATE: '' |
| 40 | + |
| 41 | +jobs: |
| 42 | +## Remove-Old-Artifacts: |
| 43 | +## runs-on: ubuntu-latest |
| 44 | +## timeout-minutes: 10 |
| 45 | +## |
| 46 | +## steps: |
| 47 | +## - name: Remove old artifacts |
| 48 | +## uses: c-hive/gha-remove-artifacts@v1.2.0 |
| 49 | +## with: |
| 50 | +## age: '1 month' |
| 51 | +## skip-recent: 8 |
| 52 | + |
| 53 | + Set-Matrix-Private: |
| 54 | + runs-on: ubuntu-latest |
| 55 | + outputs: |
| 56 | + matrix: ${{ steps.set-matrix.outputs.matrix }} |
| 57 | + steps: |
| 58 | + - name: check-config # Make sure either neither or both are set; abort if not. |
| 59 | + if: (env.PUBLIC == '') != (env.PRIVATE == '') |
| 60 | + run: | |
| 61 | + echo "Configuration problem: only one of the repositories is set." |
| 62 | + exit 1 |
| 63 | + - name: detect-repos |
| 64 | + if: env.PUBLIC == '' && env.PRIVATE == '' # Autodetect always. |
| 65 | + run: | |
| 66 | + if [[ "${{ github.repository }}" == *-private ]] |
| 67 | + then # Current repo is private. |
| 68 | + IAM="private" |
| 69 | + PRIVATE="${{ github.repository }}" |
| 70 | + PUBLIC="${PRIVATE%-private}" |
| 71 | + else # Current repo is public. |
| 72 | + IAM="public" |
| 73 | + PUBLIC="${{ github.repository }}" |
| 74 | + PRIVATE="$PUBLIC-private" |
| 75 | + fi |
| 76 | + echo "IAM=$IAM" >> $GITHUB_ENV |
| 77 | + echo "PRIVATE=$PRIVATE" >> $GITHUB_ENV |
| 78 | + echo "PUBLIC=$PUBLIC" >> $GITHUB_ENV |
| 79 | + - name: public-check # Check if the branch/tag exists in the public repository. |
| 80 | + if: env.IAM == 'private' # Only check if from private repo. |
| 81 | + run: | |
| 82 | + set +e |
| 83 | + git ls-remote --exit-code https://github.com/${{ env.PUBLIC }} ${{ github.ref }} |
| 84 | + echo "FOUND_PUBLIC=$?" >> $GITHUB_ENV |
| 85 | + - name: set-matrix |
| 86 | + id: set-matrix |
| 87 | + run: | |
| 88 | + if [[ "${{ env.IAM }}" == 'public' ]] # Public: full set. |
| 89 | + then |
| 90 | + config='{"config":[ |
| 91 | + {"os":"windows-latest", "r":"release", "timeout":360, "flags":"binaries, vignettes"}, |
| 92 | + {"os":"macOS-latest", "r":"release", "timeout":360, "flags":"binaries, ubsan, vignettes"}, |
| 93 | + {"os":"ubuntu-20.04", "r":"release", "rspm":"https://packagemanager.rstudio.com/cran/__linux__/focal/latest", "timeout":360, "flags":"full, ubsan, debug"}, |
| 94 | + {"os":"ubuntu-20.04", "r":"devel", "rspm":"https://packagemanager.rstudio.com/cran/__linux__/focal/latest", "timeout":360, "flags":"vignettes, remote"}, |
| 95 | + {"os":"ubuntu-20.04", "r":"release", "rspm":"https://packagemanager.rstudio.com/cran/__linux__/focal/latest", "timeout":360, "flags":"full, covr"}]}' |
| 96 | + elif [[ "${{ env.FOUND_PUBLIC }}" != '0' ]] # Private with no public analogue: reduced set. |
| 97 | + then |
| 98 | + config='{"config":[ |
| 99 | + {"os":"ubuntu-20.04", "r":"release", "rspm":"https://packagemanager.rstudio.com/cran/__linux__/focal/latest", "timeout":10, "flags":"none"} |
| 100 | + ]}' |
| 101 | + else # Private with public analogue: no checking. |
| 102 | + config='' |
| 103 | + fi |
| 104 | + config="${config//'%'/'%25'}" |
| 105 | + config="${config//$'\n'/'%0A'}" |
| 106 | + config="${config//$'\r'/'%0D'}" |
| 107 | + echo "::set-output name=matrix::$config" |
| 108 | +
|
| 109 | + R-CMD-check: |
| 110 | + needs: Set-Matrix-Private |
| 111 | + |
| 112 | + if: needs.Set-Matrix-Private.outputs.matrix != '' |
| 113 | + |
| 114 | + runs-on: ${{ matrix.config.os }} |
| 115 | + |
| 116 | + name: ${{ matrix.config.os }} (r ${{ matrix.config.r }}, flags ${{ matrix.config.flags }}) |
| 117 | + |
| 118 | + strategy: |
| 119 | + fail-fast: false |
| 120 | + matrix: ${{fromJson(needs.Set-Matrix-Private.outputs.matrix)}} |
| 121 | + |
| 122 | + env: |
| 123 | + R_REMOTES_NO_ERRORS_FROM_WARNINGS: true |
| 124 | + RSPM: ${{ matrix.config.rspm }} |
| 125 | + |
| 126 | + steps: |
| 127 | + - name: If available, use the Janitor's key rather than the repository-specific key. |
| 128 | + id: set-pat |
| 129 | + run: | |
| 130 | + if [[ -n "${{ secrets.JANITORS_GITHUB_PAT }}" ]] |
| 131 | + then |
| 132 | + echo "GITHUB_PAT=${{ secrets.JANITORS_GITHUB_PAT }}" >> $GITHUB_ENV |
| 133 | + else |
| 134 | + echo "GITHUB_PAT=${{ secrets.GITHUB_TOKEN }}" >> $GITHUB_ENV |
| 135 | + fi |
| 136 | + shell: bash |
| 137 | + |
| 138 | + - uses: actions/checkout@v2 |
| 139 | + |
| 140 | + - uses: r-lib/actions/setup-r@v1 |
| 141 | + with: |
| 142 | + r-version: ${{ matrix.config.r }} |
| 143 | + |
| 144 | + - name: Install GhostScript (on Linux if running vignettes) |
| 145 | + if: runner.os == 'Linux' && contains(matrix.config.flags, 'vignettes') |
| 146 | + run: | |
| 147 | + /usr/bin/sudo DEBIAN_FRONTEND=noninteractive apt-get install -y ghostscript |
| 148 | + shell: bash |
| 149 | + |
| 150 | + - uses: r-lib/actions/setup-pandoc@v1 |
| 151 | + |
| 152 | + - name: Install tinytex (system) |
| 153 | + if: contains(matrix.config.flags, 'vignettes') |
| 154 | + uses: r-lib/actions/setup-tinytex@v1 |
| 155 | + |
| 156 | + - name: Query dependencies |
| 157 | + run: | |
| 158 | + install.packages('remotes') |
| 159 | + saveRDS(remotes::dev_package_deps(dependencies = TRUE), ".github/depends.Rds", version = 2) |
| 160 | + writeLines(sprintf("R-%i.%i", getRversion()$major, getRversion()$minor), ".github/R-version") |
| 161 | + shell: Rscript {0} |
| 162 | + |
| 163 | + - name: Cache R packages |
| 164 | + if: runner.os != 'Windows' |
| 165 | + uses: actions/cache@v4 |
| 166 | + with: |
| 167 | + path: ${{ env.R_LIBS_USER }} |
| 168 | + key: ${{ runner.os }}-${{ hashFiles('.github/R-version') }}-1-${{ hashFiles('.github/depends.Rds') }} |
| 169 | + restore-keys: ${{ runner.os }}-${{ hashFiles('.github/R-version') }}-1- |
| 170 | + |
| 171 | + - name: Install system dependencies |
| 172 | + if: runner.os == 'Linux' |
| 173 | + run: | |
| 174 | + while read -r cmd |
| 175 | + do |
| 176 | + eval sudo $cmd |
| 177 | + done < <(Rscript -e 'writeLines(remotes::system_requirements("ubuntu", "20.04"))') |
| 178 | +
|
| 179 | + - name: Install dependencies |
| 180 | + run: | |
| 181 | + remotes::install_deps(dependencies = TRUE) |
| 182 | + remotes::install_cran("pkgbuild") |
| 183 | + remotes::install_cran("rcmdcheck") |
| 184 | + remotes::install_cran("covr") |
| 185 | + shell: Rscript {0} |
| 186 | + |
| 187 | + - name: Install tinytex (R) |
| 188 | + if: contains(matrix.config.flags, 'vignettes') |
| 189 | + run: | |
| 190 | + remotes::install_cran("tinytex") |
| 191 | + tinytex:::install_yihui_pkgs() |
| 192 | + tinytex::tlmgr_install("makeindex") |
| 193 | + shell: Rscript {0} |
| 194 | + |
| 195 | + - name: Build |
| 196 | + if: contains(matrix.config.flags, 'binaries') |
| 197 | + run: | |
| 198 | + dir.create("binaries", FALSE) |
| 199 | + pkgbuild::build(binary=TRUE, vignettes=FALSE, dest_path = "binaries") |
| 200 | + shell: Rscript {0} |
| 201 | + |
| 202 | + - name: Upload build results |
| 203 | + if: contains(matrix.config.flags, 'binaries') && !failure() |
| 204 | + uses: actions/upload-artifact@v4 |
| 205 | + with: |
| 206 | + name: ${{ runner.os }}-r${{ matrix.config.r }}-binaries |
| 207 | + path: binaries |
| 208 | + |
| 209 | + - name: Clean up build results |
| 210 | + if: contains(matrix.config.flags, 'binaries') |
| 211 | + run: rm -rf binaries |
| 212 | + shell: bash |
| 213 | + |
| 214 | + - name: Check |
| 215 | + if: contains(matrix.config.flags, 'covr') == false |
| 216 | + timeout-minutes: ${{ matrix.config.timeout }} |
| 217 | + env: |
| 218 | + _R_CHECK_CRAN_INCOMING_REMOTE_: ${{ contains(matrix.config.flags, 'remote') }} |
| 219 | + _R_CHECK_FORCE_SUGGESTS_: false |
| 220 | + ENABLE_statnet_TESTS: ${{ contains(matrix.config.flags, 'full') }} |
| 221 | + R_VIGNETTES: ${{ contains(matrix.config.flags, 'vignettes') }} |
| 222 | + FAIL_ON_WARN: ${{ contains(matrix.config.flags, 'strict') }} |
| 223 | + USE_UBSAN: ${{ contains(matrix.config.flags, 'ubsan') }} |
| 224 | + SET_DEBUG: ${{ contains(matrix.config.flags, 'debug') }} |
| 225 | + run: | |
| 226 | + if(Sys.getenv("R_VIGNETTES") == "true"){ |
| 227 | + check_args <- c("--as-cran") |
| 228 | + build_args <- c("--compact-vignettes=gs+qpdf") |
| 229 | + }else{ |
| 230 | + check_args <- c("--no-manual", "--as-cran", "--ignore-vignettes") |
| 231 | + build_args <- c("--no-manual", "--no-build-vignettes") |
| 232 | + } |
| 233 | +
|
| 234 | + if(Sys.getenv("ENABLE_statnet_TESTS") == "true"){ |
| 235 | + check_args <- c(check_args, "--run-donttest") |
| 236 | + } |
| 237 | +
|
| 238 | + error_on <- if(Sys.getenv("FAIL_ON_WARN") == "true") "warning" else "error" |
| 239 | +
|
| 240 | + if(Sys.getenv("USE_UBSAN") == "true"){ |
| 241 | + Sys.setenv(PKG_LIBS=paste(Sys.getenv("PKG_LIBS"), "-fsanitize=undefined"), |
| 242 | + PKG_CFLAGS=paste(Sys.getenv("PKG_CFLAGS"), "-fsanitize=undefined"), |
| 243 | + PKG_CXXFLAGS=paste(Sys.getenv("PKG_CXXFLAGS"), "-fsanitize=undefined"), |
| 244 | + UBSAN_OPTIONS=paste(Sys.getenv("UBSAN_OPTIONS"), "print_stacktrace=1")) |
| 245 | + } |
| 246 | +
|
| 247 | + if(Sys.getenv("SET_DEBUG") == "true"){ |
| 248 | + Sys.setenv(PKG_LIBS=paste(Sys.getenv("PKG_LIBS"), "-UNDEBUG -DDEBUG"), |
| 249 | + PKG_CXXFLAGS=paste(Sys.getenv("PKG_CXXFLAGS"), "-UNDEBUG -DDEBUG"), |
| 250 | + PKG_CFLAGS=paste(Sys.getenv("PKG_CFLAGS"), "-UNDEBUG -DDEBUG")) |
| 251 | + } |
| 252 | +
|
| 253 | + rcmdcheck::rcmdcheck(args = check_args, build_args = build_args, error_on = error_on, check_dir = "check") |
| 254 | + shell: Rscript {0} |
| 255 | + |
| 256 | + - name: Check UBSAN output |
| 257 | + if: contains(matrix.config.flags, 'ubsan') |
| 258 | + run: | |
| 259 | + find check/ -name '*.Rout' -print0 | xargs -r -0 grep -E '\.[hc]:[0-9]+:[0-9]+: +runtime error:' > check/ubsan.err || true |
| 260 | + if [ -s check/ubsan.err ] |
| 261 | + then |
| 262 | + echo "UBSAN errors:" >&2 |
| 263 | + cat check/ubsan.err >&2 |
| 264 | + exit 1 |
| 265 | + fi |
| 266 | + shell: bash |
| 267 | + |
| 268 | + - name: Upload check results |
| 269 | + if: contains(matrix.config.flags, 'covr') == false && failure() |
| 270 | + uses: actions/upload-artifact@v4 |
| 271 | + with: |
| 272 | + name: ${{ runner.os }}-r${{ matrix.config.r }}-results |
| 273 | + path: | |
| 274 | + check |
| 275 | + !check/*/00_pkg_src |
| 276 | +
|
| 277 | + - name: Test coverage |
| 278 | + if: contains(matrix.config.flags, 'covr') |
| 279 | + timeout-minutes: ${{ matrix.config.timeout }} |
| 280 | + env: |
| 281 | + _R_CHECK_CRAN_INCOMING_REMOTE_: false |
| 282 | + _R_CHECK_FORCE_SUGGESTS_: ${{ runner.os != 'macOS' }} # Rmpi is not available on macOS. |
| 283 | + ENABLE_statnet_TESTS: ${{ contains(matrix.config.flags, 'full') }} |
| 284 | + run: covr::codecov(type=c("tests","examples")) |
| 285 | + shell: Rscript {0} |
0 commit comments