# Multi-R-version image refactor — Implementation Plan > **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. > Steps use checkbox (`- [ ]`) syntax for tracking. **Goal:** Refactor every `.crow/` workflow, the `Justfile` recipes, and the commented-out repo-root example to use the new multi-R-version images — drop `R_VERSION` from the image tag and invoke R via `/opt/R/${R_VERSION}/bin/R` instead of `R`/`Rscript` on `PATH`. **Architecture:** Pure mechanical refactor across 64 `.crow/*.yaml` workflows, 1 `Justfile`, and 1 commented-root file. Each workflow gets three kinds of edits: image tag (drop `-` suffix), `R_VERSION` env var (added where it wasn't present), and explicit R path substitution at every call site. Three correctness bug fixes are folded in: alpine audits use the matching alpine image, package-index workflows use the matching platform image, and `ubuntu-2404` process-updates align to R 4.4.3. **Tech Stack:** Woodpecker CI / `crow` (YAML workflows), `just` (Justfile), R 4.4.3 / 4.5.3. **Spec:** [`docs/superpowers/specs/2026-05-25-multi-r-version-images-design.md`](../specs/2026-05-25-multi-r-version-images-design.md) --- ## Conventions used throughout this plan **Platform → image + R_VERSION mapping:** | Platform | New image | `R_VERSION` | |--------------|-------------------------------------------------|-------------| | alpine-322 | `reg.devxy.io/rpkgs/build-env-alpine:3.22` | 4.5.3 | | alpine-323 | `reg.devxy.io/rpkgs/build-env-alpine:3.23` | 4.5.3 | | ubuntu-2204 | `reg.devxy.io/rpkgs/build-env-ubuntu:jammy` | 4.4.3 | | ubuntu-2404 | `reg.devxy.io/rpkgs/build-env-ubuntu:noble` | 4.4.3 | | redhat-8 | `reg.devxy.io/rpkgs/build-env-redhat:8` | 4.4.3 | | redhat-9 | `reg.devxy.io/rpkgs/build-env-redhat:9` | 4.4.3 | | redhat-10 | `reg.devxy.io/rpkgs/build-env-redhat:10` | 4.5.3 | `alpine-321` (audit-only) has no matching new image — falls back to `build-env-alpine:3.23` + `R_VERSION: 4.5.3`. **Editing recipe per file (pattern-1 workflows, i.e. those with a hard-coded image tag):** 1. Replace the `image:` line — drop `-` from the tag. 2. Insert `R_VERSION: ` into the `environment:` block. Anchor the insertion to a line that already exists in that file (typically `R_LIBS_USER:`, falling back to `GIT_USER:` for files that don't cache R libraries). 3. Replace every bare `R ` invocation in `commands:` with `/opt/R/${R_VERSION}/bin/R ` (including `xvfb-run R`, `R CMD`, etc.). 4. Replace every bare `Rscript ` invocation in `commands:` with `/opt/R/${R_VERSION}/bin/Rscript `. **Pattern-2 workflows** (image already parameterised via `${OS}`/`${OS_VERSION}`/`${R_VERSION}`) skip step 2 — `R_VERSION` already arrives via `--var`. **Why `replace_all` is safe:** The `Edit` tool's `replace_all` is used in the steps below only on tokens that appear nowhere except in workflow command lines (`R -q -e`, `R CMD INSTALL`, `Rscript local/`, `xvfb-run R `). These tokens never appear inside YAML keys, env vars, comments, or quoted strings within R code in these files (verified by grep). If a future workflow contains any of these tokens in a non-command context, switch that file to per-line targeted edits. --- ## Task 1: build-all-versions-* workflows (4 files, pattern 2) **Files (modify):** - `.crow/build-all-versions-amd64.yaml` - `.crow/build-all-versions-arm64.yaml` - `.crow/build-all-versions-install-deps-amd64.yaml` - `.crow/build-all-versions-install-deps-arm64.yaml` These workflows already receive `R_VERSION` from `crow pipeline create --var R_VERSION=…`. Only the image tag and R invocations change. - [ ] **Step 1.1: Edit `.crow/build-all-versions-amd64.yaml` — image tag** Use `Edit`: ``` old_string: image: reg.devxy.io/rpkgs/build-env-${OS}:${OS_VERSION}-${R_VERSION} new_string: image: reg.devxy.io/rpkgs/build-env-${OS}:${OS_VERSION} ``` - [ ] **Step 1.2: Edit `.crow/build-all-versions-amd64.yaml` — Rscript invocation** Use `Edit`: ``` old_string: - $XVFB $XVFB_ARGS -n $SPLIT_INDEX -- Rscript local/build-all.R $SPLIT_INTO $SPLIT_INDEX $NCPUS 2>&1 new_string: - $XVFB $XVFB_ARGS -n $SPLIT_INDEX -- /opt/R/${R_VERSION}/bin/Rscript local/build-all.R $SPLIT_INTO $SPLIT_INDEX $NCPUS 2>&1 ``` - [ ] **Step 1.3: Edit `.crow/build-all-versions-amd64.yaml` — `R -q -e` invocation** Use `Edit` with `replace_all: true`: ``` old_string: R -q -e new_string: /opt/R/${R_VERSION}/bin/R -q -e ``` (The file contains one such line: `R -q -e "bincraft::process_unarchived_pkgs(...)"`.) - [ ] **Step 1.4: Repeat steps 1.1–1.3 for `.crow/build-all-versions-arm64.yaml`** The three edits are textually identical to steps 1.1–1.3 because the build-arm64 file uses the same parameterised image tag and the same `R -q -e` / `Rscript` invocations. - [ ] **Step 1.5: Edit `.crow/build-all-versions-install-deps-amd64.yaml` — image tag** Use `Edit`: ``` old_string: image: reg.devxy.io/rpkgs/build-env-${OS}:${OS_VERSION}-${R_VERSION} new_string: image: reg.devxy.io/rpkgs/build-env-${OS}:${OS_VERSION} ``` - [ ] **Step 1.6: Edit `.crow/build-all-versions-install-deps-amd64.yaml` — `R -q -e` invocations** Use `Edit` with `replace_all: true`: ``` old_string: R -q -e new_string: /opt/R/${R_VERSION}/bin/R -q -e ``` (The file contains four `R -q -e` lines.) - [ ] **Step 1.7: Repeat steps 1.5–1.6 for `.crow/build-all-versions-install-deps-arm64.yaml`** - [ ] **Step 1.8: Validate** Run: ```bash grep -nE '(^|[^/])R(script)? ' .crow/build-all-versions-*.yaml | grep -v '/opt/R/' ``` Expected: no output. Any line returned is a missed substitution — investigate before continuing. Run: ```bash grep -nE 'build-env-.*\$\{OS_VERSION\}-' .crow/build-all-versions-*.yaml ``` Expected: no output. - [ ] **Step 1.9: Commit** ```bash git add .crow/build-all-versions-amd64.yaml .crow/build-all-versions-arm64.yaml .crow/build-all-versions-install-deps-amd64.yaml .crow/build-all-versions-install-deps-arm64.yaml git commit -m "refactor(ci): use multi-R-version images in build-all-versions workflows Drop -\${R_VERSION} from the image tag and invoke R/Rscript via the explicit /opt/R/\${R_VERSION}/bin/ path." ``` --- ## Task 2: process-updates workflows (14 files, pattern 1) **Files (modify):** | File | New image | `R_VERSION` | |-----------------------------------------------|--------------------------------------------|-------------| | `process-updates-alpine-322-amd64.yaml` | `reg.devxy.io/rpkgs/build-env-alpine:3.22` | 4.5.3 | | `process-updates-alpine-322-arm64.yaml` | `reg.devxy.io/rpkgs/build-env-alpine:3.22` | 4.5.3 | | `process-updates-alpine-323-amd64.yaml` | `reg.devxy.io/rpkgs/build-env-alpine:3.23` | 4.5.3 | | `process-updates-alpine-323-arm64.yaml` | `reg.devxy.io/rpkgs/build-env-alpine:3.23` | 4.5.3 | | `process-updates-ubuntu-2204-amd64.yaml` | `reg.devxy.io/rpkgs/build-env-ubuntu:jammy`| 4.4.3 | | `process-updates-ubuntu-2204-arm64.yaml` | `reg.devxy.io/rpkgs/build-env-ubuntu:jammy`| 4.4.3 | | `process-updates-ubuntu-2404-amd64.yaml` | `reg.devxy.io/rpkgs/build-env-ubuntu:noble`| 4.4.3 | | `process-updates-ubuntu-2404-arm64.yaml` | `reg.devxy.io/rpkgs/build-env-ubuntu:noble`| 4.4.3 | | `process-updates-redhat-8-amd64.yaml` | `reg.devxy.io/rpkgs/build-env-redhat:8` | 4.4.3 | | `process-updates-redhat-8-arm64.yaml` | `reg.devxy.io/rpkgs/build-env-redhat:8` | 4.4.3 | | `process-updates-redhat-9-amd64.yaml` | `reg.devxy.io/rpkgs/build-env-redhat:9` | 4.4.3 | | `process-updates-redhat-9-arm64.yaml` | `reg.devxy.io/rpkgs/build-env-redhat:9` | 4.4.3 | | `process-updates-redhat-10-amd64.yaml` | `reg.devxy.io/rpkgs/build-env-redhat:10` | 4.5.3 | | `process-updates-redhat-10-arm64.yaml` | `reg.devxy.io/rpkgs/build-env-redhat:10` | 4.5.3 | The two `process-updates-ubuntu-2404-*` files also bump R from `4.4` to `4.4.3` (folded-in bug fix per spec). ### Worked example: `.crow/process-updates-alpine-322-amd64.yaml` - [ ] **Step 2.1: Edit image tag** Use `Edit`: ``` old_string: image: reg.devxy.io/rpkgs/build-env-alpine:3.22-4.5 new_string: image: reg.devxy.io/rpkgs/build-env-alpine:3.22 ``` - [ ] **Step 2.2: Add `R_VERSION` env var** Use `Edit` (anchor on `R_LIBS_USER:`, which appears in every process-updates file): ``` old_string: R_LIBS_USER: /mnt/cache/R-pkgs new_string: R_LIBS_USER: /mnt/cache/R-pkgs R_VERSION: 4.5.3 ``` (Preserve the existing six-space indentation.) - [ ] **Step 2.3: Substitute R path** Use `Edit` with `replace_all: true`: ``` old_string: R -q -e new_string: /opt/R/${R_VERSION}/bin/R -q -e ``` This covers both bare `R -q -e` and `xvfb-run R -q -e` lines (the latter becomes `xvfb-run /opt/R/${R_VERSION}/bin/R -q -e`, which is correct). - [ ] **Step 2.4: Validate this file** Run: ```bash grep -nE '(^|[^/])R(script)? ' .crow/process-updates-alpine-322-amd64.yaml | grep -v '/opt/R/' ``` Expected: no output. ### Apply the same three-step pattern to the remaining 13 files - [ ] **Step 2.5: Apply to `process-updates-alpine-322-arm64.yaml`** Image: `reg.devxy.io/rpkgs/build-env-alpine:3.22` · `R_VERSION: 4.5.3`. Old image tag suffix: `-4.5` (so `old_string` is `image: reg.devxy.io/rpkgs/build-env-alpine:3.22-4.5`). - [ ] **Step 2.6: Apply to `process-updates-alpine-323-amd64.yaml`** Image: `reg.devxy.io/rpkgs/build-env-alpine:3.23` · `R_VERSION: 4.5.3`. Old image tag suffix: `-4.5`. - [ ] **Step 2.7: Apply to `process-updates-alpine-323-arm64.yaml`** Image: `reg.devxy.io/rpkgs/build-env-alpine:3.23` · `R_VERSION: 4.5.3`. Old image tag suffix: `-4.5`. - [ ] **Step 2.8: Apply to `process-updates-ubuntu-2204-amd64.yaml`** Image: `reg.devxy.io/rpkgs/build-env-ubuntu:jammy` · `R_VERSION: 4.4.3`. Old image tag suffix: `-4.4.3`. - [ ] **Step 2.9: Apply to `process-updates-ubuntu-2204-arm64.yaml`** Image: `reg.devxy.io/rpkgs/build-env-ubuntu:jammy` · `R_VERSION: 4.4.3`. Old image tag suffix: `-4.4.3`. - [ ] **Step 2.10: Apply to `process-updates-ubuntu-2404-amd64.yaml`** Image: `reg.devxy.io/rpkgs/build-env-ubuntu:noble` · `R_VERSION: 4.4.3`. Old image tag suffix: `-4.4` (note: `4.4` without the patch — bug fix). - [ ] **Step 2.11: Apply to `process-updates-ubuntu-2404-arm64.yaml`** Image: `reg.devxy.io/rpkgs/build-env-ubuntu:noble` · `R_VERSION: 4.4.3`. Old image tag suffix: `-4.4`. - [ ] **Step 2.12: Apply to `process-updates-redhat-8-amd64.yaml`** Image: `reg.devxy.io/rpkgs/build-env-redhat:8` · `R_VERSION: 4.4.3`. Old image tag suffix: `-4.4.3`. - [ ] **Step 2.13: Apply to `process-updates-redhat-8-arm64.yaml`** Image: `reg.devxy.io/rpkgs/build-env-redhat:8` · `R_VERSION: 4.4.3`. Old image tag suffix: `-4.4.3`. - [ ] **Step 2.14: Apply to `process-updates-redhat-9-amd64.yaml`** Image: `reg.devxy.io/rpkgs/build-env-redhat:9` · `R_VERSION: 4.4.3`. Old image tag suffix: `-4.4.3`. - [ ] **Step 2.15: Apply to `process-updates-redhat-9-arm64.yaml`** Image: `reg.devxy.io/rpkgs/build-env-redhat:9` · `R_VERSION: 4.4.3`. Old image tag suffix: `-4.4.3`. - [ ] **Step 2.16: Apply to `process-updates-redhat-10-amd64.yaml`** Image: `reg.devxy.io/rpkgs/build-env-redhat:10` · `R_VERSION: 4.5.3`. Old image tag suffix: `-4.5.3`. - [ ] **Step 2.17: Apply to `process-updates-redhat-10-arm64.yaml`** Image: `reg.devxy.io/rpkgs/build-env-redhat:10` · `R_VERSION: 4.5.3`. Old image tag suffix: `-4.5.3`. - [ ] **Step 2.18: Validate all 14 files** Run: ```bash grep -nE '(^|[^/])R(script)? ' .crow/process-updates-*.yaml | grep -v '/opt/R/' | grep -v '^[^:]*:[0-9]*:#' ``` Expected: no output (comments starting with `#` are filtered out). Run: ```bash grep -nE 'build-env-.*:[^[:space:]]*-[0-9]' .crow/process-updates-*.yaml ``` Expected: no output. Run: ```bash grep -nE 'R_VERSION:' .crow/process-updates-*.yaml | wc -l ``` Expected: `14` (one `R_VERSION:` per file). - [ ] **Step 2.19: Commit** ```bash git add .crow/process-updates-*.yaml git commit -m "refactor(ci): use multi-R-version images in process-updates workflows Drop the R-version suffix from each image tag, add an explicit R_VERSION env var per file, and invoke R via /opt/R/\${R_VERSION}/bin/R at every call site. Also aligns ubuntu-2404 process-updates from R 4.4 to R 4.4.3, matching the audit and rebuild counterparts." ``` --- ## Task 3: weekly-rebuild-missing workflows (14 files, pattern 1) **Files (modify):** | File | New image | `R_VERSION` | |---------------------------------------------------|--------------------------------------------|-------------| | `weekly-rebuild-missing-alpine-322-amd64.yaml` | `reg.devxy.io/rpkgs/build-env-alpine:3.22` | 4.5.3 | | `weekly-rebuild-missing-alpine-322-arm64.yaml` | `reg.devxy.io/rpkgs/build-env-alpine:3.22` | 4.5.3 | | `weekly-rebuild-missing-alpine-323-amd64.yaml` | `reg.devxy.io/rpkgs/build-env-alpine:3.23` | 4.5.3 | | `weekly-rebuild-missing-alpine-323-arm64.yaml` | `reg.devxy.io/rpkgs/build-env-alpine:3.23` | 4.5.3 | | `weekly-rebuild-missing-ubuntu-2204-amd64.yaml` | `reg.devxy.io/rpkgs/build-env-ubuntu:jammy`| 4.4.3 | | `weekly-rebuild-missing-ubuntu-2204-arm64.yaml` | `reg.devxy.io/rpkgs/build-env-ubuntu:jammy`| 4.4.3 | | `weekly-rebuild-missing-ubuntu-2404-amd64.yaml` | `reg.devxy.io/rpkgs/build-env-ubuntu:noble`| 4.4.3 | | `weekly-rebuild-missing-ubuntu-2404-arm64.yaml` | `reg.devxy.io/rpkgs/build-env-ubuntu:noble`| 4.4.3 | | `weekly-rebuild-missing-redhat-8-amd64.yaml` | `reg.devxy.io/rpkgs/build-env-redhat:8` | 4.4.3 | | `weekly-rebuild-missing-redhat-8-arm64.yaml` | `reg.devxy.io/rpkgs/build-env-redhat:8` | 4.4.3 | | `weekly-rebuild-missing-redhat-9-amd64.yaml` | `reg.devxy.io/rpkgs/build-env-redhat:9` | 4.4.3 | | `weekly-rebuild-missing-redhat-9-arm64.yaml` | `reg.devxy.io/rpkgs/build-env-redhat:9` | 4.4.3 | | `weekly-rebuild-missing-redhat-10-amd64.yaml` | `reg.devxy.io/rpkgs/build-env-redhat:10` | 4.5.3 | | `weekly-rebuild-missing-redhat-10-arm64.yaml` | `reg.devxy.io/rpkgs/build-env-redhat:10` | 4.5.3 | ### Worked example: `.crow/weekly-rebuild-missing-alpine-322-amd64.yaml` - [ ] **Step 3.1: Edit image tag** Use `Edit`: ``` old_string: image: reg.devxy.io/rpkgs/build-env-alpine:3.22-4.5 new_string: image: reg.devxy.io/rpkgs/build-env-alpine:3.22 ``` - [ ] **Step 3.2: Add `R_VERSION` env var** Use `Edit` (anchor on `R_LIBS_USER:`): ``` old_string: R_LIBS_USER: /mnt/cache/R-pkgs new_string: R_LIBS_USER: /mnt/cache/R-pkgs R_VERSION: 4.5.3 ``` - [ ] **Step 3.3: Substitute R path** Use `Edit` with `replace_all: true`: ``` old_string: R -q -e new_string: /opt/R/${R_VERSION}/bin/R -q -e ``` (Also covers `$XVFB $XVFB_ARGS -- R -q -e ...` since the matched substring becomes `/opt/R/${R_VERSION}/bin/R -q -e`.) - [ ] **Step 3.4: Validate this file** ```bash grep -nE '(^|[^/])R(script)? ' .crow/weekly-rebuild-missing-alpine-322-amd64.yaml | grep -v '/opt/R/' | grep -v '^[^:]*:[0-9]*:#' ``` Expected: no output. ### Apply the same three-step pattern to the remaining 13 files - [ ] **Step 3.5: Apply to `weekly-rebuild-missing-alpine-322-arm64.yaml`** (image alpine:3.22, R_VERSION 4.5.3, old suffix `-4.5`) - [ ] **Step 3.6: Apply to `weekly-rebuild-missing-alpine-323-amd64.yaml`** (image alpine:3.23, R_VERSION 4.5.3, old suffix `-4.5`) - [ ] **Step 3.7: Apply to `weekly-rebuild-missing-alpine-323-arm64.yaml`** (image alpine:3.23, R_VERSION 4.5.3, old suffix `-4.5`) - [ ] **Step 3.8: Apply to `weekly-rebuild-missing-ubuntu-2204-amd64.yaml`** (image ubuntu:jammy, R_VERSION 4.4.3, old suffix `-4.4.3`) - [ ] **Step 3.9: Apply to `weekly-rebuild-missing-ubuntu-2204-arm64.yaml`** (image ubuntu:jammy, R_VERSION 4.4.3, old suffix `-4.4.3`) - [ ] **Step 3.10: Apply to `weekly-rebuild-missing-ubuntu-2404-amd64.yaml`** (image ubuntu:noble, R_VERSION 4.4.3, old suffix `-4.4.3`) - [ ] **Step 3.11: Apply to `weekly-rebuild-missing-ubuntu-2404-arm64.yaml`** (image ubuntu:noble, R_VERSION 4.4.3, old suffix `-4.4.3`) - [ ] **Step 3.12: Apply to `weekly-rebuild-missing-redhat-8-amd64.yaml`** (image redhat:8, R_VERSION 4.4.3, old suffix `-4.4.3`) - [ ] **Step 3.13: Apply to `weekly-rebuild-missing-redhat-8-arm64.yaml`** (image redhat:8, R_VERSION 4.4.3, old suffix `-4.4.3`) - [ ] **Step 3.14: Apply to `weekly-rebuild-missing-redhat-9-amd64.yaml`** (image redhat:9, R_VERSION 4.4.3, old suffix `-4.4.3`) - [ ] **Step 3.15: Apply to `weekly-rebuild-missing-redhat-9-arm64.yaml`** (image redhat:9, R_VERSION 4.4.3, old suffix `-4.4.3`) - [ ] **Step 3.16: Apply to `weekly-rebuild-missing-redhat-10-amd64.yaml`** (image redhat:10, R_VERSION 4.5.3, old suffix `-4.5.3`) - [ ] **Step 3.17: Apply to `weekly-rebuild-missing-redhat-10-arm64.yaml`** (image redhat:10, R_VERSION 4.5.3, old suffix `-4.5.3`) - [ ] **Step 3.18: Validate all 14 files** ```bash grep -nE '(^|[^/])R(script)? ' .crow/weekly-rebuild-missing-*.yaml | grep -v '/opt/R/' | grep -v '^[^:]*:[0-9]*:#' grep -nE 'build-env-.*:[^[:space:]]*-[0-9]' .crow/weekly-rebuild-missing-*.yaml grep -nE 'R_VERSION:' .crow/weekly-rebuild-missing-*.yaml | wc -l ``` Expected: first two return no output; the third returns `14`. - [ ] **Step 3.19: Commit** ```bash git add .crow/weekly-rebuild-missing-*.yaml git commit -m "refactor(ci): use multi-R-version images in weekly-rebuild-missing workflows Drop the R-version suffix from each image tag, add an explicit R_VERSION env var per file, and invoke R via /opt/R/\${R_VERSION}/bin/R at every call site." ``` --- ## Task 4: weekly-audit-missing workflows (16 files, pattern 1) This task folds in the **alpine audit bug fix**: all six `weekly-audit-missing-alpine-*` files currently point at `alpine:3.23-4.5` regardless of platform, even when the platform is alpine-321 or alpine-322. **Files (modify):** | File | New image | `R_VERSION` | Old tag | |---------------------------------------------------|--------------------------------------------|-------------|---------| | `weekly-audit-missing-alpine-321-amd64.yaml` | `reg.devxy.io/rpkgs/build-env-alpine:3.23` | 4.5.3 | `3.23-4.5` | | `weekly-audit-missing-alpine-321-arm64.yaml` | `reg.devxy.io/rpkgs/build-env-alpine:3.23` | 4.5.3 | `3.23-4.5` | | `weekly-audit-missing-alpine-322-amd64.yaml` | `reg.devxy.io/rpkgs/build-env-alpine:3.22` | 4.5.3 | `3.23-4.5` (bug fix: was wrong) | | `weekly-audit-missing-alpine-322-arm64.yaml` | `reg.devxy.io/rpkgs/build-env-alpine:3.22` | 4.5.3 | `3.23-4.5` (bug fix) | | `weekly-audit-missing-alpine-323-amd64.yaml` | `reg.devxy.io/rpkgs/build-env-alpine:3.23` | 4.5.3 | `3.23-4.5` | | `weekly-audit-missing-alpine-323-arm64.yaml` | `reg.devxy.io/rpkgs/build-env-alpine:3.23` | 4.5.3 | `3.23-4.5` | | `weekly-audit-missing-ubuntu-2204-amd64.yaml` | `reg.devxy.io/rpkgs/build-env-ubuntu:jammy`| 4.4.3 | `jammy-4.4.3` | | `weekly-audit-missing-ubuntu-2204-arm64.yaml` | `reg.devxy.io/rpkgs/build-env-ubuntu:jammy`| 4.4.3 | `jammy-4.4.3` | | `weekly-audit-missing-ubuntu-2404-amd64.yaml` | `reg.devxy.io/rpkgs/build-env-ubuntu:noble`| 4.4.3 | `noble-4.4.3` | | `weekly-audit-missing-ubuntu-2404-arm64.yaml` | `reg.devxy.io/rpkgs/build-env-ubuntu:noble`| 4.4.3 | `noble-4.4.3` | | `weekly-audit-missing-redhat-8-amd64.yaml` | `reg.devxy.io/rpkgs/build-env-redhat:8` | 4.4.3 | `8-4.4.3` | | `weekly-audit-missing-redhat-8-arm64.yaml` | `reg.devxy.io/rpkgs/build-env-redhat:8` | 4.4.3 | `8-4.4.3` | | `weekly-audit-missing-redhat-9-amd64.yaml` | `reg.devxy.io/rpkgs/build-env-redhat:9` | 4.4.3 | `9-4.4.3` | | `weekly-audit-missing-redhat-9-arm64.yaml` | `reg.devxy.io/rpkgs/build-env-redhat:9` | 4.4.3 | `9-4.4.3` | | `weekly-audit-missing-redhat-10-amd64.yaml` | `reg.devxy.io/rpkgs/build-env-redhat:10` | 4.5.3 | `10-4.5.3` | | `weekly-audit-missing-redhat-10-arm64.yaml` | `reg.devxy.io/rpkgs/build-env-redhat:10` | 4.5.3 | `10-4.5.3` | ### Worked example: `.crow/weekly-audit-missing-alpine-322-amd64.yaml` (includes bug fix) - [ ] **Step 4.1: Edit image tag** Use `Edit`: ``` old_string: image: reg.devxy.io/rpkgs/build-env-alpine:3.23-4.5 new_string: image: reg.devxy.io/rpkgs/build-env-alpine:3.22 ``` Note: this both drops the R version AND fixes the OS-version mismatch (was 3.23, should be 3.22). - [ ] **Step 4.2: Add `R_VERSION` env var** Use `Edit` (anchor on `R_LIBS_USER:`, which appears in every weekly-audit-missing file): ``` old_string: R_LIBS_USER: /mnt/cache/R-pkgs new_string: R_LIBS_USER: /mnt/cache/R-pkgs R_VERSION: 4.5.3 ``` - [ ] **Step 4.3: Substitute R path** Use `Edit` with `replace_all: true`: ``` old_string: R -q -e new_string: /opt/R/${R_VERSION}/bin/R -q -e ``` - [ ] **Step 4.4: Validate this file** ```bash grep -nE '(^|[^/])R(script)? ' .crow/weekly-audit-missing-alpine-322-amd64.yaml | grep -v '/opt/R/' | grep -v '^[^:]*:[0-9]*:#' ``` Expected: no output. ### Apply the same three-step pattern to the remaining 15 files For each file, use its row in the table above to get the new image and `R_VERSION`. The `old_string` for the image tag edit is `image: reg.devxy.io/rpkgs/build-env-`, where `` is the "Old tag" column. - [ ] **Step 4.5: Apply to `weekly-audit-missing-alpine-321-amd64.yaml`** (new image alpine:3.23, R_VERSION 4.5.3, old tag suffix `-4.5`) - [ ] **Step 4.6: Apply to `weekly-audit-missing-alpine-321-arm64.yaml`** (alpine:3.23, 4.5.3, old `-4.5`) - [ ] **Step 4.7: Apply to `weekly-audit-missing-alpine-322-arm64.yaml`** (alpine:3.22, 4.5.3, old `-4.5` — image bug fix) - [ ] **Step 4.8: Apply to `weekly-audit-missing-alpine-323-amd64.yaml`** (alpine:3.23, 4.5.3, old `-4.5`) - [ ] **Step 4.9: Apply to `weekly-audit-missing-alpine-323-arm64.yaml`** (alpine:3.23, 4.5.3, old `-4.5`) - [ ] **Step 4.10: Apply to `weekly-audit-missing-ubuntu-2204-amd64.yaml`** (ubuntu:jammy, 4.4.3, old `-4.4.3`) - [ ] **Step 4.11: Apply to `weekly-audit-missing-ubuntu-2204-arm64.yaml`** (ubuntu:jammy, 4.4.3, old `-4.4.3`) - [ ] **Step 4.12: Apply to `weekly-audit-missing-ubuntu-2404-amd64.yaml`** (ubuntu:noble, 4.4.3, old `-4.4.3`) - [ ] **Step 4.13: Apply to `weekly-audit-missing-ubuntu-2404-arm64.yaml`** (ubuntu:noble, 4.4.3, old `-4.4.3`) - [ ] **Step 4.14: Apply to `weekly-audit-missing-redhat-8-amd64.yaml`** (redhat:8, 4.4.3, old `-4.4.3`) - [ ] **Step 4.15: Apply to `weekly-audit-missing-redhat-8-arm64.yaml`** (redhat:8, 4.4.3, old `-4.4.3`) - [ ] **Step 4.16: Apply to `weekly-audit-missing-redhat-9-amd64.yaml`** (redhat:9, 4.4.3, old `-4.4.3`) - [ ] **Step 4.17: Apply to `weekly-audit-missing-redhat-9-arm64.yaml`** (redhat:9, 4.4.3, old `-4.4.3`) - [ ] **Step 4.18: Apply to `weekly-audit-missing-redhat-10-amd64.yaml`** (redhat:10, 4.5.3, old `-4.5.3`) - [ ] **Step 4.19: Apply to `weekly-audit-missing-redhat-10-arm64.yaml`** (redhat:10, 4.5.3, old `-4.5.3`) - [ ] **Step 4.20: Validate all 16 files** ```bash grep -nE '(^|[^/])R(script)? ' .crow/weekly-audit-missing-*.yaml | grep -v '/opt/R/' | grep -v '^[^:]*:[0-9]*:#' grep -nE 'build-env-.*:[^[:space:]]*-[0-9]' .crow/weekly-audit-missing-*.yaml grep -nE 'R_VERSION:' .crow/weekly-audit-missing-*.yaml | wc -l ``` Expected: first two return no output; the third returns `16`. Also verify the alpine bug fix took effect: ```bash grep -E 'image:' .crow/weekly-audit-missing-alpine-*.yaml ``` Expected: ``` .crow/weekly-audit-missing-alpine-321-amd64.yaml: image: reg.devxy.io/rpkgs/build-env-alpine:3.23 .crow/weekly-audit-missing-alpine-321-arm64.yaml: image: reg.devxy.io/rpkgs/build-env-alpine:3.23 .crow/weekly-audit-missing-alpine-322-amd64.yaml: image: reg.devxy.io/rpkgs/build-env-alpine:3.22 .crow/weekly-audit-missing-alpine-322-arm64.yaml: image: reg.devxy.io/rpkgs/build-env-alpine:3.22 .crow/weekly-audit-missing-alpine-323-amd64.yaml: image: reg.devxy.io/rpkgs/build-env-alpine:3.23 .crow/weekly-audit-missing-alpine-323-arm64.yaml: image: reg.devxy.io/rpkgs/build-env-alpine:3.23 ``` - [ ] **Step 4.21: Commit** ```bash git add .crow/weekly-audit-missing-*.yaml git commit -m "refactor(ci): use multi-R-version images in weekly-audit-missing workflows Drop the R-version suffix from each image tag, add an explicit R_VERSION env var per file, and invoke R via /opt/R/\${R_VERSION}/bin/R at every call site. Also fixes the alpine-322 audit image, which was previously pointing at alpine:3.23 instead of alpine:3.22. The alpine-321 audits stay on alpine:3.23 since no 3.21 image exists in the new scheme — they only query S3/CRAN, so the container OS does not affect correctness." ``` --- ## Task 5: update-package-index workflows (14 files, pattern 1) This task folds in the **package-index image bug fix**: every `update-package-index-*` file currently uses `build-env-ubuntu:noble-4.4` (or `noble-4.5` in the redhat-10 case) regardless of which platform's package index it uploads. After the refactor, each file uses the image that matches its own platform per the mapping table. The first step (`Upload PACKAGES files`) needs the refactor; the second step (`Purge CDN cache`) uses `alpine:3.23` directly, has no R calls, and is unchanged. **Files (modify):** | File | New image | `R_VERSION` | |---------------------------------------------------|--------------------------------------------|-------------| | `update-package-index-alpine-322-amd64.yaml` | `reg.devxy.io/rpkgs/build-env-alpine:3.22` | 4.5.3 | | `update-package-index-alpine-322-arm64.yaml` | `reg.devxy.io/rpkgs/build-env-alpine:3.22` | 4.5.3 | | `update-package-index-alpine-323-amd64.yaml` | `reg.devxy.io/rpkgs/build-env-alpine:3.23` | 4.5.3 | | `update-package-index-alpine-323-arm64.yaml` | `reg.devxy.io/rpkgs/build-env-alpine:3.23` | 4.5.3 | | `update-package-index-ubuntu-2204-amd64.yaml` | `reg.devxy.io/rpkgs/build-env-ubuntu:jammy`| 4.4.3 | | `update-package-index-ubuntu-2204-arm64.yaml` | `reg.devxy.io/rpkgs/build-env-ubuntu:jammy`| 4.4.3 | | `update-package-index-ubuntu-2404-amd64.yaml` | `reg.devxy.io/rpkgs/build-env-ubuntu:noble`| 4.4.3 | | `update-package-index-ubuntu-2404-arm64.yaml` | `reg.devxy.io/rpkgs/build-env-ubuntu:noble`| 4.4.3 | | `update-package-index-redhat-8-amd64.yaml` | `reg.devxy.io/rpkgs/build-env-redhat:8` | 4.4.3 | | `update-package-index-redhat-8-arm64.yaml` | `reg.devxy.io/rpkgs/build-env-redhat:8` | 4.4.3 | | `update-package-index-redhat-9-amd64.yaml` | `reg.devxy.io/rpkgs/build-env-redhat:9` | 4.4.3 | | `update-package-index-redhat-9-arm64.yaml` | `reg.devxy.io/rpkgs/build-env-redhat:9` | 4.4.3 | | `update-package-index-redhat-10-amd64.yaml` | `reg.devxy.io/rpkgs/build-env-redhat:10` | 4.5.3 | | `update-package-index-redhat-10-arm64.yaml` | `reg.devxy.io/rpkgs/build-env-redhat:10` | 4.5.3 | ### Worked example: `.crow/update-package-index-alpine-322-amd64.yaml` The current image is `build-env-ubuntu:noble-4.4` (a *wrong* OS); we repoint to `build-env-alpine:3.22` (the matching OS) and add R_VERSION. - [ ] **Step 5.1: Edit image tag** Use `Edit`: ``` old_string: image: reg.devxy.io/rpkgs/build-env-ubuntu:noble-4.4 new_string: image: reg.devxy.io/rpkgs/build-env-alpine:3.22 ``` - [ ] **Step 5.2: Add `R_VERSION` env var** Use `Edit` (anchor on `R_LIBS_USER:`, which appears in every update-package-index file): ``` old_string: R_LIBS_USER: /mnt/cache/R-pkgs new_string: R_LIBS_USER: /mnt/cache/R-pkgs R_VERSION: 4.5.3 ``` - [ ] **Step 5.3: Substitute R path** Use `Edit` with `replace_all: true`: ``` old_string: R -q -e new_string: /opt/R/${R_VERSION}/bin/R -q -e ``` - [ ] **Step 5.4: Validate this file** ```bash grep -nE '(^|[^/])R(script)? ' .crow/update-package-index-alpine-322-amd64.yaml | grep -v '/opt/R/' | grep -v '^[^:]*:[0-9]*:#' ``` Expected: no output. ### Apply the same three-step pattern to the remaining 13 files Old image tag for every file in this task except `update-package-index-redhat-10-amd64.yaml` is `reg.devxy.io/rpkgs/build-env-ubuntu:noble-4.4`. For `update-package-index-redhat-10-amd64.yaml`, the old image tag is `reg.devxy.io/rpkgs/build-env-ubuntu:noble-4.5` (verify with `grep image: .crow/update-package-index-redhat-10-amd64.yaml` before editing). - [ ] **Step 5.5: Apply to `update-package-index-alpine-322-arm64.yaml`** (new alpine:3.22, R_VERSION 4.5.3, old `noble-4.4`) - [ ] **Step 5.6: Apply to `update-package-index-alpine-323-amd64.yaml`** (alpine:3.23, 4.5.3, old `noble-4.4`) - [ ] **Step 5.7: Apply to `update-package-index-alpine-323-arm64.yaml`** (alpine:3.23, 4.5.3, old `noble-4.4`) - [ ] **Step 5.8: Apply to `update-package-index-ubuntu-2204-amd64.yaml`** (ubuntu:jammy, 4.4.3, old `noble-4.4`) - [ ] **Step 5.9: Apply to `update-package-index-ubuntu-2204-arm64.yaml`** (ubuntu:jammy, 4.4.3, old `noble-4.4`) - [ ] **Step 5.10: Apply to `update-package-index-ubuntu-2404-amd64.yaml`** (ubuntu:noble, 4.4.3, old `noble-4.4`) - [ ] **Step 5.11: Apply to `update-package-index-ubuntu-2404-arm64.yaml`** (ubuntu:noble, 4.4.3, old `noble-4.4`) - [ ] **Step 5.12: Apply to `update-package-index-redhat-8-amd64.yaml`** (redhat:8, 4.4.3, old `noble-4.4`) - [ ] **Step 5.13: Apply to `update-package-index-redhat-8-arm64.yaml`** (redhat:8, 4.4.3, old `noble-4.4`) - [ ] **Step 5.14: Apply to `update-package-index-redhat-9-amd64.yaml`** (redhat:9, 4.4.3, old `noble-4.4`) - [ ] **Step 5.15: Apply to `update-package-index-redhat-9-arm64.yaml`** (redhat:9, 4.4.3, old `noble-4.4`) - [ ] **Step 5.16: Apply to `update-package-index-redhat-10-amd64.yaml`** (redhat:10, 4.5.3, old `noble-4.5` — note the `4.5` not `4.4`!) - [ ] **Step 5.17: Apply to `update-package-index-redhat-10-arm64.yaml`** (redhat:10, 4.5.3, old `noble-4.4`) - [ ] **Step 5.18: Validate all 14 files** ```bash grep -nE '(^|[^/])R(script)? ' .crow/update-package-index-*.yaml | grep -v '/opt/R/' | grep -v '^[^:]*:[0-9]*:#' grep -nE 'build-env-.*:[^[:space:]]*-[0-9]' .crow/update-package-index-*.yaml grep -nE '^\s*R_VERSION:' .crow/update-package-index-*.yaml | wc -l ``` Expected: first two return no output; the third returns `14`. Verify each file's image matches its platform: ```bash grep -E '^\s*image: reg.devxy.io/rpkgs/build-env' .crow/update-package-index-*.yaml ``` Expected: each file's image OS/version matches its platform suffix (alpine-322 → alpine:3.22, ubuntu-2404 → ubuntu:noble, redhat-10 → redhat:10, etc.). - [ ] **Step 5.19: Commit** ```bash git add .crow/update-package-index-*.yaml git commit -m "refactor(ci): use multi-R-version images in update-package-index workflows Drop the R-version suffix from each image tag, add an explicit R_VERSION env var per file, and invoke R via /opt/R/\${R_VERSION}/bin/R at every call site. Also repoints every update-package-index workflow at the image that matches its own platform (was previously pinned to build-env-ubuntu:noble-4.4 / noble-4.5 regardless of platform)." ``` --- ## Task 6: archive-missed-packages workflow (1 file) **File (modify):** `.crow/archive-missed-packages.yaml` The image OS doesn't matter for this workflow (it only writes to S3 + Postgres). It currently uses `build-env-alpine:3.23-4.5`; the new image keeps alpine:3.23 and picks R 4.5.3 explicitly. Note: this file has no `R_LIBS_USER` env var, so anchor the `R_VERSION` insertion on `GIT_USER: pat-s` (a line that *is* present). - [ ] **Step 6.1: Edit image tag** Use `Edit`: ``` old_string: image: reg.devxy.io/rpkgs/build-env-alpine:3.23-4.5 new_string: image: reg.devxy.io/rpkgs/build-env-alpine:3.23 ``` - [ ] **Step 6.2: Add `R_VERSION` env var** Use `Edit` (anchor on `GIT_USER:`): ``` old_string: GIT_USER: pat-s new_string: GIT_USER: pat-s R_VERSION: 4.5.3 ``` - [ ] **Step 6.3: Substitute R path** Use `Edit` with `replace_all: true`: ``` old_string: R -q -e new_string: /opt/R/${R_VERSION}/bin/R -q -e ``` - [ ] **Step 6.4: Validate** ```bash grep -nE '(^|[^/])R(script)? ' .crow/archive-missed-packages.yaml | grep -v '/opt/R/' | grep -v '^[^:]*:[0-9]*:#' grep -nE 'build-env-.*:[^[:space:]]*-[0-9]' .crow/archive-missed-packages.yaml ``` Expected: both return no output. - [ ] **Step 6.5: Commit** ```bash git add .crow/archive-missed-packages.yaml git commit -m "refactor(ci): use multi-R-version image in archive-missed-packages Drop the R-version suffix from the image tag and invoke R via the explicit /opt/R/\${R_VERSION}/bin/R path. The image OS does not matter for this workflow; it stays on alpine:3.23." ``` --- ## Task 7: build-r-minor-sensitive-packages workflow (1 file) **File (modify):** `.crow/build-r-minor-sensitive-packages.yaml` This is the only workflow on the `docker.io/devxygmbh/` registry, with lowercase matrix variables. Per the spec decision: keep the registry, drop `-${r_version}` from the tag, update the matrix to use full patch versions, and substitute the explicit R path (including `R CMD INSTALL`). - [ ] **Step 7.1: Bump matrix to full-patch R versions** Use `Edit`: ``` old_string: - os: alpine os_version: 3.21 r_version: 4.5 - os: alpine os_version: 3.21 r_version: 4.4 new_string: - os: alpine os_version: 3.21 r_version: 4.5.3 - os: alpine os_version: 3.21 r_version: 4.4.3 ``` - [ ] **Step 7.2: Edit image tag** Use `Edit`: ``` old_string: image: "docker.io/devxygmbh/rpkgs-build-env-${os}:${os_version}-${r_version}" new_string: image: "docker.io/devxygmbh/rpkgs-build-env-${os}:${os_version}" ``` - [ ] **Step 7.3: Substitute `R CMD INSTALL` invocation** Use `Edit`: ``` old_string: git clone -q https://codefloe.com/rpkgs/bincraft.git /tmp/bincraft && R CMD INSTALL --library=/tmp/R-libs /tmp/bincraft && R -q -e 'packageVersion("bincraft")' new_string: git clone -q https://codefloe.com/rpkgs/bincraft.git /tmp/bincraft && /opt/R/${r_version}/bin/R CMD INSTALL --library=/tmp/R-libs /tmp/bincraft && /opt/R/${r_version}/bin/R -q -e 'packageVersion("bincraft")' ``` - [ ] **Step 7.4: Substitute the remaining R invocation** Use `Edit`: ``` old_string: $XVFB -- R -q -e new_string: $XVFB -- /opt/R/${r_version}/bin/R -q -e ``` - [ ] **Step 7.5: Validate** ```bash grep -nE '(^|[^/])R(script)? ' .crow/build-r-minor-sensitive-packages.yaml | grep -v '/opt/R/' | grep -v '^[^:]*:[0-9]*:#' grep -nE 'rpkgs-build-env-.*:[^[:space:]]*-[0-9]' .crow/build-r-minor-sensitive-packages.yaml ``` Expected: both return no output. - [ ] **Step 7.6: Commit** ```bash git add .crow/build-r-minor-sensitive-packages.yaml git commit -m "refactor(ci): use multi-R-version image in build-r-minor-sensitive-packages Drop -\${r_version} from the docker.io/devxygmbh tag and invoke R via the explicit /opt/R/\${r_version}/bin/R path (including R CMD INSTALL). Bumps the matrix r_version values from 4.5/4.4 to the full-patch 4.5.3/4.4.3, matching the rest of the refactor's 'always full patch' rule." ``` --- ## Task 8: Justfile (3 recipes) **File (modify):** `Justfile` Three recipes use `docker run … reg.devxy.io/rpkgs/build-env-{{OS}}:{{OS_VERSION}}-{{R_VERSION}} …`. Drop `-{{R_VERSION}}` from each image tag and substitute `/opt/R/{{R_VERSION}}/bin/R` for every `R ` inside the `bash -c '…'` strings. The example comment lines above each recipe still mention `4.5.0` and `3.21` (which no longer have matching images). Update those examples to a current platform/R combination so they remain runnable. - [ ] **Step 8.1: Update `build-all` recipe and its example comment** Use `Edit`: ``` old_string: # just build-all alpine 3.21 arm64 4.5.0 odbc 1 build-all OS OS_VERSION ARCH R_VERSION PACKAGE NCPUS: docker run --rm -it --platform linux/{{ARCH}} -v ./:/package -e AWS_ACCESS_KEY_ID="$HETZNER_S3_ACCESS_KEY_K3S" -e AWS_SECRET_ACCESS_KEY="$HETZNER_S3_SECRET_KEY_K3S" -e PGPASS="$PGPASS" -e NCPUS={{NCPUS}} --pull=always reg.devxy.io/rpkgs/build-env-{{OS}}:{{OS_VERSION}}-{{R_VERSION}} bash -c 'R -q -e "install.packages(\"pak\", repos = sprintf(\"https://r-lib.github.io/p/pak/stable/%s/%s/%s\", .Platform\$pkgType, R.Version()\$os, R.Version()\$arch))" && R -q -e "pak::pak(\"git::https://codefloe.com/rpkgs/bincraft.git\")" && R -q -e "bincraft::build_binary_package(\"{{PACKAGE}}\", platform = \"{{OS}}\", force=TRUE, s3_endpoint = \"https://hel1.your-objectstorage.com\", s3_region = \"hel1\", s3_bucket = \"devxy-r-package-binaries-hel1\", s3_access_key_id = Sys.getenv(\"HETZNER_S3_ACCESS_KEY_K3S\"), s3_secret_access_key = Sys.getenv(\"HETZNER_S3_SECRET_KEY_K3S\"), metadata_db_host = \"r-binaries.devxy.io\", metadata_db_name = \"build_metadata\", metadata_db_table = \"single_builds\", metadata_db_user = \"rpkgs\", metadata_db_password = Sys.getenv(\"PGPASS\"), metadata_db_sslmode = \"require\", metadata_db_port = 15432, archive = TRUE, upload = TRUE, store_build_metadata = TRUE)"' new_string: # just build-all alpine 3.22 arm64 4.5.3 odbc 1 build-all OS OS_VERSION ARCH R_VERSION PACKAGE NCPUS: docker run --rm -it --platform linux/{{ARCH}} -v ./:/package -e AWS_ACCESS_KEY_ID="$HETZNER_S3_ACCESS_KEY_K3S" -e AWS_SECRET_ACCESS_KEY="$HETZNER_S3_SECRET_KEY_K3S" -e PGPASS="$PGPASS" -e NCPUS={{NCPUS}} --pull=always reg.devxy.io/rpkgs/build-env-{{OS}}:{{OS_VERSION}} bash -c '/opt/R/{{R_VERSION}}/bin/R -q -e "install.packages(\"pak\", repos = sprintf(\"https://r-lib.github.io/p/pak/stable/%s/%s/%s\", .Platform\$pkgType, R.Version()\$os, R.Version()\$arch))" && /opt/R/{{R_VERSION}}/bin/R -q -e "pak::pak(\"git::https://codefloe.com/rpkgs/bincraft.git\")" && /opt/R/{{R_VERSION}}/bin/R -q -e "bincraft::build_binary_package(\"{{PACKAGE}}\", platform = \"{{OS}}\", force=TRUE, s3_endpoint = \"https://hel1.your-objectstorage.com\", s3_region = \"hel1\", s3_bucket = \"devxy-r-package-binaries-hel1\", s3_access_key_id = Sys.getenv(\"HETZNER_S3_ACCESS_KEY_K3S\"), s3_secret_access_key = Sys.getenv(\"HETZNER_S3_SECRET_KEY_K3S\"), metadata_db_host = \"r-binaries.devxy.io\", metadata_db_name = \"build_metadata\", metadata_db_table = \"single_builds\", metadata_db_user = \"rpkgs\", metadata_db_password = Sys.getenv(\"PGPASS\"), metadata_db_sslmode = \"require\", metadata_db_port = 15432, archive = TRUE, upload = TRUE, store_build_metadata = TRUE)"' ``` - [ ] **Step 8.2: Update `build-single` recipe and its example comments** Use `Edit`: ``` old_string: # just build-single alpine 3.21 arm64 4.5.0 odbc 1.5.0 1 # just build-single alpine 3.22 arm64 4.5.0 sf latest 1 # just build-single ubuntu noble arm64 4.2.3 rlang 1.1.6 1 build-single OS OS_VERSION ARCH R_VERSION PACKAGE TAG NCPUS: docker run --rm -it --platform linux/{{ARCH}} -v ./:/package -e AWS_ACCESS_KEY_ID="$HETZNER_S3_ACCESS_KEY_K3S" -e AWS_SECRET_ACCESS_KEY="$HETZNER_S3_SECRET_KEY_K3S" -e PGPASS="$PGPASS" -e NCPUS={{NCPUS}} --pull=always reg.devxy.io/rpkgs/build-env-{{OS}}:{{OS_VERSION}}-{{R_VERSION}} bash -c 'R -q -e "install.packages(\"pak\", repos = sprintf(\"https://r-lib.github.io/p/pak/stable/%s/%s/%s\", .Platform\$pkgType, R.Version()\$os, R.Version()\$arch))" && R -q -e "pak::pak(\"git::https://codefloe.com/rpkgs/bincraft.git\")" && R -q -e "bincraft::build_binary_package(\"{{PACKAGE}}\", tag = \"{{TAG}}\", platform = \"{{OS}}\", force=TRUE, s3_endpoint = \"https://hel1.your-objectstorage.com\", s3_region = \"hel1\", s3_bucket = \"devxy-r-package-binaries-hel1\", s3_access_key_id = Sys.getenv(\"HETZNER_S3_ACCESS_KEY_K3S\"), s3_secret_access_key = Sys.getenv(\"HETZNER_S3_SECRET_KEY_K3S\"), metadata_db_host = \"r-binaries.devxy.io\", metadata_db_name = \"build_metadata\", metadata_db_table = \"single_builds\", metadata_db_user = \"rpkgs\", metadata_db_password = Sys.getenv(\"PGPASS\"), metadata_db_sslmode = \"require\", metadata_db_port = 15432, archive = TRUE, upload = TRUE, store_build_metadata = TRUE)"' new_string: # just build-single alpine 3.22 arm64 4.5.3 odbc 1.5.0 1 # just build-single alpine 3.22 arm64 4.5.3 sf latest 1 # just build-single ubuntu noble arm64 4.4.3 rlang 1.1.6 1 build-single OS OS_VERSION ARCH R_VERSION PACKAGE TAG NCPUS: docker run --rm -it --platform linux/{{ARCH}} -v ./:/package -e AWS_ACCESS_KEY_ID="$HETZNER_S3_ACCESS_KEY_K3S" -e AWS_SECRET_ACCESS_KEY="$HETZNER_S3_SECRET_KEY_K3S" -e PGPASS="$PGPASS" -e NCPUS={{NCPUS}} --pull=always reg.devxy.io/rpkgs/build-env-{{OS}}:{{OS_VERSION}} bash -c '/opt/R/{{R_VERSION}}/bin/R -q -e "install.packages(\"pak\", repos = sprintf(\"https://r-lib.github.io/p/pak/stable/%s/%s/%s\", .Platform\$pkgType, R.Version()\$os, R.Version()\$arch))" && /opt/R/{{R_VERSION}}/bin/R -q -e "pak::pak(\"git::https://codefloe.com/rpkgs/bincraft.git\")" && /opt/R/{{R_VERSION}}/bin/R -q -e "bincraft::build_binary_package(\"{{PACKAGE}}\", tag = \"{{TAG}}\", platform = \"{{OS}}\", force=TRUE, s3_endpoint = \"https://hel1.your-objectstorage.com\", s3_region = \"hel1\", s3_bucket = \"devxy-r-package-binaries-hel1\", s3_access_key_id = Sys.getenv(\"HETZNER_S3_ACCESS_KEY_K3S\"), s3_secret_access_key = Sys.getenv(\"HETZNER_S3_SECRET_KEY_K3S\"), metadata_db_host = \"r-binaries.devxy.io\", metadata_db_name = \"build_metadata\", metadata_db_table = \"single_builds\", metadata_db_user = \"rpkgs\", metadata_db_password = Sys.getenv(\"PGPASS\"), metadata_db_sslmode = \"require\", metadata_db_port = 15432, archive = TRUE, upload = TRUE, store_build_metadata = TRUE)"' ``` - [ ] **Step 8.3: Update `process-updates` recipe and its example comment** Use `Edit`: ``` old_string: # just process-updates redhat 9 arm64 4.4.3 'lubridate::interval(lubridate::today() - 4, lubridate::today() - 4)' process-updates OS OS_VERSION ARCH R_VERSION interval: docker run --rm -it --platform linux/{{ARCH}} -e AWS_ACCESS_KEY_ID="$HETZNER_S3_ACCESS_KEY_K3S" -e AWS_SECRET_ACCESS_KEY="$HETZNER_S3_SECRET_KEY_K3S" -e PGPASS="$PGPASS" --pull=always reg.devxy.io/rpkgs/build-env-{{OS}}:{{OS_VERSION}}-{{R_VERSION}} R -q -e "bincraft::process_cran_updates(interval = {{interval}}, platform = \"{{OS}}\", s3_endpoint = \"https://hel1.your-objectstorage.com\", s3_region = \"hel1\", s3_bucket = \"devxy-r-package-binaries-hel1\", s3_access_key_id = Sys.getenv(\"HETZNER_S3_ACCESS_KEY_K3S\"), s3_secret_access_key = Sys.getenv(\"HETZNER_S3_SECRET_KEY_K3S\"), metadata_db_host = \"r-binaries.devxy.io\", metadata_db_name = \"build_metadata\", metadata_db_table = \"single_builds\", metadata_db_user = \"rpkgs\", metadata_db_password = Sys.getenv(\"PGPASS\"), metadata_db_sslmode = \"require\", metadata_db_port = 15432, archive = TRUE, upload = TRUE, store_build_metadata = TRUE)" new_string: # just process-updates redhat 9 arm64 4.4.3 'lubridate::interval(lubridate::today() - 4, lubridate::today() - 4)' process-updates OS OS_VERSION ARCH R_VERSION interval: docker run --rm -it --platform linux/{{ARCH}} -e AWS_ACCESS_KEY_ID="$HETZNER_S3_ACCESS_KEY_K3S" -e AWS_SECRET_ACCESS_KEY="$HETZNER_S3_SECRET_KEY_K3S" -e PGPASS="$PGPASS" --pull=always reg.devxy.io/rpkgs/build-env-{{OS}}:{{OS_VERSION}} /opt/R/{{R_VERSION}}/bin/R -q -e "bincraft::process_cran_updates(interval = {{interval}}, platform = \"{{OS}}\", s3_endpoint = \"https://hel1.your-objectstorage.com\", s3_region = \"hel1\", s3_bucket = \"devxy-r-package-binaries-hel1\", s3_access_key_id = Sys.getenv(\"HETZNER_S3_ACCESS_KEY_K3S\"), s3_secret_access_key = Sys.getenv(\"HETZNER_S3_SECRET_KEY_K3S\"), metadata_db_host = \"r-binaries.devxy.io\", metadata_db_name = \"build_metadata\", metadata_db_table = \"single_builds\", metadata_db_user = \"rpkgs\", metadata_db_password = Sys.getenv(\"PGPASS\"), metadata_db_sslmode = \"require\", metadata_db_port = 15432, archive = TRUE, upload = TRUE, store_build_metadata = TRUE)" ``` - [ ] **Step 8.4: Validate** ```bash grep -nE 'build-env-.*\{\{OS_VERSION\}\}-' Justfile grep -nE "(^|[^/])R " Justfile | grep -v '/opt/R/' ``` Expected: both return no output. - [ ] **Step 8.5: Commit** ```bash git add Justfile git commit -m "refactor(justfile): use multi-R-version images in build/process recipes Drop -{{R_VERSION}} from the image tag and invoke R via the explicit /opt/R/{{R_VERSION}}/bin/R path in build-all, build-single, and process-updates. Updates example comments to use current platform/R combinations." ``` --- ## Task 9: Commented-out build-all-versions-install-deps.yaml in repo root (1 file) **File (modify):** `build-all-versions-install-deps.yaml` (the *commented-out* template at repo root, not the active files under `.crow/`). Keep the example in sync with the active workflows so it remains a faithful template. Every line in this file is prefixed with `# ` (block-comment); the substitutions still happen inside the comments. - [ ] **Step 9.1: Edit image tag (inside comment)** Use `Edit`: ``` old_string: # image: reg.devxy.io/rpkgs/build-env-${OS}:${OS_VERSION}-${R_VERSION} new_string: # image: reg.devxy.io/rpkgs/build-env-${OS}:${OS_VERSION} ``` - [ ] **Step 9.2: Substitute R path inside commented commands** Use `Edit` with `replace_all: true`: ``` old_string: R -q -e new_string: /opt/R/${R_VERSION}/bin/R -q -e ``` (Three `R -q -e` lines are inside the commented commands block.) - [ ] **Step 9.3: Validate** ```bash grep -nE '(^|[^/])R(script)? ' build-all-versions-install-deps.yaml | grep -v '/opt/R/' | grep -v '^[^:]*:[0-9]*:# *#' ``` Expected: no output (filters out lines that are nested-commented). - [ ] **Step 9.4: Commit** ```bash git add build-all-versions-install-deps.yaml git commit -m "refactor: keep commented build-all-versions-install-deps example in sync Mirror the multi-R-version image refactor in the commented-out template so the example remains faithful to active .crow workflows." ``` --- ## Task 10: Final repo-wide validation No code edits — just a comprehensive grep sweep across every file the previous tasks touched. Any failures discovered here mean a previous task missed an edit; go back and fix the offending file, commit separately, then re-run this validation. - [ ] **Step 10.1: Verify no bare R/Rscript invocations remain in any workflow** Run: ```bash grep -rnE '(^|[^/])R(script)? ' .crow/ Justfile build-all-versions-install-deps.yaml | grep -v '/opt/R/' | grep -vE '^[^:]*:[0-9]+:\s*#' ``` Expected: no output. If output appears, inspect each match. False positives are possible only for content unrelated to R invocation (e.g., a yaml key starting with "R" or text inside an R code string). True positives are missed substitutions — fix and recommit. - [ ] **Step 10.2: Verify no old-style image tags remain** Run: ```bash grep -rnE 'build-env-[a-z]+:[^[:space:]]*-[0-9]+\.[0-9]' .crow/ Justfile build-all-versions-install-deps.yaml ``` Expected: no output. - [ ] **Step 10.3: Verify every pattern-1 workflow declares `R_VERSION`** Pattern-1 workflows hard-code the image tag and therefore need an explicit `R_VERSION:` env var. Pattern-2 workflows (the four `build-all-versions-*` files) receive `R_VERSION` from `--var` and should NOT have it in their `environment:` block. Count files in each group: ```bash # Pattern-1 files that MUST have R_VERSION: in their environment block. # Total expected: 14 (process-updates) + 14 (weekly-rebuild) + 16 (weekly-audit) # + 14 (update-package-index) + 1 (archive-missed-packages) = 59 ls .crow/process-updates-*.yaml .crow/weekly-rebuild-missing-*.yaml .crow/weekly-audit-missing-*.yaml .crow/update-package-index-*.yaml .crow/archive-missed-packages.yaml | wc -l # Expected: 59 grep -lE '^\s+R_VERSION:' .crow/process-updates-*.yaml .crow/weekly-rebuild-missing-*.yaml .crow/weekly-audit-missing-*.yaml .crow/update-package-index-*.yaml .crow/archive-missed-packages.yaml | wc -l # Expected: 59 # Pattern-2 files that MUST NOT have a top-level R_VERSION: env var grep -nE '^\s+R_VERSION:' .crow/build-all-versions-*.yaml # Expected: no output ``` - [ ] **Step 10.4: Spot-check one file end-to-end** Read `.crow/process-updates-alpine-322-amd64.yaml` and visually confirm: 1. `image: reg.devxy.io/rpkgs/build-env-alpine:3.22` (no `-4.5`) 2. `R_VERSION: 4.5.3` appears in the `environment:` block 3. Every `R …` line in `commands:` is prefixed by `/opt/R/${R_VERSION}/bin/` Repeat for `.crow/update-package-index-redhat-10-amd64.yaml` (the one with the old `noble-4.5` tag). Repeat for `.crow/build-r-minor-sensitive-packages.yaml` (the lowercase-var, docker.io-registry file). - [ ] **Step 10.5: Verify smoke-test list is ready** Confirm the following workflows exist and are unchanged in shape; they are the targets for post-merge smoke runs (one per workflow type, per spec §Validation): ```bash ls -1 \ .crow/process-updates-alpine-322-amd64.yaml \ .crow/weekly-rebuild-missing-redhat-9-amd64.yaml \ .crow/weekly-audit-missing-ubuntu-2204-amd64.yaml \ .crow/update-package-index-redhat-10-amd64.yaml \ .crow/archive-missed-packages.yaml \ .crow/build-all-versions-amd64.yaml \ .crow/build-all-versions-install-deps-amd64.yaml \ .crow/build-r-minor-sensitive-packages.yaml ``` Expected: all eight files listed, no errors. Smoke runs are out of scope for this plan (they happen after the PR merges). - [ ] **Step 10.6: Final no-op commit only if any fix-up was needed** If steps 10.1–10.5 surfaced any issues that required edits, commit those fixes here: ```bash git add -A git commit -m "fix(ci): catch missed substitutions from multi-R-version refactor" ``` If everything was clean, no commit is needed for this step. --- ## Notes for the executing engineer - **No tests to run.** This refactor changes CI workflow files; correctness is validated by `grep` checks at each task boundary and by smoke runs after merge. - **Order doesn't matter between tasks 1–7.** Each task is independent and self-committing. Tasks 8–9 (Justfile, commented file) are also independent. Task 10 is final and depends on all others being complete. - **If `Edit` complains that an `old_string` isn't unique:** add more surrounding context to disambiguate. The Edit tool requires the `old_string` to match exactly one location in the file. - **`replace_all` safety:** the tokens we use it on (`R -q -e`, `R CMD INSTALL`, etc.) were verified by grep to appear only inside workflow command lines, never inside YAML structure or unrelated content. If a future workflow violates that assumption, switch to per-line targeted edits. - **Don't squash commits.** Each task produces a logically coherent commit; keeping them separate makes `git bisect` useful if a smoke run regresses.