lint-changed-lines.yml
Run lintr over the R files a pull request changes, but report only the lints on lines the PR actually added or modified – not whole changed files. This lets lint rules be adopted or tightened incrementally: new and edited code must comply, while untouched legacy code in the same file is left alone until a PR naturally edits it.
Contrast with a whole-file “lint changed files” gate, which fails on function definitions a PR never touched – forcing either a repo-wide reformat or disabling the linter. lint-changed-lines is the incremental alternative. The whole-file, whole-package, and whole-project forms live in lint-changed-files.yml, selected by a scope input; that capability does not fold this one in, because the added-lines diffing here is different machinery.
How it works
The workflow checks out the PR head commit (not the default refs/pull/N/merge ref), sets up R, installs the package (so object_usage_linter can resolve the package’s own functions), then runs lintr on each changed R file and keeps only the lints whose line falls on a line the PR added or modified. Those changed lines are parsed from the GitHub “list PR files” API patch, whose line numbers are relative to the PR head – which is why the checkout is pinned to the head commit, so on-disk line numbers match.
Must be triggered by pull_request (the changed-line set comes from the PR).
A self-hosted runner that already has R keeps it: the workflow skips setup-r, whose apt-get step needs passwordless sudo.
A repository with no DESCRIPTION in path, or with install-package: false, skips setup-r-dependencies: the workflow installs gh, lintr and any extra-packages from CRAN (plain package names only) and skips R CMD INSTALL. On Linux, when the runner sets no CRAN mirror, it installs Posit Package Manager binaries.
Inputs
| Input | Type | Default | Description |
|---|---|---|---|
path |
string | '.' |
Path to the R package root (the directory holding DESCRIPTION, if any). |
install-quarto |
boolean | false |
Install Quarto when setting up R dependencies. |
use-renv |
boolean | false |
Restore dependencies with renv (setup-renv) instead of DESCRIPTION-based setup-r-dependencies. |
renv-cache-version |
string | '1' |
Cache version suffix passed to setup-renv. |
apt-packages |
string | '' |
Extra apt packages to install before setting up dependencies (space-separated). |
extra-packages |
string | '' |
Extra R packages to install (space- or comma-separated; passed to setup-r-dependencies, or installed from CRAN under use-renv: true or when path holds no DESCRIPTION or install-package is false). gh and lintr are always installed. |
install-package |
boolean | true |
Run R CMD INSTALL before linting so object_usage_linter can resolve the package’s own functions. Set false for a repo that is not an installable R package. |
fail |
boolean | true |
Fail the workflow when a lint is found on a changed line (rather than warn only). |
file-pattern |
string | '[.][Rr]$' |
Regular expression matched against each changed file’s path; only matching files are linted. Use '[.]([Rr]\|[Rr]md\|[Qq]md)$' to also lint the R chunks in Quarto and R Markdown documents. |
lms-ref |
string | '' |
Commit SHA (or tag) of Morrison-Lab/psc to install the lms package from. When set, changed lines are linted with lms::default_linters(nested_calls = TRUE) instead of the linters in the repository’s .lintr.R; the exclusions in .lintr.R still apply. |
runs-on |
string | empty (CI_RUNNER variable, else ubuntu-latest) |
Runner label for the job. Use a self-hosted label only for a private repository; never for a public one, whose pull requests can run arbitrary code on it (#1167). |
Permissions
contents: read and pull-requests: read (to read the PR’s changed-file patches).
Example
# Copy to .github/workflows/lint-changed-lines.yml in your repo.
# Lints only the lines a pull request adds or modifies (not whole changed
# files), so lint rules can be adopted or tightened incrementally.
name: lint-changed-lines
on:
pull_request:
jobs:
lint:
uses: Morrison-Lab/gha/.github/workflows/lint-changed-lines.yml@v2
# with:
# path: '.' # R package root (holding DESCRIPTION)
# use-renv: false # true to restore dependencies with setup-renv
# renv-cache-version: '1' # bump to invalidate renv cache
# apt-packages: '' # system packages required for compilation
# extra-packages: '' # extra R deps (gh + lintr always installed)
# install-package: false # skip R CMD INSTALL for a non-package repo
# fail: false # warn instead of failing on lints
# file-pattern: '[.]([Rr]|[Rr]md|[Qq]md)$' # also lint R chunks in .qmd/.Rmd
# lms-ref: <commit SHA of Morrison-Lab/psc> # lint with lms::default_linters(nested_calls = TRUE)
# runs-on: private-runner # self-hosted runner label (private repositories only)See the examples/ directory for the full caller stub (lint-changed-lines.yml).