diff --git a/.Rbuildignore b/.Rbuildignore index 5b8eb49..40a4853 100644 --- a/.Rbuildignore +++ b/.Rbuildignore @@ -10,3 +10,4 @@ LICENSE.md ^doc$ ^Meta$ .lintr +^codecov\.yml$ diff --git a/.github/workflows/R-CMD-check.yaml b/.github/workflows/R-CMD-check.yaml new file mode 100644 index 0000000..2553f54 --- /dev/null +++ b/.github/workflows/R-CMD-check.yaml @@ -0,0 +1,290 @@ +# 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. + +# For help debugging build failures open an issue on the RStudio community with the 'github-actions' tag. +# https://community.rstudio.com/new-topic?category=Package%20development&tags=github-actions + +# Each entry in the config matrix must have the following keys: +# +# os: windows-latest, macOS-latest, ubuntu-20.04, etc. (as of this writing) +# +# r: devel or release +# +# rspm: Linux only, just leave it alone. +# +# timeout: in minutes +# +# flags: a comma (or some other aesthetically pleasing and +# non-forbidden symbol) separated list of flags. Currently +# supported: +# +# binaries: build and upload as an artefact a binary version (particularly for MacOS and Windows). +# +# full: run with ENABLE_statnet_TESTS. +# +# vignettes: build vignettes and manuals; otherwise ignore. +# +# ubsan: compile with -fsanitize=undefined and fail on any errors identified. +# +# debug: compile package with -DDEBUG and -UNDEBUG +# +# covr: run in covr mode may be used with 'full' but probably not with others. +# +# +# Typical public, private, and release configurations are provided in the variables below. + +on: [push, pull_request] + +name: R-CMD-check + +# Set public and private repositories (i.e., USER/PKG). Leave blank to autodetect. +env: + PUBLIC: '' + PRIVATE: '' + PUBLIC_CONFIG: '{"config":[ + {"os":"windows-latest", "r":"release", "timeout":360, "flags":"binaries"}, + {"os":"macOS-latest", "r":"release", "timeout":360, "flags":"binaries, ubsan"}, + {"os":"ubuntu-latest", "r":"release", "timeout":360, "flags":"full, ubsan, debug"}, + {"os":"ubuntu-latest", "r":"devel", "timeout":360, "flags":"vignettes, remote"}, + {"os":"ubuntu-latest", "r":"release", "timeout":360, "flags":"full, covr"}]}' + RELEASE_CONFIG: '{"config":[ + {"os":"windows-latest", "r":"release", "timeout":360, "flags":"binaries, vignettes, remote, strict"}, + {"os":"macOS-latest", "r":"release", "timeout":360, "flags":"binaries, ubsan, vignettes, remote, strict"}, + {"os":"ubuntu-latest", "r":"release", "timeout":360, "flags":"full, ubsan, debug, strict"}, + {"os":"ubuntu-latest", "r":"devel", "timeout":360, "flags":"vignettes, remote, strict"}, + {"os":"ubuntu-latest", "r":"release", "timeout":360, "flags":"full, covr, strict"}]}' + PRIVATE_CONFIG: '{"config":[ + {"os":"ubuntu-latest", "r":"release", "timeout":10, "flags":"none"} + ]}' + +jobs: + Set-Matrix-Private: + runs-on: ubuntu-latest + outputs: + matrix: ${{ steps.set-matrix.outputs.matrix }} + steps: + - name: check-config # Make sure either neither or both are set; abort if not. + if: (env.PUBLIC == '') != (env.PRIVATE == '') + run: | + echo "Configuration problem: only one of the repositories is set." + exit 1 + - name: detect-repos + if: env.PUBLIC == '' && env.PRIVATE == '' # Autodetect always. + run: | + if [[ "${{ github.repository }}" == *-private ]] + then # Current repo is private. + IAM="private" + PRIVATE="${{ github.repository }}" + PUBLIC="${PRIVATE%-private}" + else # Current repo is public. + IAM="public" + PUBLIC="${{ github.repository }}" + PRIVATE="$PUBLIC-private" + fi + echo "IAM=$IAM" >> $GITHUB_ENV + echo "PRIVATE=$PRIVATE" >> $GITHUB_ENV + echo "PUBLIC=$PUBLIC" >> $GITHUB_ENV + - name: public-check # Check if the branch/tag exists in the public repository. + if: env.IAM == 'private' # Only check if from private repo. + run: | + set +e + git ls-remote --exit-code https://github.com/${{ env.PUBLIC }} ${{ github.ref }} + echo "FOUND_PUBLIC=$?" >> $GITHUB_ENV + - name: set-matrix + id: set-matrix + run: | + if [[ "${{ env.IAM }}" == 'public' ]] # Public: full set. + then + config='${{ env.PUBLIC_CONFIG }}' + elif [[ "${{ env.FOUND_PUBLIC }}" != '0' ]] # Private with no public analogue: reduced set. + then + config='${{ env.PRIVATE_CONFIG }}' + else # Private with public analogue: no checking. + config='' + fi + config="${config//$'\r'/' '}" + config="${config//$'\n'/' '}" + echo "matrix=$config" >> $GITHUB_OUTPUT + + R-CMD-check: + needs: Set-Matrix-Private + + if: needs.Set-Matrix-Private.outputs.matrix != '' + + runs-on: ${{ matrix.config.os }} + + name: ${{ matrix.config.os }} (r ${{ matrix.config.r }}, flags ${{ matrix.config.flags }}) + + strategy: + fail-fast: false + matrix: ${{fromJson(needs.Set-Matrix-Private.outputs.matrix)}} + + env: + HOMEBREW_NO_INSTALL_CLEANUP: 1 + R_REMOTES_NO_ERRORS_FROM_WARNINGS: true + + steps: + - name: If available, use the Janitor's key rather than the repository-specific key. + id: set-pat + run: | + if [[ -n "${{ secrets.JANITORS_GITHUB_PAT }}" ]] + then + echo "GITHUB_PAT=${{ secrets.JANITORS_GITHUB_PAT }}" >> $GITHUB_ENV + else + echo "GITHUB_PAT=${{ secrets.GITHUB_TOKEN }}" >> $GITHUB_ENV + fi + shell: bash + + - uses: actions/checkout@v4 + + - uses: r-lib/actions/setup-r@v2 + with: + use-public-rspm: true + r-version: ${{ matrix.config.r }} + + - name: Install tidy (on Linux if running vignettes) + if: runner.os == 'Linux' && contains(matrix.config.flags, 'vignettes') + run: | + /usr/bin/sudo DEBIAN_FRONTEND=noninteractive apt-get install -y tidy + shell: bash + + - uses: r-lib/actions/setup-pandoc@v2 + + - name: Install tinytex (system) + if: contains(matrix.config.flags, 'vignettes') + uses: r-lib/actions/setup-tinytex@v2 + + - uses: r-lib/actions/setup-r-dependencies@v2 + with: + extra-packages: | + any::rcmdcheck + any::covr + any::V8 + any::xml2 + needs: | + check + coverage + + - name: Install tinytex (R) + if: contains(matrix.config.flags, 'vignettes') + run: | + if(!requireNamespace("tinytex", quietly = TRUE)) pak::pkg_install("tinytex") + tinytex:::install_yihui_pkgs() + tinytex::tlmgr_install("makeindex") + shell: Rscript {0} + + - name: Build + if: contains(matrix.config.flags, 'binaries') + run: | + dir.create("binaries", FALSE) + pkgbuild::build(binary=TRUE, vignettes=FALSE, dest_path = "binaries") + shell: Rscript {0} + + - name: Upload build results + if: contains(matrix.config.flags, 'binaries') && !failure() + uses: actions/upload-artifact@v4 + with: + name: ${{ runner.os }}-r${{ matrix.config.r }}-binaries + path: binaries + + - name: Clean up build results + if: contains(matrix.config.flags, 'binaries') + run: rm -rf binaries + shell: bash + + - name: Check + if: contains(matrix.config.flags, 'covr') == false + timeout-minutes: ${{ matrix.config.timeout }} + env: + _R_CHECK_CRAN_INCOMING_REMOTE_: ${{ contains(matrix.config.flags, 'remote') }} + _R_CHECK_FORCE_SUGGESTS_: false + ENABLE_statnet_TESTS: ${{ contains(matrix.config.flags, 'full') }} + R_VIGNETTES: ${{ contains(matrix.config.flags, 'vignettes') }} + FAIL_ON_WARN: ${{ contains(matrix.config.flags, 'strict') }} + USE_UBSAN: ${{ contains(matrix.config.flags, 'ubsan') }} + SET_DEBUG: ${{ contains(matrix.config.flags, 'debug') }} + run: | + if(Sys.getenv("R_VIGNETTES") == "true"){ + check_args <- c("--as-cran") + build_args <- c("--compact-vignettes=gs+qpdf") + }else{ + check_args <- c("--no-manual", "--as-cran", "--ignore-vignettes") + build_args <- c("--no-manual", "--no-build-vignettes") + } + + if(Sys.getenv("ENABLE_statnet_TESTS") == "true"){ + check_args <- c(check_args, "--run-donttest") + } + + error_on <- if(Sys.getenv("FAIL_ON_WARN") == "true") "warning" else "error" + + extra_flags <- c() + + if(Sys.getenv("USE_UBSAN") == "true"){ + extra_flags <- c(extra_flags, "-fsanitize=undefined") + Sys.setenv(UBSAN_OPTIONS=paste(Sys.getenv("UBSAN_OPTIONS"), "print_stacktrace=1")) + } + + if(Sys.getenv("SET_DEBUG") == "true") extra_flags <- c(extra_flags, "-UNDEBUG", "-DDEBUG") + + # Before R 4.3.0, R itself was not compliant. + if(getRversion() >= "4.3") extra_flags <- c(extra_flags, "-Wstrict-prototypes") + + Sys.setenv(PKG_LIBS=paste(c(Sys.getenv("PKG_LIBS"), extra_flags), collapse=" "), + PKG_CXXFLAGS=paste(c(Sys.getenv("PKG_CXXFLAGS"), extra_flags), collapse=" "), + PKG_CFLAGS=paste(c(Sys.getenv("PKG_CFLAGS"), extra_flags), collapse=" ")) + + rcmdcheck::rcmdcheck(args = check_args, build_args = build_args, error_on = error_on, check_dir = "check") + shell: Rscript {0} + + - name: Check UBSAN output + if: contains(matrix.config.flags, 'ubsan') + # NB: xargs -r only works in GNU, so not on MacOS. + run: | + find check/ -name '*.Rout' -print0 > check/Rout.list + if [ -s check/Rout.list ] + then + cat check/Rout.list | xargs -0 grep -E '\.[hc]:[0-9]+:[0-9]+: +runtime error:' > check/ubsan.err || true + if [ -s check/ubsan.err ] + then + echo "UBSAN errors:" >&2 + cat check/ubsan.err >&2 + exit 1 + fi + fi + shell: bash + + - name: Upload check results + if: contains(matrix.config.flags, 'covr') == false && failure() + uses: actions/upload-artifact@v4 + with: + name: ${{ runner.os }}-r${{ matrix.config.r }}-results + path: | + check + !check/*/00_pkg_src + + - name: Test coverage + if: contains(matrix.config.flags, 'covr') + timeout-minutes: ${{ matrix.config.timeout }} + env: + _R_CHECK_CRAN_INCOMING_REMOTE_: false + _R_CHECK_FORCE_SUGGESTS_: ${{ runner.os != 'macOS' }} # Rmpi is not available on macOS. + ENABLE_statnet_TESTS: ${{ contains(matrix.config.flags, 'full') }} + run: | + cov <- covr::package_coverage( + type=c("tests", "examples"), + quiet = FALSE, + clean = FALSE, + install_path = file.path(normalizePath(Sys.getenv("RUNNER_TEMP"), winslash = "/"), "package") + ) + covr::to_cobertura(cov) + shell: Rscript {0} + + - name: Upload coverage results + if: contains(matrix.config.flags, 'covr') + uses: codecov/codecov-action@v4 + with: + fail_ci_if_error: ${{ github.event_name != 'pull_request' && true || false }} + file: ./cobertura.xml + plugin: noop + disable_search: true + token: ${{ secrets.CODECOV_TOKEN }} diff --git a/.github/workflows/check-standard.yaml b/.github/workflows/check-standard.yaml deleted file mode 100644 index 52c2b27..0000000 --- a/.github/workflows/check-standard.yaml +++ /dev/null @@ -1,49 +0,0 @@ -# Workflow derived from https://github.com/r-lib/actions/tree/v2/examples -# Need help debugging build failures? Start at https://github.com/r-lib/actions#where-to-find-help -on: - push: - branches: [main, master] - pull_request: - branches: [main, master] - -name: R-CMD-check - -jobs: - R-CMD-check: - runs-on: ${{ matrix.config.os }} - - name: ${{ matrix.config.os }} (${{ matrix.config.r }}) - - strategy: - fail-fast: false - matrix: - config: - - {os: macOS-latest, r: 'release'} - - {os: windows-latest, r: 'release'} - - {os: ubuntu-latest, r: 'devel', http-user-agent: 'release'} - - {os: ubuntu-latest, r: 'release'} - - {os: ubuntu-latest, r: 'oldrel-1'} - - env: - GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} - R_KEEP_PKG_SOURCE: yes - - steps: - - uses: actions/checkout@v2 - - - uses: r-lib/actions/setup-pandoc@v2 - - - uses: r-lib/actions/setup-r@v2 - with: - r-version: ${{ matrix.config.r }} - http-user-agent: ${{ matrix.config.http-user-agent }} - use-public-rspm: true - - - uses: r-lib/actions/setup-r-dependencies@v2 - with: - extra-packages: any::rcmdcheck - needs: check - - - uses: r-lib/actions/check-r-package@v2 - with: - upload-snapshots: true diff --git a/codecov.yml b/codecov.yml new file mode 100644 index 0000000..04c5585 --- /dev/null +++ b/codecov.yml @@ -0,0 +1,14 @@ +comment: false + +coverage: + status: + project: + default: + target: auto + threshold: 1% + informational: true + patch: + default: + target: auto + threshold: 1% + informational: true