Skip to content

Renovate automation

renovate-self runs Renovate on a schedule to open dependency upgrade MRs — including, notably, MRs that bump a consumer's own pinned phpboyscout/cicd component versions. It exists alongside a bundled Renovate preset at this repo's root, and the two are designed to be used together.

A thin runner, deliberately

The component itself is intentionally minimal: it pins the runner image, sets the standard GitLab-mode environment (RENOVATE_PLATFORM=gitlab, RENOVATE_ENDPOINT, RENOVATE_AUTODISCOVER=false), and wires RENOVATE_REPOSITORIES and the token from inputs. Everything else — which managers run, what schedule, automerge policy, commit-message prefixes, package-rule grouping — stays per-project Renovate config, not baked into the component.

That's a deliberate choice, not an oversight: those settings genuinely vary by ecosystem. The Go track wants semantic-commit immediate PRs; the Terraform/infra track wants Monday batching with a chore(deps): prefix. Baking either into the shared component would force every consumer into a one-size-fits-all opinion that at least one track would immediately need to override — better to keep the component's job narrow (run Renovate, correctly) and leave policy to the consumer.

The preset: how cicd's own components get tracked

A default.json5 at this repo's root ships one custom manager — a regex that matches component: gitlab.com/phpboyscout/cicd/<name>@vX.Y.Z in any .gitlab-ci.yml and points its datasource at this project. A consumer extends it:

{ "extends": ["gitlab>phpboyscout/cicd"] }

and every phpboyscout/cicd component pin in that consumer's pipeline becomes something Renovate tracks and bumps automatically. This is reflexive: renovate-self is itself one of the components a consumer's Renovate run tracks, so a new cicd release surfaces as a Renovate MR in every extending consumer without anyone having to remember to check.

The gating rule, and why it's an input

renovate-self's default if: is $CI_PIPELINE_SOURCE == "schedule" && $RENOVATE_TASK == "scan" — designed for a daily or weekly GitLab pipeline schedule that sets RENOVATE_TASK = scan. Checking a task variable rather than just "any schedule" means one GitLab pipeline schedule can, in principle, drive more than one distinct task later without the two colliding. Like the gate components' if: input, this is overridable — a consumer with a different trigger philosophy widens or narrows it.

The image-version floor, and why it exists

image_version defaults to a recent Renovate major, but it has a real floor: it must be ≥ 39. The bundled preset's custom manager uses managerFilePatterns, introduced in Renovate 39 to replace the older fileMatch key — an older image rejects that configuration outright with an error that halts MR creation entirely, not just the custom manager. This is the kind of constraint that's easy to hit accidentally by pinning an older image "just to be safe," so it's called out explicitly rather than left as a surprise.

The token, and the same self-reference trap release-plz avoids

Following the token-input convention, token defaults to $RENOVATE_TOKEN — but on GitLab Free, project and group access tokens are Premium-only, so a consumer typically needs a fine-grained personal access token with api + write_repository scope instead.

The token can't be passed straight through as a same-named job variable, though: RENOVATE_TOKEN: "$[[ inputs.token ]]" renders, for the default input value, the self-referencing job variable RENOVATE_TOKEN: "$RENOVATE_TOKEN". GitLab resolves a job variable referencing a group variable of the same name to a broken value — not the group's real value — so Renovate would authenticate with junk and every consumer on the default token would see 401 throttle_unauthenticated_api. The component aliases the token to a non-colliding runtime variable (RENOVATE_TOKEN_RUNTIME) and hands it to Renovate via explicit shell expansion instead. See the token-input convention for the full mechanism — release-plz hits the identical trap and fixes it the same way.

Testing without opening real MRs

Running Renovate for real mutates external state — it opens MRs, pushes branches. The self-test overrides the schedule gate to run unconditionally and supplies a deliberately bogus token and repository list; Renovate fails to authenticate against GitLab, the script exits non-zero, and allow_failure.exit_codes tolerates exactly that failure mode. Any other exit code — a broken image pin, bad input interpolation — still fails the self-test pipeline.

See also