Read the release manifest
This guide covers fetching a release manifest file, reading the module versions out of it, and validating it against the JSON Schema.
For what the data means — the compatibility claim a record makes, why one exact version is named rather than a range, and what neither tier claims — read The release manifest first. This page assumes you have.
Decide which tier to read
The release manifest has two tiers in two projects. Which one you read follows from whether the GitLab version you care about has published.
| Your question | Tier | Project | Access |
|---|---|---|---|
| Which module versions shipped with a published GitLab version? | Immutable record | manifests/released | Public |
| Which module versions are currently staged for a GitLab version that has not published? | Mutable catalog | manifests/unreleased | Internal |
If you are installing, reproducing, or pinning an environment, you want the record. The catalog is working state and can change until its GitLab version publishes.
The two tiers hold the same data, laid out differently. For GitLab 19.5.0:
unreleased (catalog) released (record)
19/ 19/
└── 5/ └── 5/
└── 0/ └── 0.json
├── artifact-registry.json
├── knowledge-graph.json
└── iam.jsonIn the catalog the patch number is a directory holding one file per module. In the record it is the filename, and the single file holds every module.
Read the immutable record
1. Construct the path
A record is one file per GitLab release, holding every module:
{major}/{minor}/{patch}.jsonGitLab 19.5.0 is therefore 19/5/0.json. Use the major, minor and patch
numbers only: a GitLab version string can carry a suffix — 19.2.2-ee on a
stable branch, 19.3.0-pre on master — and the path never does.
2. Fetch it
Fetch the record over its raw file URL. The record tier is public, so this needs no token. Save the record to disk, because the later steps read it rather than fetching it again:
export RECORD_URL="https://gitlab.com/gitlab-org/release/manifests/released/-/raw/main/19/5/0.json"
curl --fail --silent --output record.json "${RECORD_URL}"--fail matters: without it a missing record writes GitLab’s error page to
record.json and exits zero, and the failure surfaces later as invalid JSON.
3. Read the module versions
A record names every module that shipped with that GitLab version:
{
"version": "19.5.0",
"modules": {
"artifact-registry": {
"version": "1.275.1",
"sha": "6cdaf83c809b7d02c794d5db2dea9e101c662ac7",
"ref": "v1.275.1",
"source": { "url": "https://gitlab.com/gitlab-org/ops/artifact-registry" }
},
"knowledge-graph": {
"version": "0.95.3",
"sha": "51aab3662a8764f47616acefdff8163312bd2bc6",
"ref": "v0.95.3",
"source": { "url": "https://gitlab.com/gitlab-org/orbit/knowledge-graph" }
},
"iam": {
"version": "2.14.0",
"sha": "a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2",
"ref": "v2.14.0",
"source": { "url": "https://gitlab.com/gitlab-org/auth/iam" }
},
"membrane": {
"version": "0.8.12",
"sha": "b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3",
"ref": "v0.8.12",
"source": { "url": "https://gitlab.com/gitlab-org/cells/membrane" }
}
}
}Each module carries its own version, independent of GitLab’s and of every other module’s. Versions and commit SHAs throughout this page are illustrative.
The version to install for a module is modules.<module-id>.version:
jq -r '.modules["artifact-registry"].version' record.json| Field | Meaning |
|---|---|
version (top level) | The GitLab version this record describes |
modules | Map of module IDs to their entries. Each key is the module’s ID within Release Framework |
modules.*.version | The module version that shipped. This is the one to install |
modules.*.sha | Full 40-character Git commit SHA the module released from |
modules.*.ref | Git ref for the module release, for example v1.275.1 |
modules.*.source.url | The module’s source repository |
The keys under modules are module IDs. Read the key as it appears in the
file. You cannot derive it from the module’s project path.
Read the mutable catalog
The catalog splits the same data the other way: one file per module, not one file per release.
{major}/{minor}/{patch}/{module-id}.jsonArtifact Registry’s entry for the upcoming GitLab 19.6.0 is
19/6/0/artifact-registry.json. One module release writes the same entry to
several version directories, so an entry under a version directory does not
mean the module was built or tested against that GitLab version.
The file holds the module entry alone, without the surrounding modules map:
{
"version": "1.276.0",
"sha": "c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4",
"ref": "v1.276.0",
"source": {
"url": "https://gitlab.com/gitlab-org/ops/artifact-registry"
}
}Two consequences for reading it:
- There is no single file listing every module for an upcoming release. List
the directory with the Repository Tree API to see which modules have entries
so far, for example
path=19/6/0. - An entry can be replaced at any time until its GitLab version publishes, so do not cache one as though it were final.
Internal readers only.
manifests/unreleasedis private. GitLab team members can read it throughgitlab-orgmembership. Automation outsidegitlab-orgneeds a group share with the Reporter role, which the Delivery: Release and Deploy team grants.
Validate a release manifest file
One schema validates both tiers:
export SCHEMA_URL="https://gitlab.com/gitlab-org/release/manifests/schema/-/raw/main/json-schemas/release-manifest.schema.json"
pipx install check-jsonschema
check-jsonschema --schemafile "${SCHEMA_URL}" record.jsonThe schema resolves the two shapes with oneOf, so a file is valid when it
matches exactly one of them — a record or a catalog entry. You do not select a
schema per tier, and you do not need to know which shape a file holds before
validating it.
Two things to expect when validation fails. Because a file must match exactly
one shape, a single missing field is reported against both shapes, so most of
the errors you see will be irrelevant. Both shapes also reject unknown keys, so
adding a key such as $schema to a release manifest file fails validation.
Both release manifest projects validate every JSON file in CI, on merge requests and on the default branch. The writer commits to the default branch directly, so for a written entry the default-branch pipeline detects an invalid file rather than preventing it.
Handle a missing version or module
Three kinds of absence occur, and they do not mean the same thing:
- No record for a GitLab version. Versions published before the release manifest existed have no record. The absence says nothing about which module versions those releases shipped with.
- A record exists, but a module is not in it. That module has no recommended version for that GitLab release. This is not a statement that the module is incompatible with it.
- The entry was never written. An entry can be missing even though the module released successfully. The write does not run as part of the module’s release, and a write that fails or never runs does not fail that release. So absence does not imply the module published late.
Handle all three as “no answer”, not as an error and not as an implied version. Falling back to a neighbouring release’s record substitutes a version pair that was never published together.
Related
- The release manifest — how to interpret what you read here
- Release Manifest design document
- Release manifest schema — the JSON Schema and its test fixtures
- Onboard to Release Framework — how a module comes to have release manifest entries in the first place