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