From 108b61bbb406b06e417f5e295b0294cd1ebb7d00 Mon Sep 17 00:00:00 2001 From: Philipp Baumann Date: Thu, 12 Sep 2024 23:03:47 +0200 Subject: [PATCH 01/62] check pre-commit code checks and formatters via precommit-ci-lite bot --- .github/workflows/format-code.yaml | 25 +++++ .pre-commit-config.yaml | 155 +++++++++++++++++++++++++++++ 2 files changed, 180 insertions(+) create mode 100644 .github/workflows/format-code.yaml create mode 100644 .pre-commit-config.yaml diff --git a/.github/workflows/format-code.yaml b/.github/workflows/format-code.yaml new file mode 100644 index 00000000..b9e1b2bf --- /dev/null +++ b/.github/workflows/format-code.yaml @@ -0,0 +1,25 @@ +name: Check code +on: + pull_request: + branches: [main] + +permissions: + contents: write + pull-requests: write + +jobs: + main: + runs-on: ubuntu-22.04 + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-python@v4 + with: + python-version: 3.x + - uses: pre-commit/action@v3.0.1 + - uses: pre-commit-ci/lite-action@v1.0.3 + name: pre-commit-ci-lite + if: always() + with: + msg: apply code formatting + env: + GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 00000000..11472f54 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,155 @@ +# All available hooks: https://pre-commit.com/hooks.html +# R specific hooks: https://github.com/lorenzwalthert/precommit +repos: +- repo: https://github.com/lorenzwalthert/precommit + rev: v0.4.3 + hooks: + - id: style-files + args: [--style_pkg=styler, --style_fun=tidyverse_style, --cache-root=styler-perm] + exclude: > + (?x)^( + tests/testthat/in/.*| + renv/.* + )$ + - id: roxygenize + additional_dependencies: + - cli + - fs + - here + - magrittr + - purrr + - R.cache + - rlang + - rprojroot + - rstudioapi + - withr + - yaml + - r-lib/pkgapi + # codemeta must be above use-tidy-description when both are used + # - id: codemeta-description-updated + - id: use-tidy-description + - id: spell-check + exclude: > + (?x)^( + .*\.[rR]| + .*\.feather| + .*\.jpeg| + .*\.pdf| + .*\.png| + .*\.py| + .*\.RData| + .*\.rds| + .*\.Rds| + .*\.Rproj| + .*\.sh| + (.*/|)\.gitignore| + (.*/|)\.pre-commit-.*| + (.*/|)\.Rbuildignore| + (.*/|)\.Renviron| + (.*/|)\.Rprofile| + (.*/|)\.travis\.yml| + (.*/|)appveyor\.yml| + (.*/|)NAMESPACE| + (.*/|)renv/settings\.dcf| + (.*/|)renv\.lock| + (.*/|)WORDLIST| + \.github/workflows/.*| + LICENSE| + revdep/.*| + tests/testthat/in/.*| + )$ + - id: readme-rmd-rendered + - id: parsable-R + exclude: > + (?x)^( + tests/testthat/in/.*| + )$ + - id: no-browser-statement + exclude: > + (?x)^( + tests/testthat/in/.*| + )$ + - id: no-debug-statement + exclude: > + (?x)^( + tests/testthat/in/.*| + )$ + - id: deps-in-desc + - id: pkgdown +- repo: https://github.com/pre-commit/pre-commit-hooks + rev: v4.6.0 + hooks: + - id: check-added-large-files + - id: end-of-file-fixer + exclude: '\.Rd' # sometimes roxygen fails to generate EOF blank line. + - id: file-contents-sorter + files: '^\.Rbuildignore$' +- repo: https://github.com/pre-commit-ci/pre-commit-ci-config + rev: v1.6.1 + hooks: + # Only required when https://pre-commit.ci is used for config validation + - id: check-pre-commit-ci-config +- repo: https://github.com/lorenzwalthert/gitignore-tidy + rev: 0.1.2 + hooks: + - id: tidy-gitignore +- repo: local + hooks: + - id: consistent-release-tag + name: consistent-release-tag + entry: Rscript inst/hooks/local/consistent-release-tag.R + language: r + additional_dependencies: + - docopt + - fs + - yaml + - purrr + - glue + - rlang + - git2r + - desc + - lorenzwalthert/precommit + stages: [commit, push] + - id: hooks-config-to-inst + name: hooks-config-to-inst + entry: Rscript inst/hooks/local/hooks-config-to-inst.R + language: r + stages: [commit, push] + additional_dependencies: + - fs + require_serial: True + - id: spell-check-exclude-identical + name: spell-check-exclude-identical + entry: Rscript inst/hooks/local/spell-check-exclude-identical.R + language: r + stages: [commit, push] + additional_dependencies: + - magrittr + - purrr + - yaml + - here + - rlang + require_serial: True + - id: forbid-to-commit + name: Don't commit common R artifacts + entry: Cannot commit .Rhistory, .Rdata, .csv and similar. + language: fail + files: '\.(Rhistory|csv|RData|Rds|rds)$' + # `exclude: ` to allow committing specific files. + - id: spell-check-ordered-exclude + name: Ordered regex pattern for spell-check exclusion + description: Ensure alphabetical order in `exclude:` key of spell check. + entry: Rscript inst/hooks/local/spell-check-ordered-exclude.R + language: r + files: '^(.*/|)\.?pre-commit-config.*\.yaml$' + additional_dependencies: + - magrittr + - yaml + - purrr + - rlang + +default_stages: ["commit"] + +ci: + skip: [consistent-release-tag, spell-check-ordered-exclude, pkgdown] + autoupdate_schedule: monthly From f9b040f36c77676c5b427c4290a6e3b3b06d8148 Mon Sep 17 00:00:00 2001 From: Philipp Baumann Date: Thu, 12 Sep 2024 23:36:22 +0200 Subject: [PATCH 02/62] fix according pre-commit hoooks --- man/rix.Rd | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/man/rix.Rd b/man/rix.Rd index ea5e9abb..d3d92c16 100644 --- a/man/rix.Rd +++ b/man/rix.Rd @@ -114,7 +114,7 @@ command line, or from another editor (such as Emacs or Vim), set the \code{ide} argument to \code{"other"}. We recommend reading the \code{vignette("e-interactive-use")} for more details. -Packages to install from Github must be provided in a list of 3 elements: +Packages to install from Github or Gitlab must be provided in a list of 3 elements: "package_name", "repo_url" and "commit". To install several packages, provide a list of lists of these 3 elements, one per package to install. It is also possible to install old versions of packages by specifying a @@ -127,9 +127,9 @@ have looked at the time of \code{{ggplot2}}'s version 2.2.1 release, then use the Nix revision closest to that date, by setting \code{r_ver = "3.1.0"}, which was the version of R current at the time. This ensures that Nix builds a completely coherent environment. For security purposes, users that wish to -install packages from Github or from the CRAN archives must provide a +install packages from Github/Gitlab or from the CRAN archives must provide a security hash for each package. \code{{rix}} automatically precomputes this hash -for the source directory of R packages from GitHub or from the CRAN +for the source directory of R packages from GitHub/Gitlab or from the CRAN archives, to make sure the expected trusted sources that match the precomputed hashes in the \code{default.nix} are downloaded. If Nix is available, then the hash will be computed on the user's machine, however, From a99e688a53bed220fd4e6e083bc915b421804ecd Mon Sep 17 00:00:00 2001 From: Philipp Baumann Date: Fri, 13 Sep 2024 10:18:02 +0200 Subject: [PATCH 03/62] debug: slim down hooks to styler --- .pre-commit-config.yaml | 122 ---------------------------------------- 1 file changed, 122 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 11472f54..5c122182 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -25,128 +25,6 @@ repos: - withr - yaml - r-lib/pkgapi - # codemeta must be above use-tidy-description when both are used - # - id: codemeta-description-updated - - id: use-tidy-description - - id: spell-check - exclude: > - (?x)^( - .*\.[rR]| - .*\.feather| - .*\.jpeg| - .*\.pdf| - .*\.png| - .*\.py| - .*\.RData| - .*\.rds| - .*\.Rds| - .*\.Rproj| - .*\.sh| - (.*/|)\.gitignore| - (.*/|)\.pre-commit-.*| - (.*/|)\.Rbuildignore| - (.*/|)\.Renviron| - (.*/|)\.Rprofile| - (.*/|)\.travis\.yml| - (.*/|)appveyor\.yml| - (.*/|)NAMESPACE| - (.*/|)renv/settings\.dcf| - (.*/|)renv\.lock| - (.*/|)WORDLIST| - \.github/workflows/.*| - LICENSE| - revdep/.*| - tests/testthat/in/.*| - )$ - - id: readme-rmd-rendered - - id: parsable-R - exclude: > - (?x)^( - tests/testthat/in/.*| - )$ - - id: no-browser-statement - exclude: > - (?x)^( - tests/testthat/in/.*| - )$ - - id: no-debug-statement - exclude: > - (?x)^( - tests/testthat/in/.*| - )$ - - id: deps-in-desc - - id: pkgdown -- repo: https://github.com/pre-commit/pre-commit-hooks - rev: v4.6.0 - hooks: - - id: check-added-large-files - - id: end-of-file-fixer - exclude: '\.Rd' # sometimes roxygen fails to generate EOF blank line. - - id: file-contents-sorter - files: '^\.Rbuildignore$' -- repo: https://github.com/pre-commit-ci/pre-commit-ci-config - rev: v1.6.1 - hooks: - # Only required when https://pre-commit.ci is used for config validation - - id: check-pre-commit-ci-config -- repo: https://github.com/lorenzwalthert/gitignore-tidy - rev: 0.1.2 - hooks: - - id: tidy-gitignore -- repo: local - hooks: - - id: consistent-release-tag - name: consistent-release-tag - entry: Rscript inst/hooks/local/consistent-release-tag.R - language: r - additional_dependencies: - - docopt - - fs - - yaml - - purrr - - glue - - rlang - - git2r - - desc - - lorenzwalthert/precommit - stages: [commit, push] - - id: hooks-config-to-inst - name: hooks-config-to-inst - entry: Rscript inst/hooks/local/hooks-config-to-inst.R - language: r - stages: [commit, push] - additional_dependencies: - - fs - require_serial: True - - id: spell-check-exclude-identical - name: spell-check-exclude-identical - entry: Rscript inst/hooks/local/spell-check-exclude-identical.R - language: r - stages: [commit, push] - additional_dependencies: - - magrittr - - purrr - - yaml - - here - - rlang - require_serial: True - - id: forbid-to-commit - name: Don't commit common R artifacts - entry: Cannot commit .Rhistory, .Rdata, .csv and similar. - language: fail - files: '\.(Rhistory|csv|RData|Rds|rds)$' - # `exclude: ` to allow committing specific files. - - id: spell-check-ordered-exclude - name: Ordered regex pattern for spell-check exclusion - description: Ensure alphabetical order in `exclude:` key of spell check. - entry: Rscript inst/hooks/local/spell-check-ordered-exclude.R - language: r - files: '^(.*/|)\.?pre-commit-config.*\.yaml$' - additional_dependencies: - - magrittr - - yaml - - purrr - - rlang default_stages: ["commit"] From be6b3959d2c3968b9c5b3b592edf806f877c5f67 Mon Sep 17 00:00:00 2001 From: Philipp Baumann Date: Fri, 13 Sep 2024 10:23:15 +0200 Subject: [PATCH 04/62] remove roxygenize --- .pre-commit-config.yaml | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 5c122182..14a85db1 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -9,22 +9,7 @@ repos: exclude: > (?x)^( tests/testthat/in/.*| - renv/.* )$ - - id: roxygenize - additional_dependencies: - - cli - - fs - - here - - magrittr - - purrr - - R.cache - - rlang - - rprojroot - - rstudioapi - - withr - - yaml - - r-lib/pkgapi default_stages: ["commit"] From 1048daea1108dbe0122590ed9c3c53178c23ebfe Mon Sep 17 00:00:00 2001 From: Philipp Baumann Date: Fri, 13 Sep 2024 10:27:22 +0200 Subject: [PATCH 05/62] avoid pre-commit action and only use the lite (applies fixes) --- .github/workflows/format-code.yaml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/format-code.yaml b/.github/workflows/format-code.yaml index b9e1b2bf..d022f488 100644 --- a/.github/workflows/format-code.yaml +++ b/.github/workflows/format-code.yaml @@ -15,7 +15,6 @@ jobs: - uses: actions/setup-python@v4 with: python-version: 3.x - - uses: pre-commit/action@v3.0.1 - uses: pre-commit-ci/lite-action@v1.0.3 name: pre-commit-ci-lite if: always() From ba7ca8923be7ead170c641e0e399ecb1dcee9a98 Mon Sep 17 00:00:00 2001 From: Philipp Baumann Date: Fri, 13 Sep 2024 10:31:47 +0200 Subject: [PATCH 06/62] add space to test linter --- R/rix.R | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/R/rix.R b/R/rix.R index 9ab8ba1b..419b358c 100644 --- a/R/rix.R +++ b/R/rix.R @@ -175,7 +175,7 @@ before continuing." } if (message_type != "quiet" && r_ver %in% available_r() && - r_ver != "latest" && r_ver <= "4.1.1") { + r_ver != "latest" && r_ver <= "4.1.1") { warning( "You are generating an expression for an older version of R.\n", "To use this environment, you should directly use `nix-shell` and not ", From f2c99953f40919028e5da5b5531c24b40a1d374a Mon Sep 17 00:00:00 2001 From: Philipp Baumann Date: Fri, 13 Sep 2024 10:39:09 +0200 Subject: [PATCH 07/62] use latest; add back precommit action --- .github/workflows/format-code.yaml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/format-code.yaml b/.github/workflows/format-code.yaml index d022f488..e6d769d9 100644 --- a/.github/workflows/format-code.yaml +++ b/.github/workflows/format-code.yaml @@ -9,12 +9,13 @@ permissions: jobs: main: - runs-on: ubuntu-22.04 + runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: actions/setup-python@v4 with: python-version: 3.x + - uses: pre-commit/action@v3.0.1 - uses: pre-commit-ci/lite-action@v1.0.3 name: pre-commit-ci-lite if: always() From 026e8f6f3cb933aa443f9f79b4eed6e981e0f2dd Mon Sep 17 00:00:00 2001 From: Philipp Baumann Date: Fri, 13 Sep 2024 14:27:48 +0200 Subject: [PATCH 08/62] now after pre-commit-lite ci enabled --- R/rix.R | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/R/rix.R b/R/rix.R index 419b358c..731543ea 100644 --- a/R/rix.R +++ b/R/rix.R @@ -162,7 +162,7 @@ rix <- function(r_ver = "latest", message_type = "simple", shell_hook = NULL) { message_type <- match.arg(message_type, - choices = c("quiet", "simple", "verbose") + choices = c("quiet", "simple", "verbose") ) if (!(message_type %in% c("simple", "quiet")) && r_ver %in% c("bleeding_edge", "frozen_edge")) { From 8c51a0c27cc7a42e556062894d64141839aa84a1 Mon Sep 17 00:00:00 2001 From: Philipp Baumann Date: Fri, 13 Sep 2024 14:33:04 +0200 Subject: [PATCH 09/62] disable unused hooks --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 14a85db1..97edb784 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -14,5 +14,5 @@ repos: default_stages: ["commit"] ci: - skip: [consistent-release-tag, spell-check-ordered-exclude, pkgdown] + # skip: [consistent-release-tag, spell-check-ordered-exclude, pkgdown] autoupdate_schedule: monthly From b8d6bf044adaca369a4c0548cac6b1096b0048b3 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Fri, 13 Sep 2024 12:33:31 +0000 Subject: [PATCH 10/62] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- R/rix.R | 4 ++-- create_dev_env.R | 26 +++++++++++++++----------- 2 files changed, 17 insertions(+), 13 deletions(-) diff --git a/R/rix.R b/R/rix.R index 731543ea..9ab8ba1b 100644 --- a/R/rix.R +++ b/R/rix.R @@ -162,7 +162,7 @@ rix <- function(r_ver = "latest", message_type = "simple", shell_hook = NULL) { message_type <- match.arg(message_type, - choices = c("quiet", "simple", "verbose") + choices = c("quiet", "simple", "verbose") ) if (!(message_type %in% c("simple", "quiet")) && r_ver %in% c("bleeding_edge", "frozen_edge")) { @@ -175,7 +175,7 @@ before continuing." } if (message_type != "quiet" && r_ver %in% available_r() && - r_ver != "latest" && r_ver <= "4.1.1") { + r_ver != "latest" && r_ver <= "4.1.1") { warning( "You are generating an expression for an older version of R.\n", "To use this environment, you should directly use `nix-shell` and not ", diff --git a/create_dev_env.R b/create_dev_env.R index 7562fe5b..07bae46c 100644 --- a/create_dev_env.R +++ b/create_dev_env.R @@ -1,13 +1,17 @@ library(rix) -rix(r_ver = "bleeding_edge", - r_pkgs = c("devtools", "diffviewer", "fledge", "lintr", "styler", - "codetools", "jsonlite", "httr", "sys", "testthat", "knitr", - "rmarkdown", "rhub"), - system_pkgs = c("R", "glibcLocalesUtf8", "pandoc", "nix"), - tex_pkgs = "scheme-small", - ide = "other", - project_path = ".", - overwrite = TRUE, - print = FALSE, - shell_hook = NULL) +rix( + r_ver = "bleeding_edge", + r_pkgs = c( + "devtools", "diffviewer", "fledge", "lintr", "styler", + "codetools", "jsonlite", "httr", "sys", "testthat", "knitr", + "rmarkdown", "rhub" + ), + system_pkgs = c("R", "glibcLocalesUtf8", "pandoc", "nix"), + tex_pkgs = "scheme-small", + ide = "other", + project_path = ".", + overwrite = TRUE, + print = FALSE, + shell_hook = NULL +) From 6c46d03655a5b9996b4e0b7bd6537d5adc568a24 Mon Sep 17 00:00:00 2001 From: Philipp Baumann Date: Fri, 13 Sep 2024 14:41:20 +0200 Subject: [PATCH 11/62] parsable R and no browser() --- .pre-commit-config.yaml | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 97edb784..0634556e 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -10,6 +10,16 @@ repos: (?x)^( tests/testthat/in/.*| )$ + - id: parsable-R + exclude: > + (?x)^( + tests/testthat/in/.*| + )$ + - id: no-browser-statement + exclude: > + (?x)^( + tests/testthat/in/.*| + )$ default_stages: ["commit"] From f38c3a271fa23c2eef50a36fe2944ba18713ac18 Mon Sep 17 00:00:00 2001 From: Philipp Baumann Date: Fri, 13 Sep 2024 14:52:51 +0200 Subject: [PATCH 12/62] remove old lintr action --- .github/workflows/lint.yaml | 38 ------------------------------------- 1 file changed, 38 deletions(-) delete mode 100644 .github/workflows/lint.yaml diff --git a/.github/workflows/lint.yaml b/.github/workflows/lint.yaml deleted file mode 100644 index 974c286e..00000000 --- a/.github/workflows/lint.yaml +++ /dev/null @@ -1,38 +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: lint - -permissions: read-all - -jobs: - lint: - runs-on: ubuntu-latest - env: - GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} - steps: - - uses: actions/checkout@v4 - - - name: Install Nix - uses: DeterminateSystems/nix-installer-action@main - - - uses: cachix/cachix-action@v15 - with: - name: rstats-on-nix - # If you chose signing key for write access - # signingKey: '${{ secrets.CACHIX_SIGNING_KEY }}' - # If you chose API tokens for write access OR if you have a private cache - authToken: '${{ secrets.CACHIX_AUTH }}' - - - name: Build dev env - run: nix-build - - - name: Run lintr - run: nix-shell --run "Rscript -e 'lintr::lint_package()'" - env: - LINTR_ERROR_ON_LINT: false From 9e8b9db6424a22b7cd6900b7a7d0a845bba47adf Mon Sep 17 00:00:00 2001 From: Philipp Baumann Date: Fri, 13 Sep 2024 15:08:27 +0200 Subject: [PATCH 13/62] avoid race conditions --- .pre-commit-config.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 0634556e..e08f3c24 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -6,6 +6,7 @@ repos: hooks: - id: style-files args: [--style_pkg=styler, --style_fun=tidyverse_style, --cache-root=styler-perm] + require_serial: true exclude: > (?x)^( tests/testthat/in/.*| From 17894c4aa1f902d2fed4809d6828d5ea4d96d65e Mon Sep 17 00:00:00 2001 From: Philipp Baumann Date: Fri, 13 Sep 2024 15:14:13 +0200 Subject: [PATCH 14/62] also add .lintr hook --- .pre-commit-config.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index e08f3c24..49c31489 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -11,6 +11,7 @@ repos: (?x)^( tests/testthat/in/.*| )$ + - id: lintr - id: parsable-R exclude: > (?x)^( From 95efc16bd900a89efc5af2cdf3b8fabc9d38cac3 Mon Sep 17 00:00:00 2001 From: Philipp Baumann Date: Fri, 13 Sep 2024 15:35:42 +0200 Subject: [PATCH 15/62] please lintr --- R/fetchers.R | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/R/fetchers.R b/R/fetchers.R index 5ada15be..e8adcc54 100644 --- a/R/fetchers.R +++ b/R/fetchers.R @@ -96,7 +96,11 @@ fetchzip <- function(archive_pkg, sri_hash = NULL) { #' @noRd remove_base <- function(list_imports) { imports_nobase <- gsub( - "(^base$)|(^compiler$)|(^datasets$)|(^grDevices$)|(^graphics$)|(^grid$)|(^methods$)|(^parallel$)|(^profile$)|(^splines$)|(^stats$)|(^stats4$)|(^tcltk$)|(^tools$)|(^translations$)|(^utils$)", + paste0( + "(^base$)|(^compiler$)|(^datasets$)|(^grDevices$)|(^graphics$)|(^grid$)|", + "(^methods$)|(^parallel$)|(^profile$)|(^splines$)|(^stats$)|", + "(^stats4$)|(^tcltk$)|(^tools$)|(^translations$)|(^utils$)" + ), NA_character_, list_imports ) @@ -226,7 +230,14 @@ fetchgits <- function(git_pkgs) { } else if (all(vapply(git_pkgs, is.list, logical(1)))) { paste(lapply(git_pkgs, fetchgit), collapse = "\n") } else { - stop("There is something wrong with the input. Make sure it is either a list of three elements 'package_name', 'repo_url' and 'commit' or a list of lists with these three elements") + stop( + paste0( + "There is something wrong with the input. ", + "Make sure it is either a list of three elements ", + "'package_name', 'repo_url' and 'commit', or ", + "a list of lists with these three elements" + ) + ) } } From 276b3b6e4c5c4b523664680f3340a10f4bdc6d12 Mon Sep 17 00:00:00 2001 From: Philipp Baumann Date: Fri, 13 Sep 2024 15:36:00 +0200 Subject: [PATCH 16/62] exclude test-fetchers.R --- .pre-commit-config.yaml | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 49c31489..b626406c 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -7,21 +7,17 @@ repos: - id: style-files args: [--style_pkg=styler, --style_fun=tidyverse_style, --cache-root=styler-perm] require_serial: true - exclude: > + - id: lintr + exclude: > (?x)^( - tests/testthat/in/.*| + tests/testthat/test-fetchers.R )$ - - id: lintr - id: parsable-R exclude: > (?x)^( tests/testthat/in/.*| )$ - id: no-browser-statement - exclude: > - (?x)^( - tests/testthat/in/.*| - )$ default_stages: ["commit"] From 4aba03ff52ce8de8e319916bcffa315aafc84a91 Mon Sep 17 00:00:00 2001 From: Philipp Baumann Date: Fri, 13 Sep 2024 15:36:20 +0200 Subject: [PATCH 17/62] exclude lintr --- .lintr | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.lintr b/.lintr index 1375c994..a8ac14e9 100644 --- a/.lintr +++ b/.lintr @@ -1,5 +1,6 @@ linters: linters_with_defaults( line_length_linter(100), - commented_code_linter = NULL + commented_code_linter = NULL, + object_usage_linter=NULL ) encoding: "UTF-8" From d393956a67868cc9120008c492a32daff1e4aac5 Mon Sep 17 00:00:00 2001 From: Philipp Baumann Date: Sat, 14 Sep 2024 13:22:33 +0200 Subject: [PATCH 18/62] update linter config, fix lints --- .lintr | 3 ++- R/rix.R | 39 ++++++++++++++++++++++++++++----------- R/with_nix_helpers.R | 13 ++++++++----- 3 files changed, 38 insertions(+), 17 deletions(-) diff --git a/.lintr b/.lintr index a8ac14e9..286405bd 100644 --- a/.lintr +++ b/.lintr @@ -1,6 +1,7 @@ linters: linters_with_defaults( line_length_linter(100), commented_code_linter = NULL, - object_usage_linter=NULL + object_usage_linter = NULL, + cyclocomp_linter = NULL ) encoding: "UTF-8" diff --git a/R/rix.R b/R/rix.R index 9ab8ba1b..02b943db 100644 --- a/R/rix.R +++ b/R/rix.R @@ -19,7 +19,7 @@ #' @param system_pkgs Vector of characters. List further software you wish to #' install that are not R packages such as command line applications for #' example. You can look for available software on the NixOS website -#' \url{https://search.nixos.org/packages?channel=unstable&from=0&size=50&sort=relevance&type=packages&query=} +#' \url{https://search.nixos.org/packages?channel=unstable&from=0&size=50&sort=relevance&type=packages&query=} # nolint #' @param git_pkgs List. A list of packages to install from Git. See details for #' more information. #' @param local_r_pkgs List. A list of local packages to install. These packages @@ -129,7 +129,8 @@ #' environment will be up-to-date on the date that the `default.nix` will be #' generated, and then each subsequent call to `nix-build` will result in the #' same environment. We highly recommend you read the vignette titled -#' "z - Advanced topic: Understanding the rPackages set release cycle and using bleeding edge packages". +#' "z - Advanced topic: Understanding the rPackages set release cycle and +#' using bleeding edge packages". #' @export #' @examples #' \dontrun{ @@ -165,7 +166,10 @@ rix <- function(r_ver = "latest", choices = c("quiet", "simple", "verbose") ) - if (!(message_type %in% c("simple", "quiet")) && r_ver %in% c("bleeding_edge", "frozen_edge")) { + if ( + !(message_type %in% c("simple", "quiet")) && + r_ver %in% c("bleeding_edge", "frozen_edge") + ) { warning( "You chose 'bleeding_edge' or 'frozen_edge' as the value for `r_ver`. Please read the vignette @@ -174,8 +178,10 @@ before continuing." ) } - if (message_type != "quiet" && r_ver %in% available_r() && - r_ver != "latest" && r_ver <= "4.1.1") { + if ( + message_type != "quiet" && r_ver %in% available_r() && + r_ver != "latest" && r_ver <= "4.1.1" + ) { warning( "You are generating an expression for an older version of R.\n", "To use this environment, you should directly use `nix-shell` and not ", @@ -195,8 +201,10 @@ before continuing." ide <- match.arg(ide, c("other", "code", "radian", "rstudio", "rserver")) - if (identical(ide, "rstudio") && is.null(r_pkgs) && is.null(git_pkgs) && - is.null(local_r_pkgs)) { + if ( + identical(ide, "rstudio") && is.null(r_pkgs) && is.null(git_pkgs) && + is.null(local_r_pkgs) + ) { stop( paste0( "You chose 'rstudio' as the IDE, but didn't add any R packages", @@ -213,7 +221,10 @@ before continuing." rserver = "rstudioServerWrapper" ) - if (message_type != "quiet" && Sys.info()["sysname"] == "Darwin" && ide == "rstudio") { + if ( + message_type != "quiet" && Sys.info()["sysname"] == "Darwin" && + ide == "rstudio" + ) { warning( "Your detected operating system is macOS, and you chose 'rstudio' as the IDE. Please note that 'rstudio' is not @@ -231,8 +242,10 @@ for more details." project_path <- normalizePath(path = project_path) } + # nolint start: object_name_linter default.nix_path <- file.path(project_path, "default.nix") .Rprofile_path <- file.path(project_path, ".Rprofile") + # nolint end # Find url to use # In case of bleeding or frozen edge, the rstats-on-nix/nixpkgs @@ -246,7 +259,9 @@ for more details." cran_pkgs <- get_rpkgs(r_pkgs, ide) # If there are R packages, passes the string "rpkgs" to buildInputs - flag_rpkgs <- if (is.null(cran_pkgs$rPackages) | cran_pkgs$rPackages == "") { + flag_rpkgs <- if ( + is.null(cran_pkgs$rPackages) || cran_pkgs$rPackages == "" + ) { "" } else { "rpkgs" @@ -260,7 +275,9 @@ for more details." } # If there are R packages from Git, passes the string "git_archive_pkgs" to buildInputs - flag_git_archive <- if (!is.null(cran_pkgs$archive_pkgs) | !is.null(git_pkgs)) { + flag_git_archive <- if ( + !is.null(cran_pkgs$archive_pkgs) || !is.null(git_pkgs) + ) { "git_archive_pkgs" } else { "" @@ -284,7 +301,7 @@ for more details." "" } - # Generate default.nix file + # Generate default.nix file # nolint next: object_name_linter default.nix <- paste( generate_header( nix_repo, diff --git a/R/with_nix_helpers.R b/R/with_nix_helpers.R index 4f8051fd..cc522994 100644 --- a/R/with_nix_helpers.R +++ b/R/with_nix_helpers.R @@ -47,7 +47,7 @@ serialize_lobjs <- function(lobjs, temp_dir) { # for unnamed arguments like `expr = function(x) print(x)` # x would be an empty symbol, see also ; i.e. arguments without # default expressions; i.e. tagged arguments with no value - # https://stackoverflow.com/questions/3892580/create-missing-objects-aka-empty-symbols-empty-objects-needed-for-f + # https://stackoverflow.com/questions/3892580/create-missing-objects-aka-empty-symbols-empty-objects-needed-for-f # nolint lobjs[[i]] <- as.symbol(names(lobjs)[i]) } saveRDS( @@ -280,8 +280,11 @@ classify_globals <- function(globals_expr, args_vec) { if (length(globs_empty) == 0L) { globs_empty <- NULL } - globs_other <- vec_envs_check[!names(vec_envs_check) %in% - names(c(globs_pkg, globs_globalenv, globs_empty, globs_base))] + globs_other <- vec_envs_check[ + !names(vec_envs_check) %in% names( + c(globs_pkg, globs_globalenv, globs_empty, globs_base) + ) + ] if (length(globs_other) == 0L) { globs_other <- NULL } @@ -612,8 +615,8 @@ with_assign_vec_call <- function(vec) { #' @return representation of `expr` as character vector of length 1 #' @author R Core Team #' @noRd -deparse_chr1 <- function(expr, width.cutoff = 500L, collapse = " ", ...) { - paste(deparse(expr, width.cutoff, ...), collapse = collapse) +deparse_chr1 <- function(expr, width_cutoff = 500L, collapse = " ", ...) { + paste(deparse(expr, width_cutoff, ...), collapse = collapse) } From 07755af7099554536c1428b6f05a2395cfe55e5a Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sat, 14 Sep 2024 11:23:06 +0000 Subject: [PATCH 19/62] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- R/rix.R | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/R/rix.R b/R/rix.R index 02b943db..81f09555 100644 --- a/R/rix.R +++ b/R/rix.R @@ -129,7 +129,7 @@ #' environment will be up-to-date on the date that the `default.nix` will be #' generated, and then each subsequent call to `nix-build` will result in the #' same environment. We highly recommend you read the vignette titled -#' "z - Advanced topic: Understanding the rPackages set release cycle and +#' "z - Advanced topic: Understanding the rPackages set release cycle and #' using bleeding edge packages". #' @export #' @examples @@ -168,7 +168,7 @@ rix <- function(r_ver = "latest", if ( !(message_type %in% c("simple", "quiet")) && - r_ver %in% c("bleeding_edge", "frozen_edge") + r_ver %in% c("bleeding_edge", "frozen_edge") ) { warning( "You chose 'bleeding_edge' or 'frozen_edge' as the value for From 1699a4bf0c09f23529bd25a928a337f7a751fe5b Mon Sep 17 00:00:00 2001 From: Philipp Baumann Date: Sat, 14 Sep 2024 13:41:28 +0200 Subject: [PATCH 20/62] fix lints --- .../{format-code.yaml => lints-and-code-formatter.yaml} | 2 +- R/detect_os.R | 5 ++++- R/nix_build.R | 9 ++++++--- R/rix.R | 1 + R/with_nix_helpers.R | 1 + 5 files changed, 13 insertions(+), 5 deletions(-) rename .github/workflows/{format-code.yaml => lints-and-code-formatter.yaml} (90%) diff --git a/.github/workflows/format-code.yaml b/.github/workflows/lints-and-code-formatter.yaml similarity index 90% rename from .github/workflows/format-code.yaml rename to .github/workflows/lints-and-code-formatter.yaml index e6d769d9..2e5ead16 100644 --- a/.github/workflows/format-code.yaml +++ b/.github/workflows/lints-and-code-formatter.yaml @@ -20,6 +20,6 @@ jobs: name: pre-commit-ci-lite if: always() with: - msg: apply code formatting + msg: check lints and apply code formatting env: GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} diff --git a/R/detect_os.R b/R/detect_os.R index ca98e349..d6adcb32 100644 --- a/R/detect_os.R +++ b/R/detect_os.R @@ -21,7 +21,10 @@ detect_os <- function() { #' @noRd generate_locale_archive <- function(os) { if (os == "Linux" || os == "Darwin") { - 'LOCALE_ARCHIVE = if pkgs.system == \"x86_64-linux\" then \"${pkgs.glibcLocales}/lib/locale/locale-archive\" else \"\";' + paste0( + 'LOCALE_ARCHIVE = if pkgs.system == \"x86_64-linux\" then ', + '\"${pkgs.glibcLocales}/lib/locale/locale-archive\" else \"\";' + ) } else { stop("Operating System unsupported") } diff --git a/R/nix_build.R b/R/nix_build.R index 51ae9347..3c24c9e2 100644 --- a/R/nix_build.R +++ b/R/nix_build.R @@ -28,18 +28,19 @@ nix_build <- function(project_path = ".", choices = c("simple", "quiet", "verbose") ) # if nix store is not PATH variable; e.g. on macOS (system's) RStudio - PATH <- set_nix_path() + PATH <- set_nix_path() # nolint: object_name_linter if (isTRUE(nzchar(Sys.getenv("NIX_STORE")))) { # for Nix R sessions, guarantee that the system's user library # (R_LIBS_USER) is not in the search path for packages => run-time purity current_libpaths <- .libPaths() # don't do this in covr test environment, because this sets R_LIBS_USER # to multiple paths - R_LIBS_USER <- Sys.getenv("R_LIBS_USER") + R_LIBS_USER <- Sys.getenv("R_LIBS_USER") # nolint: object_name_linter if (isFALSE(nzchar(Sys.getenv("R_COVR")))) { remove_r_libs_user() } } else { + # nolint next: object_name_linter LD_LIBRARY_PATH_default <- Sys.getenv("LD_LIBRARY_PATH") if (nzchar(LD_LIBRARY_PATH_default)) { # On some systems, like Ubuntu 22.04, we found that a preset @@ -56,7 +57,7 @@ nix_build <- function(project_path = ".", fix_ld_library_path() cat( "* Current LD_LIBRARY_PATH in system R session is:", - LD_LIBRARY_PATH_default + LD_LIBRARY_PATH_default # nolint: object_name_linter ) cat("\n", "Setting `LD_LIBRARY_PATH` to `''` during `nix_build()`") } @@ -65,6 +66,7 @@ nix_build <- function(project_path = ".", nix_dir <- normalizePath(project_path) nix_file <- file.path(nix_dir, "default.nix") + # nolint start: line_length_linter stopifnot( "`project_path` must be character of length 1." = is.character(project_path) && length(project_path) == 1L, @@ -73,6 +75,7 @@ nix_build <- function(project_path = ".", "`nix-build` not available. To install, we suggest you follow https://zero-to-nix.com/start/install ." = isTRUE(has_nix_build) ) + # nolint end max_jobs <- getOption("rix.nix_build_max_jobs", default = 1L) stopifnot( diff --git a/R/rix.R b/R/rix.R index 02b943db..2b80ea16 100644 --- a/R/rix.R +++ b/R/rix.R @@ -321,6 +321,7 @@ for more details." collapse = "\n" ) + # nolint next: object_name_linter default.nix <- readLines(textConnection(default.nix)) if (print) { diff --git a/R/with_nix_helpers.R b/R/with_nix_helpers.R index cc522994..37c4e18f 100644 --- a/R/with_nix_helpers.R +++ b/R/with_nix_helpers.R @@ -73,6 +73,7 @@ serialize_args <- function(args, temp_dir) { # for unnamed arguments like `expr = function(x) print(x)` # x would be an empty symbol, see also ; i.e. arguments without # default expressions; i.e., tagged arguments with no value + # nolint next: line_length_linter # https://stackoverflow.com/questions/3892580/create-missing-objects-aka-empty-symbols-empty-objects-needed-for-f args[[i]] <- as.symbol(names(args)[i]) } From b55a0972e93f7c02805ed64fa66016bb64dd52b8 Mon Sep 17 00:00:00 2001 From: Philipp Baumann Date: Sat, 14 Sep 2024 13:54:12 +0200 Subject: [PATCH 21/62] fix more lints --- R/make_nixpkgs_url.R | 4 +++- R/nix_hash.R | 9 +++++++-- R/with_nix.R | 6 +++++- 3 files changed, 15 insertions(+), 4 deletions(-) diff --git a/R/make_nixpkgs_url.R b/R/make_nixpkgs_url.R index a41dd6ee..9adeffcc 100644 --- a/R/make_nixpkgs_url.R +++ b/R/make_nixpkgs_url.R @@ -15,7 +15,9 @@ make_nixpkgs_url <- function(r_ver) { latest_commit <- get_latest(r_ver) list( - "url" = paste0("https://github.com/", github_repo, "archive/", latest_commit, ".tar.gz"), + "url" = paste0( + "https://github.com/", github_repo, "archive/", latest_commit, ".tar.gz" + ), "latest_commit" = latest_commit, "r_ver" = r_ver ) diff --git a/R/nix_hash.R b/R/nix_hash.R index 2a1c33d1..68e7c0bc 100644 --- a/R/nix_hash.R +++ b/R/nix_hash.R @@ -104,16 +104,21 @@ nix_sri_hash <- function(path) { # not needed for Nix R sessions, workaround on Debian and Debian-based # systems with nix installed + # nolint start: object_name_linter LD_LIBRARY_PATH_default <- Sys.getenv("LD_LIBRARY_PATH") needs_ld_fix <- isFALSE(nzchar(Sys.getenv("NIX_STORE"))) && nzchar(LD_LIBRARY_PATH_default) + # nolint end if (isTRUE(needs_ld_fix)) { # On Debian and Debian-based systems, like Ubuntu 22.04, we found that a # preset `LD_LIBRARY_PATH` environment variable in the system's R session # leads to errors like - # nix-hash: /usr/lib/x86_64-linux-gnu/libc.so.6: version `GLIBC_2.38' not found (required by nix-hash) - # nix-hash: /usr/lib/x86_64-linux-gnu/libc.so.6: version `GLIBC_2.38' not found (required by /nix/store/4z754a0vzl98asv0pa95i5d9szw5jqbs-lowdown-1.0.2-lib/lib/liblowdown.so.3) + # nix-hash: /usr/lib/x86_64-linux-gnu/libc.so.6: version `GLIBC_2.38' + # not found (required by nix-hash) + # nix-hash: /usr/lib/x86_64-linux-gnu/libc.so.6: version `GLIBC_2.38' + # not found (required by # nolint next: line_length_linter + # /nix/store/4z754a0vzl98asv0pa95i5d9szw5jqbs-lowdown-1.0.2-lib/lib/liblowdown.so.3) # etc... # for both `nix-hash`; it occurs via # `sys::exec_internal`, `base::system()` or `base::system2()` from R. diff --git a/R/with_nix.R b/R/with_nix.R index c6a93cd2..7b8ead27 100644 --- a/R/with_nix.R +++ b/R/with_nix.R @@ -134,6 +134,7 @@ with_nix <- function(expr, project_path = ".", message_type = c("simple", "quiet", "verbose")) { nix_file <- file.path(project_path, "default.nix") + # nolint start: line_length_linter stopifnot( "`project_path` must be character of length 1." = is.character(project_path) && length(project_path) == 1L, @@ -143,6 +144,7 @@ with_nix <- function(expr, "`expr` needs to be a call or function for `program = R`, and character of length 1 for `program = shell`" = is.function(expr) || is.call(expr) || (is.character(expr) && length(expr) == 1L) ) + # nolint end program <- match.arg(program, choices = c("R", "shell")) message_type <- match.arg(message_type, @@ -170,11 +172,12 @@ with_nix <- function(expr, current_libpaths <- .libPaths() # don't do this in covr test environment, because this sets R_LIBS_USER # to multiple paths - R_LIBS_USER <- Sys.getenv("R_LIBS_USER") + R_LIBS_USER <- Sys.getenv("R_LIBS_USER") # nolint: object_name_linter if (isFALSE(nzchar(Sys.getenv("R_COVR")))) { remove_r_libs_user() } } else { + # lolint start: object_name_linter LD_LIBRARY_PATH_default <- Sys.getenv("LD_LIBRARY_PATH") if (nzchar(LD_LIBRARY_PATH_default)) { # On some systems, like Ubuntu 22.04, we found that a preset @@ -195,6 +198,7 @@ with_nix <- function(expr, ) cat("\n", "Setting `LD_LIBRARY_PATH` to `''` during `nix_build()`") } + # nolint end } has_nix_shell <- nix_shell_available() # TRUE if yes, FALSE if no From ee536cca237901e4f05965159710d4dbee4f9d02 Mon Sep 17 00:00:00 2001 From: Philipp Baumann Date: Sat, 14 Sep 2024 13:58:33 +0200 Subject: [PATCH 22/62] fix lint cmd --- R/with_nix.R | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/R/with_nix.R b/R/with_nix.R index 7b8ead27..6f3f72f5 100644 --- a/R/with_nix.R +++ b/R/with_nix.R @@ -177,7 +177,7 @@ with_nix <- function(expr, remove_r_libs_user() } } else { - # lolint start: object_name_linter + # nolint start: object_name_linter LD_LIBRARY_PATH_default <- Sys.getenv("LD_LIBRARY_PATH") if (nzchar(LD_LIBRARY_PATH_default)) { # On some systems, like Ubuntu 22.04, we found that a preset From 24d802fe99123256ec78319dcf1bbc661c7b0cad Mon Sep 17 00:00:00 2001 From: Philipp Baumann Date: Sat, 14 Sep 2024 14:02:43 +0200 Subject: [PATCH 23/62] lint --- data-raw/DATASET.R | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/data-raw/DATASET.R b/data-raw/DATASET.R index 0a3589ea..78f79c0e 100644 --- a/data-raw/DATASET.R +++ b/data-raw/DATASET.R @@ -1,6 +1,5 @@ ## code to prepare `DATASET` dataset goes here - library(rix) # This script is only needed for the developers of `{rix}`. @@ -16,8 +15,11 @@ library(rix) # library(rvest) # library(dplyr) # library(janitor) -# -# r_nix_revs <- read_html("https://lazamar.co.uk/nix-versions/?channel=nixpkgs-unstable&package=r") |> + +# r_nix_revs <- +# read_html( +# "https://lazamar.co.uk/nix-versions/?channel=nixpkgs-unstable&package=r" +# ) |> # html_element("table") |> # html_table() |> # clean_names() |> From 1d7ccb97bfe7cda6fd65d9b1b7bc3dfe687885e3 Mon Sep 17 00:00:00 2001 From: Philipp Baumann Date: Sat, 14 Sep 2024 14:24:17 +0200 Subject: [PATCH 24/62] again fix remaing lint errors --- R/make_nixpkgs_url.R | 3 ++- R/rix_helpers.R | 50 ++++++++++++++++++++++++++++------------ R/rix_init.R | 54 ++++++++++++++++++++++++++++++++------------ 3 files changed, 76 insertions(+), 31 deletions(-) diff --git a/R/make_nixpkgs_url.R b/R/make_nixpkgs_url.R index 9adeffcc..9bba1887 100644 --- a/R/make_nixpkgs_url.R +++ b/R/make_nixpkgs_url.R @@ -1,5 +1,6 @@ #' make_nixpkgs_url Find the right Nix revision -#' @param r_version Character. R version to look for, for example, "4.2.0". If a nixpkgs revision is provided instead, this gets returned. +#' @param r_version Character. R version to look for, for example, "4.2.0". If a +#' nixpkgs revision is provided instead, this gets returned. #' @return A character. The url to use #' #' @examples diff --git a/R/rix_helpers.R b/R/rix_helpers.R index fc79a3a3..52c5b39e 100644 --- a/R/rix_helpers.R +++ b/R/rix_helpers.R @@ -1,6 +1,9 @@ -#' generate_header Internal function used to generate the header of the `default.nix` file. -#' @param nix_repo Character. nixpkgs reop to use (upstream or rstats-on-nix fork) with latest commit hash. -#' @param r_version Character. R version to look for, for example, "4.2.0". If a nixpkgs revision is provided instead, this gets returned. +#' generate_header Internal function used to generate the header of the +#' `default.nix` file. +#' @param nix_repo Character. nixpkgs reop to use (upstream or rstats-on-nix +#' fork) with latest commit hash. +#' @param r_version Character. R version to look for, for example, "4.2.0". If a +#' nixpkgs revision is provided instead, this gets returned. #' @param rix_call Character, call to rix(). #' @noRd generate_header <- function(nix_repo, @@ -70,9 +73,11 @@ let } } -#' generate_rix_call Internal function used to generate the call to `rix()` as shown in `default.nix` +#' generate_rix_call Internal function used to generate the call to `rix()` as +#' shown in `default.nix` #' @param rix_call Character, call to rix(). -#' @param nix_repo Character. nixpkgs reop to use (upstream or rstats-on-nix fork) with latest commit hash. +#' @param nix_repo Character. nixpkgs reop to use (upstream or rstats-on-nix +#' fork) with latest commit hash. #' @noRd generate_rix_call <- function(rix_call, nix_repo) { if (grepl("NixOS", nix_repo$url)) { @@ -108,9 +113,11 @@ get_rpkgs <- function(r_pkgs, ide) { r_pkgs } + # nolint start: object_name_linter rPackages <- paste(c("", r_pkgs), collapse = "\n ") rPackages <- gsub("\\.", "_", rPackages) + # nolint end list( "rPackages" = rPackages, @@ -118,10 +125,12 @@ get_rpkgs <- function(r_pkgs, ide) { ) } -#' generate_rpkgs Internal function that generates the string containing the correct Nix expression to get R packages. +#' generate_rpkgs Internal function that generates the string containing the +#' correct Nix expression to get R packages. #' @param rPackages Character, list of R packages to install. #' @param flag_rpkgs Character, are there any R packages at all? #' @noRd +#' # nolint start: object_name_linter generate_rpkgs <- function(rPackages, flag_rpkgs) { if (flag_rpkgs == "") { NULL @@ -136,8 +145,10 @@ generate_rpkgs <- function(rPackages, flag_rpkgs) { ) } } +# nolint end: object_name_linter -#' generate_local_r_pkgs Internal function that generates the string containing the correct Nix expression for installing local packages +#' generate_local_r_pkgs Internal function that generates the string containing +#' the correct Nix expression for installing local packages #' @param local_r_pkgs Character, list of local R packages to install. #' @param flag_local_r_pkgs Character, are there any local R packages at all? #' @noRd @@ -156,7 +167,8 @@ generate_local_r_pkgs <- function(local_r_pkgs, flag_local_r_pkgs) { } } -#' generate_tex_pkgs Internal function that generates the string containing the correct Nix expression to get LaTeX packages. +#' generate_tex_pkgs Internal function that generates the string containing the +#' correct Nix expression to get LaTeX packages. #' @param tex_pkgs Character, list of LaTeX packages to install. #' @noRd generate_tex_pkgs <- function(tex_pkgs) { @@ -176,7 +188,8 @@ generate_tex_pkgs <- function(tex_pkgs) { } } -#' generate_system_pkgs Internal function that formats the system package names correctly for Nix. +#' generate_system_pkgs Internal function that formats the system package names +#' correctly for Nix. #' @param system_pkgs Character, list of LaTeX packages to install. #' @param r_pkgs Character, list of LaTeX packages to install. #' @noRd @@ -184,7 +197,8 @@ get_system_pkgs <- function(system_pkgs, r_pkgs) { # We always need these packages system_pkgs <- c(system_pkgs, "R", "glibcLocales", "nix") - # If the user wants the R {quarto} package, then the quarto software needs to be installed + # If the user wants the R {quarto} package, then the quarto software needs to + # be installed system_pkgs <- if (any(grepl("quarto", r_pkgs))) { unique(c(system_pkgs, "quarto")) } else { @@ -194,7 +208,8 @@ get_system_pkgs <- function(system_pkgs, r_pkgs) { } -#' generate_system_pkgs Internal function that generates the string containing the correct Nix expression to get system packages. +#' generate_system_pkgs Internal function that generates the string containing +#' the correct Nix expression to get system packages. #' @param system_pkgs Character, list of LaTeX packages to install. #' @param r_pkgs Character, list of LaTeX packages to install. #' @noRd @@ -210,12 +225,16 @@ generate_system_pkgs <- function(system_pkgs, r_pkgs) { } -#' generate_git_archived_pkgs Internal function that generates the string containing the correct Nix expression to get system packages. +#' generate_git_archived_pkgs Internal function that generates the string +#' containing the correct Nix expression to get system packages. #' @param git_pkgs Character, list of R packages to install from Github. -#' @param archive_pkgs Character, list of R packages to install from the CRAN archives. +#' @param archive_pkgs Character, list of R packages to install from the CRAN +#' archives. #' @param flag_git_archive Character, are there R packages from Github at all? #' @noRd -generate_git_archived_pkgs <- function(git_pkgs, archive_pkgs, flag_git_archive) { +generate_git_archived_pkgs <- function(git_pkgs, + archive_pkgs, + flag_git_archive) { if (flag_git_archive == "") { NULL } else { @@ -225,7 +244,8 @@ generate_git_archived_pkgs <- function(git_pkgs, archive_pkgs, flag_git_archive) } -#' generate_locale_variables Internal function that generates the string containing the correct Nix expression to set locales. +#' generate_locale_variables Internal function that generates the string +#' containing the correct Nix expression to set locales. #' @noRd generate_locale_variables <- function() { locale_defaults <- list( diff --git a/R/rix_init.R b/R/rix_init.R index 97bee94a..9d3af2a5 100644 --- a/R/rix_init.R +++ b/R/rix_init.R @@ -1,13 +1,13 @@ #' Initiate and maintain an isolated, project-specific, and runtime-pure R #' setup via Nix. #' -#' Creates an isolated project folder for a Nix-R configuration. `rix::rix_init()` -#' also adds, appends, or updates with or without backup a custom `.Rprofile` -#' file with code that initializes a startup R environment without system's user -#' libraries within a Nix software environment. Instead, it restricts search -#' paths to load R packages exclusively from the Nix store. Additionally, it -#' makes Nix utilities like `nix-shell` available to run system commands from -#' the system's RStudio R session, for both Linux and macOS. +#' Creates an isolated project folder for a Nix-R configuration. +#' `rix::rix_init()` also adds, appends, or updates with or without backup a +#' custom `.Rprofile` file with code that initializes a startup R environment +#' without system's user libraries within a Nix software environment. Instead, +#' it restricts search paths to load R packages exclusively from the Nix store. +#' Additionally, it makes Nix utilities like `nix-shell` available to run system +#' commands from the system's RStudio R session, for both Linux and macOS. #' #' **Enhancement of computational reproducibility for Nix-R environments:** #' @@ -75,12 +75,14 @@ #' does exist; `"append"` appends the existing file with code that is tailored #' to an isolated Nix-R project setup. #' @param message_type Character. Message type, defaults to `"simple"`, which -#' gives minimal but sufficient feedback. Other values are currently -#' `"quiet`, which writes `.Rprofile` without message, and `"verbose"`, -#' which displays the mechanisms implemented to achieve fully controlled R project environments in Nix. +#' gives minimal but sufficient feedback. Other values are currently `"quiet`, +#' which writes `.Rprofile` without message, and `"verbose"`, which displays +#' the mechanisms implemented to achieve fully controlled R project +#' environments in Nix. #' @export #' @seealso [with_nix()] -#' @return Nothing, this function only has the side-effect of writing a file called ".Rprofile" to the specified path. +#' @return Nothing, this function only has the side-effect of writing a file +#' called ".Rprofile" to the specified path. #' @examples #' \dontrun{ #' # create an isolated, runtime-pure R setup via Nix @@ -272,6 +274,7 @@ message_rprofile <- function(action_string = "Added", #' @return Character vector that lists `PATH` entries after modification, which #' are separated by `":"`. #' @noRd +# nolint start: object_name_linter set_message_session_PATH <- function(message_type = c("simple", "quiet", "verbose")) { message_type <- match.arg(message_type, @@ -293,6 +296,7 @@ set_message_session_PATH <- function(message_type = cat("\n\n* Updated `PATH` variable is:\n\n", PATH) } } +# nolint end: object_name_linter #' Report whether the current R session is running in Nix and RStudio, or not. @@ -392,6 +396,7 @@ set_nix_path <- function() { #' @return language object with parsed expression #' @noRd nix_rprofile <- function() { + # nolint start: object_name_linter quote({ is_rstudio <- Sys.getenv("RSTUDIO") == "1" is_nix_r <- nzchar(Sys.getenv("NIX_STORE")) @@ -417,19 +422,37 @@ nix_rprofile <- function() { if (isTRUE(is_nix_r)) { install.packages <- function(...) { - stop("You are currently in an R session running from Nix.\nDon't install packages using install.packages(),\nadd them to the default.nix file instead.") + stop( + "You are currently in an R session running from Nix.\n", + "Don't install packages using install.packages(),\nadd them to ", + "the default.nix file instead." + ) } update.packages <- function(...) { - stop("You are currently in an R session running from Nix.\nDon't update packages using update.packages(),\ngenerate a new default.nix with a more recent version of R. If you need bleeding edge packages, read the 'Understanding the rPackages set release cycle and using bleeding edge packages' vignette.") + stop( + "You are currently in an R session running from Nix.\n", + "Don't update packages using update.packages(),\n", + "generate a new default.nix with a more recent version of R. ", + "If you need bleeding edge packages, read the", + "'Understanding the rPackages set release cycle and using ", + "bleeding edge packages' vignette." + ) } remove.packages <- function(...) { - stop("You are currently in an R session running from Nix.\nDon't remove packages using remove.packages(),\ndelete them from the default.nix file instead.") + stop( + "You are currently in an R session running from Nix.\n", + "Don't remove packages using `remove.packages()``,\ndelete them ", + "from the default.nix file instead.") } current_paths <- .libPaths() userlib_paths <- Sys.getenv("R_LIBS_USER") - user_dir <- grep(paste(userlib_paths, collapse = "|"), current_paths, fixed = TRUE) + user_dir <- grep( + paste(userlib_paths, collapse = "|"), + current_paths, + fixed = TRUE + ) new_paths <- current_paths[-user_dir] # sets new library path without user library, making nix-R pure at # run-time @@ -439,4 +462,5 @@ nix_rprofile <- function() { rm(is_rstudio, is_nix_r) }) + # nolint end: object_name } From a16202d34ee8971041cf653dcb3f323df6e4a0b6 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sat, 14 Sep 2024 12:25:01 +0000 Subject: [PATCH 25/62] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- R/rix_init.R | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/R/rix_init.R b/R/rix_init.R index 9d3af2a5..aff4d49a 100644 --- a/R/rix_init.R +++ b/R/rix_init.R @@ -434,7 +434,7 @@ nix_rprofile <- function() { "You are currently in an R session running from Nix.\n", "Don't update packages using update.packages(),\n", "generate a new default.nix with a more recent version of R. ", - "If you need bleeding edge packages, read the", + "If you need bleeding edge packages, read the", "'Understanding the rPackages set release cycle and using ", "bleeding edge packages' vignette." ) @@ -444,7 +444,8 @@ nix_rprofile <- function() { stop( "You are currently in an R session running from Nix.\n", "Don't remove packages using `remove.packages()``,\ndelete them ", - "from the default.nix file instead.") + "from the default.nix file instead." + ) } current_paths <- .libPaths() userlib_paths <- Sys.getenv("R_LIBS_USER") From f3bcb1d0d1c148df391ec5e439228b178288e130 Mon Sep 17 00:00:00 2001 From: Philipp Baumann Date: Sat, 14 Sep 2024 14:30:06 +0200 Subject: [PATCH 26/62] fix lints --- R/rix_helpers.R | 6 ++++-- .../c-using-rix-to-build-project-specific-environments.Rmd | 4 +--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/R/rix_helpers.R b/R/rix_helpers.R index 52c5b39e..f402f730 100644 --- a/R/rix_helpers.R +++ b/R/rix_helpers.R @@ -283,7 +283,8 @@ generate_locale_variables <- function() { } -#' generate_wrapped_pkgs Internal function that generates the string containing the correct Nix expression to get wrapped packages. +#' generate_wrapped_pkgs Internal function that generates the string containing +#' the correct Nix expression to get wrapped packages. #' @param ide Character, defaults to "other". If you wish to use RStudio to work #' interactively use "rstudio" or "rserver" for the server version. Use "code" #' for Visual Studio Code. You can also use "radian", an interactive REPL. For @@ -319,7 +320,8 @@ generate_wrapped_pkgs <- function(ide, } -#' generate_wrapped_pkgs Internal function that generates the string containing the correct Nix expression to get wrapped packages. +#' generate_wrapped_pkgs Internal function that generates the string containing +#' the correct Nix expression to get wrapped packages. #' @param flag_git_archive Character, are there R packages from Github at all? #' @param flag_rpkgs Character, are there any R packages at all? #' @param flag_tex_pkgs Character, are there any LaTex packages at all? diff --git a/vignettes/c-using-rix-to-build-project-specific-environments.Rmd b/vignettes/c-using-rix-to-build-project-specific-environments.Rmd index c39b1b28..066707b3 100644 --- a/vignettes/c-using-rix-to-build-project-specific-environments.Rmd +++ b/vignettes/c-using-rix-to-build-project-specific-environments.Rmd @@ -18,8 +18,6 @@ knitr::opts_chunk$set( library(rix) ``` - - ## Project-specific Nix environments Now that you have the required software installed, it’s to time learn more about @@ -50,7 +48,7 @@ file. You need to provide the following inputs to `rix()`: Run the following command to generate the right `default.nix` file: -```{r, eval = F} +```{r, eval = FALSE} path_default_nix <- tempdir() rix( From e83d9cc75134ca942aee237c37093f100136a1ef Mon Sep 17 00:00:00 2001 From: Philipp Baumann Date: Sat, 14 Sep 2024 14:32:46 +0200 Subject: [PATCH 27/62] exclude from lintr --- .pre-commit-config.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index b626406c..9180fda5 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -11,6 +11,7 @@ repos: exclude: > (?x)^( tests/testthat/test-fetchers.R + vignettes/c-using-rix-to-build-project-specific-environments.Rmd )$ - id: parsable-R exclude: > From e1a05c7e10b72e28b15f979a0fda090601edc4e3 Mon Sep 17 00:00:00 2001 From: Philipp Baumann Date: Sat, 14 Sep 2024 14:34:45 +0200 Subject: [PATCH 28/62] fix rhub nix runner to use current branch --- .github/workflows/run_rhub.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/run_rhub.yaml b/.github/workflows/run_rhub.yaml index bbcf973f..997bcdb7 100644 --- a/.github/workflows/run_rhub.yaml +++ b/.github/workflows/run_rhub.yaml @@ -30,4 +30,4 @@ jobs: authToken: '${{ secrets.CACHIX_AUTH }}' - name: Run checks - run: nix-shell --run "Rscript -e \"rhub::rhub_check(platforms = c('linux','macos','macos-arm64','windows','ubuntu-next','ubuntu-release'), branch = 'main')\"" + run: nix-shell --run "Rscript -e \"rhub::rhub_check(platforms = c('linux','macos','macos-arm64','windows','ubuntu-next','ubuntu-release'))\"" From d3eb26fbb1f17d4d204317accc98c7f1a57ddd21 Mon Sep 17 00:00:00 2001 From: Philipp Baumann Date: Sat, 14 Sep 2024 14:43:19 +0200 Subject: [PATCH 29/62] exclude vignettes --- .pre-commit-config.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 9180fda5..3ee27346 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -10,8 +10,8 @@ repos: - id: lintr exclude: > (?x)^( - tests/testthat/test-fetchers.R - vignettes/c-using-rix-to-build-project-specific-environments.Rmd + tests/testthat/test-fetchers.\.R| + vignettes/.* )$ - id: parsable-R exclude: > From 5d937269444358d295dbae520b50a598559e9e6c Mon Sep 17 00:00:00 2001 From: Philipp Baumann Date: Sat, 14 Sep 2024 14:50:18 +0200 Subject: [PATCH 30/62] more fixes --- .pre-commit-config.yaml | 2 +- R/get_latest.R | 18 ++++++++++++++---- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 3ee27346..24f3e1a5 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -10,7 +10,7 @@ repos: - id: lintr exclude: > (?x)^( - tests/testthat/test-fetchers.\.R| + tests/testthat/test-fetchers\.R| vignettes/.* )$ - id: parsable-R diff --git a/R/get_latest.R b/R/get_latest.R index e396978d..2edb2c49 100644 --- a/R/get_latest.R +++ b/R/get_latest.R @@ -1,5 +1,6 @@ #' Get the latest R version and packages -#' @param r_version Character. R version to look for, for example, "4.2.0". If a nixpkgs revision is provided instead, this gets returned. +#' @param r_version Character. R version to look for, for example, "4.2.0". If a +#' nixpkgs revision is provided instead, this gets returned. #' @return A character. The commit hash of the latest nixpkgs-unstable revision #' @importFrom curl new_handle curl_fetch_memory handle_reset #' @importFrom jsonlite fromJSON @@ -18,7 +19,13 @@ get_latest <- function(r_version) { } else if ( !(r_version %in% c("bleeding_edge", "frozen_edge", available_r())) ) { - stop("The provided R version is likely wrong.\nPlease check that you provided a correct R version.\nYou can list available versions using `available_r()`.\nYou can also directly provide a commit, but you need to make sure it points to the right repo used by `rix()`.\nYou can also use 'bleeding_edge' and 'frozen_edge'.") + stop( + "The provided R version is likely wrong.\nPlease check that you ", + "provided a correct R version.\nYou can list available versions using ", + "`available_r()`.\nYou can also directly provide a commit, but you need ", + "to make sure it points to the right repo used by `rix()`.\nYou can ", + "also use 'bleeding_edge' and 'frozen_edge'." + ) } else if (!is_online) { stop("ERROR! You don't seem to be connected to the internet.") } else if (r_version == "bleeding_edge") { @@ -33,11 +40,14 @@ get_latest <- function(r_version) { #' @noRd get_right_commit <- function(r_version) { if (r_version == "frozen_edge") { + # nolint next: line_length_linter api_url <- "https://api.github.com/repos/rstats-on-nix/nixpkgs/commits?sha=r-daily" - } else if (r_version %in% Filter(function(x) `!=`(x, "latest"), available_r())) { # all but latest - + } else if ( + r_version %in% Filter(function(x) `!=`(x, "latest"), available_r()) + ) { # all but latest return(sysdata$revision[sysdata$version == r_version]) } else { + # nolint next: line_length_linter api_url <- "https://api.github.com/repos/NixOS/nixpkgs/commits?sha=nixpkgs-unstable" } From e2aedaabe96753a4f5fb04f171651de268e237e5 Mon Sep 17 00:00:00 2001 From: Philipp Baumann Date: Sat, 14 Sep 2024 15:23:08 +0200 Subject: [PATCH 31/62] try fix rhub gh url --- .github/workflows/run_rhub.yaml | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/.github/workflows/run_rhub.yaml b/.github/workflows/run_rhub.yaml index 997bcdb7..48d7606a 100644 --- a/.github/workflows/run_rhub.yaml +++ b/.github/workflows/run_rhub.yaml @@ -28,6 +28,9 @@ jobs: # signingKey: '${{ secrets.CACHIX_SIGNING_KEY }}' # If you chose API tokens for write access OR if you have a private cache authToken: '${{ secrets.CACHIX_AUTH }}' - + - name: Run checks - run: nix-shell --run "Rscript -e \"rhub::rhub_check(platforms = c('linux','macos','macos-arm64','windows','ubuntu-next','ubuntu-release'))\"" + run: | + git remote add origin https://github.com/ropensci/rix + nix-shell --run "Rscript -e \"rhub::rhub_check(platforms = c('linux','macos','macos-arm64','windows','ubuntu-next','ubuntu-release'))\"" + From 39a39f8a3adcf1bdf7a2192d64af8584a1e48771 Mon Sep 17 00:00:00 2001 From: Philipp Baumann Date: Sat, 14 Sep 2024 15:24:14 +0200 Subject: [PATCH 32/62] remote already there --- .github/workflows/run_rhub.yaml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/run_rhub.yaml b/.github/workflows/run_rhub.yaml index 48d7606a..532edb18 100644 --- a/.github/workflows/run_rhub.yaml +++ b/.github/workflows/run_rhub.yaml @@ -31,6 +31,5 @@ jobs: - name: Run checks run: | - git remote add origin https://github.com/ropensci/rix nix-shell --run "Rscript -e \"rhub::rhub_check(platforms = c('linux','macos','macos-arm64','windows','ubuntu-next','ubuntu-release'))\"" From c10c2bdfe009f9b106f7532713bd31733aaec9de Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 16 Sep 2024 13:33:16 +0000 Subject: [PATCH 33/62] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- R/rix_helpers.R | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/R/rix_helpers.R b/R/rix_helpers.R index 00f639e5..0d0409dc 100644 --- a/R/rix_helpers.R +++ b/R/rix_helpers.R @@ -114,7 +114,7 @@ get_rpkgs <- function(r_pkgs, ide) { } r_pkgs <- sort(r_pkgs) - + # nolint start: object_name_linter rPackages <- paste(c("", r_pkgs), collapse = "\n ") From b5fe46d722669d59c3859c4325351c6d7dc6df4c Mon Sep 17 00:00:00 2001 From: Philipp Baumann Date: Mon, 16 Sep 2024 19:35:06 +0200 Subject: [PATCH 34/62] add docopt for precommit nix R hooks --- .../workflows/lints-and-code-formatter.yaml | 10 +++++++ create_dev_env.R | 2 +- default.nix | 26 ++++++++++--------- 3 files changed, 25 insertions(+), 13 deletions(-) diff --git a/.github/workflows/lints-and-code-formatter.yaml b/.github/workflows/lints-and-code-formatter.yaml index 2e5ead16..963668f2 100644 --- a/.github/workflows/lints-and-code-formatter.yaml +++ b/.github/workflows/lints-and-code-formatter.yaml @@ -12,6 +12,16 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 + - name: Install Nix + uses: DeterminateSystems/nix-installer-action@main + + - uses: cachix/cachix-action@v15 + with: + name: rstats-on-nix + # If you chose signing key for write access + # signingKey: '${{ secrets.CACHIX_SIGNING_KEY }}' + # If you chose API tokens for write access OR if you have a private cache + authToken: '${{ secrets.CACHIX_AUTH }}' - uses: actions/setup-python@v4 with: python-version: 3.x diff --git a/create_dev_env.R b/create_dev_env.R index 07bae46c..c1c9bed9 100644 --- a/create_dev_env.R +++ b/create_dev_env.R @@ -5,7 +5,7 @@ rix( r_pkgs = c( "devtools", "diffviewer", "fledge", "lintr", "styler", "codetools", "jsonlite", "httr", "sys", "testthat", "knitr", - "rmarkdown", "rhub" + "rmarkdown", "rhub", "docopt" ), system_pkgs = c("R", "glibcLocalesUtf8", "pandoc", "nix"), tex_pkgs = "scheme-small", diff --git a/default.nix b/default.nix index af0a64a8..0ab6a43b 100644 --- a/default.nix +++ b/default.nix @@ -1,4 +1,4 @@ -# This file was generated by the {rix} R package v0.7.1 on 2024-06-28 +# This file was generated by the {rix} R package v0.11.0 on 2024-09-16 # with following call: # >rix(r_ver = "bleeding_edge", # > r_pkgs = c("devtools", @@ -13,7 +13,8 @@ # > "testthat", # > "knitr", # > "rmarkdown", -# > "precommit"), +# > "rhub", +# > "docopt"), # > system_pkgs = c("R", # > "glibcLocalesUtf8", # > "pandoc", @@ -34,19 +35,20 @@ let rpkgs = builtins.attrValues { inherit (pkgs.rPackages) + codetools devtools diffviewer + docopt fledge - lintr - styler - codetools - jsonlite httr - sys - testthat + jsonlite knitr + lintr + rhub rmarkdown - rhub; + styler + sys + testthat; }; tex = (pkgs.texlive.combine { @@ -56,11 +58,11 @@ let system_packages = builtins.attrValues { inherit (pkgs) - R + glibcLocales glibcLocalesUtf8 - pandoc nix - glibcLocales; + pandoc + R; }; in From a421aba19d000816b4a9287ee9e04f850d3a463b Mon Sep 17 00:00:00 2001 From: Philipp Baumann Date: Mon, 16 Sep 2024 19:35:47 +0200 Subject: [PATCH 35/62] use forked action --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 24f3e1a5..10a1ecd5 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,7 +1,7 @@ # All available hooks: https://pre-commit.com/hooks.html # R specific hooks: https://github.com/lorenzwalthert/precommit repos: -- repo: https://github.com/lorenzwalthert/precommit +- repo: https://github.com/philipp-baumann/precommit-r-nix rev: v0.4.3 hooks: - id: style-files From 9eefc15338bc93c2270ca8c6fa0bbf9b7423b1b4 Mon Sep 17 00:00:00 2001 From: Philipp Baumann Date: Mon, 16 Sep 2024 19:44:21 +0200 Subject: [PATCH 36/62] use fixed fork revision --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 10a1ecd5..80dbacd6 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -2,7 +2,7 @@ # R specific hooks: https://github.com/lorenzwalthert/precommit repos: - repo: https://github.com/philipp-baumann/precommit-r-nix - rev: v0.4.3 + rev: 9c54f8c09f17e305db1338cb8159aac9b58f86a3 hooks: - id: style-files args: [--style_pkg=styler, --style_fun=tidyverse_style, --cache-root=styler-perm] From ad1211a8169b4a8c5a679d9263187d8bd27de2d7 Mon Sep 17 00:00:00 2001 From: Philipp Baumann Date: Mon, 16 Sep 2024 19:48:51 +0200 Subject: [PATCH 37/62] test nix styler --- R/nix_build.R | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/R/nix_build.R b/R/nix_build.R index 3c24c9e2..8edcaf89 100644 --- a/R/nix_build.R +++ b/R/nix_build.R @@ -24,7 +24,7 @@ #' } nix_build <- function(project_path = ".", message_type = c("simple", "quiet", "verbose")) { - message_type <- match.arg(message_type, + message_type <- match.arg(message_type, choices = c("simple", "quiet", "verbose") ) # if nix store is not PATH variable; e.g. on macOS (system's) RStudio From 049e68ab8472d0cc86adc1392ef448854788fe07 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 16 Sep 2024 17:49:28 +0000 Subject: [PATCH 38/62] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- R/nix_build.R | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/R/nix_build.R b/R/nix_build.R index 8edcaf89..3c24c9e2 100644 --- a/R/nix_build.R +++ b/R/nix_build.R @@ -24,7 +24,7 @@ #' } nix_build <- function(project_path = ".", message_type = c("simple", "quiet", "verbose")) { - message_type <- match.arg(message_type, + message_type <- match.arg(message_type, choices = c("simple", "quiet", "verbose") ) # if nix store is not PATH variable; e.g. on macOS (system's) RStudio From b6020de18d35465af83c20ce4fc0ad4973e23734 Mon Sep 17 00:00:00 2001 From: Philipp Baumann Date: Mon, 16 Sep 2024 20:00:37 +0200 Subject: [PATCH 39/62] no renv --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 80dbacd6..7fccf9ef 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -2,7 +2,7 @@ # R specific hooks: https://github.com/lorenzwalthert/precommit repos: - repo: https://github.com/philipp-baumann/precommit-r-nix - rev: 9c54f8c09f17e305db1338cb8159aac9b58f86a3 + rev: 93c9802d28ad9c648a81c2b69dbffc90aab03fcb hooks: - id: style-files args: [--style_pkg=styler, --style_fun=tidyverse_style, --cache-root=styler-perm] From ceb15e6cc4b166bfeada73b93509f1204682d263 Mon Sep 17 00:00:00 2001 From: Philipp Baumann Date: Mon, 16 Sep 2024 20:12:44 +0200 Subject: [PATCH 40/62] add precommit to the dev env --- create_dev_env.R | 2 +- default.nix | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/create_dev_env.R b/create_dev_env.R index c1c9bed9..5ab7075e 100644 --- a/create_dev_env.R +++ b/create_dev_env.R @@ -5,7 +5,7 @@ rix( r_pkgs = c( "devtools", "diffviewer", "fledge", "lintr", "styler", "codetools", "jsonlite", "httr", "sys", "testthat", "knitr", - "rmarkdown", "rhub", "docopt" + "rmarkdown", "rhub", "docopt", "precommit" ), system_pkgs = c("R", "glibcLocalesUtf8", "pandoc", "nix"), tex_pkgs = "scheme-small", diff --git a/default.nix b/default.nix index 0ab6a43b..c91e11dc 100644 --- a/default.nix +++ b/default.nix @@ -14,7 +14,8 @@ # > "knitr", # > "rmarkdown", # > "rhub", -# > "docopt"), +# > "docopt", +# > "precommit"), # > system_pkgs = c("R", # > "glibcLocalesUtf8", # > "pandoc", @@ -44,6 +45,7 @@ let jsonlite knitr lintr + precommit rhub rmarkdown styler From 83f2e301984eb8086911a773db7155b16f91990e Mon Sep 17 00:00:00 2001 From: Philipp Baumann Date: Mon, 16 Sep 2024 20:19:32 +0200 Subject: [PATCH 41/62] update fork revision --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 7fccf9ef..ed67f202 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -2,7 +2,7 @@ # R specific hooks: https://github.com/lorenzwalthert/precommit repos: - repo: https://github.com/philipp-baumann/precommit-r-nix - rev: 93c9802d28ad9c648a81c2b69dbffc90aab03fcb + rev: 71f194a0aef52d395f200023741c932d5ea4bf45 hooks: - id: style-files args: [--style_pkg=styler, --style_fun=tidyverse_style, --cache-root=styler-perm] From 8c66d2f337b73bc795386c9d4e01c272866ed00d Mon Sep 17 00:00:00 2001 From: Philipp Baumann Date: Mon, 16 Sep 2024 20:27:48 +0200 Subject: [PATCH 42/62] lint with flint --- R/rix.R | 2 +- tests/testthat/test-with_nix.R | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/R/rix.R b/R/rix.R index f6c90dd3..16f929dc 100644 --- a/R/rix.R +++ b/R/rix.R @@ -336,7 +336,7 @@ for more details." writeLines(default.nix, default.nix_path) if (file.exists(.Rprofile_path)) { - if (all(!grepl( + if (!any(grepl( "File generated by `rix::rix_init()", readLines(.Rprofile_path) ))) { diff --git a/tests/testthat/test-with_nix.R b/tests/testthat/test-with_nix.R index 44f17cd0..520e61c6 100644 --- a/tests/testthat/test-with_nix.R +++ b/tests/testthat/test-with_nix.R @@ -27,7 +27,7 @@ testthat::test_that("Testing `with_nix()` if Nix is installed", { out_subshell <- with_nix( expr = function() { set.seed(1234) - a <- sample(seq(1, 10), 5) + a <- sample(seq_len(10), 5) set.seed(NULL) return(a) }, From 17a57414b5f94d9ba12c88d074efda2208650ebc Mon Sep 17 00:00:00 2001 From: Philipp Baumann Date: Mon, 16 Sep 2024 20:55:41 +0200 Subject: [PATCH 43/62] update precommit fork revision --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index ed67f202..5428cb27 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -2,7 +2,7 @@ # R specific hooks: https://github.com/lorenzwalthert/precommit repos: - repo: https://github.com/philipp-baumann/precommit-r-nix - rev: 71f194a0aef52d395f200023741c932d5ea4bf45 + rev: 1f1f92bd00e4fcdcba3d4311d2e1c0810500302d hooks: - id: style-files args: [--style_pkg=styler, --style_fun=tidyverse_style, --cache-root=styler-perm] From 016a51e09f9e668d7afd4408842bb2c25dc4d7a5 Mon Sep 17 00:00:00 2001 From: Philipp Baumann Date: Mon, 16 Sep 2024 21:10:11 +0200 Subject: [PATCH 44/62] do an extra precommit clean and use original R action --- .pre-commit-config.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 5428cb27..5322d988 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,8 +1,8 @@ # All available hooks: https://pre-commit.com/hooks.html # R specific hooks: https://github.com/lorenzwalthert/precommit repos: -- repo: https://github.com/philipp-baumann/precommit-r-nix - rev: 1f1f92bd00e4fcdcba3d4311d2e1c0810500302d +- repo: https://github.com/lorenzwalthert/precommit + rev: v0.4.3.9001 hooks: - id: style-files args: [--style_pkg=styler, --style_fun=tidyverse_style, --cache-root=styler-perm] From d4d8f2dde3e05bd692161508cb72daafbccf8b65 Mon Sep 17 00:00:00 2001 From: Philipp Baumann Date: Mon, 16 Sep 2024 21:19:52 +0200 Subject: [PATCH 45/62] clean cache --- .github/workflows/lints-and-code-formatter.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/lints-and-code-formatter.yaml b/.github/workflows/lints-and-code-formatter.yaml index 963668f2..83d6113c 100644 --- a/.github/workflows/lints-and-code-formatter.yaml +++ b/.github/workflows/lints-and-code-formatter.yaml @@ -26,6 +26,7 @@ jobs: with: python-version: 3.x - uses: pre-commit/action@v3.0.1 + - run: pre-commit clean - uses: pre-commit-ci/lite-action@v1.0.3 name: pre-commit-ci-lite if: always() From 2ffc9de35e1b724320f86c4e32288679ef57be4b Mon Sep 17 00:00:00 2001 From: Philipp Baumann Date: Mon, 16 Sep 2024 21:54:05 +0200 Subject: [PATCH 46/62] direct styling, no extra PR --- .github/workflows/styler.yaml | 32 +++++++------------------------- 1 file changed, 7 insertions(+), 25 deletions(-) diff --git a/.github/workflows/styler.yaml b/.github/workflows/styler.yaml index 5b46d29d..743c2b81 100644 --- a/.github/workflows/styler.yaml +++ b/.github/workflows/styler.yaml @@ -33,31 +33,13 @@ jobs: - name: Run styler::style_pkg run: nix-shell --run "Rscript -e 'styler::style_pkg()'" - - name: Check if PR exists - id: check_pr + - name: commit run: | - PR=$(gh pr list -S 'Style package' --json number --jq '.[0].number') - echo "PR_NUMBER=$PR" >> $GITHUB_ENV + git config --local user.name "$GITHUB_ACTOR" + git config --local user.email "$GITHUB_ACTOR@users.noreply.github.com" + git add \*.R + git commit -m 'Style' - - name: Configure user and check for changes - run: | - git config --global user.email "ga-ci@no-reply.com" - git config --global user.name "CI Robot" - git diff-index --quiet HEAD || echo "has_changes=true" >> $GITHUB_ENV - - - name: Commit and push changes - if: env.has_changes == 'true' - run: | - git add . - git commit -m "Styled package" - git push origin main:style_pkg --force - - - name: Create Pull Request - if: env.PR_NUMBER == '' - uses: peter-evans/create-pull-request@v6 + - uses: r-lib/actions/pr-push@v2 with: - branch: style_pkg - title: 'Style package' - body: 'Automated PR to style package using `styler:style_pkg()`' - base: main - branch-suffix: '' + repo-token: ${{ secrets.GITHUB_TOKEN }} From 363c25ae315feafec4bea526c50c296de31a67c1 Mon Sep 17 00:00:00 2001 From: Philipp Baumann Date: Mon, 16 Sep 2024 21:58:33 +0200 Subject: [PATCH 47/62] fix styling --- R/rix_init.R | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/R/rix_init.R b/R/rix_init.R index aff4d49a..7f84f776 100644 --- a/R/rix_init.R +++ b/R/rix_init.R @@ -223,7 +223,9 @@ rix_init <- function(project_path = ".", cat(readLines(con = file(rprofile_file)), sep = "\n") } - on.exit(close(file(rprofile_file))) + on.exit({ + close(file(rprofile_file)) + }) } #' Get character vector of length two with comment and code write `.Rprofile` From 65c8faa2989fc09168f3178be6ce4a5bb0cd8cd7 Mon Sep 17 00:00:00 2001 From: Philipp Baumann Date: Mon, 16 Sep 2024 22:04:08 +0200 Subject: [PATCH 48/62] manual fix --- R/with_nix.R | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/R/with_nix.R b/R/with_nix.R index 6f3f72f5..5be36395 100644 --- a/R/with_nix.R +++ b/R/with_nix.R @@ -326,7 +326,11 @@ with_nix <- function(expr, if (nzchar(LD_LIBRARY_PATH_default)) { # set old LD_LIBRARY_PATH (only if system's R session and if it wasn't # `""`) - on.exit(Sys.setenv(LD_LIBRARY_PATH = LD_LIBRARY_PATH_default)) + on.exit( + { + Sys.setenv(LD_LIBRARY_PATH = LD_LIBRARY_PATH_default) + } + ) } } From 42c94c74bf24817a758a83aae03501b56662b727 Mon Sep 17 00:00:00 2001 From: Philipp Baumann Date: Mon, 16 Sep 2024 22:08:39 +0200 Subject: [PATCH 49/62] do pull before committing --- .github/workflows/styler.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/styler.yaml b/.github/workflows/styler.yaml index 743c2b81..b63cd51d 100644 --- a/.github/workflows/styler.yaml +++ b/.github/workflows/styler.yaml @@ -37,6 +37,7 @@ jobs: run: | git config --local user.name "$GITHUB_ACTOR" git config --local user.email "$GITHUB_ACTOR@users.noreply.github.com" + git pull git add \*.R git commit -m 'Style' From 3ae73f5ec4d726446ef5330f0b23258a8ba0d47e Mon Sep 17 00:00:00 2001 From: Philipp Baumann Date: Mon, 16 Sep 2024 22:14:07 +0200 Subject: [PATCH 50/62] add fetch PR --- .github/workflows/styler.yaml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/workflows/styler.yaml b/.github/workflows/styler.yaml index b63cd51d..09c9b46b 100644 --- a/.github/workflows/styler.yaml +++ b/.github/workflows/styler.yaml @@ -20,6 +20,10 @@ jobs: steps: - uses: actions/checkout@v4 + - uses: r-lib/actions/pr-fetch@v2 + with: + repo-token: ${{ secrets.GITHUB_TOKEN }} + - name: Install Nix uses: DeterminateSystems/nix-installer-action@main @@ -37,7 +41,6 @@ jobs: run: | git config --local user.name "$GITHUB_ACTOR" git config --local user.email "$GITHUB_ACTOR@users.noreply.github.com" - git pull git add \*.R git commit -m 'Style' From 40774757efac93cba53493eec5f11c6d301e5a94 Mon Sep 17 00:00:00 2001 From: philipp-baumann Date: Mon, 16 Sep 2024 20:16:24 +0000 Subject: [PATCH 51/62] Style --- R/with_nix.R | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/R/with_nix.R b/R/with_nix.R index 5be36395..a275c362 100644 --- a/R/with_nix.R +++ b/R/with_nix.R @@ -326,11 +326,9 @@ with_nix <- function(expr, if (nzchar(LD_LIBRARY_PATH_default)) { # set old LD_LIBRARY_PATH (only if system's R session and if it wasn't # `""`) - on.exit( - { - Sys.setenv(LD_LIBRARY_PATH = LD_LIBRARY_PATH_default) - } - ) + on.exit({ + Sys.setenv(LD_LIBRARY_PATH = LD_LIBRARY_PATH_default) + }) } } From 2786531dbdf5e4d768e8e732a8e2dbe615276c66 Mon Sep 17 00:00:00 2001 From: Philipp Baumann Date: Mon, 16 Sep 2024 22:20:03 +0200 Subject: [PATCH 52/62] remove precommit --- .../workflows/lints-and-code-formatter.yaml | 36 ------------------- 1 file changed, 36 deletions(-) delete mode 100644 .github/workflows/lints-and-code-formatter.yaml diff --git a/.github/workflows/lints-and-code-formatter.yaml b/.github/workflows/lints-and-code-formatter.yaml deleted file mode 100644 index 83d6113c..00000000 --- a/.github/workflows/lints-and-code-formatter.yaml +++ /dev/null @@ -1,36 +0,0 @@ -name: Check code -on: - pull_request: - branches: [main] - -permissions: - contents: write - pull-requests: write - -jobs: - main: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - name: Install Nix - uses: DeterminateSystems/nix-installer-action@main - - - uses: cachix/cachix-action@v15 - with: - name: rstats-on-nix - # If you chose signing key for write access - # signingKey: '${{ secrets.CACHIX_SIGNING_KEY }}' - # If you chose API tokens for write access OR if you have a private cache - authToken: '${{ secrets.CACHIX_AUTH }}' - - uses: actions/setup-python@v4 - with: - python-version: 3.x - - uses: pre-commit/action@v3.0.1 - - run: pre-commit clean - - uses: pre-commit-ci/lite-action@v1.0.3 - name: pre-commit-ci-lite - if: always() - with: - msg: check lints and apply code formatting - env: - GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} From fd411f77fec34905d22bc376535056db667e8b2c Mon Sep 17 00:00:00 2001 From: Philipp Baumann Date: Mon, 16 Sep 2024 22:28:58 +0200 Subject: [PATCH 53/62] commit only if changes --- .github/workflows/styler.yaml | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/.github/workflows/styler.yaml b/.github/workflows/styler.yaml index 09c9b46b..0591d370 100644 --- a/.github/workflows/styler.yaml +++ b/.github/workflows/styler.yaml @@ -37,12 +37,17 @@ jobs: - name: Run styler::style_pkg run: nix-shell --run "Rscript -e 'styler::style_pkg()'" - - name: commit + - name: config bot user and check for changes run: | git config --local user.name "$GITHUB_ACTOR" git config --local user.email "$GITHUB_ACTOR@users.noreply.github.com" + git diff-index --quiet HEAD || echo "has_changes=true" >> $GITHUB_ENV + + - name: commit if changes + if: env.has_changes == 'true' + run: | git add \*.R - git commit -m 'Style' + git commit -m 'Style via {styler}' - uses: r-lib/actions/pr-push@v2 with: From 2d5f20ad3ca3b60e39dfb5602275f18bf2cb621c Mon Sep 17 00:00:00 2001 From: Philipp Baumann Date: Mon, 16 Sep 2024 22:33:21 +0200 Subject: [PATCH 54/62] run rhub nix action only on main not in PR --- .github/workflows/run_rhub.yaml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.github/workflows/run_rhub.yaml b/.github/workflows/run_rhub.yaml index 532edb18..266e6700 100644 --- a/.github/workflows/run_rhub.yaml +++ b/.github/workflows/run_rhub.yaml @@ -2,8 +2,6 @@ on: push: branches: [main, master] - pull_request: - branches: [main, master] name: run-rhub-checks From 705e3b2c8094ffd6a4702350981834245d217a5d Mon Sep 17 00:00:00 2001 From: Philipp Baumann Date: Mon, 16 Sep 2024 22:40:25 +0200 Subject: [PATCH 55/62] add lintr and combine lint-and-style action --- .github/workflows/style-and-lint.yaml | 59 +++++++++++++++++++++++++++ 1 file changed, 59 insertions(+) create mode 100644 .github/workflows/style-and-lint.yaml diff --git a/.github/workflows/style-and-lint.yaml b/.github/workflows/style-and-lint.yaml new file mode 100644 index 00000000..32d10f11 --- /dev/null +++ b/.github/workflows/style-and-lint.yaml @@ -0,0 +1,59 @@ +# 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: styler + +permissions: write-all + +jobs: + style_pkg: + runs-on: ubuntu-latest + env: + GH_TOKEN: ${{ github.token }} + GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} + + steps: + - uses: actions/checkout@v4 + + - uses: r-lib/actions/pr-fetch@v2 + with: + repo-token: ${{ secrets.GITHUB_TOKEN }} + + - name: Install Nix + uses: DeterminateSystems/nix-installer-action@main + + - uses: cachix/cachix-action@v15 + with: + name: rstats-on-nix + + - name: Build dev env + run: nix-build + + - name: Run styler::style_pkg + run: nix-shell --run "Rscript -e 'styler::style_pkg()'" + + name: Run lintr + run: nix-shell --run "Rscript -e 'lintr::lint_package()'" + env: + LINTR_ERROR_ON_LINT: true + + - name: config bot user and check for changes + run: | + git config --local user.name "$GITHUB_ACTOR" + git config --local user.email "$GITHUB_ACTOR@users.noreply.github.com" + git diff-index --quiet HEAD || echo "has_changes=true" >> $GITHUB_ENV + + - name: commit if changes + if: env.has_changes == 'true' + run: | + git add \*.R + git commit -m 'Style via {styler}' + + - uses: r-lib/actions/pr-push@v2 + with: + repo-token: ${{ secrets.GITHUB_TOKEN }} From a68af1f2b0cf0f1fa2f04f90cdd6682ad9f0e6f7 Mon Sep 17 00:00:00 2001 From: Philipp Baumann Date: Mon, 16 Sep 2024 22:41:18 +0200 Subject: [PATCH 56/62] name and rename action --- .github/workflows/style-and-lint.yaml | 2 +- .github/workflows/styler.yaml | 54 --------------------------- 2 files changed, 1 insertion(+), 55 deletions(-) delete mode 100644 .github/workflows/styler.yaml diff --git a/.github/workflows/style-and-lint.yaml b/.github/workflows/style-and-lint.yaml index 32d10f11..13e6cf0c 100644 --- a/.github/workflows/style-and-lint.yaml +++ b/.github/workflows/style-and-lint.yaml @@ -6,7 +6,7 @@ on: pull_request: branches: [main, master] -name: styler +name: style-and-lint permissions: write-all diff --git a/.github/workflows/styler.yaml b/.github/workflows/styler.yaml deleted file mode 100644 index 0591d370..00000000 --- a/.github/workflows/styler.yaml +++ /dev/null @@ -1,54 +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: styler - -permissions: write-all - -jobs: - style_pkg: - runs-on: ubuntu-latest - env: - GH_TOKEN: ${{ github.token }} - GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} - - steps: - - uses: actions/checkout@v4 - - - uses: r-lib/actions/pr-fetch@v2 - with: - repo-token: ${{ secrets.GITHUB_TOKEN }} - - - name: Install Nix - uses: DeterminateSystems/nix-installer-action@main - - - uses: cachix/cachix-action@v15 - with: - name: rstats-on-nix - - - name: Build dev env - run: nix-build - - - name: Run styler::style_pkg - run: nix-shell --run "Rscript -e 'styler::style_pkg()'" - - - name: config bot user and check for changes - run: | - git config --local user.name "$GITHUB_ACTOR" - git config --local user.email "$GITHUB_ACTOR@users.noreply.github.com" - git diff-index --quiet HEAD || echo "has_changes=true" >> $GITHUB_ENV - - - name: commit if changes - if: env.has_changes == 'true' - run: | - git add \*.R - git commit -m 'Style via {styler}' - - - uses: r-lib/actions/pr-push@v2 - with: - repo-token: ${{ secrets.GITHUB_TOKEN }} From 0137a6f6bbbe06a2636762d5695cae55aa283cc5 Mon Sep 17 00:00:00 2001 From: Philipp Baumann Date: Mon, 16 Sep 2024 22:42:34 +0200 Subject: [PATCH 57/62] fix syntax --- .github/workflows/style-and-lint.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/style-and-lint.yaml b/.github/workflows/style-and-lint.yaml index 13e6cf0c..2fb82d0b 100644 --- a/.github/workflows/style-and-lint.yaml +++ b/.github/workflows/style-and-lint.yaml @@ -37,7 +37,7 @@ jobs: - name: Run styler::style_pkg run: nix-shell --run "Rscript -e 'styler::style_pkg()'" - name: Run lintr + - name: Run lintr run: nix-shell --run "Rscript -e 'lintr::lint_package()'" env: LINTR_ERROR_ON_LINT: true From 673734b3076325c66f1546d2fcc585b2deeee350 Mon Sep 17 00:00:00 2001 From: Philipp Baumann Date: Mon, 16 Sep 2024 22:49:19 +0200 Subject: [PATCH 58/62] exclude files from lints --- .lintr | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.lintr b/.lintr index 286405bd..7ef17297 100644 --- a/.lintr +++ b/.lintr @@ -4,4 +4,8 @@ linters: linters_with_defaults( object_usage_linter = NULL, cyclocomp_linter = NULL ) +exclusions: list( + "tests/testthat/test-fetchers.R", + "vignettes/" +) encoding: "UTF-8" From 385f5cfc6896da97050355144079e2d8ac35c37d Mon Sep 17 00:00:00 2001 From: Philipp Baumann Date: Mon, 16 Sep 2024 22:54:05 +0200 Subject: [PATCH 59/62] reformat --- .lintr | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.lintr b/.lintr index 7ef17297..ab25bef9 100644 --- a/.lintr +++ b/.lintr @@ -1,9 +1,9 @@ linters: linters_with_defaults( - line_length_linter(100), - commented_code_linter = NULL, - object_usage_linter = NULL, - cyclocomp_linter = NULL - ) + line_length_linter(100), + commented_code_linter = NULL, + object_usage_linter = NULL, + cyclocomp_linter = NULL +) exclusions: list( "tests/testthat/test-fetchers.R", "vignettes/" From 2ecc2e093b7a5b0e8001ee3cb7d60c0522e87d71 Mon Sep 17 00:00:00 2001 From: Philipp Baumann Date: Mon, 16 Sep 2024 23:02:01 +0200 Subject: [PATCH 60/62] fix linter config format --- .lintr | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/.lintr b/.lintr index ab25bef9..3c29bc69 100644 --- a/.lintr +++ b/.lintr @@ -1,11 +1,11 @@ linters: linters_with_defaults( - line_length_linter(100), - commented_code_linter = NULL, - object_usage_linter = NULL, - cyclocomp_linter = NULL -) + line_length_linter = line_length_linter(100), + commented_code_linter = NULL, + object_usage_linter = NULL, + cyclocomp_linter = NULL + ) exclusions: list( - "tests/testthat/test-fetchers.R", - "vignettes/" -) + "tests/testthat/test-fetchers.R", + "vignettes/" + ) encoding: "UTF-8" From dc9a962d983107910e5ea3055134883bd26c1c5c Mon Sep 17 00:00:00 2001 From: Philipp Baumann Date: Mon, 16 Sep 2024 23:18:13 +0200 Subject: [PATCH 61/62] add flint to ignores and set up flint --- .Rbuildignore | 5 +- .github/workflows/flint-code-formatter.yaml | 55 +++++++++++++++++++++ .gitignore | 1 + 3 files changed, 60 insertions(+), 1 deletion(-) create mode 100644 .github/workflows/flint-code-formatter.yaml diff --git a/.Rbuildignore b/.Rbuildignore index ea5f8885..76727e35 100644 --- a/.Rbuildignore +++ b/.Rbuildignore @@ -23,4 +23,7 @@ create_dev_env.R ^\.pre-commit-config\.yaml$ ^data-raw$ .envrc -.direnv \ No newline at end of file +.direnv + +# flint files +^flint$ \ No newline at end of file diff --git a/.github/workflows/flint-code-formatter.yaml b/.github/workflows/flint-code-formatter.yaml new file mode 100644 index 00000000..9af5998e --- /dev/null +++ b/.github/workflows/flint-code-formatter.yaml @@ -0,0 +1,55 @@ +# 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] + release: + types: [published] + workflow_dispatch: + +name: flint-code-formatter + +jobs: + flint: + runs-on: ubuntu-latest + # Only restrict concurrency for non-PR jobs + concurrency: + group: rix-${{ github.event_name != 'pull_request' || github.run_id }} + env: + GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} + permissions: + contents: write + steps: + - uses: actions/checkout@v4 + + - uses: r-lib/actions/pr-fetch@v2 + with: + repo-token: ${{ secrets.GITHUB_TOKEN }} + + - uses: r-lib/actions/setup-r@v2 + + - name: Install flint + run: install.packages("flint", repos = c("https://etiennebacher.r-universe.dev/", getOption("repos"))) + shell: Rscript {0} + + - name: Run flint + run: flint::lint_package(exclude_path = "inst") + shell: Rscript {0} + + - name: config bot user and check for changes + run: | + git config --local user.name "$GITHUB_ACTOR" + git config --local user.email "$GITHUB_ACTOR@users.noreply.github.com" + git diff-index --quiet HEAD || echo "has_changes=true" >> $GITHUB_ENV + + - name: commit if changes + if: env.has_changes == 'true' + run: | + git add \*.R + git commit -m 'Check and format code with {flint}' + + - uses: r-lib/actions/pr-push@v2 + with: + repo-token: ${{ secrets.GITHUB_TOKEN }} diff --git a/.gitignore b/.gitignore index 630f4210..0b98b66d 100644 --- a/.gitignore +++ b/.gitignore @@ -7,6 +7,7 @@ .httr-oauth inst/doc docs +flint/cache_file_state.rds result /doc/ /Meta/ From fdd288954c879afba3f85da1ca9e1fe63a4e2dbd Mon Sep 17 00:00:00 2001 From: Philipp Baumann Date: Mon, 16 Sep 2024 23:18:48 +0200 Subject: [PATCH 62/62] add flint rules --- flint/config.yml | 44 ++++++++++ flint/rules/T_and_F_symbol.yml | 97 ++++++++++++++++++++ flint/rules/absolute_path.yml | 13 +++ flint/rules/any_duplicated.yml | 91 +++++++++++++++++++ flint/rules/any_is_na.yml | 7 ++ flint/rules/class_equals.yml | 42 +++++++++ flint/rules/double_assignment.yml | 23 +++++ flint/rules/duplicate_argument.yml | 46 ++++++++++ flint/rules/empty_assignment.yml | 15 ++++ flint/rules/equal_assignment.yml | 10 +++ flint/rules/equals_na.yml | 37 ++++++++ flint/rules/expect_comparison.yml | 37 ++++++++ flint/rules/expect_length.yml | 12 +++ flint/rules/expect_named.yml | 79 +++++++++++++++++ flint/rules/expect_not.yml | 23 +++++ flint/rules/expect_null.yml | 22 +++++ flint/rules/expect_true_false.yml | 28 ++++++ flint/rules/expect_type.yml | 51 +++++++++++ flint/rules/for_loop_index.yml | 27 ++++++ flint/rules/function_return.yml | 11 +++ flint/rules/implicit_assignment.yml | 69 +++++++++++++++ flint/rules/is_numeric.yml | 25 ++++++ flint/rules/length_levels.yml | 7 ++ flint/rules/length_test.yml | 59 +++++++++++++ flint/rules/lengths.yml | 59 +++++++++++++ flint/rules/library_call.yml | 26 ++++++ flint/rules/literal_coercion.yml | 89 +++++++++++++++++++ flint/rules/matrix_apply.yml | 110 +++++++++++++++++++++++ flint/rules/missing_argument.yml | 44 ++++++++++ flint/rules/nested_ifelse.yml | 29 ++++++ flint/rules/numeric_leading_zero.yml | 11 +++ flint/rules/outer_negation.yml | 29 ++++++ flint/rules/package_hooks.yml | 127 +++++++++++++++++++++++++++ flint/rules/paste.yml | 75 ++++++++++++++++ flint/rules/redundant_equals.yml | 29 ++++++ flint/rules/redundant_ifelse.yml | 67 ++++++++++++++ flint/rules/right_assignment.yml | 10 +++ flint/rules/semicolon.yml | 10 +++ flint/rules/seq.yml | 121 +++++++++++++++++++++++++ flint/rules/sort.yml | 85 ++++++++++++++++++ flint/rules/todo_comment.yml | 7 ++ flint/rules/undesirable_function.yml | 13 +++ flint/rules/undesirable_operator.yml | 29 ++++++ flint/rules/unnecessary_nesting.yml | 31 +++++++ flint/rules/unreachable_code.yml | 64 ++++++++++++++ 45 files changed, 1940 insertions(+) create mode 100644 flint/config.yml create mode 100644 flint/rules/T_and_F_symbol.yml create mode 100644 flint/rules/absolute_path.yml create mode 100644 flint/rules/any_duplicated.yml create mode 100644 flint/rules/any_is_na.yml create mode 100644 flint/rules/class_equals.yml create mode 100644 flint/rules/double_assignment.yml create mode 100644 flint/rules/duplicate_argument.yml create mode 100644 flint/rules/empty_assignment.yml create mode 100644 flint/rules/equal_assignment.yml create mode 100644 flint/rules/equals_na.yml create mode 100644 flint/rules/expect_comparison.yml create mode 100644 flint/rules/expect_length.yml create mode 100644 flint/rules/expect_named.yml create mode 100644 flint/rules/expect_not.yml create mode 100644 flint/rules/expect_null.yml create mode 100644 flint/rules/expect_true_false.yml create mode 100644 flint/rules/expect_type.yml create mode 100644 flint/rules/for_loop_index.yml create mode 100644 flint/rules/function_return.yml create mode 100644 flint/rules/implicit_assignment.yml create mode 100644 flint/rules/is_numeric.yml create mode 100644 flint/rules/length_levels.yml create mode 100644 flint/rules/length_test.yml create mode 100644 flint/rules/lengths.yml create mode 100644 flint/rules/library_call.yml create mode 100644 flint/rules/literal_coercion.yml create mode 100644 flint/rules/matrix_apply.yml create mode 100644 flint/rules/missing_argument.yml create mode 100644 flint/rules/nested_ifelse.yml create mode 100644 flint/rules/numeric_leading_zero.yml create mode 100644 flint/rules/outer_negation.yml create mode 100644 flint/rules/package_hooks.yml create mode 100644 flint/rules/paste.yml create mode 100644 flint/rules/redundant_equals.yml create mode 100644 flint/rules/redundant_ifelse.yml create mode 100644 flint/rules/right_assignment.yml create mode 100644 flint/rules/semicolon.yml create mode 100644 flint/rules/seq.yml create mode 100644 flint/rules/sort.yml create mode 100644 flint/rules/todo_comment.yml create mode 100644 flint/rules/undesirable_function.yml create mode 100644 flint/rules/undesirable_operator.yml create mode 100644 flint/rules/unnecessary_nesting.yml create mode 100644 flint/rules/unreachable_code.yml diff --git a/flint/config.yml b/flint/config.yml new file mode 100644 index 00000000..6ae436e8 --- /dev/null +++ b/flint/config.yml @@ -0,0 +1,44 @@ +keep: + - any_duplicated + - any_is_na + - class_equals + - double_assignment + - duplicate_argument + - empty_assignment + - equal_assignment + - equals_na + - expect_comparison + - expect_length + - expect_named + - expect_not + - expect_null + - expect_true_false + - expect_type + - for_loop_index + - function_return + - implicit_assignment + - is_numeric + - length_levels + - length_test + - lengths + - library_call + - literal_coercion + - matrix_apply + - missing_argument + - nested_ifelse + - numeric_leading_zero + - outer_negation + - package_hooks + - paste + - redundant_equals + - redundant_ifelse + - right_assignment + - semicolon + - seq + - sort + - T_and_F_symbol + - todo_comment + - undesirable_function + - undesirable_operator + - unnecessary_nesting + - unreachable_code diff --git a/flint/rules/T_and_F_symbol.yml b/flint/rules/T_and_F_symbol.yml new file mode 100644 index 00000000..8bfd14df --- /dev/null +++ b/flint/rules/T_and_F_symbol.yml @@ -0,0 +1,97 @@ +id: true_false_symbol +language: r +severity: warning +rule: + pattern: T + kind: identifier + not: + any: + - precedes: + any: + - pattern: <- + - pattern: = + - regex: ^~$ + - follows: + any: + - pattern: $ + - regex: ^~$ + - inside: + any: + - kind: parameter + - kind: call + - kind: binary_operator + follows: + regex: ^~$ + stopBy: end + stopBy: + kind: + argument +fix: TRUE +message: Use TRUE instead of the symbol T. + +--- + +id: true_false_symbol_2 +language: r +severity: warning +rule: + pattern: F + kind: identifier + not: + any: + - precedes: + any: + - pattern: <- + - pattern: = + - regex: ^~$ + - follows: + any: + - pattern: $ + - regex: ^~$ + - inside: + any: + - kind: parameter + - kind: call + - kind: binary_operator + follows: + regex: ^~$ + stopBy: end + stopBy: + kind: + argument +fix: FALSE +message: Use FALSE instead of the symbol F. + +--- + +id: true_false_symbol_3 +language: r +severity: warning +rule: + pattern: T + kind: identifier + precedes: + any: + - pattern: <- + - pattern: = + not: + inside: + kind: argument +message: Don't use T as a variable name, as it can break code relying on T being TRUE. + +--- + +id: true_false_symbol_4 +language: r +severity: warning +rule: + pattern: F + kind: identifier + precedes: + any: + - pattern: <- + - pattern: = + not: + inside: + kind: argument +message: Don't use F as a variable name, as it can break code relying on F being FALSE. diff --git a/flint/rules/absolute_path.yml b/flint/rules/absolute_path.yml new file mode 100644 index 00000000..3c1e9345 --- /dev/null +++ b/flint/rules/absolute_path.yml @@ -0,0 +1,13 @@ +id: absolute_path-1 +language: r +severity: warning +rule: + kind: string_content + any: + - regex: '^~[[:alpha:]]' + - regex: '^~/[[:alpha:]]' + - regex: '^[[:alpha:]]:' + - regex: '^(/|~)$' + - regex: '^/[[:alpha:]]' + - regex: '^\\' +message: Do not use absolute paths. diff --git a/flint/rules/any_duplicated.yml b/flint/rules/any_duplicated.yml new file mode 100644 index 00000000..514b028f --- /dev/null +++ b/flint/rules/any_duplicated.yml @@ -0,0 +1,91 @@ +id: any_duplicated-1 +language: r +severity: warning +rule: + pattern: any($$$ duplicated($MYVAR) $$$) +fix: anyDuplicated(~~MYVAR~~) > 0 +message: anyDuplicated(x, ...) > 0 is better than any(duplicated(x), ...). + +--- + +id: any_duplicated-2 +language: r +severity: warning +rule: + any: + - pattern: length(unique($MYVAR)) == length($MYVAR) + - pattern: length($MYVAR) == length(unique($MYVAR)) +fix: anyDuplicated(~~MYVAR~~) == 0L +message: anyDuplicated(x) == 0L is better than length(unique(x)) == length(x). + +--- + +id: any_duplicated-3 +language: r +severity: warning +rule: + pattern: length(unique($MYVAR)) != length($MYVAR) +fix: anyDuplicated(~~MYVAR~~) != 0L +message: | + Use anyDuplicated(x) != 0L (or > or <) instead of length(unique(x)) != length(x) + (or > or <). + +--- + +id: any_duplicated-4 +language: r +severity: warning +rule: + any: + - pattern: nrow($DATA) != length(unique($DATA$µCOL)) + - pattern: length(unique($DATA$µCOL)) != nrow($DATA) +fix: anyDuplicated(~~DATA~~$~~COL~~) != 0L +message: | + anyDuplicated(DF$col) != 0L is better than length(unique(DF$col)) != nrow(DF) + +--- + +# id: any_duplicated-5 +# language: r +# severity: warning +# rule: +# any: +# - pattern: +# context: nrow($DATA) != length(unique($DATA[["µCOL"]])) +# strictness: ast +# - pattern: +# context: length(unique($DATA[["µCOL"]])) != nrow($DATA) +# strictness: ast +# fix: anyDuplicated(~~DATA~~[["~~COL~~"]]) != 0L +# message: | +# anyDuplicated(DF[["col"]]) != 0L is better than length(unique(DF[["col"]])) != nrow(DF) +# +# --- + +id: any_duplicated-6 +language: r +severity: warning +rule: + any: + - pattern: nrow($DATA) == length(unique($DATA$µCOL)) + - pattern: length(unique($DATA$µCOL)) == nrow($DATA) +fix: anyDuplicated(~~DATA~~$~~COL~~) == 0L +message: | + anyDuplicated(DF$col) == 0L is better than length(unique(DF$col)) == nrow(DF) + +# --- +# +# id: any_duplicated-7 +# language: r +# severity: warning +# rule: +# any: +# - pattern: +# context: nrow($DATA) == length(unique($DATA[["µCOL"]])) +# strictness: ast +# - pattern: +# context: length(unique($DATA[["µCOL"]])) == nrow($DATA) +# strictness: ast +# fix: anyDuplicated(~~DATA~~[["~~COL~~"]]) == 0L +# message: | +# anyDuplicated(DF[["col"]]) == 0L is better than length(unique(DF[["col"]])) == nrow(DF) diff --git a/flint/rules/any_is_na.yml b/flint/rules/any_is_na.yml new file mode 100644 index 00000000..7b05a75b --- /dev/null +++ b/flint/rules/any_is_na.yml @@ -0,0 +1,7 @@ +id: any_na-1 +language: r +severity: warning +rule: + pattern: any($$$ is.na($MYVAR) $$$) +fix: anyNA(~~MYVAR~~) +message: anyNA(x) is better than any(is.na(x)). diff --git a/flint/rules/class_equals.yml b/flint/rules/class_equals.yml new file mode 100644 index 00000000..9b8804f7 --- /dev/null +++ b/flint/rules/class_equals.yml @@ -0,0 +1,42 @@ +id: class_equals_1 +language: r +severity: warning +rule: + any: + - pattern: class($VAR) == $CLASSNAME + - pattern: $CLASSNAME == class($VAR) + not: + inside: + kind: argument +fix: inherits(~~VAR~~, ~~CLASSNAME~~) +message: Instead of comparing class(x) with ==, use inherits(x, 'class-name') or is. or is(x, 'class') + +--- + +id: class_equals_2 +language: r +severity: warning +rule: + any: + - pattern: class($VAR) != $CLASSNAME + - pattern: $CLASSNAME != class($VAR) + not: + inside: + kind: argument +fix: "!inherits(~~VAR~~, ~~CLASSNAME~~)" +message: "Instead of comparing class(x) with !=, use !inherits(x, 'class-name') or is. or is(x, 'class')" + +--- + +id: class_equals_3 +language: r +severity: warning +rule: + any: + - pattern: $CLASSNAME %in% class($VAR) + - pattern: class($VAR) %in% $CLASSNAME +constraints: + CLASSNAME: + kind: string +fix: inherits(~~VAR~~, ~~CLASSNAME~~) +message: Instead of comparing class(x) with %in%, use inherits(x, 'class-name') or is. or is(x, 'class') diff --git a/flint/rules/double_assignment.yml b/flint/rules/double_assignment.yml new file mode 100644 index 00000000..60a04e23 --- /dev/null +++ b/flint/rules/double_assignment.yml @@ -0,0 +1,23 @@ +id: right_double_assignment +language: r +severity: hint +rule: + pattern: $RHS ->> $LHS + has: + field: rhs + kind: identifier +message: ->> can have hard-to-predict behavior; prefer assigning to a + specific environment instead (with assign() or <-). + +--- + +id: left_double_assignment +language: r +severity: hint +rule: + pattern: $LHS <<- $RHS + has: + field: lhs + kind: identifier +message: <<- can have hard-to-predict behavior; prefer assigning to a + specific environment instead (with assign() or <-). diff --git a/flint/rules/duplicate_argument.yml b/flint/rules/duplicate_argument.yml new file mode 100644 index 00000000..415b9ff8 --- /dev/null +++ b/flint/rules/duplicate_argument.yml @@ -0,0 +1,46 @@ +id: duplicate_argument-1 +language: r +severity: warning +rule: + # Look for a function argument... + kind: argument + any: + - has: + kind: identifier + field: name + pattern: $OBJ + - has: + kind: string_content + pattern: $OBJ + stopBy: end + + # ... that follows other argument(s) with the same name... + follows: + kind: argument + stopBy: end + has: + stopBy: end + kind: identifier + field: name + pattern: $OBJ + + # ... inside a function call (or a subset environment for data.table)... + inside: + kind: arguments + follows: + any: + - kind: identifier + pattern: $FUN + - kind: string + inside: + any: + - kind: call + - kind: subset + +# ... that is not a function listed below. +constraints: + FUN: + not: + regex: ^(mutate|transmute)$ + +message: Avoid duplicate arguments in function calls. diff --git a/flint/rules/empty_assignment.yml b/flint/rules/empty_assignment.yml new file mode 100644 index 00000000..ccc995fa --- /dev/null +++ b/flint/rules/empty_assignment.yml @@ -0,0 +1,15 @@ +id: empty_assignment-1 +language: r +severity: warning +rule: + any: + - pattern: $OBJ <- {} + - pattern: $OBJ <- {$CONTENT} + - pattern: $OBJ = {} + - pattern: $OBJ = {$CONTENT} +constraints: + CONTENT: + regex: ^\s+$ +message: | + Assign NULL explicitly or, whenever possible, allocate the empty object with + the right type and size. diff --git a/flint/rules/equal_assignment.yml b/flint/rules/equal_assignment.yml new file mode 100644 index 00000000..77074fe1 --- /dev/null +++ b/flint/rules/equal_assignment.yml @@ -0,0 +1,10 @@ +id: equal_assignment +language: r +severity: hint +rule: + pattern: $LHS = $RHS + has: + field: lhs + kind: identifier +fix: ~~LHS~~ <- ~~RHS~~ +message: Use <-, not =, for assignment. diff --git a/flint/rules/equals_na.yml b/flint/rules/equals_na.yml new file mode 100644 index 00000000..d044f304 --- /dev/null +++ b/flint/rules/equals_na.yml @@ -0,0 +1,37 @@ +id: equals_na +language: r +severity: warning +rule: + any: + - pattern: $MYVAR == NA + - pattern: $MYVAR == NA_integer_ + - pattern: $MYVAR == NA_real_ + - pattern: $MYVAR == NA_complex_ + - pattern: $MYVAR == NA_character_ + - pattern: NA == $MYVAR + - pattern: NA_integer_ == $MYVAR + - pattern: NA_real_ == $MYVAR + - pattern: NA_complex_ == $MYVAR + - pattern: NA_character_ == $MYVAR +fix: is.na(~~MYVAR~~) +message: Use is.na for comparisons to NA (not == or !=). + +--- + +id: equals_na_2 +language: r +severity: warning +rule: + any: + - pattern: $MYVAR != NA + - pattern: $MYVAR != NA_integer_ + - pattern: $MYVAR != NA_real_ + - pattern: $MYVAR != NA_complex_ + - pattern: $MYVAR != NA_character_ + - pattern: NA != $MYVAR + - pattern: NA_integer_ != $MYVAR + - pattern: NA_real_ != $MYVAR + - pattern: NA_complex_ != $MYVAR + - pattern: NA_character_ != $MYVAR +fix: is.na(~~MYVAR~~) +message: Use is.na for comparisons to NA (not == or !=). diff --git a/flint/rules/expect_comparison.yml b/flint/rules/expect_comparison.yml new file mode 100644 index 00000000..6af9bb13 --- /dev/null +++ b/flint/rules/expect_comparison.yml @@ -0,0 +1,37 @@ +id: expect_comparison-1 +language: r +severity: warning +rule: + pattern: expect_true($X > $Y) +fix: expect_gt(~~X~~, ~~Y~~) +message: expect_gt(x, y) is better than expect_true(x > y). + +--- + +id: expect_comparison-2 +language: r +severity: warning +rule: + pattern: expect_true($X >= $Y) +fix: expect_gte(~~X~~, ~~Y~~) +message: expect_gte(x, y) is better than expect_true(x >= y). + +--- + +id: expect_comparison-3 +language: r +severity: warning +rule: + pattern: expect_true($X < $Y) +fix: expect_lt(~~X~~, ~~Y~~) +message: expect_lt(x, y) is better than expect_true(x < y). + +--- + +id: expect_comparison-4 +language: r +severity: warning +rule: + pattern: expect_true($X <= $Y) +fix: expect_lte(~~X~~, ~~Y~~) +message: expect_lte(x, y) is better than expect_true(x <= y). diff --git a/flint/rules/expect_length.yml b/flint/rules/expect_length.yml new file mode 100644 index 00000000..cd5f2db0 --- /dev/null +++ b/flint/rules/expect_length.yml @@ -0,0 +1,12 @@ +id: expect_length-1 +language: r +severity: warning +rule: + any: + - pattern: $FUN(length($OBJ), $VALUE) + - pattern: $FUN($VALUE, length($OBJ)) +constraints: + FUN: + regex: ^(expect_identical|expect_equal)$ +fix: expect_length(~~OBJ~~, ~~VALUE~~) +message: expect_length(x, n) is better than ~~FUN~~(length(x), n). diff --git a/flint/rules/expect_named.yml b/flint/rules/expect_named.yml new file mode 100644 index 00000000..bf88f366 --- /dev/null +++ b/flint/rules/expect_named.yml @@ -0,0 +1,79 @@ +id: expect_named-1 +language: r +severity: warning +rule: + any: + - pattern: + context: expect_identical(names($OBJ), $VALUES) + strictness: ast + - pattern: + context: expect_identical($VALUES, names($OBJ)) + strictness: ast +constraints: + VALUES: + not: + regex: colnames|rownames|dimnames|NULL + has: + kind: null +fix: expect_named(~~OBJ~~, ~~VALUES~~) +message: expect_named(x, n) is better than expect_identical(names(x), n). + +--- + +id: expect_named-2 +language: r +severity: warning +rule: + any: + - pattern: + context: expect_equal(names($OBJ), $VALUES) + strictness: ast + - pattern: + context: expect_equal($VALUES, names($OBJ)) + strictness: ast +constraints: + VALUES: + not: + regex: colnames|rownames|dimnames|NULL +fix: expect_named(~~OBJ~~, ~~VALUES~~) +message: expect_named(x, n) is better than expect_equal(names(x), n). + +--- + +id: expect_named-3 +language: r +severity: warning +rule: + any: + - pattern: + context: testthat::expect_identical(names($OBJ), $VALUES) + strictness: ast + - pattern: + context: testthat::expect_identical($VALUES, names($OBJ)) + strictness: ast +constraints: + VALUES: + not: + regex: colnames|rownames|dimnames|NULL +fix: testthat::expect_named(~~OBJ~~, ~~VALUES~~) +message: expect_named(x, n) is better than expect_identical(names(x), n). + +--- + +id: expect_named-4 +language: r +severity: warning +rule: + any: + - pattern: + context: testthat::expect_equal(names($OBJ), $VALUES) + strictness: ast + - pattern: + context: testthat::expect_equal($VALUES, names($OBJ)) + strictness: ast +constraints: + VALUES: + not: + regex: colnames|rownames|dimnames|NULL +fix: testthat::expect_named(~~OBJ~~, ~~VALUES~~) +message: expect_named(x, n) is better than expect_equal(names(x), n). diff --git a/flint/rules/expect_not.yml b/flint/rules/expect_not.yml new file mode 100644 index 00000000..3a23e9f2 --- /dev/null +++ b/flint/rules/expect_not.yml @@ -0,0 +1,23 @@ +id: expect_not-1 +language: r +severity: warning +rule: + all: + - pattern: expect_true(!$COND) + - not: + regex: '^expect_true\(!!' +fix: expect_false(~~COND~~) +message: expect_false(x) is better than expect_true(!x), and vice versa. + +--- + +id: expect_not-2 +language: r +severity: warning +rule: + all: + - pattern: expect_false(!$COND) + - not: + regex: '^expect_false\(!!' +fix: expect_true(~~COND~~) +message: expect_false(x) is better than expect_true(!x), and vice versa. diff --git a/flint/rules/expect_null.yml b/flint/rules/expect_null.yml new file mode 100644 index 00000000..7888fdb0 --- /dev/null +++ b/flint/rules/expect_null.yml @@ -0,0 +1,22 @@ +id: expect_null-1 +language: r +severity: warning +rule: + any: + - pattern: $FUN(NULL, $VALUES) + - pattern: $FUN($VALUES, NULL) +constraints: + FUN: + regex: ^(expect_identical|expect_equal)$ +fix: expect_null(~~VALUES~~) +message: expect_null(x) is better than ~~FUN~~(x, NULL). + +--- + +id: expect_null-2 +language: r +severity: warning +rule: + pattern: expect_true(is.null($VALUES)) +fix: expect_null(~~VALUES~~) +message: expect_null(x) is better than expect_true(is.null(x)). diff --git a/flint/rules/expect_true_false.yml b/flint/rules/expect_true_false.yml new file mode 100644 index 00000000..784843d8 --- /dev/null +++ b/flint/rules/expect_true_false.yml @@ -0,0 +1,28 @@ +id: expect_true_false-1 +language: r +severity: warning +rule: + any: + - pattern: $FUN(TRUE, $VALUES) + - pattern: $FUN($VALUES, TRUE) +constraints: + FUN: + regex: ^(expect_identical|expect_equal)$ +fix: expect_true(~~VALUES~~) +message: expect_true(x) is better than ~~FUN~~(x, TRUE). + +--- + +id: expect_true_false-2 +language: r +severity: warning +rule: + any: + - pattern: $FUN(FALSE, $VALUES) + - pattern: $FUN($VALUES, FALSE) +constraints: + FUN: + regex: ^(expect_identical|expect_equal)$ +fix: expect_false(~~VALUES~~) +message: expect_false(x) is better than ~~FUN~~(x, FALSE). + diff --git a/flint/rules/expect_type.yml b/flint/rules/expect_type.yml new file mode 100644 index 00000000..1ab20eca --- /dev/null +++ b/flint/rules/expect_type.yml @@ -0,0 +1,51 @@ +id: expect_type-1 +language: r +severity: warning +rule: + any: + - pattern: + context: expect_identical(typeof($OBJ), $VALUES) + strictness: ast + - pattern: + context: expect_identical($VALUES, typeof($OBJ)) + strictness: ast +constraints: + VALUES: + not: + regex: typeof +fix: expect_type(~~OBJ~~, ~~VALUES~~) +message: expect_type(x, t) is better than expect_identical(typeof(x), t). + +--- + +id: expect_type-2 +language: r +severity: warning +rule: + any: + - pattern: + context: expect_equal(typeof($OBJ), $VALUES) + strictness: ast + - pattern: + context: expect_equal($VALUES, typeof($OBJ)) + strictness: ast +constraints: + VALUES: + not: + regex: typeof +fix: expect_type(~~OBJ~~, ~~VALUES~~) +message: expect_type(x, t) is better than expect_equal(typeof(x), t). + +--- + +id: expect_type-3 +language: r +severity: warning +rule: + pattern: expect_true($FUN($OBJ)) +constraints: + FUN: + regex: ^is\. + not: + regex: data\.frame$ +message: expect_type(x, t) is better than expect_true(is.(x)). diff --git a/flint/rules/for_loop_index.yml b/flint/rules/for_loop_index.yml new file mode 100644 index 00000000..e5b28b43 --- /dev/null +++ b/flint/rules/for_loop_index.yml @@ -0,0 +1,27 @@ +id: for_loop_index-1 +language: r +severity: warning +rule: + pattern: for ($IDX in $IDX) +message: Don't re-use any sequence symbols as the index symbol in a for loop. + +--- + +id: for_loop_index-2 +language: r +severity: warning +rule: + pattern: for ($IDX in $SEQ) +constraints: + SEQ: + kind: call + has: + kind: arguments + has: + kind: argument + stopBy: end + has: + kind: identifier + field: value + pattern: $IDX +message: Don't re-use any sequence symbols as the index symbol in a for loop. diff --git a/flint/rules/function_return.yml b/flint/rules/function_return.yml new file mode 100644 index 00000000..31ba46be --- /dev/null +++ b/flint/rules/function_return.yml @@ -0,0 +1,11 @@ +id: function_return-1 +language: r +severity: warning +rule: + any: + - pattern: return($OBJ <- $VAL) + - pattern: return($OBJ <<- $VAL) + - pattern: return($VAL -> $OBJ) + - pattern: return($VAL ->> $OBJ) +message: | + Move the assignment outside of the return() clause, or skip assignment altogether. diff --git a/flint/rules/implicit_assignment.yml b/flint/rules/implicit_assignment.yml new file mode 100644 index 00000000..133a40ef --- /dev/null +++ b/flint/rules/implicit_assignment.yml @@ -0,0 +1,69 @@ +id: implicit_assignment-1 +language: r +severity: warning +rule: + any: + - pattern: $RECEIVER <- $VALUE + - pattern: $RECEIVER <<- $VALUE + - pattern: $VALUE -> $RECEIVER + - pattern: $VALUE ->> $RECEIVER + inside: + any: + - kind: if_statement + - kind: while_statement + field: condition + stopBy: end + strictness: cst +message: | + Avoid implicit assignments in function calls. For example, instead of + `if (x <- 1L) { ... }`, write `x <- 1L; if (x) { ... }`. + +--- + +id: implicit_assignment-2 +language: r +severity: warning +rule: + any: + - pattern: $RECEIVER <- $VALUE + - pattern: $RECEIVER <<- $VALUE + - pattern: $VALUE -> $RECEIVER + - pattern: $VALUE ->> $RECEIVER + inside: + kind: for_statement + field: sequence + stopBy: end + strictness: cst +message: | + Avoid implicit assignments in function calls. For example, instead of + `if (x <- 1L) { ... }`, write `x <- 1L; if (x) { ... }`. + +# --- +# +# id: implicit_assignment-3 +# language: r +# severity: warning +# rule: +# any: +# - pattern: $RECEIVER <- $VALUE +# - pattern: $RECEIVER <<- $VALUE +# - pattern: $VALUE -> $RECEIVER +# - pattern: $VALUE ->> $RECEIVER +# inside: +# kind: argument +# field: value +# strictness: cst +# stopBy: end +# not: +# inside: +# kind: call +# field: function +# has: +# kind: identifier +# regex: ^(lapply)$ +# stopBy: end +# strictness: cst +# message: | +# Avoid implicit assignments in function calls. For example, instead of +# `if (x <- 1L) { ... }`, write `x <- 1L; if (x) { ... }`. + diff --git a/flint/rules/is_numeric.yml b/flint/rules/is_numeric.yml new file mode 100644 index 00000000..6ef8e223 --- /dev/null +++ b/flint/rules/is_numeric.yml @@ -0,0 +1,25 @@ +id: is_numeric_1 +language: r +severity: warning +rule: + any: + - pattern: is.numeric($VAR) || is.integer($VAR) + - pattern: is.integer($VAR) || is.numeric($VAR) +message: is.numeric(x) || is.integer(x) can be simplified to is.numeric(x). Use + is.double(x) to test for objects stored as 64-bit floating point. + +--- + +id: is_numeric_2 +language: r +severity: warning +rule: + any: + - pattern: + context: class($VAR) %in% c("numeric", "integer") + strictness: ast + - pattern: + context: class($VAR) %in% c("integer", "numeric") + strictness: ast +message: class(x) %in% c("numeric", "integer") can be simplified to is.numeric(x). Use + is.double(x) to test for objects stored as 64-bit floating point. diff --git a/flint/rules/length_levels.yml b/flint/rules/length_levels.yml new file mode 100644 index 00000000..e1421dfc --- /dev/null +++ b/flint/rules/length_levels.yml @@ -0,0 +1,7 @@ +id: length_levels_1 +language: r +severity: warning +rule: + pattern: length(levels($VAR)) +fix: nlevels(~~VAR~~) +message: nlevels(x) is better than length(levels(x)). df diff --git a/flint/rules/length_test.yml b/flint/rules/length_test.yml new file mode 100644 index 00000000..26b77083 --- /dev/null +++ b/flint/rules/length_test.yml @@ -0,0 +1,59 @@ +# Strangely, having something like pattern: length($VAR $OP $VAR2) doesn't work + +id: length_test_1 +language: r +severity: warning +rule: + pattern: length($VAR == $VAR2) +fix: length(~~VAR~~) == ~~VAR2~~ +message: Checking the length of a logical vector is likely a mistake. + +--- + +id: length_test_2 +language: r +severity: warning +rule: + pattern: length($VAR != $VAR2) +fix: length(~~VAR~~) != ~~VAR2~~ +message: Checking the length of a logical vector is likely a mistake. + +--- + +id: length_test_3 +language: r +severity: warning +rule: + pattern: length($VAR > $VAR2) +fix: length(~~VAR~~) > ~~VAR2~~ +message: Checking the length of a logical vector is likely a mistake. + +--- + +id: length_test_4 +language: r +severity: warning +rule: + pattern: length($VAR >= $VAR2) +fix: length(~~VAR~~) >= ~~VAR2~~ +message: Checking the length of a logical vector is likely a mistake. + +--- + +id: length_test_5 +language: r +severity: warning +rule: + pattern: length($VAR < $VAR2) +fix: length(~~VAR~~) < ~~VAR2~~ +message: Checking the length of a logical vector is likely a mistake. + +--- + +id: length_test_6 +language: r +severity: warning +rule: + pattern: length($VAR <= $VAR2) +fix: length(~~VAR~~) <= ~~VAR2~~ +message: Checking the length of a logical vector is likely a mistake. diff --git a/flint/rules/lengths.yml b/flint/rules/lengths.yml new file mode 100644 index 00000000..a4164402 --- /dev/null +++ b/flint/rules/lengths.yml @@ -0,0 +1,59 @@ +id: sapply_lengths-1 +language: r +severity: warning +rule: + any: + - pattern: sapply($MYVAR, length) + - pattern: sapply(FUN = length, $MYVAR) + - pattern: sapply($MYVAR, FUN = length) + - pattern: vapply($MYVAR, length $$$) + + - pattern: map_dbl($MYVAR, length) + - pattern: map_dbl($MYVAR, .f = length) + - pattern: map_dbl(.f = length, $MYVAR) + - pattern: map_int($MYVAR, length) + - pattern: map_int($MYVAR, .f = length) + - pattern: map_int(.f = length, $MYVAR) + + - pattern: purrr::map_dbl($MYVAR, length) + - pattern: purrr::map_dbl($MYVAR, .f = length) + - pattern: purrr::map_dbl(.f = length, $MYVAR) + - pattern: purrr::map_int($MYVAR, length) + - pattern: purrr::map_int($MYVAR, .f = length) + - pattern: purrr::map_int(.f = length, $MYVAR) +fix: lengths(~~MYVAR~~) +message: Use lengths() to find the length of each element in a list. + +--- + +id: sapply_lengths-2 +language: r +severity: warning +rule: + any: + - pattern: $MYVAR |> sapply(length) + - pattern: $MYVAR |> sapply(FUN = length) + - pattern: $MYVAR |> vapply(length $$$) + - pattern: $MYVAR |> map_int(length) + - pattern: $MYVAR |> map_int(length $$$) + - pattern: $MYVAR |> purrr::map_int(length) + - pattern: $MYVAR |> purrr::map_int(length $$$) +fix: ~~MYVAR~~ |> lengths() +message: Use lengths() to find the length of each element in a list. + +--- + +id: sapply_lengths-3 +language: r +severity: warning +rule: + any: + - pattern: $MYVAR %>% sapply(length) + - pattern: $MYVAR %>% sapply(FUN = length) + - pattern: $MYVAR %>% vapply(length $$$) + - pattern: $MYVAR %>% map_int(length) + - pattern: $MYVAR %>% map_int(length $$$) + - pattern: $MYVAR %>% purrr::map_int(length) + - pattern: $MYVAR %>% purrr::map_int(length $$$) +fix: ~~MYVAR~~ %>% lengths() +message: Use lengths() to find the length of each element in a list. diff --git a/flint/rules/library_call.yml b/flint/rules/library_call.yml new file mode 100644 index 00000000..ef14d540 --- /dev/null +++ b/flint/rules/library_call.yml @@ -0,0 +1,26 @@ +id: library_call +language: r +severity: warning +rule: + kind: call + has: + regex: ^library|require$ + kind: identifier + follows: + not: + any: + - kind: call + has: + regex: ^library|require$ + kind: identifier + - kind: comment + not: + inside: + any: + - kind: function_definition + - kind: call + has: + pattern: suppressPackageStartupMessages + kind: identifier +message: Move all library/require calls to the top of the script. + diff --git a/flint/rules/literal_coercion.yml b/flint/rules/literal_coercion.yml new file mode 100644 index 00000000..e61fb24a --- /dev/null +++ b/flint/rules/literal_coercion.yml @@ -0,0 +1,89 @@ +id: literal_coercion-1 +language: r +severity: warning +rule: + pattern: $FUN($VALUE) +constraints: + VALUE: + kind: argument + has: + kind: float + not: + regex: 'e' + FUN: + regex: ^(int|as\.integer)$ +fix: ~~VALUE~~L +message: | + Use ~~VALUE~~L instead of ~~FUN~~(~~VALUE~~), i.e., use literals directly + where possible, instead of coercion. + +--- + +id: literal_coercion-2 +language: r +severity: warning +rule: + pattern: as.character(NA) +fix: NA_character_ +message: | + Use NA_character_ instead of as.character(NA), i.e., use literals directly + where possible, instead of coercion. + +--- + +id: literal_coercion-3 +language: r +severity: warning +rule: + pattern: as.logical($VAR) +constraints: + VAR: + kind: argument + has: + any: + - regex: ^1L$ + - regex: ^1$ + - regex: 'true' +fix: TRUE +message: Use TRUE instead of as.logical(~~VAR~~). + +--- + +id: literal_coercion-4 +language: r +severity: warning +rule: + pattern: $FUN($VAR) +constraints: + VAR: + kind: argument + has: + kind: float + FUN: + regex: ^(as\.numeric|as\.double)$ +fix: ~~VAR~~ +message: Use ~~VAR~~ instead of ~~FUN~~(~~VAR~~). + +--- + +id: literal_coercion-5 +language: r +severity: warning +rule: + pattern: as.integer(NA) +fix: NA_integer_ +message: Use NA_integer_ instead of as.integer(NA). + +--- + +id: literal_coercion-6 +language: r +severity: warning +rule: + pattern: $FUN(NA) +constraints: + FUN: + regex: ^(as\.numeric|as\.double)$ +fix: NA_real_ +message: Use NA_real_ instead of ~~FUN~~(NA). + diff --git a/flint/rules/matrix_apply.yml b/flint/rules/matrix_apply.yml new file mode 100644 index 00000000..bbc68505 --- /dev/null +++ b/flint/rules/matrix_apply.yml @@ -0,0 +1,110 @@ +id: matrix_apply-1 +language: r +severity: warning +rule: + any: + - pattern: apply($INPUT, 2, sum) + - pattern: apply($INPUT, MARGIN = 2, sum) + - pattern: apply($INPUT, 2, FUN = sum) + - pattern: apply($INPUT, MARGIN = 2, FUN = sum) +fix: colSums(~~INPUT~~) +message: Use colSums(x) rather than apply(x, 1, sum) + +--- + +id: matrix_apply-2 +language: r +severity: warning +rule: + any: + - pattern: apply($INPUT, 2, sum, na.rm = $NARM) + - pattern: apply($INPUT, MARGIN = 2, sum, na.rm = $NARM) + - pattern: apply($INPUT, 2, FUN = sum, na.rm = $NARM) + - pattern: apply($INPUT, MARGIN = 2, FUN = sum, na.rm = $NARM) +fix: colSums(~~INPUT~~, na.rm = ~~NARM~~) +message: Use colSums(x, na.rm = ~~NARM~~) rather than apply(x, 2, sum, na.rm = ~~NARM~~). + +--- + +id: matrix_apply-3 +language: r +severity: warning +rule: + any: + - pattern: apply($INPUT, 1, sum) + - pattern: apply($INPUT, MARGIN = 1, sum) + - pattern: apply($INPUT, 1, FUN = sum) + - pattern: apply($INPUT, MARGIN = 1, FUN = sum) +fix: rowSums(~~INPUT~~) +message: Use rowSums(x) rather than apply(x, 1, sum) + +--- + +id: matrix_apply-4 +language: r +severity: warning +rule: + any: + - pattern: apply($INPUT, 1, sum, na.rm = $NARM) + - pattern: apply($INPUT, MARGIN = 1, sum, na.rm = $NARM) + - pattern: apply($INPUT, 1, FUN = sum, na.rm = $NARM) + - pattern: apply($INPUT, MARGIN = 1, FUN = sum, na.rm = $NARM) +fix: rowSums(~~INPUT~~, na.rm = ~~NARM~~) +message: Use rowSums(x, na.rm = ~~NARM~~) rather than apply(x, 1, sum, na.rm = ~~NARM~~). + +--- + +id: matrix_apply-5 +language: r +severity: warning +rule: + any: + - pattern: apply($INPUT, 1, mean) + - pattern: apply($INPUT, MARGIN = 1, mean) + - pattern: apply($INPUT, 1, FUN = mean) + - pattern: apply($INPUT, MARGIN = 1, FUN = mean) +fix: rowMeans(~~INPUT~~) +message: Use rowMeans(x) rather than apply(x, 1, mean). + +--- + +id: matrix_apply-6 +language: r +severity: warning +rule: + any: + - pattern: apply($INPUT, 1, mean, na.rm = $NARM) + - pattern: apply($INPUT, MARGIN = 1, mean, na.rm = $NARM) + - pattern: apply($INPUT, 1, FUN = mean, na.rm = $NARM) + - pattern: apply($INPUT, MARGIN = 1, FUN = mean, na.rm = $NARM) +fix: rowMeans(~~INPUT~~, na.rm = ~~NARM~~) +message: Use rowMeans(x, na.rm = ~~NARM~~) rather than apply(x, 1, mean, na.rm = ~~NARM~~). + +--- + +id: matrix_apply-7 +language: r +severity: warning +rule: + any: + - pattern: apply($INPUT, 2, mean) + - pattern: apply($INPUT, MARGIN = 2, mean) + - pattern: apply($INPUT, 2, FUN = mean) + - pattern: apply($INPUT, MARGIN = 2, FUN = mean) +fix: colMeans(~~INPUT~~) +message: Use colMeans(x) rather than apply(x, 2, mean). + +--- + +id: matrix_apply-8 +language: r +severity: warning +rule: + any: + - pattern: apply($INPUT, 2, mean, na.rm = $NARM) + - pattern: apply($INPUT, MARGIN = 2, mean, na.rm = $NARM) + - pattern: apply($INPUT, 2, FUN = mean, na.rm = $NARM) + - pattern: apply($INPUT, MARGIN = 2, FUN = mean, na.rm = $NARM) +fix: colMeans(~~INPUT~~, na.rm = ~~NARM~~) +message: Use colMeans(x, na.rm = ~~NARM~~) rather than apply(x, 2, mean, na.rm = ~~NARM~~). + diff --git a/flint/rules/missing_argument.yml b/flint/rules/missing_argument.yml new file mode 100644 index 00000000..9f47d17a --- /dev/null +++ b/flint/rules/missing_argument.yml @@ -0,0 +1,44 @@ +id: missing_argument-1 +language: r +severity: warning +rule: + kind: arguments + has: + kind: comma + any: + - precedes: + stopBy: neighbor + any: + - regex: '^\)$' + - kind: comma + - follows: + any: + - regex: '^\($' + - kind: argument + regex: '=$' + follows: + kind: identifier + not: + regex: '^(quote|switch|alist)$' + inside: + kind: call +message: Missing argument in function call. + +--- + +id: missing_argument-2 +language: r +severity: warning +rule: + kind: arguments + regex: '=(\s+|)\)$' + follows: + any: + - kind: identifier + - kind: extract_operator + - kind: namespace_operator + not: + regex: '^(quote|switch|alist)$' + inside: + kind: call +message: Missing argument in function call. diff --git a/flint/rules/nested_ifelse.yml b/flint/rules/nested_ifelse.yml new file mode 100644 index 00000000..64dcb08e --- /dev/null +++ b/flint/rules/nested_ifelse.yml @@ -0,0 +1,29 @@ +id: nested_ifelse-1 +language: r +severity: warning +rule: + pattern: $FUN($COND, $TRUE, $FALSE) +constraints: + FALSE: + regex: ^(ifelse|if_else|fifelse) + FUN: + regex: ^(ifelse|if_else|fifelse) +message: | + Don't use nested ~~FUN~~() calls; instead, try (1) data.table::fcase; + (2) dplyr::case_when; or (3) using a lookup table. + +--- + +id: nested_ifelse-2 +language: r +severity: warning +rule: + pattern: $FUN($COND, $TRUE, $FALSE) +constraints: + TRUE: + regex: ^(ifelse|if_else|fifelse) + FUN: + regex: ^(ifelse|if_else|fifelse) +message: | + Don't use nested ~~FUN~~() calls; instead, try (1) data.table::fcase; + (2) dplyr::case_when; or (3) using a lookup table. diff --git a/flint/rules/numeric_leading_zero.yml b/flint/rules/numeric_leading_zero.yml new file mode 100644 index 00000000..51807870 --- /dev/null +++ b/flint/rules/numeric_leading_zero.yml @@ -0,0 +1,11 @@ +id: numeric_leading_zero_1 +language: r +severity: warning +rule: + pattern: $VALUE + any: + - kind: float + - kind: identifier + regex: ^\.[0-9] +fix: 0~~VALUE~~ +message: Include the leading zero for fractional numeric constants. diff --git a/flint/rules/outer_negation.yml b/flint/rules/outer_negation.yml new file mode 100644 index 00000000..ca377de3 --- /dev/null +++ b/flint/rules/outer_negation.yml @@ -0,0 +1,29 @@ +id: outer_negation-1 +language: r +severity: warning +rule: + pattern: all(!$VAR) +constraints: + VAR: + not: + regex: '^!' +fix: '!any(~~VAR~~)' +message: | + !any(x) is better than all(!x). The former applies negation only once after + aggregation instead of many times for each element of x. + +--- + +id: outer_negation-2 +language: r +severity: warning +rule: + pattern: any(! $VAR) +constraints: + VAR: + not: + regex: '^!' +fix: '!all(~~VAR~~)' +message: | + !all(x) is better than any(!x). The former applies negation only once after + aggregation instead of many times for each element of x. diff --git a/flint/rules/package_hooks.yml b/flint/rules/package_hooks.yml new file mode 100644 index 00000000..f4dfa756 --- /dev/null +++ b/flint/rules/package_hooks.yml @@ -0,0 +1,127 @@ +id: package_hooks-1 +language: r +severity: warning +rule: + pattern: packageStartupMessage($$$) + inside: + stopBy: end + kind: binary_operator + has: + stopBy: end + field: lhs + pattern: .onLoad +message: Put packageStartupMessage() calls in .onAttach(), not .onLoad(). + +--- + +id: package_hooks-2 +language: r +severity: warning +rule: + pattern: library.dynam($$$) + inside: + stopBy: end + kind: binary_operator + has: + stopBy: end + field: lhs + pattern: .onAttach +message: Put library.dynam() calls in .onLoad(), not .onAttach(). + +--- + +id: package_hooks-3 +language: r +severity: warning +rule: + pattern: $FN($$$) + inside: + stopBy: end + kind: binary_operator + has: + stopBy: end + field: lhs + pattern: .onLoad +constraints: + FN: + regex: '^(cat|installed.packages|message|packageStartupMessage|print|writeLines)$' +message: Don't use ~~FN~~() in .onLoad(). + +--- + +id: package_hooks-4 +language: r +severity: warning +rule: + pattern: $FN($$$) + inside: + stopBy: end + kind: binary_operator + has: + stopBy: end + field: lhs + pattern: .onAttach +constraints: + FN: + # library.dynam already has its own linter + regex: '^(cat|installed.packages|message|print|writeLines)$' +message: Don't use ~~FN~~() in .onAttach(). + +--- + +id: package_hooks-5 +language: r +severity: warning +rule: + pattern: $FN($$$) + inside: + stopBy: end + kind: binary_operator + has: + stopBy: end + field: lhs + pattern: $LOAD +constraints: + LOAD: + regex: '^(\.onAttach|\.onLoad)$' + FN: + regex: '^(require|library)$' +message: Don't alter the search() path in ~~LOAD~~() by calling ~~FN~~(). + +--- + +id: package_hooks-6 +language: r +severity: warning +rule: + pattern: installed.packages($$$) + inside: + stopBy: end + kind: binary_operator + has: + stopBy: end + field: lhs + pattern: $LOAD +constraints: + LOAD: + regex: '^(\.onAttach|\.onLoad)$' +message: Don't slow down package load by running installed.packages() in ~~LOAD~~(). + +--- + +id: package_hooks-7 +language: r +severity: warning +rule: + pattern: library.dynam.unload($$$) + inside: + stopBy: end + kind: binary_operator + has: + stopBy: end + field: lhs + pattern: $LOAD +constraints: + LOAD: + regex: '^(\.onDetach|\.Last\.lib)$' +message: Use library.dynam.unload() calls in .onUnload(), not ~~LOAD~~(). diff --git a/flint/rules/paste.yml b/flint/rules/paste.yml new file mode 100644 index 00000000..0d4faed3 --- /dev/null +++ b/flint/rules/paste.yml @@ -0,0 +1,75 @@ +id: paste_1 +language: r +severity: warning +rule: + pattern: + context: paste($$$CONTENT sep = "" $$$CONTENT2) + strictness: ast +# fix: paste0($$$CONTENT) +message: paste0(...) is better than paste(..., sep = ""). + +--- + +id: paste_2 +language: r +severity: warning +rule: + any: + - pattern: + context: paste($CONTENT, collapse = ", ") + strictness: ast + - pattern: + context: paste(collapse = ", ", $CONTENT) + strictness: ast +# fix: paste0($$$CONTENT) +message: toString(.) is more expressive than paste(., collapse = ", "). + +--- + +id: paste_3 +language: r +severity: warning +rule: + pattern: + context: paste0($$$CONTENT sep = $USELESS $$$CONTENT2) + strictness: ast +# fix: paste0($$$CONTENT) +message: | + sep= is not a formal argument to paste0(); did you mean to use paste(), or + collapse=? + +--- + +id: paste_4 +language: r +severity: warning +rule: + any: + - pattern: + context: paste0($CONTENT, collapse = $FOO) + strictness: ast + - pattern: + context: paste0(collapse = $FOO, $CONTENT) + strictness: ast + not: + has: + regex: sep + kind: argument +# fix: paste0($$$CONTENT) +message: | + Use paste(), not paste0(), to collapse a character vector when sep= is not used. + +# --- +# +# id: paste_5 +# language: r +# severity: warning +# rule: +# pattern: +# context: paste0(rep($VAR, $TIMES), collapse = "") +# strictness: ast +# constraints: +# VAR: +# kind: string +# fix: strrep(~~VAR~~, ~~TIMES~~) +# message: strrep(x, times) is better than paste0(rep(x, times), collapse = ""). diff --git a/flint/rules/redundant_equals.yml b/flint/rules/redundant_equals.yml new file mode 100644 index 00000000..79a6abcf --- /dev/null +++ b/flint/rules/redundant_equals.yml @@ -0,0 +1,29 @@ +id: redundant_equals-1 +language: r +severity: warning +rule: + any: + - pattern: $VAR == TRUE + - pattern: TRUE == $VAR + - pattern: $VAR == FALSE + - pattern: FALSE == $VAR +message: | + Using == on a logical vector is redundant. Well-named logical vectors can be + used directly in filtering. For data.table's `i` argument, wrap the column + name in (), like `DT[(is_treatment)]`. + +--- + +id: redundant_equals-2 +language: r +severity: warning +rule: + any: + - pattern: $VAR != TRUE + - pattern: TRUE != $VAR + - pattern: $VAR != FALSE + - pattern: FALSE != $VAR +message: | + Using != on a logical vector is redundant. Well-named logical vectors can be + used directly in filtering. For data.table's `i` argument, wrap the column + name in (), like `DT[(is_treatment)]`. diff --git a/flint/rules/redundant_ifelse.yml b/flint/rules/redundant_ifelse.yml new file mode 100644 index 00000000..e2528cfd --- /dev/null +++ b/flint/rules/redundant_ifelse.yml @@ -0,0 +1,67 @@ +id: redundant_ifelse_1 +language: r +severity: warning +rule: + pattern: $FUN($COND, $VAL1, $VAL2) +constraints: + VAL1: + regex: ^TRUE$ + VAL2: + regex: ^FALSE$ + FUN: + regex: ^(ifelse|fifelse|if_else)$ +fix: ~~COND~~ +message: | + Use ~~COND~~ directly instead of calling ~~FUN~~(~~COND~~, TRUE, FALSE). + +--- + +id: redundant_ifelse_2 +language: r +severity: warning +rule: + pattern: $FUN($COND, $VAL1, $VAL2) +constraints: + VAL1: + regex: ^FALSE$ + VAL2: + regex: ^TRUE$ + FUN: + regex: ^(ifelse|fifelse|if_else)$ +fix: !(~~COND~~) +message: | + Use !(~~COND~~) directly instead of calling ~~FUN~~(~~COND~~, FALSE, TRUE). + +--- + +id: redundant_ifelse_3 +language: r +severity: warning +rule: + pattern: $FUN($COND, $VAL1, $VAL2) +constraints: + VAL1: + regex: ^(1|1L)$ + VAL2: + regex: ^(0|0L)$ + FUN: + regex: ^(ifelse|fifelse|if_else)$ +fix: as.integer(~~COND~~) +message: Prefer as.integer(~~COND~~) to ~~FUN~~(~~COND~~, ~~VAL1~~, ~~VAL2~~). + +--- + +id: redundant_ifelse_4 +language: r +severity: warning +rule: + pattern: $FUN($COND, $VAL1, $VAL2) +constraints: + VAL1: + regex: ^(0|0L)$ + VAL2: + regex: ^(1|1L)$ + FUN: + regex: ^(ifelse|fifelse|if_else)$ +fix: as.integer(!(~~COND~~)) +message: Prefer as.integer(!(~~COND~~)) to ~~FUN~~(~~COND~~, ~~VAL1~~, ~~VAL2~~). diff --git a/flint/rules/right_assignment.yml b/flint/rules/right_assignment.yml new file mode 100644 index 00000000..76b736e9 --- /dev/null +++ b/flint/rules/right_assignment.yml @@ -0,0 +1,10 @@ +id: right_assignment +language: r +severity: hint +rule: + pattern: $RHS -> $LHS + has: + field: rhs + kind: identifier +fix: ~~LHS~~<- ~~RHS~~ +message: Use <-, not ->, for assignment. diff --git a/flint/rules/semicolon.yml b/flint/rules/semicolon.yml new file mode 100644 index 00000000..ff2e63b4 --- /dev/null +++ b/flint/rules/semicolon.yml @@ -0,0 +1,10 @@ +id: semicolon_1 +language: r +severity: warning +rule: + regex: ;\s+$ + not: + inside: + kind: string + stopBy: end +message: Trailing semicolons are not needed. diff --git a/flint/rules/seq.yml b/flint/rules/seq.yml new file mode 100644 index 00000000..87f39689 --- /dev/null +++ b/flint/rules/seq.yml @@ -0,0 +1,121 @@ +id: seq_1 +language: r +severity: warning +rule: + pattern: seq(length($VAR)) +fix: seq_along(~~VAR~~) +message: | + seq(length(...)) is likely to be wrong in the empty edge case. Use seq_along(...) instead. + +--- + +id: seq_2 +language: r +severity: warning +rule: + any: + - pattern: 1:nrow($VAR) + - pattern: 1L:nrow($VAR) + regex: ^1 +fix: seq_len(nrow(~~VAR~~)) +message: | + 1:nrow(...) is likely to be wrong in the empty edge case. Use seq_len(nrow(...)) instead. + +--- + +id: seq_3 +language: r +severity: warning +rule: + any: + - pattern: 1:n() + - pattern: 1L:n() + regex: ^1 +fix: seq_len(n()) +message: | + 1:n() is likely to be wrong in the empty edge case. Use seq_len(n()) instead. + +--- + +id: seq_4 +language: r +severity: warning +rule: + pattern: seq(nrow($VAR)) +fix: seq_len(nrow(~~VAR~~)) +message: | + seq(nrow(...)) is likely to be wrong in the empty edge case. Use seq_len(nrow(...)) instead. + +--- + +id: seq_5 +language: r +severity: warning +rule: + any: + - pattern: 1:length($VAR) + - pattern: 1L:length($VAR) + regex: ^1 +fix: seq_along(~~VAR~~) +message: | + 1:length(...) is likely to be wrong in the empty edge case. Use seq_along(...) instead. + +--- + +id: seq_6 +language: r +severity: warning +rule: + any: + - pattern: 1:ncol($VAR) + - pattern: 1L:ncol($VAR) + regex: ^1 +fix: seq_len(ncol(~~VAR~~)) +message: | + 1:ncol(...) is likely to be wrong in the empty edge case. Use seq_len(ncol(...)) instead. + +--- + +id: seq_7 +language: r +severity: warning +rule: + any: + - pattern: 1:NCOL($VAR) + - pattern: 1L:NCOL($VAR) + regex: ^1 +fix: seq_len(NCOL(~~VAR~~)) +message: | + 1:NCOL(...) is likely to be wrong in the empty edge case. Use seq_len(NCOL(...)) instead. + +--- + +id: seq_8 +language: r +severity: warning +rule: + any: + - pattern: 1:NROW($VAR) + - pattern: 1L:NROW($VAR) + regex: ^1 +fix: seq_len(NROW(~~VAR~~)) +message: | + 1:NROW(...) is likely to be wrong in the empty edge case. Use seq_len(NROW(...)) instead. + + +--- + +id: seq_9 +language: r +severity: warning +rule: + pattern: seq(1, $VAL) + not: + pattern: seq(1, 0) +constraints: + VAL: + regex: ^\d+(|L)$ +fix: seq_len(~~VAL~~) +message: seq_len(~~VAL~~) is more efficient than seq(1, ~~VAL~~). + + diff --git a/flint/rules/sort.yml b/flint/rules/sort.yml new file mode 100644 index 00000000..930f5c6d --- /dev/null +++ b/flint/rules/sort.yml @@ -0,0 +1,85 @@ +id: sort-1 +language: r +severity: warning +rule: + pattern: $OBJ[order($OBJ)] +fix: sort(~~OBJ~~, na.last = TRUE) +message: sort(~~OBJ~~, na.last = TRUE) is better than ~~OBJ~~[order(~~OBJ~~)]. + +--- + +id: sort-2 +language: r +severity: warning +rule: + any: + - pattern: $OBJ[order($OBJ, decreasing = $DECREASING)] + - pattern: $OBJ[order(decreasing = $DECREASING, $OBJ)] +constraints: + DECREASING: + regex: ^(TRUE|FALSE)$ +fix: sort(~~OBJ~~, decreasing = ~~DECREASING~~, na.last = TRUE) +message: | + sort(~~OBJ~~, decreasing = ~~DECREASING~~, na.last = TRUE) is better than + ~~OBJ~~[order(~~OBJ~~, decreasing = ~~DECREASING~~)]. + +--- + +id: sort-3 +language: r +severity: warning +rule: + any: + - pattern: $OBJ[order($OBJ, na.last = $NALAST)] + - pattern: $OBJ[order(na.last = $NALAST, $OBJ)] +constraints: + NALAST: + regex: ^(TRUE|FALSE)$ +fix: sort(~~OBJ~~, na.last = ~~NALAST~~, na.last = TRUE) +message: | + sort(~~OBJ~~, na.last = ~~NALAST~~, na.last = TRUE) is better than + ~~OBJ~~[order(~~OBJ~~, na.last = ~~NALAST~~)]. + +--- + +id: sort-4 +language: r +severity: warning +rule: + any: + - pattern: $OBJ[order($OBJ, decreasing = TRUE, na.last = FALSE)] + - pattern: $OBJ[order($OBJ, na.last = FALSE, decreasing = TRUE)] + - pattern: $OBJ[order(decreasing = TRUE, $OBJ, na.last = FALSE)] + - pattern: $OBJ[order(decreasing = TRUE, na.last = FALSE, $OBJ)] + - pattern: $OBJ[order(na.last = FALSE, decreasing = TRUE, $OBJ)] + - pattern: $OBJ[order(na.last = FALSE, $OBJ, decreasing = TRUE)] +fix: sort(~~OBJ~~, decreasing = TRUE, na.last = FALSE) +message: | + sort(~~OBJ~~, decreasing = TRUE, na.last = FALSE) is better than + ~~OBJ~~[order(~~OBJ~~, na.last = FALSE, decreasing = TRUE)]. + +--- + +id: sort-5 +language: r +severity: warning +rule: + any: + - pattern: sort($OBJ) == $OBJ + - pattern: $OBJ == sort($OBJ) +fix: !is.unsorted(~~OBJ~~) +message: | + Use !is.unsorted(~~OBJ~~) to test the sortedness of a vector. + +--- + +id: sort-6 +language: r +severity: warning +rule: + any: + - pattern: sort($OBJ) != $OBJ + - pattern: $OBJ != sort($OBJ) +fix: is.unsorted(~~OBJ~~) +message: | + Use is.unsorted(~~OBJ~~) to test the unsortedness of a vector. diff --git a/flint/rules/todo_comment.yml b/flint/rules/todo_comment.yml new file mode 100644 index 00000000..83d86edf --- /dev/null +++ b/flint/rules/todo_comment.yml @@ -0,0 +1,7 @@ +id: todo_comment-1 +language: r +severity: warning +rule: + kind: comment + regex: '(?i)#(|\s+)\b(todo|fixme)\b' +message: Remove TODO comments. diff --git a/flint/rules/undesirable_function.yml b/flint/rules/undesirable_function.yml new file mode 100644 index 00000000..c9b17562 --- /dev/null +++ b/flint/rules/undesirable_function.yml @@ -0,0 +1,13 @@ +id: undesirable_function-1 +language: r +severity: warning +rule: + pattern: $FUN + kind: identifier + not: + inside: + kind: argument +constraints: + FUN: + regex: ^(\.libPaths|attach|browser|debug|debugcall|debugonce|detach|par|setwd|structure|Sys\.setenv|Sys\.setlocale|trace|undebug|untrace)$ +message: Function "~~FUN~~()" is undesirable. diff --git a/flint/rules/undesirable_operator.yml b/flint/rules/undesirable_operator.yml new file mode 100644 index 00000000..7d635137 --- /dev/null +++ b/flint/rules/undesirable_operator.yml @@ -0,0 +1,29 @@ +id: undesirable_operator-1 +language: r +severity: warning +rule: + any: + - pattern: $X <<- $Y + - pattern: $X ->> $Y +message: | + Avoid undesirable operators `<<-` and `->>`. They assign outside the current + environment in a way that can be hard to reason about. Prefer fully-encapsulated + functions wherever possible, or, if necessary, assign to a specific environment + with assign(). Recall that you can create an environment at the desired scope + with new.env(). + +--- + +id: undesirable_operator-2 +language: r +severity: warning +rule: + kind: namespace_operator + has: + pattern: ':::' +message: | + Operator `:::` is undesirable. It accesses non-exported functions inside + packages. Code relying on these is likely to break in future versions of the + package because the functions are not part of the public interface and may be + changed or removed by the maintainers without notice. Use public functions + via :: instead. diff --git a/flint/rules/unnecessary_nesting.yml b/flint/rules/unnecessary_nesting.yml new file mode 100644 index 00000000..ac08090b --- /dev/null +++ b/flint/rules/unnecessary_nesting.yml @@ -0,0 +1,31 @@ +id: unnecessary_nesting-1 +language: r +severity: warning +rule: + kind: if_statement + any: + - has: + kind: 'braced_expression' + field: consequence + has: + kind: if_statement + stopBy: neighbor + not: + any: + - has: + nthChild: 2 + - precedes: + regex: "^else$" + - has: + kind: if_statement + field: consequence + stopBy: neighbor + # Can be in if(), but not else if() + not: + inside: + field: alternative + kind: if_statement +message: | + Don't use nested `if` statements, where a single `if` with the combined + conditional expression will do. For example, instead of `if (x) { if (y) { ... }}`, + use `if (x && y) { ... }`. diff --git a/flint/rules/unreachable_code.yml b/flint/rules/unreachable_code.yml new file mode 100644 index 00000000..56eb4974 --- /dev/null +++ b/flint/rules/unreachable_code.yml @@ -0,0 +1,64 @@ +id: unreachable_code-1 +language: r +severity: warning +rule: + regex: '[^}]+' + not: + regex: 'else' + follows: + any: + - pattern: return($$$A) + - pattern: stop($$$A) + not: + precedes: + regex: 'else' + stopBy: end +message: Code and comments coming after a return() or stop() should be removed. + +--- + +id: unreachable_code-2 +language: r +severity: warning +rule: + regex: '[^}]+' + not: + regex: 'else' + follows: + any: + - pattern: next + - pattern: break + stopBy: end +message: Remove code and comments coming after `next` or `break` + +--- + +id: unreachable_code-3 +language: r +severity: warning +rule: + inside: + any: + - kind: if_statement + pattern: if (FALSE) + - kind: while_statement + pattern: while (FALSE) + stopBy: end +message: Remove code inside a conditional loop with a deterministically false condition. + +--- + +id: unreachable_code-4 +language: r +severity: warning +rule: + inside: + any: + - kind: if_statement + pattern: if (TRUE) + - kind: while_statement + pattern: while (TRUE) + stopBy: end +message: | + One branch has a a deterministically true condition. The other branches can + be removed.