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: 1 addition & 1 deletion DESCRIPTION
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
Type: Package
Package: audit.base
Title: Base package for Posit Checks
Version: 0.7.5
Version: 0.7.6
Authors@R:
person("Jumping", "Rivers", , "info@jumpingrivers.com", role = c("aut", "cre"))
Description: Base package for sharing classes between posit audit
Expand Down
4 changes: 4 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
# audit.base 0.7.6 _2026-10-16_
* fix: `check_sys_deps()` reads the distribution from os-release `ID` and `ID_LIKE`, and passes `VERSION_ID` to Posit Package Manager, so Ubuntu 26.04, RHEL 10, Debian, Rocky Linux and AlmaLinux work. If P3M or the package listing doesn't support a system, it warns with the reason and returns `NA` rather than stopping the audit (#58)
* fix: System requirements come from `packagemanager.posit.co`, Posit Package Manager's current address

# audit.base 0.7.5 _2026-10-16_
* fix: The Quarto version list comes from GitHub's releases, so a new minor release no longer marks the previous one as past EOL (#60)

Expand Down
43 changes: 24 additions & 19 deletions R/check_sys_deps.R
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@ check_sys_deps = function(os_release, installed_libs, debug_level = 0:2) {
cli::cli_h2("Systems Libraries")
# Clean up response
pkg_look_up = get_os_sys_deps(os_release)
# get_os_sys_deps() and clean_libs() have warned why
if (!is.data.frame(pkg_look_up) || anyNA(installed_libs)) {
return(NA)
}

# Determine which libs are missing
missing_libs = pkg_look_up[!(pkg_look_up$sys_libs %in% installed_libs), ]
Expand All @@ -31,21 +35,18 @@ check_sys_deps = function(os_release, installed_libs, debug_level = 0:2) {
# Contents of /etc/os-release
get_os_sys_deps = function(os_release) {
os_release_df = os_release_to_df(os_release)
version_id = os_release_df[os_release_df$name == "version_id", ]$value
if (is_ubuntu(os_release_df)) {
id = "ubuntu"
} else if (is_redhat(os_release_df)) {
id = "redhat"
version_id = stringr::str_remove(version_id, "\\..*")
} else if (is_centos(os_release_df)) {
id = "centos"
version_id = stringr::str_remove(version_id, "\\..*")
} else {
cli::cli_abort("This OS isn't supported")
system = get_p3m_system(os_release_df)
if (is.na(system$distribution)) {
os_name = os_release_value(os_release_df, "pretty_name") # nolint
cli::cli_warn("Can't check system libraries: Posit Package Manager doesn't support {os_name}")
return(NA)
}
reqs = get_pkg_requirements(distribution = system$distribution, release = system$release)
if (!is.data.frame(reqs)) {
return(NA)
}
reqs = get_pkg_requirements(distribution = id, release = version_id)
reqs = reqs[!is.na(reqs$sys_libs), ]
if (id == "ubuntu") {
if (system$distribution %in% c("ubuntu", "debian")) {
reqs$sys_libs = switch_ubuntu_aliases(reqs$sys_libs)
}
reqs
Expand All @@ -59,14 +60,15 @@ get_os_sys_deps = function(os_release) {
get_installed_libs = function() {
os_release = get_os_release()
os_release_df = os_release_to_df(os_release)
if (is_ubuntu(os_release_df)) {
tool = get_package_tool(os_release_df)
if (identical(tool, "apt")) {
libs = processx::run("apt", args = c("list", "--installed"))
libs = stringr::str_split(libs$stdout, "\n")[[1]][-1]
} else if (is_redhat(os_release_df) || is_centos(os_release_df)) {
} else if (identical(tool, "yum")) {
libs = processx::run("yum", args = c("list", "installed"))$stdout
libs = stringr::str_split(libs, "\n")[[1]][-(1:2)]
} else {
cli::cli_abort("We don't support this OS yet")
libs = character(0)
}
clean_libs(os_release, libs)
}
Expand All @@ -77,13 +79,16 @@ get_installed_libs = function() {
#' @rdname get_installed_libs
clean_libs = function(os_release, libs) {
os_release_df = os_release_to_df(os_release)
if (is_ubuntu(os_release_df)) {
tool = get_package_tool(os_release_df)
if (identical(tool, "apt")) {
libs = stringr::str_match(libs, "^[^/]*")[, 1]
libs = switch_ubuntu_aliases(libs)
} else if (is_redhat(os_release_df) || is_centos(os_release_df)) {
} else if (identical(tool, "yum")) {
libs = stringr::str_match(libs, "^[^\\.]*")[, 1]
} else {
cli::cli_abort("We don't support this OS yet")
os_name = os_release_value(os_release_df, "pretty_name") # nolint
cli::cli_warn("Can't list the installed system libraries on {os_name}")
return(NA_character_)
}
sort(libs)
}
Expand Down
20 changes: 13 additions & 7 deletions R/get_pkg_requirements.R
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
# https://packagemanager.rstudio.com/__api__/swagger/index.html#/default/get_repos
# https://packagemanager.posit.co/__api__/swagger/index.html#/default/get_repos
# NA, with P3M's reason as a warning, when P3M doesn't support the system
get_pkg_requirements = function(
distribution = c("ubuntu", "redhat", "centos"),
release = c("18.04", "20.04", "22.04", "24.04", "7", "8", "9"),
base_url = "https://packagemanager.rstudio.com/__api__/repos/",
distribution,
release,
base_url = "https://packagemanager.posit.co/__api__/repos/",
repo_id = 2
) {
distribution = match.arg(distribution)
release = match.arg(release)
config_url = glue::glue(
"{repo_id}/sysreqs?all=true&distribution={distribution}&release={release}"
) # nolint
Expand All @@ -15,8 +14,15 @@ get_pkg_requirements = function(
"Connecting to {url} for system requirements"
)

res = httr::GET(url)
res = httr::GET(url, httr::timeout(60))
r = httr::content(res)
if (httr::http_error(res)) {
reason = if (is.list(r) && is.character(r$error)) r$error else httr::http_status(res)$message # nolint
cli::cli_warn(
"Posit Package Manager can't list system requirements for {distribution} {release}: {reason}"
)
return(NA)
}

purrr::map_df(
r$requirements,
Expand Down
44 changes: 35 additions & 9 deletions R/os-helper-functions.R
Original file line number Diff line number Diff line change
@@ -1,16 +1,42 @@
is_ubuntu = function(os_release_df) {
os_name = os_release_df[os_release_df$name == "name", ]$value
stringr::str_detect(os_name, pattern = "ubuntu")
# Posit Package Manager's distribution and release, from os-release ID, then ID_LIKE.
# Rocky Linux and AlmaLinux list rhel in ID_LIKE, so they use the Red Hat requirements.
get_p3m_system = function(os_release_df) {
p3m_names = c(
ubuntu = "ubuntu",
debian = "debian",
centos = "centos",
rhel = "redhat",
"opensuse-leap" = "opensuse",
sles = "sle"
)
ids = c(
os_release_value(os_release_df, "id"),
stringr::str_split(os_release_value(os_release_df, "id_like"), " ")[[1]]
)
distribution = unname(p3m_names[ids[ids %in% names(p3m_names)]])[1L]
release = os_release_value(os_release_df, "version_id")
# P3M uses the major version for these: 9, not 9.4
if (distribution %in% c("debian", "redhat", "centos")) {
release = stringr::str_remove(release, "\\..*")
}
list(distribution = distribution, release = release)
}

is_redhat = function(os_release_df) {
os_name = os_release_df[os_release_df$name == "name", ]$value
stringr::str_detect(os_name, pattern = "red hat")
# apt or yum, which list installed packages in different formats; NA for any other OS
get_package_tool = function(os_release_df) {
distribution = get_p3m_system(os_release_df)$distribution
if (distribution %in% c("ubuntu", "debian")) {
"apt"
} else if (distribution %in% c("redhat", "centos")) {
"yum"
} else {
NA_character_
}
}

is_centos = function(os_release_df) {
os_name = os_release_df[os_release_df$name == "name", ]$value
stringr::str_detect(os_name, pattern = "centos")
os_release_value = function(os_release_df, name) {
value = os_release_df$value[os_release_df$name == name]
if (length(value) == 0L) "" else value[1L]
}

get_os_release = function() {
Expand Down
4 changes: 4 additions & 0 deletions R/quarto-helpers.R
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,10 @@ get_quarto_server_header = function(out) {
#' @export
get_quarto_sys_deps = function(out) {
sys_deps = out$sys_deps
# NA when the system libraries couldn't be checked
if (!is.data.frame(sys_deps)) {
sys_deps = tibble::tibble(pkg = character(0), sys_libs = character(0))
}
sys_deps %>%
dplyr::group_by(.data$sys_libs) %>%
dplyr::reframe(
Expand Down
44 changes: 44 additions & 0 deletions tests/testthat/test-os-helper-functions.R
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,47 @@ test_that("OS tests", {
libs = get_installed_libs()
expect_gt(length(libs), 1000)
})

test_that("get_p3m_system() maps os-release to P3M's distribution and release", {
os_releases = list(
ubuntu = c("ID=ubuntu", "ID_LIKE=debian", "VERSION_ID=\"26.04\""),
debian = c("ID=debian", "VERSION_ID=\"12\""),
rhel = c("ID=\"rhel\"", "ID_LIKE=\"fedora\"", "VERSION_ID=\"10.0\""),
centos = c("ID=\"centos\"", "ID_LIKE=\"rhel fedora\"", "VERSION_ID=\"7\""),
rocky = c("ID=\"rocky\"", "ID_LIKE=\"rhel centos fedora\"", "VERSION_ID=\"9.4\""),
alma = c("ID=\"almalinux\"", "ID_LIKE=\"rhel centos fedora\"", "VERSION_ID=\"8.10\""),
suse = c("ID=\"opensuse-leap\"", "ID_LIKE=\"suse opensuse\"", "VERSION_ID=\"15.6\""),
arch = c("ID=arch", "BUILD_ID=rolling")
)
expected = list(
ubuntu = c("ubuntu", "26.04"),
debian = c("debian", "12"),
rhel = c("redhat", "10"),
centos = c("centos", "7"),
rocky = c("redhat", "9"),
alma = c("redhat", "8"),
suse = c("opensuse", "15.6"),
arch = c(NA, "")
)
for (os in names(os_releases)) {
system = get_p3m_system(os_release_to_df(os_releases[[os]]))
expect_identical(c(system$distribution, system$release), expected[[os]], label = os)
}
})

test_that("Unsupported systems warn and give NA instead of stopping", {
arch = c("PRETTY_NAME=\"Arch Linux\"", "ID=arch")
expect_warning(check_sys_deps(arch, "zlib"), "arch linux")
expect_identical(suppressWarnings(check_sys_deps(arch, "zlib")), NA)
expect_warning(clean_libs(arch, "zlib"), "arch linux")
expect_identical(suppressWarnings(clean_libs(arch, "zlib")), NA_character_)

out = get_quarto_sys_deps(list(sys_deps = NA))
expect_equal(colnames(out), c("sys_libs", "pkg", "n"))
})

test_that("P3M's reason for rejecting a system reaches the user", {
skip_if_offline("packagemanager.posit.co")
expect_warning(get_pkg_requirements("rockylinux", "8"), "Unsupported system")
expect_identical(suppressWarnings(get_pkg_requirements("rockylinux", "8")), NA)
})
Loading