Graduate BioC blocklist helper into R/; expand write_qualified_pkg_lists() by default (#183) - #184
Conversation
…verse The existing blocklist-BioC.txt from write_qualified_pkg_lists() is the inverse of the *assessed* BioC set only. BioC packages dropped at remote_reduce (or otherwise never seen) are absent from both the allowlist and the blocklist, so PPM would silently serve them. dev/dev_build_bioc_blocklist.R pulls the full BioC universe via available.packages() against the config's BioC repo(s) and emits blocklist = universe − Low-risk allowlist as a CSV with a 'reason' column (not_assessed vs assessed_<risk>). Repo detection uses a case-insensitive substring match on 'bioc' against both the repo alias and the URL, so non-OSS forks that consolidate the four BioC sub-repos into a single entry are covered (both alias and URL contain 'bioc'). Smoke test against R_4.5.2/20260730/qual_metadata.rds: universe 2300 assessed by pipeline 150 Low-risk allowlist 13 full blocklist 2287 (137 assessed_High/Medium + 2150 not_assessed) Fixes #183 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
🟡 Changes recommended
Historical config snapshots and URL-only BioC repository aliases are handled incorrectly, potentially producing an inaccurate blocklist.
Get a fresh assessment by requesting another Copilot review.
Pull request overview
Adds a development helper to generate a complete BioC provisioning blocklist from the full repository universe.
Changes:
- Detects BioC repositories and queries
available.packages(). - Generates a reasoned CSV blocklist excluding Low-risk packages.
- Bumps the package version and documents the change.
File summaries
| File | Description |
|---|---|
dev/dev_build_bioc_blocklist.R |
Adds the blocklist-generation helper. |
NEWS.md |
Documents the BioC blocklist fix. |
DESCRIPTION |
Bumps the version to 0.1.60. |
Review details
- Files reviewed: 3/3 changed files
- Comments generated: 2
- Review effort level: Balanced
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
Two correctness fixes from the PR #184 review: 1. When val_dir is provided and the caller didn't pin config_path, default to <val_dir>/config.yml if it exists. Historical runs have a snapshotted config next to their qual_metadata.rds; without this, the BioC universe would be resolved against the session/installed config and compared with decisions from a different BioC release (config drift). 2. The qm-side BioC mask previously used only grepl('bioc', qm$repo_name, ignore.case = TRUE). That misses the URL-only-alias case (e.g. alias 'sci' -> bioconductor URL) — qm$repo_name stores the alias 'sci' with no 'bioc' substring, so those Low-risk pkgs would be excluded from the allowlist and wrongly blocklisted. Fix is a union: match against names(bioc_repos) OR the case-insensitive substring fallback. The fallback is still needed because riskmetric sometimes emits labels like 'BioCsoft' that don't literally equal the config alias 'BioC'. Smoke tests validate both mask paths end-to-end and confirm the 2,287-pkg blocklist result is unchanged for the 20260730 prod run (both mask paths agree there). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Two correctness fixes from the PR #184 review: 1. When val_dir is provided and the caller didn't pin config_path, default to <val_dir>/config.yml if it exists. Historical runs have a snapshotted config next to their qual_metadata.rds; without this, the BioC universe would be resolved against the session/installed config and compared with decisions from a different BioC release (config drift). 2. The qm-side BioC mask previously used only grepl('bioc', qm$repo_name, ignore.case = TRUE). That misses the URL-only-alias case (e.g. alias 'sci' -> bioconductor URL) — qm$repo_name stores the alias 'sci' with no 'bioc' substring, so those Low-risk pkgs would be excluded from the allowlist and wrongly blocklisted. Fix is a union: match against names(bioc_repos) OR the case-insensitive substring fallback. The fallback is still needed because riskmetric sometimes emits labels like 'BioCsoft' that don't literally equal the config alias 'BioC'. Smoke tests validate both mask paths end-to-end and confirm the 2,287-pkg blocklist result is unchanged for the 20260730 prod run (both mask paths agree there). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
78a2eb8 to
f05ad6f
Compare
|
Adopted both Copilot suggestions in f05ad6f. 1. Snapshotted config (line 132) — legit. When 2. URL-only-alias mask (line 181) — legit and subtle. Fixed as a union of two checks: Verification:
Housekeeping in the same push: extended |
…sts by default Fixes #183 (scope: full integration, breaking). Motivation ---------- write_qualified_pkg_lists() previously wrote blocklist-BioC.txt as the inverse of the *assessed* BioC set. BioC pkgs dropped at remote_reduce (or otherwise never seen by the pipeline) were absent from both the allowlist and the blocklist, so PPM would silently serve them. The 2026-07-30 prod run showed 2,150 BioC packages leaking this way. Changes ------- 1. **NEW exported** `build_bioc_blocklist(qual_metadata, opt_repos = NULL, config_path = NULL, qualified_decision = 'Low', min_universe = 100L)` returns the blocklist as a data frame with columns `package`, `version`, `matched_repo`, `reason` (`not_assessed` vs `assessed_<risk>`), sorted by reason then package. 2. **NEW exported** `is_bioc_repo(repos)` — case-insensitive substring match on 'bioc' against BOTH alias names and URLs. Covers non-OSS forks that consolidate the four BioC sub-repos into a single entry, plus URL-only aliases (e.g. mirror named 'sci' whose URL is a Bioconductor path). 3. **BREAKING**: `write_qualified_pkg_lists()` gains `use_full_universe = TRUE` (default). For any BioC-detected source in blocklist_sources, the blocklist is expanded against the full available.packages() universe via build_bioc_blocklist(). Set FALSE to restore pre-0.2.0 behaviour. Also gains `opt_repos` and `config_path` passthrough args for that call. Network failure at runtime is non-fatal: falls back to assessed-only inverse with a warning so surrounding qualified-<other>.txt writes still succeed. Test coverage ------------- - 10 tests in test-build_bioc_blocklist.R: is_bioc_repo dispatch (alias/URL/both/neither/unnamed); expansion + reason tagging; URL-only alias mask; riskmetric-derived BioCsoft label; no-BioC-repo error; min_universe floor; empty available.packages error; empty-blocklist warning; 'package' key col support. - 2 new tests in test-write_qualified_pkg_lists.R: expansion path end-to-end (120 filler pkgs verify the 'not_assessed' leak is actually closed); network-failure fallback. - 5 existing tests updated to `use_full_universe = FALSE` where they were exercising the pre-0.2.0 assessed-only inverse behaviour. Mocking uses testthat 3's local_mocked_bindings() with `.package = 'utils'` — no new test-time dep. Full suite: 1105 pass, 0 fail, 1 skip (pre-existing). Housekeeping ------------ - dev/dev_build_bioc_blocklist.R removed (redundant). - .gitignore drops the CSV output pattern (no longer written to dev/). - DESCRIPTION: 0.1.60 -> 0.2.0 (breaking on exported function). - NEWS.md: rewritten for 0.2.0. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
Big rework in 7b4c3c4: graduated the helper into New API:
Wire-up: Test coverage: 12 new tests (10 in Full suite: 1105 pass, 0 fail, 1 skip (pre-existing riskmetric-not-installed check). PR title/body updated. |
There was a problem hiding this comment.
🟡 Changes recommended
URL-only BioC sources and empty expansions can produce incorrect blocklists, and custom configuration defaults are not consistently honored.
Get a fresh assessment by requesting another Copilot review.
Review details
Files not reviewed (3)
- man/build_bioc_blocklist.Rd: Generated file
- man/is_bioc_repo.Rd: Generated file
- man/write_qualified_pkg_lists.Rd: Generated file
- Files reviewed: 7/11 changed files
- Comments generated: 3
- Review effort level: Balanced
| qualified_decision = pull_config(val = "decisions_lst", | ||
| rule_type = "default")[1], |
| # Network failures are non-fatal here: fall back to the | ||
| # assessed-only inverse with a warning so the surrounding | ||
| # allowlist writes still succeed. | ||
| if (isTRUE(use_full_universe) && any(is_bioc_repo(stats::setNames("", src)))) { |
| if (!is.null(expanded) && nrow(expanded) > 0L) { | ||
| pkgs <- sort(unique(expanded$package)) | ||
| } |
…y-alias wire-up, non-null expansion result Three legit correctness fixes from the PR #184 review round 2: 1. build_bioc_blocklist() qualified_decision default previously evaluated pull_config() in the signature, which reads the session/ installed config regardless of the caller's config_path. Passing a historical config with a different first decision was silently ignored. Fix: resolve in the body when NULL, forwarding config_path. 2. write_qualified_pkg_lists() BioC gating used is_bioc_repo(setNames('', src)) — classifying by ALIAS only. That missed the URL-only-alias case: blocklist_sources = 'sci' with opt_repos = list(sci = '.../bioconductor/...') would silently fall back to the pre-0.2.0 assessed-only blocklist. Fix: resolve the effective repo map once outside the loop, then classify each src by BOTH its alias and the URL mapped from that map. 3. write_qualified_pkg_lists() previously kept the assessed-only 'pkgs' when the expansion returned zero rows, but 0 rows is a legitimate result from build_bioc_blocklist() (every universe pkg is on the allowlist). A previously-assessed_High pkg absent from the current universe would stay wrongly blocklisted. Fix: only NULL (== error caught) preserves the fallback; empty df replaces. Test coverage ------------- Two new tests in test-write_qualified_pkg_lists.R: - URL-only-alias BioC repo (alias 'sci' -> bioc URL): 120 filler pkgs on blocklist proves expansion actually triggered. - Empty expansion result: universe contains only Low-risk pkgs + fillers all allowlisted, expansion returns 0 rows; verify the written blocklist is empty (NOT the assessed-only inverse that would contain GhostHighPkg). Full targeted suite: 94/94 pass. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
Adopted all three Copilot suggestions in 2644482. All legit: 1. 2. URL-only-alias wire-up (R/utils.R:3113) 3. Empty expansion result (R/utils.R:3133) New tests:
Full targeted suite: 94/94 pass (23 build_bioc_blocklist + 71 write_qualified_pkg_lists). |
Closes #183.
Scope 3 (per Aaron's ask): graduate the dev/ helper into
R/, wire it intowrite_qualified_pkg_lists()as the new default, remove the dev/ script. Breaking change → 0.2.0.What changed
New exported API
build_bioc_blocklist(qual_metadata, opt_repos = NULL, config_path = NULL, qualified_decision = "Low", min_universe = 100L)— returns the blocklist as a data frame withpackage,version,matched_repo,reasoncolumns. Use this directly for auditing/reporting.is_bioc_repo(repos)— case-insensitive substring match on"bioc"against both alias names and URLs. Covers:BioC:config alias."bioc").sciwhose URL is a Bioconductor path).Breaking change to existing exported function
write_qualified_pkg_lists()gains three new args:use_full_universe = TRUE(default). For any BioC-detected source inblocklist_sources, the blocklist is expanded against the fullavailable.packages()universe viabuild_bioc_blocklist(). Pre-0.2.0 behaviour (assessed-only inverse) is available viause_full_universe = FALSE.opt_repos = NULL,config_path = NULL— passthrough tobuild_bioc_blocklist().Failure handling: if
available.packages()throws (offline runner, config error, etc.), the function falls back to the assessed-only blocklist for that source with a warning — it does NOT abort. Surroundingqualified-CRAN.txtwrites continue.Housekeeping
dev/dev_build_bioc_blocklist.Rdeleted (redundant)..gitignoredrops the CSV output pattern (no longer written to dev/).Test coverage
New file
tests/testthat/test-build_bioc_blocklist.R— 10 tests, all mockingutils::available.packages()viatestthat::local_mocked_bindings(.package = "utils")(no new dep):is_bioc_repo() matches alias, URL, both, and neitherbuild_bioc_blocklist() expands universe and tags reasons... URL-only aliases in qm mask... riskmetric-derived labels (BioCsoft)... errors on no BioC reposis_bioc_repo()returns all-FALSE... enforces min_universe floor... errors on empty available.packages()... warns when blocklist is empty... supports 'package' key col tootests/testthat/test-write_qualified_pkg_lists.R— added 2 tests:available.packagesmocked tostop(); expect warning, expect CRAN allowlist still written.5 existing tests updated to
use_full_universe = FALSEwhere they were exercising the pre-0.2.0 assessed-only inverse behaviour explicitly (documents that intent).Full suite: 1105 pass, 0 fail, 1 skip (pre-existing).
Why 0.2.0 (breaking bump)
write_qualified_pkg_lists()is an exported function whose default behaviour changed. Any downstream caller running on an offline runner that getsblocklist-BioC.txtwritten will now see either:Neither breaks correctness, but the file contents differ from prior versions on the happy path. Per the branch/version policy, that's a minor bump, not a patch.
Follow-ups (deferred)
use_full_universearg and the fallback semantics.