Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions azure-pipelines.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,10 @@ jobs:
Write-Host "##vso[task.prependpath]C:\Strawberry\perl\site\bin"
Write-Host "##vso[task.prependpath]C:\Strawberry\c\bin"
displayName: Add strawberry perl to PATH
- task: PowerShell@2
inputs:
filePath: "testing/get-r.ps1"
displayName: install R
- template: job--python-tox.yml@asottile
parameters:
toxenvs: [py37]
Expand All @@ -42,6 +46,8 @@ jobs:
testing/get-swift.sh
echo '##vso[task.prependpath]/tmp/swift/usr/bin'
displayName: install swift
- bash: testing/get-r.sh
displayName: install R
- template: job--python-tox.yml@asottile
parameters:
toxenvs: [pypy3, py36, py37, py38, py39]
Expand All @@ -56,3 +62,5 @@ jobs:
testing/get-swift.sh
echo '##vso[task.prependpath]/tmp/swift/usr/bin'
displayName: install swift
- bash: testing/get-r.sh
displayName: install R
2 changes: 2 additions & 0 deletions pre_commit/languages/all.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
from pre_commit.languages import perl
from pre_commit.languages import pygrep
from pre_commit.languages import python
from pre_commit.languages import r
from pre_commit.languages import ruby
from pre_commit.languages import rust
from pre_commit.languages import script
Expand Down Expand Up @@ -52,6 +53,7 @@ class Language(NamedTuple):
'perl': Language(name='perl', ENVIRONMENT_DIR=perl.ENVIRONMENT_DIR, get_default_version=perl.get_default_version, healthy=perl.healthy, install_environment=perl.install_environment, run_hook=perl.run_hook), # noqa: E501
'pygrep': Language(name='pygrep', ENVIRONMENT_DIR=pygrep.ENVIRONMENT_DIR, get_default_version=pygrep.get_default_version, healthy=pygrep.healthy, install_environment=pygrep.install_environment, run_hook=pygrep.run_hook), # noqa: E501
'python': Language(name='python', ENVIRONMENT_DIR=python.ENVIRONMENT_DIR, get_default_version=python.get_default_version, healthy=python.healthy, install_environment=python.install_environment, run_hook=python.run_hook), # noqa: E501
'r': Language(name='r', ENVIRONMENT_DIR=r.ENVIRONMENT_DIR, get_default_version=r.get_default_version, healthy=r.healthy, install_environment=r.install_environment, run_hook=r.run_hook), # noqa: E501
'ruby': Language(name='ruby', ENVIRONMENT_DIR=ruby.ENVIRONMENT_DIR, get_default_version=ruby.get_default_version, healthy=ruby.healthy, install_environment=ruby.install_environment, run_hook=ruby.run_hook), # noqa: E501
'rust': Language(name='rust', ENVIRONMENT_DIR=rust.ENVIRONMENT_DIR, get_default_version=rust.get_default_version, healthy=rust.healthy, install_environment=rust.install_environment, run_hook=rust.run_hook), # noqa: E501
'script': Language(name='script', ENVIRONMENT_DIR=script.ENVIRONMENT_DIR, get_default_version=script.get_default_version, healthy=script.healthy, install_environment=script.install_environment, run_hook=script.run_hook), # noqa: E501
Expand Down
141 changes: 141 additions & 0 deletions pre_commit/languages/r.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
import contextlib
import os
import shlex
import shutil
from typing import Generator
from typing import Sequence
from typing import Tuple

from pre_commit.envcontext import envcontext
from pre_commit.envcontext import PatchesT
from pre_commit.hook import Hook
from pre_commit.languages import helpers
from pre_commit.prefix import Prefix
from pre_commit.util import clean_path_on_failure
from pre_commit.util import cmd_output_b

ENVIRONMENT_DIR = 'renv'
get_default_version = helpers.basic_get_default_version
healthy = helpers.basic_healthy


def get_env_patch(venv: str) -> PatchesT:
return (
('R_PROFILE_USER', os.path.join(venv, 'activate.R')),
)


@contextlib.contextmanager
def in_env(
prefix: Prefix,
language_version: str,
) -> Generator[None, None, None]:
envdir = _get_env_dir(prefix, language_version)
with envcontext(get_env_patch(envdir)):
yield


