Skip to content

Commit 5e697c9

Browse files
committed
Roll a build
1 parent 6833d72 commit 5e697c9

File tree

76 files changed

+4982
-0
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

76 files changed

+4982
-0
lines changed

NAMESPACE

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
# Generated by roxygen2: do not edit by hand
2+
3+
export(build_image)
4+
export(cleanup_container)
5+
export(dockerize)
6+
export(export)
7+
export(run_container)
8+
export(sitrep_app_conversion)
9+
export(sitrep_docker)
10+
export(stop_container)

R/app-detection.R

+225
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,225 @@
1+
#' Detect the type of Shiny application (R or Python)
2+
#'
3+
#' @param app_dir Character. Path to the Shiny application directory.
4+
#'
5+
#' @return Character. Either "r" or "python".
6+
#'
7+
#' @keywords internal
8+
detect_app_type <- function(app_dir) {
9+
# Look for R files
10+
r_files <- list.files(app_dir, pattern = "\\.R$|\\.r$", recursive = TRUE)
11+
12+
# Look for Python files
13+
py_files <- list.files(app_dir, pattern = "\\.py$", recursive = TRUE)
14+
15+
# Check for Python's app.py or server.py
16+
has_py_app <- any(grepl("app\\.py$|server\\.py$", py_files))
17+
18+
# Check for R's app.R, server.R, or ui.R
19+
has_r_app <- any(grepl("app\\.R$|server\\.R$|ui\\.R$", r_files, ignore.case = TRUE))
20+
21+
if (has_py_app && !has_r_app) {
22+
return("python")
23+
} else if (has_r_app || length(r_files) > 0) {
24+
return("r")
25+
} else if (length(py_files) > 0) {
26+
return("python")
27+
} else {
28+
# If we can't determine, default to R
29+
warning("Could not determine app type, defaulting to R. Specify app_type manually if needed.")
30+
return("r")
31+
}
32+
}
33+
34+
#' Check if directory contains Shiny app files
35+
#'
36+
#' @param app_dir Character. Path to the Shiny application directory.
37+
#'
38+
#' @return Logical. TRUE if the directory contains valid Shiny app files.
39+
#'
40+
#' @keywords internal
41+
has_shiny_app_files <- function(app_dir) {
42+
# Check for R Shiny app files
43+
has_r_app <- file.exists(file.path(app_dir, "app.R")) ||
44+
(file.exists(file.path(app_dir, "ui.R")) &&
45+
file.exists(file.path(app_dir, "server.R")))
46+
47+
# Check for Python Shiny app files
48+
has_py_app <- file.exists(file.path(app_dir, "app.py")) ||
49+
(file.exists(file.path(app_dir, "ui.py")) &&
50+
file.exists(file.path(app_dir, "server.py")))
51+
52+
return(has_r_app || has_py_app)
53+
}
54+
55+
#' Check if directory contains valid Shiny app files and abort if not
56+
#'
57+
#' @param app_dir Character. Path to the Shiny application directory.
58+
#'
59+
#' @return Invisibly returns TRUE if valid, otherwise aborts with an error message.
60+
#'
61+
#' @keywords internal
62+
check_shiny_app_files <- function(app_dir) {
63+
if (!has_shiny_app_files(app_dir)) {
64+
cli::cli_abort(c(
65+
"Directory does not appear to contain a valid Shiny application.",
66+
"i" = "Expected files for R apps: {.file app.R}, or {.file ui.R} and {.file server.R}",
67+
"i" = "Expected files for Python apps: {.file app.py}, or {.file ui.py} and {.file server.py}"
68+
))
69+
}
70+
71+
invisible(TRUE)
72+
}
73+
74+
#' Detect dependencies for a Shiny application
75+
#'
76+
#' @param app_dir Character. Path to the Shiny application directory.
77+
#' @param app_type Character. Either "r" or "python".
78+
#'
79+
#' @return Character vector with detected dependencies.
80+
#'
81+
#' @keywords internal
82+
detect_dependencies <- function(app_dir, app_type) {
83+
if (app_type == "r") {
84+
return(detect_r_dependencies(app_dir))
85+
} else if (app_type == "python") {
86+
return(detect_python_dependencies(app_dir))
87+
}
88+
}
89+
#' Extract package names from library() and require() calls
90+
#'
91+
#' @param r_code Character vector. R code to analyze.
92+
#'
93+
#' @return Character vector of package names.
94+
#'
95+
#' @keywords internal
96+
extract_library_packages <- function(r_code) {
97+
lib_pattern <- "library\\(([^,)]+)\\)|require\\(([^,)]+)\\)"
98+
lib_matches <- gregexpr(lib_pattern, r_code, perl = TRUE)
99+
100+
packages <- character(0)
101+
for (i in seq_along(r_code)) {
102+
if (lib_matches[[i]][1] != -1) {
103+
matches <- regmatches(r_code[i], lib_matches[[i]])
104+
for (match in matches) {
105+
# Extract the package name from library()/require() call
106+
pkg_name <- gsub("library\\(([^,)]+)\\)|require\\(([^,)]+)\\)", "\\1\\2", match, perl = TRUE)
107+
# Remove quotes if present
108+
pkg_name <- gsub("^['\"]|['\"]$", "", pkg_name)
109+
packages <- c(packages, pkg_name)
110+
}
111+
}
112+
}
113+
114+
return(packages)
115+
}
116+
117+
#' Extract package names from namespace calls (package::function)
118+
#'
119+
#' @param r_code Character vector. R code to analyze.
120+
#'
121+
#' @return Character vector of package names.
122+
#'
123+
#' @keywords internal
124+
extract_namespace_packages <- function(r_code) {
125+
pkg_pattern <- "([[:alnum:].]+)::"
126+
pkg_matches <- gregexpr(pkg_pattern, r_code, perl = TRUE)
127+
128+
packages <- character(0)
129+
for (i in seq_along(r_code)) {
130+
if (pkg_matches[[i]][1] != -1) {
131+
matches <- regmatches(r_code[i], pkg_matches[[i]])
132+
for (match in matches) {
133+
# Extract the package name from package:: call
134+
pkg_name <- gsub("([[:alnum:].]+)::", "\\1", match, perl = TRUE)
135+
packages <- c(packages, pkg_name)
136+
}
137+
}
138+
}
139+
140+
return(packages)
141+
}
142+
143+
#' Detect R package dependencies
144+
#'
145+
#' @param app_dir Character. Path to the Shiny application directory.
146+
#'
147+
#' @return Character vector of detected R package dependencies.
148+
#'
149+
#' @keywords internal
150+
detect_r_dependencies <- function(app_dir) {
151+
# Look for R files
152+
r_files <- list.files(app_dir, pattern = "\\.R$|\\.r$", full.names = TRUE, recursive = TRUE)
153+
154+
if (length(r_files) == 0) {
155+
warning("No R files found in the application directory.")
156+
return(c("shiny"))
157+
}
158+
159+
# Read all R files
160+
r_code <- unlist(lapply(r_files, readLines))
161+
162+
# Extract packages from different patterns
163+
library_packages <- extract_library_packages(r_code)
164+
namespace_packages <- extract_namespace_packages(r_code)
165+
166+
# Combine and ensure shiny is included
167+
packages <- unique(c("shiny", library_packages, namespace_packages))
168+
return(sort(packages))
169+
}
170+
171+
#' Detect Python package dependencies
172+
#'
173+
#' @param app_dir Character. Path to the Shiny application directory.
174+
#'
175+
#' @return Character vector of detected Python package dependencies.
176+
#'
177+
#' @keywords internal
178+
detect_python_dependencies <- function(app_dir) {
179+
# Look for requirements.txt
180+
req_file <- file.path(app_dir, "requirements.txt")
181+
182+
if (file.exists(req_file)) {
183+
# Read requirements.txt
184+
packages <- readLines(req_file)
185+
# Remove comments and empty lines
186+
packages <- packages[!grepl("^\\s*#", packages) & nzchar(trimws(packages))]
187+
# Remove version specifications
188+
packages <- gsub("([^<>=~!]+)[<>=~!].*", "\\1", packages)
189+
# Trim whitespace
190+
packages <- trimws(packages)
191+
return(packages)
192+
}
193+
194+
# If no requirements.txt, look for import statements in Python files
195+
py_files <- list.files(app_dir, pattern = "\\.py$", full.names = TRUE, recursive = TRUE)
196+
197+
if (length(py_files) == 0) {
198+
warning("No Python files found in the application directory.")
199+
return(c("shiny"))
200+
}
201+
202+
# Read all Python files
203+
py_code <- unlist(lapply(py_files, readLines))
204+
205+
# Look for import statements
206+
import_pattern <- "^\\s*import\\s+([^\\s.]+)|^\\s*from\\s+([^\\s.]+)"
207+
import_matches <- gregexpr(import_pattern, py_code, perl = TRUE)
208+
209+
# Extract package names
210+
packages <- character(0)
211+
for (i in seq_along(py_code)) {
212+
if (import_matches[[i]][1] != -1) {
213+
matches <- regmatches(py_code[i], import_matches[[i]])
214+
for (match in matches) {
215+
# Extract the package name from import or from statement
216+
pkg_name <- gsub("^\\s*import\\s+([^\\s.]+)|^\\s*from\\s+([^\\s.]+)", "\\1\\2", match, perl = TRUE)
217+
packages <- c(packages, pkg_name)
218+
}
219+
}
220+
}
221+
222+
# Remove duplicates and sort
223+
packages <- sort(unique(c("shiny", packages)))
224+
return(packages)
225+
}

0 commit comments

Comments
 (0)