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

76 lines
3.9 KiB
Markdown

# 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.