Publish snapshots with GitLab CI
Publish an installable Kubb Studio snapshot from GitLab CI so reviewers can compare generated files against the target branch and previous pipelines. Store KUBB_TOKEN as a masked CI/CD variable, commit your npm lockfile, and keep kubb in the development dependencies so npm ci installs the CLI.
Prerequisites
- A repository with a working
kubb.config.tson Kubb 5.6.2 or later. Follow Installation if you have not set up Kubb. - A Kubb Studio organization and an organization CI API key, stored as
KUBB_TOKENin a secret only trusted pipelines can read. Snapshot installation uses a separate registry key. Create both in Studio under Settings > Developer > Tokens, with type CI forKUBB_TOKENand type Registry for installs.
Configure the job
Add this job:
snapshot:
stage: build
image: node:22
script:
- npm ci
- npx kubb studio snapshot
rules:
- if: $CI_MERGE_REQUEST_IID
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCHkubb ships the Studio runtime, so npm ci is the only setup. The rules run the job on merge request pipelines and on the default branch, so a merge request has a snapshot of its target branch to compare with.
Post the result as a note
Use --json to capture the snapshot result, then post it as a merge request note with this script. It updates the same note on each pipeline and uses only Node:
import { readFileSync } from 'node:fs'
const snapshot = JSON.parse(readFileSync('snapshot.json', 'utf8'))
const marker = '<!-- kubb-snapshot -->'
const counts = (c) => `${c.added.length} added · ${c.changed.length} changed · ${c.removed.length} removed`
const lines = [marker, `Kubb snapshot: \`npm i ${snapshot.url}\``]
const { branchChanges: branch, changes } = snapshot
if (branch) {
if (branch.base) lines.push(`Changes against \`${branch.branch}\`: ${counts(branch)}`)
else if (branch.baseFound) lines.push(`No snapshot of \`${branch.branch}\` for this package yet`)
else lines.push(`No snapshot of \`${branch.branch}\` to compare with`)
}
if (changes?.base) lines.push(`Changes since ${changes.base.commit?.slice(0, 7) ?? changes.base.createdAt}: ${counts(changes)}`)
const { CI_API_V4_URL, CI_PROJECT_ID, CI_MERGE_REQUEST_IID, GITLAB_API_TOKEN } = process.env
const notes = `${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/merge_requests/${CI_MERGE_REQUEST_IID}/notes`
const headers = { 'PRIVATE-TOKEN': GITLAB_API_TOKEN, 'Content-Type': 'application/json' }
const existing = (await (await fetch(`${notes}?per_page=100`, { headers })).json()).find((note) => note.body.startsWith(marker))
const response = await fetch(existing ? `${notes}/${existing.id}` : notes, {
method: existing ? 'PUT' : 'POST',
headers,
body: JSON.stringify({ body: lines.join('\n\n') }),
})
if (!response.ok) throw new Error(`GitLab answered ${response.status}`) script:
- npm ci
- npx kubb studio snapshot --json > snapshot.json
- '[ -z "$CI_MERGE_REQUEST_IID" ] || node scripts/kubb-note.mjs'api scope, stored as GITLAB_API_TOKEN. CI_JOB_TOKEN does not carry it.Review and install the result
Open the snapshot in Studio to review generated changes. To consume the generated package, follow Install the snapshot.