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
4 changes: 3 additions & 1 deletion .github/workflows/R-CMD-check.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,15 @@ jobs:
- {os: ubuntu-latest, r: 'devel', http-user-agent: 'release'}
- {os: ubuntu-latest, r: 'release'}
- {os: ubuntu-latest, r: 'oldrel-1'}
# Minimum R in DESCRIPTION
- {os: ubuntu-latest, r: '4.1'}

env:
GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }}
R_KEEP_PKG_SOURCE: yes

steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v5

- uses: r-lib/actions/setup-pandoc@v2

Expand Down
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.10
Version: 0.7.11
Authors@R:
person("Jumping", "Rivers", , "info@jumpingrivers.com", role = c("aut", "cre"))
Description: Base package for sharing classes between posit audit
Expand Down
3 changes: 3 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
# audit.base 0.7.11 _2026-10-16_
* chore: The sys-deps check and the R, Python, Quarto and Posit version lookups are tested offline against fixtures, and CI also checks on R 4.1, the minimum version (#64)

# audit.base 0.7.10 _2026-10-16_
* chore: The package is MIT licensed (#63)

Expand Down
11 changes: 7 additions & 4 deletions R/get_latest_versions_remote.R
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,7 @@ get_latest_versions_remote = function() {
# The latest patch of each Quarto minor release. Most releases are prereleases, so the newest 100
# reach back several minors; older minors come from the fixed list
get_quarto_versions = function() {
releases = jsonlite::read_json(
"https://api.github.com/repos/quarto-dev/quarto-cli/releases?per_page=100"
)
releases = fetch_json("https://api.github.com/repos/quarto-dev/quarto-cli/releases?per_page=100")
stable = purrr::discard(releases, ~ isTRUE(.x$prerelease))
tags = stringr::str_remove(purrr::map_chr(stable, "tag_name"), "^v")
old = c("1.0.38", "1.1.189", "1.2.475", "1.3.450", "1.4.557", "1.5.57", "1.6.43")
Expand All @@ -42,7 +40,7 @@ latest_patch_per_minor = function(versions) {
get_latest_versions_from_posit = function(type = c("r", "python")) {
type = match.arg(type)
url = glue::glue("https://cdn.posit.co/{type}/versions.json")
versions = unlist(jsonlite::read_json(url))
versions = unlist(fetch_json(url))
versions = unname(versions)
tibble::tibble(
patch = get_patch(versions),
Expand All @@ -56,3 +54,8 @@ get_latest_versions_from_posit = function(type = c("r", "python")) {
dplyr::slice(1) %>%
dplyr::pull(versions)
}

# JSON from a URL as nested lists
fetch_json = function(url) {
jsonlite::fromJSON(url, simplifyVector = FALSE)
}
19 changes: 14 additions & 5 deletions R/get_pkg_requirements.R
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,10 @@ get_pkg_requirements = function(
"Connecting to {url} for system requirements"
)

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
r = read_p3m(url)
if (!is.null(r$error)) {
cli::cli_warn(
"Posit Package Manager can't list system requirements for {distribution} {release}: {reason}"
"Posit Package Manager can't list system requirements for {distribution} {release}: {r$error}"
)
return(NA)
}
Expand All @@ -29,3 +27,14 @@ get_pkg_requirements = function(
~ tibble::tibble(pkg = .x$name, sys_libs = unlist(.x$requirements$packages))
)
}

# P3M's response as a list. On an HTTP error, `error` holds P3M's reason
read_p3m = function(url) {
res = httr::GET(url, httr::timeout(60))
r = httr::content(res)
if (!httr::http_error(res)) {
return(r)
}
reason = if (is.list(r) && is.character(r$error)) r$error else httr::http_status(res)$message
list(error = reason)
}
6 changes: 5 additions & 1 deletion R/get_posit_remote_versions.R
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ get_posit_remote_versions = function(type = c("connect", "workbench")) {
} else {
"https://docs.posit.co/ide/news/"
}
page = rvest::read_html(url)
page = read_posit_news(url)
sections = rvest::html_elements(page, "section")
v_tibbles = purrr::map_df(sections, extract_posit_cves)
all_v = get_all_remote_versions(page) %>%
Expand Down Expand Up @@ -47,3 +47,7 @@ get_all_remote_versions = function(page) {
v = stringr::str_extract(versions, "20[0-9]{2}\\.[0-9]{2}\\.[0-9]{1,2}")
tibble::tibble(version = v[!is.na(v)], cve = "")
}

read_posit_news = function(url) {
rvest::read_html(url)
}
2 changes: 1 addition & 1 deletion R/get_uv_python_versions.R
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
get_uv_python_versions = function() {
url = "https://raw.githubusercontent.com/astral-sh/uv/main/crates/uv-python-managed/download-metadata.json" # nolint: line_length_linter
raw_json = jsonlite::fromJSON(url, simplifyVector = FALSE)
raw_json = fetch_json(url)

if (length(raw_json) == 0) {
warning("Fetched Python JSON is empty -- check the URL/branch/tag.")
Expand Down
41 changes: 41 additions & 0 deletions tests/testthat/fixtures/p3m-ubuntu-24.04.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
{
"requirements": [
{
"name": "curl",
"requirements": {
"packages": [
"libcurl4-openssl-dev",
"libssl-dev"
]
}
},
{
"name": "fftw",
"requirements": {
"packages": [
"libfftw3-dev"
]
}
},
{
"name": "sf",
"requirements": {
"packages": [
"libgdal-dev",
"gdal-bin",
"libgeos-dev",
"libproj-dev",
"libsqlite3-dev"
]
}
},
{
"name": "xml2",
"requirements": {
"packages": [
"libxml2-dev"
]
}
}
]
}
33 changes: 33 additions & 0 deletions tests/testthat/fixtures/posit-news.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
<!-- Trimmed from https://docs.posit.co/connect/news/ -->
<html><body>
<section id="introduction" class="level2">
<h2 class="anchored" data-anchor-id="introduction">Introduction</h2>
<p>This document contains the notes associated with each release of Posit Connect.</p>
</section>
<section id="posit-connect-2026.10.0" class="level2">
<h2 class="anchored" data-anchor-id="posit-connect-2026.10.0">2026.10.0</h2>
<section id="posit-connect-2026.10.0-new" class="level3">
<h3 class="anchored" data-anchor-id="posit-connect-2026.10.0-new">New</h3>
<ul>
<li><p>A built-in MCP server enables AI assistants to inspect, publish, and deploy content.</p></li>
</ul>
</section>
</section>
<section id="posit-connect-2025.09.0" class="level2">
<h2 class="anchored" data-anchor-id="posit-connect-2025.09.0">2025.09.0</h2>
<section id="posit-connect-2025.09.0-fixed" class="level3">
<h3 class="anchored" data-anchor-id="posit-connect-2025.09.0-fixed">Fixed</h3>
<ul>
<li><p>Updated software components used by Posit Connect, including responses to the following:</p>
<ul>
<li>CVE-2025-47906 <!--https://go.dev/issue/74466 --></li>
<li>CVE-2025-47907 <!-- https://go.dev/issue/74831 --></li>
</ul></li>
</ul>
</section>
</section>
<section id="posit-connect-2025.07.0" class="level2">
<h2 class="anchored" data-anchor-id="posit-connect-2025.07.0">2025.07.0</h2>
<p>July 2025</p>
</section>
</body></html>
18 changes: 18 additions & 0 deletions tests/testthat/fixtures/quarto-releases.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
[
{
"tag_name": "v1.11.6",
"prerelease": true
},
{
"tag_name": "v1.10.19",
"prerelease": false
},
{
"tag_name": "v1.10.18",
"prerelease": false
},
{
"tag_name": "v1.9.38",
"prerelease": false
}
]
10 changes: 10 additions & 0 deletions tests/testthat/fixtures/r-versions.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"r_versions": [
"next",
"devel",
"4.6.1",
"4.6.0",
"4.5.3",
"4.5.2"
]
}
86 changes: 86 additions & 0 deletions tests/testthat/fixtures/uv-metadata.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
{
"cpython-3.16.0a1-linux-x86_64-gnu": {
"name": "cpython",
"arch": {
"family": "x86_64",
"variant": null
},
"os": "linux",
"libc": "gnu",
"major": 3,
"minor": 16,
"patch": 0,
"prerelease": "a1",
"variant": null
},
"cpython-3.14.8-linux-x86_64-gnu": {
"name": "cpython",
"arch": {
"family": "x86_64",
"variant": null
},
"os": "linux",
"libc": "gnu",
"major": 3,
"minor": 14,
"patch": 8,
"prerelease": "",
"variant": null
},
"cpython-3.14.7-linux-x86_64-gnu": {
"name": "cpython",
"arch": {
"family": "x86_64",
"variant": null
},
"os": "linux",
"libc": "gnu",
"major": 3,
"minor": 14,
"patch": 7,
"prerelease": "",
"variant": null
},
"cpython-3.14.8-darwin-aarch64-none": {
"name": "cpython",
"arch": {
"family": "aarch64",
"variant": null
},
"os": "darwin",
"libc": "none",
"major": 3,
"minor": 14,
"patch": 8,
"prerelease": "",
"variant": null
},
"cpython-3.14.8-linux-x86_64-musl": {
"name": "cpython",
"arch": {
"family": "x86_64",
"variant": null
},
"os": "linux",
"libc": "musl",
"major": 3,
"minor": 14,
"patch": 8,
"prerelease": "",
"variant": null
},
"cpython-3.13.16-linux-x86_64-gnu": {
"name": "cpython",
"arch": {
"family": "x86_64",
"variant": null
},
"os": "linux",
"libc": "gnu",
"major": 3,
"minor": 13,
"patch": 16,
"prerelease": "",
"variant": null
}
}
31 changes: 31 additions & 0 deletions tests/testthat/helper-fixtures.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# Offline stand-ins for the remote lookups, read from tests/testthat/fixtures
read_fixture_json = function(name) {
jsonlite::read_json(testthat::test_path("fixtures", name))
}

# The NEWS page fixture serves both Connect and Workbench
local_posit_news = function(env = parent.frame()) {
testthat::local_mocked_bindings(
read_posit_news = function(url) {
rvest::read_html(testthat::test_path("fixtures", "posit-news.html"))
},
.env = env
)
}

local_version_json = function(env = parent.frame()) {
testthat::local_mocked_bindings(
fetch_json = function(url) {
if (grepl("quarto-cli", url, fixed = TRUE)) {
read_fixture_json("quarto-releases.json")
} else if (grepl("cdn.posit.co/r/", url, fixed = TRUE)) {
read_fixture_json("r-versions.json")
} else if (grepl("download-metadata.json", url, fixed = TRUE)) {
read_fixture_json("uv-metadata.json")
} else {
stop("No fixture for ", url)
}
},
.env = env
)
}
Loading
Loading