Reusable GitLab CI/CD templates for R packages in the lab.
gla provides shared CI/CD pipeline templates that can be included in any
R package repo via GitLab's
include: mechanism.
This eliminates copy-paste of CI jobs across repos and provides a single place
to update pipeline logic.
gla uses major version tags (v1, v2, …) for breaking changes.
Non-breaking improvements land on main continuously.
ref: value |
Behavior |
|---|---|
main |
Always latest — you get fixes immediately but may hit breaking changes |
v2 |
Pinned to a stable release — opt in to breaking changes on your schedule |
Current stable tag:
v2(2025-06-17) — renamedclaude-respondstage toclaude. See CHANGELOG.md for details and migration steps.
To find the latest tag:
- GitLab UI: Repository → Tags (sorted by date)
- CLI:
git ls-remote --tags --refs https://gitlab.example.com/example-group/gla.git | sed 's#.*/##' | sort -V | tail -1
In your repo's .gitlab-ci.yml:
include:
- project: 'example-group/gla'
ref: v2 # or 'main' for always-latest
file:
# ── Required ──────────────────────────────────────────────────────
- '/templates/workflow.yml' # Prevents duplicate pipelines
# ── Standard R package CI ─────────────────────────────────────────
- '/templates/check-package.yml' # R CMD check
- '/templates/lint.yml' # lintr + Code Quality report
# Semantic line breaks check. Added after the v2 tag, so it is only
# available on `ref: main` until the next tag is cut -- uncommenting it
# under `ref: v2` fails pipeline creation with "file not found".
# - '/templates/check-new-line-breaks.yml'
- '/templates/test-coverage.yml' # covr coverage badge
- '/templates/docs-check.yml' # roxygen2 up-to-date check
- '/templates/version-check.yml' # DESCRIPTION version bump check
# ── Documentation site ───────────────────────────────────────────
- '/templates/pkgdown.yml' # Build pkgdown site
- '/templates/pkgdown-deploy.yml' # Multi-branch site deployment
# ── Security ─────────────────────────────────────────────────────
- '/templates/security.yml' # SAST + secret detection
# PHI scan (gha's check-phi.py). Added after the v2 tag, so it is only
# available on `ref: main` until the next tag is cut -- uncommenting it
# under `ref: v2` fails pipeline creation with "file not found".
# - '/templates/check-phi.yml'
# ── AI review (optional) ─────────────────────────────────────────
# - '/templates/claude.yml' # @claude agent + auto-review
image: rocker/verse:latest
variables:
# Choose dependency manager: "pak" (default) or "renv"
GLA_DEP_MANAGER: "pak"
stages:
- build
- secrets
- test
- lint
- docs
- version
- pkgdown
- pkgdown-move
- prepare-deploy
- deploy
# - claude # Uncomment if enabling claude.ymlTip: List
workflow.ymlfirst in the include list for readability (include-list ordering has no functional effect on GitLab CI — it's purely a convention to signal that this is the foundational template).Important: GitLab allows only one top-level
workflow:block per assembled pipeline. Do not define your ownworkflow:block when including this template — it will conflict regardless of include order. See the template header comments for details.
Always include
workflow.yml. Without it, GitLab creates both a branch pipeline and an MR-event pipeline on every push when an MR is open — doubling resource usage and cluttering the Pipelines page.
workflow.yml defines a single top-level workflow: block that suppresses
MR-event pipelines in favor of branch pipelines. This means:
CI_MERGE_REQUEST_*variables are not available. Jobs needing MR context (labels, target branch, etc.) query the GitLab API instead (seeversion-check.ymlfor an example).- GitLab's "Pipelines must succeed" merge setting still works — it gates on the latest pipeline for the source branch.
- Trigger pipelines (for
@claudewebhook), scheduled pipelines, and tag pipelines always run.
| Template | Job(s) | Description |
|---|---|---|
workflow.yml |
(none — pipeline-level) | Required. Prevents duplicate branch + MR pipelines |
check-package.yml |
check-package |
R CMD check with --no-manual |
lint.yml |
linting |
lintr with GitLab Code Quality report |
check-new-line-breaks.yml |
check-new-line-breaks |
Diff-scoped check for missing semantic line breaks |
test-coverage.yml |
test_coverage |
covr with coverage badge |
docs-check.yml |
docs_check |
Verify roxygen2 docs are up to date |
version-check.yml |
version-check |
Ensure DESCRIPTION version bumped vs main |
pkgdown.yml |
integration |
Build pkgdown site |
pkgdown-deploy.yml |
integration-*, gh-pages-prep, pages |
Multi-branch site deployment |
security.yml |
sast, secret_detection |
GitLab SAST + secret detection |
check-phi.yml |
check-phi |
Scan for PHI-like content (SSNs, MRNs, DOBs, study IDs, PHI column headers). Runs Morrison-Lab/gha's check-phi.py, fetched at a pinned SHA. Matched values are never logged. |
claude.yml |
claude-respond, claude-review, claude-companion-review |
(Optional.) AI code review and @claude coding agent. All jobs run in the claude stage; the companion review is opt-in. |
agent-review.yml |
agent-review |
(Experimental, opt-in.) A read-only Claude Code review harness. It loads ai-config through Claude Code's native marketplace mechanism and requires a separately configured Anthropic-compatible gateway. |
Set these in your repo's .gitlab-ci.yml variables: block to customize
behavior:
| Variable | Default | Description |
|---|---|---|
GLA_DEP_MANAGER |
"pak" |
Dependency manager: "pak" or "renv" |
GLA_R_LIBS_USER |
${CI_PROJECT_DIR}/ci/lib |
Library path for installed packages |
GLA_CHECK_ARGS |
"--no-manual" |
Extra args for R CMD check |
GLA_CHECK_ERROR_ON |
"warning" |
rcmdcheck error level |
GLA_COVERAGE_ALLOW_FAILURE |
"true" |
Whether coverage job can fail |
GLA_PKGDOWN_INSTALL_QUARTO |
"false" |
Install quarto for .qmd vignettes |
LINTR_ERROR_ON_LINT |
"true" |
Fail pipeline on lint findings |
GLA_R_HELPERS_DIR |
".gla-R" |
Directory the lint job fetches gla's scripts/R/ helpers into |
NLB_GLOBS |
"*.md" |
Space-separated pathspecs for semantic line break check |
NLB_PATHS_IGNORE |
"" |
Comma/newline-separated glob patterns to skip in line break check |
NLB_FAIL |
"true" |
Fail pipeline on semantic line break violations |
NLB_CLAUSE_BREAKS |
"true" |
Flag long lines with mid-line semicolons (SemBr rule 5) |
NLB_CLAUSE_MIN_LENGTH |
"80" |
Minimum visible length before clause check applies |
GLA_NLB_REF |
pinned commit SHA | Commit SHA to fetch check-new-line-breaks.py at |
GLA_NLB_SHA256 |
digest of pinned file | Expected SHA256 checksum of check-new-line-breaks.py |
SITE_BY_BRANCH |
"TRUE" |
Deploy per-branch pkgdown sites |
GLA_PROJECT_ID |
(set in the template) | Project ID of gla repo (for script fetching) |
GLA_REF |
"main" |
Branch/tag of gla to fetch scripts from |
REVIEW_MODEL |
"databricks-gpt-5-6-sol" |
Databricks endpoint used for automatic review |
GLA_CLAUDE_COMPANION_REVIEW |
"false" |
Run an additional Claude Sonnet review |
GLA_REVIEW_PROMPT_ADDENDUM |
"" |
Optional repository-specific review guidance appended to the agent contract |
GLA_REVIEW_AGENT |
"true" |
Enable bounded read-only repository exploration; set "false" for prompt-only mode |
GLA_MAX_REVIEW_TOOL_ROUNDS |
8 |
Maximum read-only exploration rounds per review |
GLA_MAX_REVIEW_TOOL_CALLS |
16 |
Maximum read-only tool calls in one model response |
GLA_MAX_REVIEW_TOOL_RESULT_CHARS |
120000 |
Total tool-result characters retained across a review |
GLA_MAX_REVIEW_RESPONSE_CHARS |
200000 |
Maximum endpoint response characters retained per request |
GLA_REVIEW_DEADLINE_SECONDS |
480 |
Overall automatic-review deadline, below the 10-minute job timeout |
GLA_MAX_REVIEW_NOTES_CHARS |
500000 |
Cumulative MR-note response budget while fetching history |
GLA_MAX_REVIEW_HISTORY_CHARS |
12000 |
Maximum historical review context passed to the reviewer |
GLA_AGENT_REVIEW_ENABLED |
"false" |
Enable the experimental Claude Code agent-review job |
GLA_AGENT_REVIEW_MODEL |
(required) | Model identifier exposed by the agent gateway |
GLA_AGENT_REVIEW_BASE_URL |
(required) | Anthropic-compatible gateway base URL; do not assume the direct Databricks invocation URL works |
GLA_AGENT_REVIEW_AUTH_TOKEN |
(required) | Protected token accepted by the agent gateway |
GLA_AGENT_REVIEW_MAX_TURNS |
"12" |
Maximum Claude Code tool-use turns per review |
GLA_AGENT_REVIEW_MAX_DIFF_BYTES |
"250000" |
Positive canonical decimal maximum MR-diff bytes (up to 1,000,000,000); truncation is disclosed in the prompt |
GLA_AGENT_REVIEW_MAX_OUTPUT_CHARS |
"50000" |
Positive canonical decimal maximum characters posted in the agent review note (up to 1,000,000,000) |
The agent-review job runs only on a protected source branch with an open merge request. This keeps its protected gateway and GitLab credentials unavailable to ordinary feature-branch pipelines. Protect the source-branch pattern used for agent-reviewed changes before enabling it.
The scanner is fetched from GitHub at job time, so runners need egress to
raw.githubusercontent.com, and the image must be Debian/Ubuntu-based (the job
installs curl and git with apt-get).
| Variable | Default | Description |
|---|---|---|
GLA_PHI_IMAGE |
"python:3.13-slim" |
Image providing python3. Debian/Ubuntu-based unless you override before_script |
GLA_GHA_REPO |
"Morrison-Lab/gha" |
Repo hosting check-phi.py |
GLA_GHA_REF |
pinned commit SHA | Commit to fetch at. Pin to a SHA, never a branch: the fetched script is executed |
GLA_PHI_SHA256 |
digest of the pinned file | Verified before execution. Update in the same change as GLA_GHA_REF |
GLA_PHI_DETECTORS |
"" (all defaults) |
Comma-separated subset. Defaults: ssn, mrn, dob, csv_phi_header, study_id; also phone, email |
GLA_PHI_PATHS_IGNORE |
"" |
Comma/newline-separated globs to skip; supports ** |
GLA_PHI_ALLOWLIST_FILE |
"" |
Regex allowlist. When unset, .gitlab/phi-allowlist.txt then .github/phi-allowlist.txt are used if present |
GLA_PHI_FAIL |
"true" |
"false" warns instead of failing the job |
The check-phi job sets GIT_DEPTH: 0 at job level, which overrides a global
GIT_DEPTH (the shipped examples set 10). That is deliberate and not
configurable: diff mode needs the default branch reachable, and a shallow clone
makes the merge base unavailable, so the job would silently widen to a
whole-tree scan.
claude-review.sh bounds every component of the prompt it sends. Raise these if
reviews are missing context; lower them to cut token spend.
| Variable | Default | Description |
|---|---|---|
GLA_MAX_DIFF_BYTES |
250000 |
MR diff cap. Over this, the diff is truncated and the prompt says so |
GLA_MAX_KEY_FILES_BYTES |
250000 |
Total cap on inlined contents of changed files |
GLA_MAX_SINGLE_FILE_PCT |
60 |
Per-file cap, as a percentage of the total |
GLA_MAX_SINGLE_FILE_BYTES |
derived | Explicit per-file cap; overrides the percentage |
GLA_MAX_COMMIT_LINES |
100 |
Commit-message lines included |
GLA_MAX_MR_DESCRIPTION_CHARS |
2000 |
MR description characters |
GLA_MAX_PRIOR_REVIEW_CHARS |
3000 |
Previous auto-review characters fed back in |
GLA_MAX_AUTHOR_RESPONSE_CHARS |
2000 |
Author's reply-to-review characters |
MAX_LAB_MANUAL_BYTES |
40000 |
Lab manual pages fetched for convention citations |
GLA_MAX_LAB_MANUAL_BYTES |
40000 |
Lab manual pages fetched for convention citations; takes precedence over the legacy name |
GLA_LAB_MANUAL_BASE_URL |
SERG manual | Base URL for fetched manual pages |
GLA_LAB_MANUAL_DEFAULT_PAGES |
coding-style.html coding-practices.html |
Space-separated default page list |
GLA_LAB_MANUAL_ALWAYS_PAGES |
coding-practices.html for SERG |
Space-separated pages included in every diff-aware selection |
GLA_LAB_MANUAL_PAGE_MAP |
SERG page map | Space-separated `glob |
GLA_LAB_MANUAL_NAME |
SERG Lab Manual |
Attribution heading for fetched manual context |
Two things worth knowing:
Truncation is always disclosed. When any component is cut, the prompt tells the model what it is missing and instructs it to scope its verdict accordingly. A review over a partial diff says so rather than reading as a clean pass.
Changed-file context prioritizes source and test files before ordinary text and
lockfile or generated artifacts.
This preserves the bounded prompt while giving the files most relevant to
correctness the best chance of being complete.
Any remaining partial or omitted files are still disclosed to the reviewer.
The default 50% first-pass reserve can be adjusted with
GLA_CONTEXT_PRIORITY_RESERVE_PCT, or set to 0 to retain the legacy allocation.
The per-file cap is a share of the total, not a fixed size. It exists so one
large file cannot consume the whole budget and starve the other changed files,
which is a claim about proportion — so raising GLA_MAX_KEY_FILES_BYTES moves
the per-file cap with it. Set GLA_MAX_SINGLE_FILE_BYTES only if you need a
fixed ceiling regardless of the total.
Sizing headroom: at the defaults, the worst-case prompt is roughly 512,000 bytes (~128,000 tokens) against ~191,000 tokens of usable input on a 200k-token model, so there is about 1.5x room. These defaults are deliberately not near the limit.
Templates will use pak::pak() and pak::pkg_install() for dependency
installation. No extra setup needed.
Set GLA_DEP_MANAGER: "renv" and ensure your repo has:
renv.lock(committed)renv/activate.R(committed)
Templates will call renv::restore() before each job.
The scripts/ directory contains shared scripts used by the templates:
- R helper scripts — used by R package CI jobs
check-new-line-breaks.py— diff-scoped semantic line breaks checkerclaude-review.sh— automated MR code review (reviews full MR diff)claude-respond.sh— responds to @claude mentions in MR/issue comments
All scripts are fetched at runtime via curl from the gla repo
(using $CI_JOB_TOKEN for authentication). If a local copy exists in your
repo at .gitlab/scripts/, it takes precedence over the gla version.
To enable, uncomment
'/templates/claude.yml'in your include list and addclaudeto yourstages:list (see Quick Start above).
The claude.yml template provides automated code review and @claude agent
capabilities. See docs/claude-setup.md for the
full step-by-step setup guide.
Mention @claude at the start of a line (after optional blockquote markers
and a single list bullet) in an MR or issue comment:
@claude fix the typo in README.md
> @claude summarize this diff
- @claude add tests for the new helper
1. @claude refactor this function
> > @claude (nested blockquotes OK)
> - @claude (blockquoted list item OK)Mid-line mentions (e.g. please ask @claude) are not detected — this
prevents false triggers from discussion text. Single-backtick inline mentions
(`@claude`) are also ignored. Note: @claude inside fenced code blocks
(triple-backtick) will match if it appears at line-start — avoid putting
literal @claude at the start of a line in code examples.
Automatic reviews use GPT-5.6 Sol by default. Set REVIEW_MODEL in GitLab
CI/CD variables to use one different Databricks endpoint instead. For example,
databricks-claude-sonnet-4-6 gives you Claude-only automatic review.
Set GLA_CLAUDE_COMPANION_REVIEW=true to run Claude Sonnet 4.6 in addition to
the primary reviewer. Each pass labels findings with its reviewer identity and
endpoint, so the findings stay independent even if both passes use the same
model. The two jobs share a resource group, so they run serially rather than
creating a token burst. Only the primary review can trigger the @claude
auto-fix loop or adopt unresolved findings from the legacy untagged format;
companion reviews only manage their own reviewer-tagged findings.
Automatic review runs in bounded read-only agent mode by default.
The agent can inspect any tracked repository file, read the fetched MR discussion
and prior review history, and fetch only the UC Davis SERG Lab Manual or GitLab
documentation URLs on the built-in allowlist.
It cannot edit files, post comments, push, resolve threads, or merge.
Set GLA_REVIEW_AGENT=false to use the prompt-only reviewer.
If the selected endpoint rejects function tools, the job retries prompt-only.
GLA_MAX_REVIEW_TOOL_ROUNDS limits exploration (8 by default), and
GLA_MAX_REVIEW_TOOL_CALLS limits tool calls in each model response (16 by
default).
Calls beyond either bound receive a bounded error result and cannot execute
repository or network reads.
GLA_MAX_REVIEW_TOOL_RESULT_CHARS bounds accumulated tool output across the
review, and GLA_REVIEW_DEADLINE_SECONDS leaves cleanup time inside the job's
10-minute timeout.
GLA_MAX_REVIEW_HISTORY_CHARS limits historical review context (12,000 by
default).
CLAUDE_MODEL remains a compatibility fallback for automatic review, but new
configuration should use REVIEW_MODEL.
The @claude response workflow continues to use Claude Sonnet by default.
To use a different model for a specific request, add --model <name> anywhere
in your comment:
@claude --model opus refactor this into smaller functions
@claude fix the tests
--model haikuSupported aliases:
| Alias | Resolves to |
|---|---|
sonnet (default) |
databricks-claude-sonnet-4-6 |
opus |
databricks-claude-opus-4-8 |
haiku |
databricks-claude-haiku-3-5 |
You can also pass a full model name (e.g., --model databricks-claude-opus-4-8).
To change the default response model for an entire project, set the
CLAUDE_MODEL CI/CD variable in Settings → CI/CD → Variables.
- Databricks PAT creation
- GitLab project access token setup
- Pipeline trigger token and webhook configuration
- CI/CD variable configuration
- Troubleshooting and security considerations
See examples/ for complete .gitlab-ci.yml files:
examples/pak-gitlab-ci.yml— for repos using pak (like the example)examples/renv-gitlab-ci.yml— for repos using renv (for example, a consumer repo)
To migrate an existing repo:
- Remove
inst/ci/scripts from your repo (they now live in gla) - Replace your
.gitlab-ci.ymlwith the appropriate example - Customize variables as needed
- Commit and push — the pipeline will pull templates from gla
See REVDEPS.md for a list of repos that consume gla templates. This list helps us notify consumers quickly when we make breaking changes — but it's advisory, not authoritative. Always verify with a group-wide search when releasing a breaking change.
If your repo uses gla, please open an MR adding it to REVDEPS.md
(or file an issue). If you stop using gla, open an MR or issue to be
removed.
Edit templates in templates/ and scripts in scripts/. Changes to main
are immediately picked up by repos that reference ref: main. For breaking
changes:
- Merge the breaking change to
main - Create a new major version tag (
git tag -a vN -m "..."+ push) - Update CHANGELOG.md with migration instructions
- Notify consumer repos (see REVDEPS.md for known consumers)
- Update the "Current stable tag" line in this README
- Douglas Ezra Morrison: author and maintainer.
- Matt Ponzini (
mdponzini): made the code-review size cap a CI/CD variable inscripts/claude-review.sh(2026-08-04).