50 KiB
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 -<R version> 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
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):
- Replace the
image:line — drop-<R version>from the tag. - Insert
R_VERSION: <value>into theenvironment:block. Anchor the insertion to a line that already exists in that file (typicallyR_LIBS_USER:, falling back toGIT_USER:for files that don't cache R libraries). - Replace every bare
Rinvocation incommands:with/opt/R/${R_VERSION}/bin/R(includingxvfb-run R,R CMD, etc.). - Replace every bare
Rscriptinvocation incommands: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 -einvocation
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 -einvocations
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:
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:
grep -nE 'build-env-.*\$\{OS_VERSION\}-' .crow/build-all-versions-*.yaml
Expected: no output.
- Step 1.9: Commit
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_VERSIONenv 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:
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:
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:
grep -nE 'build-env-.*:[^[:space:]]*-[0-9]' .crow/process-updates-*.yaml
Expected: no output.
Run:
grep -nE 'R_VERSION:' .crow/process-updates-*.yaml | wc -l
Expected: 14 (one R_VERSION: per file).
- Step 2.19: Commit
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_VERSIONenv 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
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
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
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_VERSIONenv 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
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-<old tag>, where <old tag> 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
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:
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
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_VERSIONenv 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
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, oldnoble-4.4) -
Step 5.6: Apply to
update-package-index-alpine-323-amd64.yaml(alpine:3.23, 4.5.3, oldnoble-4.4) -
Step 5.7: Apply to
update-package-index-alpine-323-arm64.yaml(alpine:3.23, 4.5.3, oldnoble-4.4) -
Step 5.8: Apply to
update-package-index-ubuntu-2204-amd64.yaml(ubuntu:jammy, 4.4.3, oldnoble-4.4) -
Step 5.9: Apply to
update-package-index-ubuntu-2204-arm64.yaml(ubuntu:jammy, 4.4.3, oldnoble-4.4) -
Step 5.10: Apply to
update-package-index-ubuntu-2404-amd64.yaml(ubuntu:noble, 4.4.3, oldnoble-4.4) -
Step 5.11: Apply to
update-package-index-ubuntu-2404-arm64.yaml(ubuntu:noble, 4.4.3, oldnoble-4.4) -
Step 5.12: Apply to
update-package-index-redhat-8-amd64.yaml(redhat:8, 4.4.3, oldnoble-4.4) -
Step 5.13: Apply to
update-package-index-redhat-8-arm64.yaml(redhat:8, 4.4.3, oldnoble-4.4) -
Step 5.14: Apply to
update-package-index-redhat-9-amd64.yaml(redhat:9, 4.4.3, oldnoble-4.4) -
Step 5.15: Apply to
update-package-index-redhat-9-arm64.yaml(redhat:9, 4.4.3, oldnoble-4.4) -
Step 5.16: Apply to
update-package-index-redhat-10-amd64.yaml(redhat:10, 4.5.3, oldnoble-4.5— note the4.5not4.4!) -
Step 5.17: Apply to
update-package-index-redhat-10-arm64.yaml(redhat:10, 4.5.3, oldnoble-4.4) -
Step 5.18: Validate all 14 files
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:
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
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_VERSIONenv 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
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
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 INSTALLinvocation
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
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
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-allrecipe 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-singlerecipe 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-updatesrecipe 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
grep -nE 'build-env-.*\{\{OS_VERSION\}\}-' Justfile
grep -nE "(^|[^/])R " Justfile | grep -v '/opt/R/'
Expected: both return no output.
- Step 8.5: Commit
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
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
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:
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:
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:
# 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:
image: reg.devxy.io/rpkgs/build-env-alpine:3.22(no-4.5)R_VERSION: 4.5.3appears in theenvironment:block- Every
R …line incommands: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):
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:
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
grepchecks 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
Editcomplains that anold_stringisn't unique: add more surrounding context to disambiguate. The Edit tool requires theold_stringto match exactly one location in the file. replace_allsafety: 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 bisectuseful if a smoke run regresses.