Centrally-managed, parameterized GitHub Actions for the org's R packages.
CI logic lives in reusable workflows here in poissonconsulting/.github; each package carries thin caller workflows that pass its classification.
Update a reusable workflow and every package inherits it on the next run.
Reusable workflows (.github/workflows/, workflow_call; callers must use secrets: inherit):
| Workflow | Inputs | Purpose |
|---|---|---|
R-CMD-check.yaml |
tier, jags, cmdstan, tex, private |
R CMD check; the tier selects the OS/R matrix and error_on. |
test-coverage.yaml |
cmdstan, tex, private |
covr + Codecov. |
pkgdown.yaml |
cmdstan, private |
Build + deploy the docs site to gh-pages. |
check-no-suggests.yaml |
private |
R CMD check with hard dependencies only (active CRAN packages). |
R-hub (workflow-templates/rhub.yaml) is dispatch-triggered and r-hub-owned, so it is vendored into a package as a template rather than invoked as a reusable caller.
Active CRAN packages only.
Each package's .github/workflows/ holds matching thin callers (generated by the sync tool), plus the fledge callers (fledge-bump.yaml, fledge-tag-on-merge.yaml) and any bespoke workflows (paper.yaml, slack-check-package.yaml).
The sync tool preserves the bespoke workflows and migrates fledge callers to the current .yaml templates (a stale .yml fledge caller is replaced, not preserved).
Callers run on pushes to main/master and on pull requests targeting main, master, or dev (pkgdown builds but does not deploy on pull requests).
Tooling:
tools/package-tiers.tsv— the tier registry (source of truth fortier).tools/excluded-repos.sh— shared exclude registry (sandboxes/templates), sourced bysync-ci.sh,rollout-fledge-automation.sh, andset-fledge-branch-protection.sh.tools/sync-ci.sh— renders callers from the registry + auto-detection and opens routed PRs.
Per package, the sync tool resolves:
- tier: registry entry, else active-on-CRAN ->
cran, elseunimportant(default). - private: from repo visibility -> selects
GITHUB_PAT(PRIVATE_ACTIONS_PATfor private repos so private dependencies install;GITHUB_TOKENotherwise). - jags: from DESCRIPTION (
rjags|runjags|jagsUI|R2jags, a tidy one-per-linejmbrdependency entry, or a JAGSSystemRequirements) -> adds the macOS/Windows JAGS installs (Linux gets JAGS viasetup-r-dependenciessystem requirements). - cmdstan: from DESCRIPTION (a tidy one-per-line
cmdstanrorsmbr2dependency entry, or a CmdStanSystemRequirements) -> installs the CmdStan toolchain in the check, coverage, and pkgdown builds. - tex: from repo contents (a
.Rnwvignette, or an.Rmd/R source targeting a LaTeX output) -> installs TinyTeX. - cran: active on CRAN -> also gets the
check-no-suggestscaller and the vendoredrhub.yaml, independent of tier.
Only tier is hand-maintained, and only to promote a non-CRAN package to important (or force a tier).
private, jags, cmdstan, tex, and CRAN membership are auto-detected at sync time and never listed in the registry.
CRAN membership is detected against the src/contrib/PACKAGES index, which lists only currently published packages.
Archived packages (removed from CRAN) are therefore not treated as CRAN and resolve to unimportant unless the registry pins them.
The per-package web/packages/<pkg>/index.html page is not used for detection because it persists for archived packages.
| tier | matrix | error_on | extra |
|---|---|---|---|
| cran | macOS-release, Windows-release, Ubuntu-{devel,release,oldrel-1} | warning | + check-no-suggests + rhub |
| important | macOS-release, Windows-release, Ubuntu-release, Ubuntu-oldrel-1 | warning | — |
| unimportant | Ubuntu-release | error | — |
tools/package-tiers.tsv, one <package> <tier> per line (# comments).
List only packages to promote to important or to force a tier; everything else defaults to unimportant, and active CRAN packages auto-resolve to cran.
tools/sync-ci.sh # dry run: classify every package + planned action
tools/sync-ci.sh --apply # render callers + open routed PRs
tools/sync-ci.sh --apply pkgA pkgB # restrict to named packages (piloting)
tools/sync-ci.sh --close-old # close the prior f-standardize-actions PRs/issues/branches
ENGINE_REF=branch tools/sync-ci.sh --apply pkgA # pin callers to a branch instead of @v1Routing follows the CODEOWNER: a normal PR when Joe owns the repo, otherwise an issue plus a PR assigned to the owner with review requested.
The tool is dry-run by default, throttled, and isolates per-repo failures so one transient does not abort the batch.
On --apply, if the CI system already has an open PR for a repo (its f-ci branch), it closes that PR (and its linked tracking issue) and opens a fresh one, so a re-run always reflects the current classification.
Note: a very high burst of API calls can trip GitHub's secondary rate limiter, which makes a dry run under-report (packages drop based on the throttle window). It clears within minutes; re-run and it converges.
A package whose deployed R-CMD-check caller says tier: important, but which is neither in the registry nor active on CRAN, would otherwise be silently downgraded to unimportant on the next sync.
Before any rollout, a pre-flight surfaces these candidates:
- dry run lists them (
CANDIDATE <pkg> (deployed=important, computed=unimportant)), no prompt and no change. --applywith a terminal (TTY) asks per candidate whether to add it to the registry: answering yes appends<pkg> importantto the localtools/package-tiers.tsv(kept important for this run) and answering no downgrades it.--applywithout a TTY (CI, pipes, the Claude Code!prefix, agent tools) skips candidates (no PR) so a batch run can never silently downgrade a manually promoted package.
The prompt only appears in an interactive terminal, so run tools/sync-ci.sh --apply <pkg> from a real shell to answer it.
When you keep a candidate important, commit the one-line tools/package-tiers.tsv change and open a PR to .github so the registry matches; otherwise the next sync re-flags it.
- Promote a package to
important: add<pkg> importanttotools/package-tiers.tsv, thentools/sync-ci.sh --apply <pkg>. - Roll out a reusable-workflow change: merge it into
main, move thev1tag forward (git tag -f v1 && git push -f origin v1), and the change propagates to every package on its next run. - Re-sync a package after a classification change (e.g. CRAN archival):
tools/sync-ci.sh --apply <pkg>; any openf-ciPR is closed and replaced automatically. - Add CI to a new package:
tools/sync-ci.sh --apply <pkg>; the package must have aDESCRIPTIONand (for the full sweep) a fledge-managedNEWS.md.
The one-time go-live sequence and rollout status are recorded in GO-LIVE.md and CI-ROLLOUT-STATUS.md; fledge version-bump automation is documented in fledge-automation.md.
ghauthenticated (withworkflowscope for--apply),git(SSH), andcurl.PRIVATE_ACTIONS_PAT,CODECOV_TOKEN, andGOOGLE_MAPS_ELEVATION_API_KEYorg secrets available to the package repos (the last is passed into the check and coverage environments for packages whose tests call the elevation API).- Callers pin the reusable workflows at
@v1; keep thev1tag pointing at the current.githubmain.
- Moving
v1also moves the fledge callers' pin, but the fledge reusable workflows are unchanged, so they keep working. - A genuine package failure (e.g. tests needing live API credentials) stays red regardless of tier; that is a real bug to fix in the package, not a CI issue.
- The reusable-workflow matrix is built with
fromJSON(needs.matrix.outputs.config), a standard supported pattern.