Skip to content

Commit 94431bc

Browse files
committed
Introduce and document .ci/lint.R
For use in both .github/workflows/code-quality.yaml and manually, especially when developing the linters.
1 parent f5ef6b8 commit 94431bc

File tree

2 files changed

+65
-0
lines changed

2 files changed

+65
-0
lines changed

.ci/README.md

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,33 @@ Base R implemented helper script, [originally proposed to base R](https://svn.r-
4949

5050
Base R implemented helper script to orchestrate generation of most artifacts and to arrange them nicely. It is being used only in [_integration_ stage in GitLab CI pipeline](./../.gitlab-ci.yml).
5151

52+
### [`lint.R`](./lint.R)
53+
54+
Base R runner for the manual (non-`lintr`) lint checks to be run from GitHub Actions during the code quality check. The command line arguments are as follows:
55+
1. Path to the directory containing files defining the linters. A linter is a function that accepts one argument (typically the path to the file) and signals an error if it fails the lint check.
56+
2. Path to the directory containing files to check.
57+
3. A regular expression matching the files to check.
58+
4. An R expression evaluating to a function that processes a single file path into a form understood by the lint functions. Additionally, the function may return `NULL` to indicate that the file must be skipped.
59+
60+
Example command lines:
61+
62+
```sh
63+
# C linters expect a three-element named list, not the file path
64+
Rscript .ci/lint.R .ci/linters/c src '[.][ch]$' '
65+
function (f) list(
66+
c_obj = f, lines = readLines(f),
67+
preprocessed = system2("gcc", shQuote(c("-fpreprocessed", "-E", f)), stdout = TRUE, stderr = FALSE)
68+
)
69+
'
70+
# po linters must skip files that don't differ from master
71+
Rscript .ci/lint.R .ci/linters/po po '[.]po$' '
72+
function (f) {
73+
diff_v_master = system2("git", c("diff", "master", f), stdout=TRUE)
74+
if (length(diff_v_master)) f
75+
}
76+
'
77+
```
78+
5279
## GitLab Open Source Program
5380

5481
We are currently part of the [GitLab for Open Source Program](https://about.gitlab.com/solutions/open-source/). This gives us 50,000 compute minutes per month for our GitLab CI. Our license needs to be renewed yearly (around July) and is currently managed by @ben-schwen.

.ci/lint.R

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
#!/usr/bin/Rscript
2+
# Runner for the manual lint checks in .ci/linters
3+
args = commandArgs(TRUE)
4+
if (identical(args, '--help')) {
5+
writeLines(c(
6+
'Usage: Rscript .ci/lint.R .ci/linters/<KIND> <WHERE> <WHAT> [PREPROCESS]',
7+
'KIND must name the directory containing the *.R files defining the linter functions.',
8+
'WHERE must name the directory containing the files to lint, e.g. "po", or "src".',
9+
"WHAT must contain the regular expression matching the files to lint, e.g., '[.]po$', or '[.][ch]$'.",
10+
'PREPROCESS is an optional R function that accepts a file path and returns an R object accepted by the lint functions, or NULL to skip the file.'
11+
))
12+
q('no')
13+
}
14+
stopifnot(`Invalid arguments, see .ci/lint.R --help` = length(args) %in% 3:4)
15+
16+
linter_env = new.env()
17+
for (f in list.files(args[[1]], full.names=TRUE)) sys.source(f, linter_env)
18+
if (!length(ls(linter_env))) stop(
19+
"No linters found after sourcing files in ", dQuote(args[[1]])
20+
)
21+
22+
sources = list.files(args[[2]], pattern = args[[3]], full.names = TRUE, recursive = TRUE)
23+
if (!length(sources)) stop(
24+
"No files to lint found in directory ", dQuote(args[[2]]), " for mask ", dQuote(args[[3]])
25+
)
26+
preprocess = if (length(args) == 4) eval(parse(text = args[[4]])) else identity
27+
sources = Filter(Negate(is.null), lapply(setNames(nm = sources), preprocess))
28+
29+
okay = TRUE
30+
for (src in names(sources))
31+
for (linter in ls(linter_env)) tryCatch(
32+
linter_env[[linter]](sources[[src]]),
33+
error = function(e) {
34+
message('Source file ', dQuote(src), ' failed lint check ', dQuote(linter), ': ', conditionMessage(e))
35+
okay <<- FALSE
36+
}
37+
)
38+
stopifnot(`Please fix the issues above.` = okay)

0 commit comments

Comments
 (0)