def _get_env_dir(prefix: Prefix, version: str) -> str:
return prefix.path(helpers.environment_dir(ENVIRONMENT_DIR, version))


def _prefix_if_file_entry(
entry: Sequence[str],
prefix: Prefix,
) -> Sequence[str]:
if entry[1] == '-e':
return entry[1:]
else:
return (prefix.path(entry[1]),)


def _entry_validate(entry: Sequence[str]) -> None:
"""
Allowed entries:
# Rscript -e expr
# Rscript path/to/file
"""
if entry[0] != 'Rscript':
raise ValueError('entry must start with `Rscript`.')

if entry[1] == '-e':
if len(entry) > 3:
raise ValueError('You can supply at most one expression.')
elif len(entry) > 2:
raise ValueError(
'The only valid syntax is `Rscript -e {expr}`',
'or `Rscript path/to/hook/script`',
)


def _cmd_from_hook(hook: Hook) -> Tuple[str, ...]:
opts = ('--no-save', '--no-restore', '--no-site-file', '--no-environ')
entry = shlex.split(hook.entry)
_entry_validate(entry)

return (
*entry[:1], *opts,
*_prefix_if_file_entry(entry, hook.prefix),
*hook.args,
)


def install_environment(
prefix: Prefix,
version: str,
additional_dependencies: Sequence[str],
) -> None:
env_dir = _get_env_dir(prefix, version)
with clean_path_on_failure(env_dir):
os.makedirs(env_dir, exist_ok=True)
path_desc_source = prefix.path('DESCRIPTION')
if os.path.exists(path_desc_source):
shutil.copy(path_desc_source, env_dir)
shutil.copy(prefix.path('renv.lock'), env_dir)
cmd_output_b(
'Rscript', '--vanilla', '-e',
"""\
missing_pkgs <- setdiff(
"renv", unname(installed.packages()[, "Package"])
)
options(
repos = c(CRAN = "https://cran.rstudio.com"),
renv.consent = TRUE
)
install.packages(missing_pkgs)
renv::activate()
renv::restore()
activate_statement <- paste0(
'renv::activate("', file.path(getwd()), '"); '
)
writeLines(activate_statement, 'activate.R')
is_package <- tryCatch(
suppressWarnings(
unname(read.dcf('DESCRIPTION')[,'Type'] == "Package")
),
error = function(...) FALSE
)
if (is_package) {
renv::install(normalizePath('.'))
}
""",
cwd=env_dir,
)
if additional_dependencies:
cmd_output_b(
'Rscript', '-e',
'renv::install(commandArgs(trailingOnly = TRUE))',
*additional_dependencies,
cwd=env_dir,
)


def run_hook(
hook: Hook,
file_args: Sequence[str],
color: bool,
) -> Tuple[int, bytes]:
with in_env(hook.prefix, hook.language_version):
return helpers.run_xargs(
hook, _cmd_from_hook(hook), file_args, color=color,
)
20 changes: 20 additions & 0 deletions pre_commit/resources/empty_template_renv.lock
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
{
"R": {
"Version": "4.0.3",
"Repositories": [
{
"Name": "CRAN",
"URL": "https://cran.rstudio.com"
}
]
},
"Packages": {
"renv": {
"Package": "renv",
"Version": "0.12.5",
"Source": "Repository",
"Repository": "CRAN",
"Hash": "5c0cdb37f063c58cdab3c7e9fbb8bd2c"
}
}
}
2 changes: 1 addition & 1 deletion pre_commit/store.py
Original file line number Diff line number Diff line change
Expand Up @@ -189,7 +189,7 @@ def _git_cmd(*args: str) -> None:
LOCAL_RESOURCES = (
'Cargo.toml', 'main.go', 'go.mod', 'main.rs', '.npmignore',
'package.json', 'pre_commit_dummy_package.gemspec', 'setup.py',
'environment.yml', 'Makefile.PL',
'environment.yml', 'Makefile.PL', 'renv.lock',
)

def make_local(self, deps: Sequence[str]) -> str:
Expand Down
6 changes: 3 additions & 3 deletions testing/gen-languages-all
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@
import sys

