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
2 changes: 2 additions & 0 deletions .Rprofile
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
source("rv/scripts/rvr.R")
source("rv/scripts/activate.R")
123 changes: 123 additions & 0 deletions .github/workflows/r-tests.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
name: R CI

on:
push:
branches: [main]
pull_request:
workflow_dispatch:

permissions:
contents: read

concurrency:
group: r-ci-${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

env:
R_VERSION: '4.5.2'
RV_VERSION: '0.22.2'
RV_SHA256: '9805dbad14eb049265c0052635873c9b7891bc7334d445662c13fbad61cb269c'

jobs:
standard:
name: Standard checks
runs-on: ubuntu-latest
timeout-minutes: 30
steps:
- uses: actions/checkout@v6

- uses: r-lib/actions/setup-r@v2
with:
r-version: ${{ env.R_VERSION }}

- name: Install rv
shell: bash
run: |
mkdir -p "$HOME/.local/bin"
curl -fsSL \
"https://github.com/A2-ai/rv/releases/download/v${RV_VERSION}/rv-v${RV_VERSION}-x86_64-unknown-linux-gnu.tar.gz" \
-o "$RUNNER_TEMP/rv.tar.gz"
echo "$RV_SHA256 $RUNNER_TEMP/rv.tar.gz" | sha256sum --check
tar -xzf "$RUNNER_TEMP/rv.tar.gz" -C "$HOME/.local/bin"
echo "$HOME/.local/bin" >> "$GITHUB_PATH"

- name: Restore R package library
uses: actions/cache@v6
with:
path: rv/library
key: rv-${{ env.RV_VERSION }}-${{ runner.os }}-${{ runner.arch }}-r-${{ env.R_VERSION }}-${{ hashFiles('rproject.toml') }}

- name: Install system dependencies
shell: bash
run: |
sudo apt-get update
rv sysdeps --only-absent | xargs --no-run-if-empty sudo apt-get install -y

- name: Sync R dependencies
run: rv sync

- name: Validate minimal demo inputs
run: |
Rscript validate.R --data-root data/vqa-demo1-min
Rscript validate.R --data-root data/vqa-demo2-min

- name: Run standard tests
run: Rscript tests/testthat.R

- name: Run lintr
shell: Rscript {0}
run: |
files <- c(
"analyse.R",
"validate.R",
list.files(
c(
"analysis", "pipeline", "preprocessing", "validation",
"tests/testthat", "tests/refactor/testthat"
),
pattern = "[.]R$", recursive = TRUE, full.names = TRUE
)
)
lints <- unlist(lapply(files, lintr::lint), recursive = FALSE)
print(lints)
quit(status = as.integer(length(lints) > 0L))

refactor:
name: Refactor parity
runs-on: ubuntu-latest
timeout-minutes: 60
steps:
- uses: actions/checkout@v6

- uses: r-lib/actions/setup-r@v2
with:
r-version: ${{ env.R_VERSION }}

- name: Install rv
shell: bash
run: |
mkdir -p "$HOME/.local/bin"
curl -fsSL \
"https://github.com/A2-ai/rv/releases/download/v${RV_VERSION}/rv-v${RV_VERSION}-x86_64-unknown-linux-gnu.tar.gz" \
-o "$RUNNER_TEMP/rv.tar.gz"
echo "$RV_SHA256 $RUNNER_TEMP/rv.tar.gz" | sha256sum --check
tar -xzf "$RUNNER_TEMP/rv.tar.gz" -C "$HOME/.local/bin"
echo "$HOME/.local/bin" >> "$GITHUB_PATH"

- name: Restore R package library
uses: actions/cache@v6
with:
path: rv/library
key: rv-${{ env.RV_VERSION }}-${{ runner.os }}-${{ runner.arch }}-r-${{ env.R_VERSION }}-${{ hashFiles('rproject.toml') }}

- name: Install system dependencies
shell: bash
run: |
sudo apt-get update
rv sysdeps --only-absent | xargs --no-run-if-empty sudo apt-get install -y

- name: Sync R dependencies
run: rv sync

- name: Run refactor parity tests
run: Rscript tests/refactor/testthat.R
2 changes: 0 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,9 @@ CHANGELOG_ps.md

# R project files
## rv
rv/
rv.lock

## R
.Rprofile
.Rhistory
.Rapp.history

Expand Down
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
# Vegetation Quality Assessment (VQA)

[![R CI](https://github.com/OxfordRSE/OpenVQA/actions/workflows/r-tests.yml/badge.svg)](https://github.com/OxfordRSE/OpenVQA/actions/workflows/r-tests.yml)

***Warning: this is an outdated README. Will update soon for OpenVQA***

VQA is a semi-automated R pipeline for estimating quality of reclaimed vegetation relative to undisturbed native vegetation.
Expand Down Expand Up @@ -84,6 +86,9 @@ You can read more about VQA in detail in this open access [publication](https://

## Developer Setup

GitHub Actions validates both minimal demo projects, runs the standard and refactor parity
test suites, and lints the actively refactored R code on pull requests and pushes to `main`.

If you are working on the codebase itself, use the tracked `pre-commit` configuration to run formatting and linting automatically before commits.

1. Install the R project dependencies.
Expand Down
6 changes: 4 additions & 2 deletions rproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,10 @@ r_version = "4.5"

# A list of repositories to fetch packages from. Order matters: we will try to get a package from each repository in order.
# The alias is only used in this file if you want to specifically require a dependency to come from a certain repository.
# Example: { alias = "PPM", url = "https://packagemanager.posit.co/cran/latest" },
repositories = [{ alias = "CRAN", url = "https://cloud.r-project.org/" }]
repositories = [
{ alias = "PPM", url = "https://packagemanager.posit.co/cran/latest" },
{ alias = "CRAN", url = "https://cloud.r-project.org/" },
]

# A list of packages to install and any additional configuration
# Examples:
Expand Down
1 change: 1 addition & 0 deletions rv/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
library
94 changes: 94 additions & 0 deletions rv/scripts/activate.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
local({
if (!nzchar(Sys.which("rv"))) {
warning(
"rv is not installed! Install rv, then restart your R session",
call. = FALSE
)
return()
}
rv_info <- system2(
"rv",
c("info", "--library", "--r-version", "--repositories"),
stdout = TRUE
)
if (!is.null(attr(rv_info, "status"))) {
# if system2 fails it'll add a status attribute with the error code
warning("failed to run rv info, check your console for messages")
return()
}
get_val <- function(prefix) {
line <- grep(paste0("^", prefix, ":"), rv_info, value = TRUE)
sub(paste0("^", prefix, ":\\s*"), "", line)
}

# Set repos option
repo_str <- get_val("repositories")

repo_parts <- strsplit(repo_str, "), ", fixed = TRUE)[[1]]
repo_parts <- gsub("[()]", "", repo_parts)

repo_urls <- character(length(repo_parts))
repo_names <- character(length(repo_parts))

for (i in seq_along(repo_parts)) {
parts <- strsplit(repo_parts[i], ",", fixed = TRUE)[[1]]
repo_names[i] <- trimws(parts[1])
repo_urls[i] <- trimws(parts[2])
}
names(repo_urls) <- repo_names
options(repos = repo_urls)

# Check R version and set library
rv_r_ver <- get_val("r-version")
sys_r <- sprintf("%s.%s", R.version$major, R.version$minor)
r_match <- grepl(paste0("^", rv_r_ver), sys_r)

rv_lib <- if (r_match) {
normalizePath(get_val("library"), mustWork = FALSE)
} else {
message(sprintf(
"WARNING: R version specified in config (%s) does not match session version (%s).
rv library will not be activated until the issue is resolved. Entering safe mode...
",
rv_r_ver,
sys_r
))
file.path(tempdir(), "__rv_R_mismatch")
}

if (!dir.exists(rv_lib)) {
if (r_match) {
message("creating rv library: ", rv_lib, "\n")
} else {
message("creating temporary library: ", rv_lib, "\n")
}
dir.create(rv_lib, recursive = TRUE)
}

.libPaths(rv_lib, include.site = FALSE)
Sys.setenv("R_LIBS_USER" = rv_lib)
Sys.setenv("R_LIBS_SITE" = rv_lib)

# Results
if (interactive()) {
message(
"rv repositories active!\nrepositories: \n",
paste0(
" ",
names(getOption("repos")),
": ",
getOption("repos"),
collapse = "\n"
),
"\n"
)
message(
if (r_match) {
"rv libpaths active!\nlibrary paths: \n"
} else {
"rv libpaths are not active due to R version mismatch. Using temp directory: \n"
},
paste0(" ", .libPaths(), collapse = "\n")
)
}
})
26 changes: 26 additions & 0 deletions rv/scripts/rvr.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
.rv <- new.env()
.rv$config_path <- file.path(normalizePath(getwd()), "rproject.toml")
.rv$summary <- function(json = FALSE) {
command <- c("summary")
if (json) { command <- c(command, "--json") }
.rv$command(command)
}
.rv$plan <- function() { .rv$command("plan") }
.rv$sync <- function() { .rv$command("sync") }
.rv$add <- function(..., dry_run = FALSE) {
dots <- unlist(list(...))
command <- c("add", dots)
if (dry_run) { command <- c(command, "--dry-run") }
.rv$command(command)
}

.rv$command <- function(command) {
# underlying system calls to rv
args <- c(command, "-c", .rv$config_path)
res <- system2("rv", args, stdout = TRUE)
if (!is.null(attr(res, "status"))) {
warning(sprintf("failed to run `rv %s`, check your console for messages", paste(args, collapse = " ")))
} else {
message(paste(res, collapse = "\n"))
}
}
Loading