build-cran-binaries/docs/superpowers/plans/2026-05-25-multi-r-version-images.md

50 KiB
Raw Blame History

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):

  1. Replace the image: line — drop -<R version> from the tag.
  2. Insert R_VERSION: <value> 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.yamlR -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.11.3 for .crow/build-all-versions-arm64.yaml

The three edits are textually identical to steps 1.11.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.yamlR -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.51.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_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:

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_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
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_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
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_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
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

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_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
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 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
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-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
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:

  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):

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.110.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 grep checks at each task boundary and by smoke runs after merge.
  • Order doesn't matter between tasks 17. Each task is independent and self-committing. Tasks 89 (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.