LANGUAGES = [
'conda', 'coursier', 'docker', 'dotnet', 'docker_image', 'fail', 'golang',
'node', 'perl', 'pygrep', 'python', 'ruby', 'rust', 'script', 'swift',
'system',
'conda', 'coursier', 'docker', 'docker_image', 'dotnet', 'fail', 'golang',
'node', 'perl', 'pygrep', 'python', 'r', 'ruby', 'rust', 'script',
'swift', 'system',
]
FIELDS = [
'ENVIRONMENT_DIR', 'get_default_version', 'healthy', 'install_environment',
Expand Down
6 changes: 6 additions & 0 deletions testing/get-r.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
$dir = $Env:Temp
$urlR = "https://cran.r-project.org/bin/windows/base/old/4.0.4/R-4.0.4-win.exe"
$outputR = "$dir\R-win.exe"
$wcR = New-Object System.Net.WebClient
$wcR.DownloadFile($urlR, $outputR)
Start-Process -FilePath $outputR -ArgumentList "/S /v/qn"
9 changes: 9 additions & 0 deletions testing/get-r.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
#!/usr/bin/env bash
sudo apt install r-base
# create empty folder for user library.
# necessary for non-root users who have
# never installed an R package before.
# Alternatively, we require the renv
# package to be installed already, then we can
# omit that.
Rscript -e 'dir.create(Sys.getenv("R_LIBS_USER"), recursive = TRUE)'
48 changes: 48 additions & 0 deletions testing/resources/r_hooks_repo/.pre-commit-hooks.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
# parsing file
- id: parse-file-no-opts-no-args
name: Say hi
entry: Rscript parse-file-no-opts-no-args.R
language: r
types: [r]
- id: parse-file-no-opts-args
name: Say hi
entry: Rscript parse-file-no-opts-args.R
args: [--no-cache]
language: r
types: [r]
## parsing expr
- id: parse-expr-no-opts-no-args-1
name: Say hi
entry: Rscript -e '1+1'
language: r
types: [r]
- id: parse-expr-args-in-entry-2
name: Say hi
entry: Rscript -e '1+1' -e '3' --no-cache3
language: r
types: [r]
# real world
- id: hello-world
name: Say hi
entry: Rscript hello-world.R
args: [blibla]
language: r
types: [r]
- id: hello-world-inline
name: Say hi
entry: |
Rscript -e
'stopifnot(
packageVersion("rprojroot") == "1.0",
packageVersion("gli.clu") == "0.0.0.9000"
)
cat(commandArgs(trailingOnly = TRUE), "from R!\n", sep = ", ")
'
args: ['Hi-there']
language: r
types: [r]
- id: additional-deps
name: Check additional deps
entry: Rscript additional-deps.R
language: r
types: [r]
19 changes: 19 additions & 0 deletions testing/resources/r_hooks_repo/DESCRIPTION
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
Package: gli.clu
Title: What the Package Does (One Line, Title Case)
Type: Package
Version: 0.0.0.9000
Authors@R:
person(given = "First",
family = "Last",
role = c("aut", "cre"),
email = "first.last@example.com",
comment = c(ORCID = "YOUR-ORCID-ID"))
Description: What the package does (one paragraph).
License: `use_mit_license()`, `use_gpl3_license()` or friends to
pick a license
Encoding: UTF-8
LazyData: true
Roxygen: list(markdown = TRUE)
RoxygenNote: 7.1.1
Imports:
rprojroot
2 changes: 2 additions & 0 deletions testing/resources/r_hooks_repo/additional-deps.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
suppressPackageStartupMessages(library("cachem"))
cat("OK\n")
5 changes: 5 additions & 0 deletions testing/resources/r_hooks_repo/hello-world.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
stopifnot(
packageVersion('rprojroot') == '1.0',
packageVersion('gli.clu') == '0.0.0.9000'
)
cat("Hello, World, from R!\n")
27 changes: 27 additions & 0 deletions testing/resources/r_hooks_repo/renv.lock
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
{
"R": {
"Version": "4.0.3",
"Repositories": [
{
"Name": "CRAN",
"URL": "https://cloud.r-project.org"
}
]
},
"Packages": {
"renv": {
"Package": "renv",
"Version": "0.12.5",
"Source": "Repository",
"Repository": "CRAN",
"Hash": "5c0cdb37f063c58cdab3c7e9fbb8bd2c"
},
"rprojroot": {
"Package": "rprojroot",
"Version": "1.0",
"Source": "Repository",
"Repository": "CRAN",
"Hash": "86704667fe0860e4fec35afdfec137f3"
}
}
}
Loading