build-cran-binaries/.superpowers/handoff/final-fix-bincraft-report.md

3.9 KiB

Final Fix Report: bincraft package-patching branch

C1 Fix — prepare_patched_repo() src/contrib layout (R/patches.R)

Problem: cranlike::add_PACKAGES was being called for local file indexing, but it delegates to s3fs::s3_file_exists internally which only works for S3 paths, not local filesystem paths. Additionally, the tarball and index were placed flat in repo_dir, but R/pak resolve file:// repos via contrib.url() which appends src/contrib — so the flat layout was invisible to pak.

Fix applied in R/patches.R prepare_patched_repo():

  • After creating repo_dir, creates contrib <- file.path(repo_dir, "src", "contrib") and dir.create(contrib, recursive = TRUE, showWarnings = FALSE).
  • target is now file.path(contrib, ...) instead of file.path(repo_dir, ...).
  • build_patched_binary(entry, version, contrib) — builds directly into contrib.
  • Replaced cranlike::add_PACKAGES(list.files(repo_dir, ...), repo_dir) with tools::write_PACKAGES(contrib, type = "source") — cranlike uses s3fs internally and does not work for local paths; tools::write_PACKAGES is the correct CRAN-standard indexer for local repos.
  • Returns repo_dir (the root) unchanged so run_pak_install_with_mutex keeps prepending file://<repo_dir> and pak's contrib.url() resolves to <repo_dir>/src/contrib.
  • Cache (cache_dir) stays flat — only the served repo uses src/contrib.

RED/GREEN Evidence for available.packages() test

RED (flat layout, pre-fix):

Warning: cannot open compressed file '.../flat_repo_.../src/contrib/PACKAGES',
         probable reason 'No such file or directory'
pkgfoo found via available.packages(): FALSE

GREEN (src/contrib layout, post-fix):

pkgfoo found via available.packages(): TRUE

This directly demonstrates that the flat layout was invisible to R's available.packages() / pak resolution.

New Test — test-patches.R

Added test: "prepare_patched_repo src/contrib layout is resolvable by available.packages"

  • Builds a minimal valid source package tarball (pkgfoo_1.0.0.tar.gz) using real utils::tar().
  • Mocks only resolve_patch_version (→ "1.0.0") and build_patched_binary (copies real tarball to dest_dir, returns path).
  • Does NOT mock cranlike::add_PACKAGES or tools::write_PACKAGES — uses the real indexer.
  • Asserts:
    1. file.exists(file.path(out, "src", "contrib", "pkgfoo_1.0.0.tar.gz")) is TRUE.
    2. "pkgfoo" %in% rownames(available.packages(repos = paste0("file://", out), type = "source")) is TRUE.
    3. ap["pkgfoo", "Version"] == "1.0.0".
  • This test FAILS on the flat layout (warning + empty matrix) and PASSES after the fix.

Also updated the existing A5 test ("prepare_patched_repo serves a cached binary and writes an index") to:

  • Check file.path(repo, "src", "contrib", "glue_1.0.0.tar.gz") and file.path(repo, "src", "contrib", "PACKAGES") (formerly flat paths).
  • Mock tools::write_PACKAGES instead of cranlike::add_PACKAGES.

Test Results

[ FAIL 0 | WARN 0 | SKIP 1 | PASS 41 ]

(1 skip: E2E test gated on BINCRAFT_PATCH_E2E env var, by design.)

Doc Changes

R/build_binaries.R:

  • Added #' @template param-patches to execute_package_builds roxygen block.
  • Added #' @template param-patches to handle_system_dependencies roxygen block.

R/install_helpers.R:

  • Added #' @param patched_repo Optional path to a local patched-binary repo to prepend to pak's repos for this install (internal). to run_pak_install_with_mutex roxygen block.

devtools::document() output:

Writing 'execute_package_builds.Rd'
Writing 'handle_system_dependencies.Rd'
Writing 'run_pak_install_with_mutex.Rd'

All three Rd files regenerated successfully.

Deferred: I2

Duplicate registry entries resolving to the same package_version silently overwrite each other in prepare_patched_repo — the last one wins because both write to the same target path. This is a known limitation and is deferred; no action taken.