build-cran-binaries/.superpowers/handoff/task-A2-brief.md

3.8 KiB

Task A2: Platform matching and version-constraint satisfaction

Files:

  • Modify: R/patches.R
  • Test: tests/testthat/test-patches.R

Interfaces:

  • Consumes: normalized entries from Task A1.

  • Produces: build_platform_tokens(platform, arch) → character vector; entry_matches_platform(entry, tokens) → logical; match_patch_entries(registry, platform, arch) → filtered list; version_satisfies(version, constraint) → logical (constraint forms: "*" handled by caller, "x.y.z" exact, ">=x", "<=x", ">x", "<x", "==x").

  • Step 1: Write the failing test

test_that("build_platform_tokens expands family and arch", {
  expect_setequal(
    build_platform_tokens("ubuntu-2604", "amd64"),
    c("ubuntu-2604", "ubuntu", "amd64")
  )
  expect_setequal(
    build_platform_tokens("alpine-324", "arm64"),
    c("alpine-324", "alpine", "arm64")
  )
})

test_that("match_patch_entries matches family, codename, arch, and wildcard", {
  reg <- list(
    list(package = "A", versions = "*", platforms = "alpine", reason = "r"),
    list(package = "B", versions = "*", platforms = "ubuntu-2604", reason = "r"),
    list(package = "C", versions = "*", platforms = "*", reason = "r"),
    list(package = "D", versions = "*", platforms = "redhat", reason = "r")
  )
  got <- vapply(
    match_patch_entries(reg, "ubuntu-2604", "amd64"),
    function(e) e$package, character(1L)
  )
  expect_setequal(got, c("B", "C"))
})

test_that("version_satisfies handles operators and hyphenated versions", {
  expect_true(version_satisfies("5.1.11-2", "5.1.11-2"))
  expect_true(version_satisfies("5.1.11-2", "==5.1.11-2"))
  expect_true(version_satisfies("5.1.12", ">=5.1.0"))
  expect_false(version_satisfies("5.0.0", ">=5.1.0"))
  expect_true(version_satisfies("5.1.11-2", "<=5.1.11-2"))
  expect_false(version_satisfies("5.1.12", "<=5.1.11-2"))
})
  • Step 2: Run test to verify it fails

Run: Rscript -e 'devtools::load_all("."); testthat::test_file("tests/testthat/test-patches.R")' Expected: FAIL with "could not find function build_platform_tokens".

  • Step 3: Write minimal implementation (append to R/patches.R)
#' Build platform tokens for patch matching
#' @keywords internal
build_platform_tokens <- function(platform, arch) {
  family <- sub("-.*$", "", platform)
  unique(c(platform, family, arch))
}

#' Does a patch entry apply to the current platform tokens?
#' @keywords internal
entry_matches_platform <- function(entry, tokens) {
  any(entry$platforms == "*") ||
    length(intersect(entry$platforms, tokens)) > 0L
}

#' Filter registry entries applicable to the current build
#' @keywords internal
match_patch_entries <- function(registry, platform, arch) {
  if (length(registry) == 0L) {
    return(list())
  }
  tokens <- build_platform_tokens(platform, arch)
  Filter(function(e) entry_matches_platform(e, tokens), registry)
}

#' Test whether a version satisfies a single constraint
#'
#' @param version A version string (CRAN style, may contain `-`).
#' @param constraint One of `"x.y.z"`, `"==x"`, `">=x"`, `"<=x"`, `">x"`, `"<x"`.
#' @keywords internal
version_satisfies <- function(version, constraint) {
  constraint <- trimws(constraint)
  parts <- regmatches(
    constraint,
    regexec("^(>=|<=|==|>|<)?\\s*(.+)$", constraint)
  )[[1L]]
  op <- parts[2L]
  target <- parts[3L]
  v <- package_version(version)
  t <- package_version(target)
  if (op == "" || op == "==") {
    return(v == t)
  }
  switch(
    op,
    ">=" = v >= t,
    "<=" = v <= t,
    ">" = v > t,
    "<" = v < t,
    FALSE
  )
}
  • Step 4: Run test to verify it passes

Run: Rscript -e 'devtools::load_all("."); testthat::test_file("tests/testthat/test-patches.R")' Expected: PASS.

  • Step 5: Commit
git add R/patches.R tests/testthat/test-patches.R
git commit -m "feat(patches): platform matching and version-constraint checks"