GitLab version support¶
The role supports the GitLab versions GitLab itself supports: the current major line and the two before it — today that is 19.x, 18.x, and 17.x. If your instance is on an officially supported GitLab release, this role is meant to work against it, and a failure on one of those versions is a bug to report, not an unsupported configuration.
Verified profiles¶
| Profile | How it was verified |
|---|---|
19.2-ee |
The catalogue and GraphQL contract are pinned to it; full live probe suite against the gitlab-v19 lab instance (19.2.1-ee), 2026-08-01. |
18.11-ee |
Full live probe suite plus write-mode validation, 2026-07-30. |
17.11-ee |
Full live probe suite plus write-mode validation, 2026-07-30. |
19.1-ee |
The long-lived primary lab instance; every feature probe develops and re-runs against it. |
19.1-ce |
Full live probe suite with edition-aware declarations (EE-only children withheld, license gaps declared), 2026-08-01. |
18.11-ce |
Full live probe suite with edition-aware declarations, 2026-08-01. |
17.11-ce |
Full live probe suite with edition-aware declarations, 2026-08-01. |
The 17.x/18.x verification runs surfaced three real differences, each fixed with behavior-keyed logic rather than a version gate — the warning-plus-named-failure model still has no per-version code paths:
- GitLab 17 returns 500 when the custom-attributes endpoints receive a URL-encoded parent path, so those policies resolve the parent's numeric id first (valid on every version).
- GitLab 17 adjourns top-level group deletion without the path-freeing
rename GitLab 18+ performs, and offers no API verb strong enough to
remove the marked group; the reconciler reproduces the upstream rename
so
state: absentmeans a reusable path everywhere. - GitLab 17's request validation times out on any document declaring the value-stream stage input variable, so the default create/update documents omit it and stage-bearing variants run only for declarations that carry stages.
How the model works¶
The support model is deliberately simple — one catalogue, one behavior contract, no per-version code paths:
- One pinned catalogue, newest first. The API catalogue and GraphQL
document contract are pinned to the newest verified profile (currently
19.2-ee). GitLab guarantees REST v4 stays additive within and across the supported majors, so older supported versions run the same declarations against the same catalogue. - Proceed and warn. An instance outside the verified set gets exactly one warning naming both versions and which side is ahead — then the run proceeds. Malformed version metadata still fails.
- Named failures, never silent ones. A declaration that uses a surface newer than the instance fails with the field or endpoint named. A surface the instance has removed or moved gets a diagnosis naming the cause — the audit-event-destination migration was found exactly this way, live, and fixed. GitLab's own validation messages ride role errors, bounded and credential-redacted.
- The measured drift is small. The role's bound API surface was compared across 18.11 → 19.2 during the version-handling work: zero breaking drift on the endpoints and fields the role uses. That is why one catalogue covers the supported window.
What "supported" means concretely¶
- Declarations for surfaces that exist on your version converge and are idempotent.
- Declarations for surfaces your version lacks fail loudly with the missing field or endpoint named — never a silent partial apply (the write gate preflights every operation before anything writes).
- Instance-configuration gaps (a disabled feature, a missing identity provider) are named as such, distinct from version gaps.
How alignment is maintained¶
- Verification is a live run, not a promise. A profile enters the
verified set when the live feature probes (
tests/live/*.yml) andmake live-validatepass against an instance of that version. Running them against a 17.x or 18.x instance is exactly how those profiles get verified — and any finding becomes a work item, the same way every finding in this project has. - Version-skew naming. When a request 404s on an endpoint the catalogue declares and the instance is outside the verified set, the diagnosis appends a hedged fragment naming both versions — the surface may not exist on that GitLab version, or may have been moved or removed upstream. Verified profiles never see the fragment, and it rides after GitLab's own error detail rather than replacing anything.
- No speculative matrix. A per-version catalogue matrix and CI fleet stay deferred until a live run against a supported older major actually misbehaves — the warning-plus-named-failure model has covered every case observed so far. If that changes, the matrix is the named next step, and the repository roadmap records the decision either way.
Running against your version¶
export GITLAB_URL=https://gitlab.example.com
export GITLAB_TOKEN=<admin PAT with api scope>
make live-validate # read-only proof against your instance
The feature probes (tests/live/groups.yml, tests/live/projects.yml)
are the full write-path proof; they create and delete disposable resources
under ansible-groups/ansible-projects top-level groups, so point them
only at an instance where that is acceptable.
Refreshing the pinned catalogue for a new GitLab release¶
Moving the pin (say, to a hypothetical v19.4.0-ee) is a mechanical,
reviewable cycle. The runtime never fetches API specifications — all of
this is development-time tooling, and CI re-verifies the recorded digests
so a refresh cannot drift from its claimed upstream source.
1. Regenerate the REST catalogue¶
Fetches GitLab's pinned OpenAPI v3 document and settings.md from that
exact tag and deterministically rewrites files/gitlab_rest_catalog.json
plus its provenance metadata (source URLs, SHA-256 digests of every
input, generator version).
2. Review the drift before adopting it¶
Reports path/method additions and removals, request-field changes, and —
the part that matters — any path a committed role policy uses that
disappeared or changed shape. --check exits non-zero on policy-breaking
drift, so the diff is a review artifact, not a blind swap. (This is the
check that would have caught the group audit-event-destination REST
removal before a live 404 did.)
3. Re-dump and re-project the GraphQL schema¶
# On an instance actually running the new release (a lab toolbox works):
# tools/read_graphql_schema_source.rb -> full introspection dump
python3 tools/project_graphql_schema.py \
--source <dump.json> \
--output files/graphql/gitlab_schema.json.gz \
--documents-dir files/graphql
python3 tools/render_graphql_metadata.py
python3 tools/validate_graphql_documents.py
Record the dump's commit/pipeline provenance in
files/graphql/metadata.yml. Every committed operation document must
validate against the new schema — upstream renames (the 19.2
namespacePath → fullPath input change, for example) surface here,
before any live call.
4. Regenerate derived assets and run the gates¶
python3 tools/render_public_assets.py
python3 tools/render_capability_docs.py
make lint && python -m pytest tests/unit && tests/integration/run.sh
The contract tests verify every policy path against the new catalogue and every auth binding against its endpoints. Whatever fails is the honest work list for that GitLab release. New fields become declarable wherever the refreshed catalogue admits them; extend policies and schema docs for anything the role should actively manage.
5. Verify the profile (optional but what removes the warning)¶
Build a lab instance of the release, run the feature probes, and add the
profile to SUPPORTED_PROFILES with its evidence row in the table above.
Until then the new release runs under the ordinary
warning-plus-named-failure model like any unverified profile.