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
6 changes: 2 additions & 4 deletions .Rbuildignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,11 @@
^\.DS_Store$
^vignettes/\.DS_Store$
^man/figures/.*\.png$
build/
^Makefile$
^\.gdal_doc_cache
^\.gdal_doc_cache.*$
^LICENSE\.md$
^docs$
^\.\.Rcheck$
^.*\.Rcheck$
^\.test_cache$
^\.git$
^\.claude$
Expand All @@ -27,5 +26,4 @@ build/
^examples$
^scratch$
^venv$
^venv/
^build/
3 changes: 3 additions & 0 deletions .github/dockerfiles/Dockerfile.template
Original file line number Diff line number Diff line change
Expand Up @@ -186,9 +186,12 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
libxml2-dev \
locales \
pandoc \
python3-pip \
qpdf && \
rm -rf /var/lib/apt/lists/*

RUN pip3 install --no-cache-dir osgeo-gdal

# Generate en_US.UTF-8 locale to prevent Sys.setlocale() warnings
RUN echo "en_US.UTF-8 UTF-8" > /etc/locale.gen && \
locale-gen en_US.UTF-8 && \
Expand Down
33 changes: 14 additions & 19 deletions .github/workflows/R-CMD-check-docker.yml
Original file line number Diff line number Diff line change
Expand Up @@ -103,10 +103,21 @@ jobs:
run: |
Rscript -e "roxygen2::roxygenise()"

- name: Run R CMD check
- name: Clean build and cache artifacts
run: |
rm -rf build/gdal_repo build/*.o build/*.so .gdal_doc_cache_*

- name: Build source package
run: |
cd ..
R CMD build --no-manual gdalcli

- name: Run R CMD check on tarball
run: |
export LANG=en_US.UTF-8
R CMD check --no-manual .
cd ..
R CMD check --no-manual *.tar.gz
mv gdalcli.Rcheck gdalcli/gdalcli.Rcheck

- name: Install package
run: |
Expand Down Expand Up @@ -138,27 +149,11 @@ jobs:
}
"

- name: Show test and examples output
if: always()
run: |
if [ -f "gdalcli.Rcheck/tests/runtests.Rout.fail" ]; then
echo "=== Test Output (Failures) ==="
tail -100 "gdalcli.Rcheck/tests/runtests.Rout.fail"
elif [ -f "gdalcli.Rcheck/tests/runtests.Rout" ]; then
echo "=== Test Output ==="
tail -50 "gdalcli.Rcheck/tests/runtests.Rout"
fi

if [ -f "gdalcli.Rcheck/gdalcli-Ex.Rout" ]; then
echo "=== Examples Output ==="
tail -30 "gdalcli.Rcheck/gdalcli-Ex.Rout"
fi

- name: Upload check results
uses: actions/upload-artifact@v7
if: always()
with:
name: check-results-${{ matrix.r-version }}-gdal-${{ matrix.gdal-version }}
path: gdalcli.Rcheck/
retention-days: 7
if-no-files-found: ignore
if-no-files-found: warn
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,5 @@ venv/
scratch/
.Rproj.user
*.Rproj
*.Rhistory
*.Rhistory
gdalcli.Rcheck/
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -142,4 +142,4 @@ dev: regen docs check-man
@echo "============================================"
@echo "[OK] Quick dev build complete!"
@echo "============================================"
@echo ""
@echo ""
2 changes: 1 addition & 1 deletion NEWS.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# gdalcli 0.5.2 (2026-03-21)
# gdalcli 0.5.2 (2026-03-25)

- Added Arrow support for in-memory vector processing
- Added arrow R package to Docker dependencies
Expand Down
21 changes: 13 additions & 8 deletions R/core-gdalg-transpiler.R
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,11 @@
#' @keywords internal
#' @noRd
.quote_argument <- function(str) {
# Empty strings must be quoted
if (str == "") {
return("''")
}

# Characters that are safe without quoting (alphanumeric + common safe chars)
# This covers most common filenames and simple arguments
if (grepl("^[a-zA-Z0-9/_.:=-]*$", str)) {
Expand Down Expand Up @@ -66,21 +71,21 @@

# Generate the CLI flag from argument name
# Special mappings for common deviations
flag_mapping <- c(
flag_mapping <- list(
"resolution" = "--resolution",
"size" = "--ts",
"extent" = "--te"
)
cli_flag <- if (arg_name %in% names(flag_mapping)) {
flag_mapping[arg_name]
flag_mapping[[arg_name]]
} else {
paste0("--", gsub("_", "-", arg_name))
}

# Handle logical/boolean arguments
if (is.logical(value)) {
if (value) {
return(cli_flag) # Just the flag for TRUE
return(unname(cli_flag)) # Just the flag for TRUE
} else {
return(character(0)) # Nothing for FALSE
}
Expand All @@ -89,7 +94,7 @@
# Check if this is a composite (fixed-count, comma-separated) argument
is_composite <- FALSE
arg_meta <- arg_mapping[[arg_name]]
if (!is.null(arg_meta) && !is.null(arg_meta$min_count) && !is.null(arg_meta$max_count)) {
if (!is.null(arg_meta) && is.list(arg_meta) && !is.null(arg_meta$min_count) && !is.null(arg_meta$max_count)) {
is_composite <- arg_meta$min_count == arg_meta$max_count && arg_meta$min_count > 1
}

Expand All @@ -98,7 +103,7 @@
if (is_composite) {
# Composite: comma-separated
formatted_values <- sapply(value, function(v) .quote_argument(as.character(v)), USE.NAMES = FALSE)
return(c(cli_flag, paste(formatted_values, collapse = ",")))
return(unname(c(cli_flag, paste(formatted_values, collapse = ","))))
} else {
# Repeatable: repeated flags
formatted_values <- sapply(value, function(v) .quote_argument(as.character(v)), USE.NAMES = FALSE)
Expand All @@ -107,13 +112,13 @@
for (val in formatted_values) {
result <- c(result, cli_flag, val)
}
return(result)
return(unname(result))
}
}

# Single value: quote and return
formatted_value <- .quote_argument(as.character(value))
c(cli_flag, formatted_value)
unname(c(cli_flag, formatted_value))
}


Expand Down Expand Up @@ -329,7 +334,7 @@

# For now, use a simpler grepl-based approach that's more reliable
# This regex matches either single-quoted strings or sequences of non-whitespace
pattern <- "(['\"][^'\"]*['\"]|[^ \\t]+)"
pattern <- "(['\"][^'\"]*['\"]|[^ \t]+)"

# Use gregexpr with the ENTIRE result to pass to regmatches
matches <- gregexpr(pattern, step_str)
Expand Down
7 changes: 6 additions & 1 deletion R/core-optional-features.R
Original file line number Diff line number Diff line change
Expand Up @@ -62,11 +62,16 @@

#' Check Arrow Vectors Capability
#'
#' Internal check: Arrow support available in GDAL
#' Internal check: Arrow support available in GDAL (requires GDAL 3.12+)
#'
#' @keywords internal
#' @noRd
.check_arrow_vectors_available <- function() {
# Requires GDAL 3.12+
if (!gdal_check_version("3.12", op = ">=")) {
return(FALSE)
}

# Check if GDAL was compiled with Arrow support
.check_gdal_has_arrow_driver()
}
Expand Down
13 changes: 12 additions & 1 deletion R/core-options.R
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
#' @param audit_logging Logical. Log all job executions. Default: FALSE.
#' @param stream_out_format Character. Default output streaming format:
#' NULL (no streaming), "text", "raw", "json", or "stdout". Default: NULL.
#' @param ... Additional arguments (not allowed; raises error if provided).
#'
#' @return Invisibly returns a list of current gdalcli options (before modification).
#'
Expand Down Expand Up @@ -111,7 +112,17 @@ gdalcli_options <- function(checkpoint = NULL,
backend = NULL,
verbose = NULL,
audit_logging = NULL,
stream_out_format = NULL) {
stream_out_format = NULL,
...) {
# Check for unknown arguments
dots <- list(...)
if (length(dots) > 0) {
unknown_arg <- names(dots)[1]
cli::cli_abort(
"Unknown option: {unknown_arg}. Valid options are: checkpoint, checkpoint_dir, backend, verbose, audit_logging, stream_out_format"
)
}

# Store current options for return value
current_opts <- .get_all_gdalcli_options()

Expand Down
5 changes: 4 additions & 1 deletion man/gdalcli_options.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 7 additions & 2 deletions tests/testthat/setup.R
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,11 @@ if (requireNamespace("reticulate", quietly = TRUE)) {
}

# Ensure step mappings are loaded for tests
if (is.null(.gdalcli_env$step_mappings)) {
.load_step_mappings()
if (requireNamespace("gdalcli", quietly = TRUE)) {
if (exists(".gdalcli_env", where = asNamespace("gdalcli"))) {
env <- get(".gdalcli_env", envir = asNamespace("gdalcli"))
if (is.null(env$step_mappings)) {
gdalcli:::.load_step_mappings()
}
}
}
6 changes: 3 additions & 3 deletions tests/testthat/test_arrow_integration.R
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ test_that("arrow table has schema", {
tbl <- arrow::arrow_table(df)
schema <- arrow::schema(tbl)

expect_type(schema, "list")
expect_true(inherits(schema, c("Schema", "ArrowObject", "R6")))
expect_true(length(schema) > 0)
})

Expand Down Expand Up @@ -345,8 +345,8 @@ test_that("Round-trip conversion preserves basic structure", {
tbl <- arrow::arrow_table(df)
arrow::write_feather(tbl, temp_file)

# Read back
tbl_read <- arrow::read_feather(temp_file)
# Read back as Arrow table (as_data_frame = FALSE to get Arrow table)
tbl_read <- arrow::read_feather(temp_file, as_data_frame = FALSE)
expect_s3_class(tbl_read, "ArrowTabular")
}
})
Expand Down
26 changes: 13 additions & 13 deletions tests/testthat/test_backends.R
Original file line number Diff line number Diff line change
Expand Up @@ -348,21 +348,21 @@ find_python_gdal_env <- function() {
if (!requireNamespace("reticulate", quietly = TRUE)) {
return(FALSE)
}
tryCatch({

suppressWarnings(tryCatch({
# Check current environment first
if (reticulate::py_module_available("osgeo.gdal")) {
return(TRUE)
}

# Try venv in project root and common relative locations
venv_paths <- c(
file.path(getwd(), "venv"), # venv in current dir
file.path(dirname(getwd()), "venv"), # venv in parent dir
"venv", # relative venv
"~/.venv" # user home venv
)

for (path in venv_paths) {
expanded_path <- path.expand(path)
if (dir.exists(expanded_path)) {
Expand All @@ -372,7 +372,7 @@ find_python_gdal_env <- function() {
}
}
}

# Try standard virtualenv locations
venvs <- reticulate::virtualenv_list()
for (venv in venvs) {
Expand All @@ -385,7 +385,7 @@ find_python_gdal_env <- function() {
}
}
FALSE
}, error = function(e) FALSE)
}, error = function(e) FALSE))
}

# Helper function to check if Python GDAL is available
Expand Down Expand Up @@ -534,7 +534,7 @@ test_that("gdalraster backend returns text output from raster_info", {

# If we got output, verify it's character
if (!is.null(result)) {
expect_is(result, "character")
expect_type(result, "character")
expect_gt(nchar(result), 0)
}
})
Expand Down Expand Up @@ -748,9 +748,9 @@ test_that("gdalraster backend produces text output from raster_info", {
# Execute with gdalraster backend requesting text output
job <- gdal_raster_info(input = sample_file)
result <- gdal_job_run(job, backend = "gdalraster", stream_out_format = "text")

# Verify output
expect_is(result, "character")
expect_type(result, "character")
expect_gt(nchar(result), 0)

# Should contain GDAL output characteristics
Expand Down Expand Up @@ -779,7 +779,7 @@ test_that("reticulate backend produces text output from raster_info", {
expect_true(result)
} else {
# Should be character text
expect_is(result, "character")
expect_type(result, "character")
expect_gt(nchar(result), 0)
}
})
Expand Down Expand Up @@ -878,12 +878,12 @@ test_that("execution tests handle both backends with stream_out_format parameter
# Test text format
job1 <- gdal_raster_info(input = sample_file)
result_text <- gdal_job_run(job1, backend = "gdalraster", stream_out_format = "text")
expect_is(result_text, "character")
expect_type(result_text, "character")

# Test raw format
job2 <- gdal_raster_info(input = sample_file)
result_raw <- gdal_job_run(job2, backend = "gdalraster", stream_out_format = "raw")
expect_is(result_raw, "raw")
expect_type(result_raw, "raw")

# Raw and text should both represent the same output
expect_equal(length(result_raw), nchar(result_text))
Expand Down Expand Up @@ -984,7 +984,7 @@ test_that("backends handle environment variables properly", {

# Should produce valid output or be skipped
if (result != "skipped") {
expect_is(result, "character")
expect_type(result, "character")
expect_gt(nchar(result), 0)
}
})
Expand Down
Loading