unlock unlimited code runs for $79, limited time →
NOSSLETTER.techLIVE
back to OSSpedia
configuration-managementtesting-strategydependency-management

GH-46646: [dev][R] Replace linr with jarl for R linting / pre-commit check

Project: apache/arrow | Language: C++ | Concepts: configuration-management, testing-strategy, dependency-management | Difficulty: mid
Link: https://github.com/apache/arrow/pull/50851

The problem

The project used the lintr R package for linting R code in pre-commit checks. This required a working R installation, which caused failures in environments without R (or with non-standard setups like conda). This made the developer and CI experience unreliable and brittle.

Why it's non-trivial

Simply removing or replacing the linter isn't enough:

  • The replacement must work cross-platform, without requiring R.
  • The new linter must be integrated into pre-commit and CI.
  • Generated files and known problematic rules must be excluded to avoid false positives and CI failures.
  • Documentation and developer workflow instructions need updating.

The approach

1. Remove R dependency from CI and pre-commit cache:

-            r-base \

Removes the installation of R from the CI workflow, since it's no longer needed for linting.

-            ~/.local/share/renv/cache

Removes the R package cache from pre-commit caching, further decoupling from R.

2. Replace the lintr pre-commit hook with jarl:

-  - repo: local
+  - repo: https://github.com/etiennebacher/jarl-pre-commit
+    rev: "0.5.0"
     hooks:
-      - id: lintr
+      - id: jarl-check
         alias: r
-        name: R Lint
-        language: r
-        additional_dependencies:
-          - cyclocomp
-          - lintr
-          - testthat
-        entry: |
-          Rscript -e "Sys.setenv(NOT_CRAN = 'TRUE'); lintr::expect_lint_free('r')"
-        pass_filenames: false
-        files: >-
-          ^r/.*\.(R|Rmd)$
+        name: R Lint (jarl)

Swaps out the local lintr hook (which required R and extra dependencies) for the Rust-native, standalone jarl linter. This means linting no longer depends on R being installed.

3. Add a jarl configuration file to control linting behavior:

# r/jarl.toml
[lint]
exclude = ["R/arrowExports.R", "R/dplyr-funcs-doc.R", "data-raw/codegen.R"]

ignore = [
  "any_is_na",
  "comparison_negation",
  "download_file",
  "duplicated_arguments",
  "equals_null",
  "implicit_assignment",
  "internal_function",
  "numeric_leading_zero",
  "outer_negation",
  "redundant_equals",
  "redundant_ifelse",
  "repeat",
  "seq",
  "sprintf",
  "unused_function",
]

Defines which files to exclude (generated or codegen files) and which rules to ignore (existing violations that would otherwise fail CI). This ensures a smooth transition and green CI.

4. Update developer documentation and workflow instructions:

-* [`lintr`](https://github.com/r-lib/lintr) for code analysis
+* [`jarl`](https://etiennebacher.github.io/jarl/) for code analysis

Replaces references to lintr with jarl in documentation and vignettes.

-```r
-lintr::lint_package()
-```
+```
+jarl check .
+```

Updates usage instructions so developers know how to run the new linter.

From the review thread

Copilot:

jarl.toml is added under r/, but the pre-commit hook runs from the repo root. Per Jarl’s config discovery, it only searches jarl.toml in the current working directory and parent directories, so r/jarl.toml won’t be picked up; the excludes there won’t apply and generated files may get linted unexpectedly. Consider either moving jarl.toml to the repo root, or changing the hook to execute with cwd=r/ (bearing in mind Windows support / filename path rewriting).

jorisvandenbossche:

The pre-commit logs in the failing CI clearly indicates Used '/home/runner/work/arrow/arrow/r/jarl.toml', so it seems the config file is correctly used

Takeaways

  • Prefer standalone, language-agnostic tools for CI checks to avoid fragile system dependencies.
  • When swapping out a linter or formatter, update all related configuration, documentation, and ignore lists to ensure a smooth transition.
  • Always verify how configuration files are discovered and loaded in your toolchain, especially when changing directory structures or tool invocation paths.