This document describes how @zereight/mcp-gitlab is released, what needs
maintainer approval, and which versions get fixes. Contributors don't need to
do anything for a release.
- Stable release: every Tuesday at 18:55 US Eastern (
America/New_York). That is 22:55 UTC during daylight saving time and 23:55 UTC otherwise. It ships everything merged tomainsince the last release. - Skipped week: when nothing user-facing merged since the last release, there is no stable release that week. Docs-only, CI-only, and bot-only changes don't trigger a release on their own.
- Hotfix: out of band, as soon as the fix is merged. See Hotfixes.
A PR merged after the Tuesday cut ships in the next release that is actually cut (normally the following Tuesday).
A change ships in the next stable release when:
- It is merged to
mainthrough a reviewed pull request. - CI is green: the PR checks passed, and the latest
Live API TestsandDeploy Documentationruns onmainpassed. - It has maintainer approval if it needs one (see Approval).
A hotfix ships out of band for:
- A security fix. Follow Security releases.
- An outage. The released package or image fails to install, start, or authenticate for a supported setup.
- A severe regression. A recent release breaks a core workflow, loses or corrupts data, or writes to the wrong GitLab resource.
Process:
- Open an issue with the impact and the affected versions. Security issues use a private advisory instead.
- Fix it on a branch from
mainwith a regression test. Keep the PR minimal. - Merge after review and green CI.
- Check
git log <last tag>..origin/main.scripts/release.shreleasesmainas it is, so everything merged since the last tag ships with the hotfix. Revert anything that isn't ready. - Cut the release with
scripts/release.sh. There is no local publish: nonpm publishand no image push from a checkout. If a publish step fails, retry it as described in How publishing works. - Comment on the issue with the published version.
The maintainer cuts every release. These changes need explicit maintainer approval, as an approving review on the PR, before they are released:
- Breaking changes. Only in a major release, and only with a migration
path: a deprecation warning in an earlier release, plus migration steps in
CHANGELOG.mdand the release notes. The v3.0 removal of legacy flags (#815) is the model. - User-facing behavior or config. Tool names, arguments, defaults, env
vars, CLI flags, and permission modes.
README*.mdanddocs/must match in the same PR. - Workflows, permissions, and publishing.
.github/workflows/, workflowpermissions:or secrets, release and publish scripts, and package or registry metadata. These paths are listed in.github/CODEOWNERS. - Security releases. The fix, the advisory text, and the publish time.
Vulnerabilities are reported through GitHub private vulnerability reporting (see SECURITY.md).
- Triage in a private advisory. Confirm the report, rate the severity, and record the affected versions.
- Fix in private. Use the advisory's temporary private fork or a private branch. Don't push the fix to a public branch or open a public PR before the release. Keep exploit details out of commit messages and PR titles.
- Request a CVE from the advisory. The GHSA ID exists once the advisory is drafted.
- Coordinate the publish. Agree on a date with the reporter, merge the fix
into
main, and cut a hotfix withscripts/release.shright away. - Publish the advisory only after the fixed version is on npm
(
npm view @zereight/mcp-gitlab@<version>) and Docker Hub (zereight050/gitlab-mcp:<version>). Set the patched version first. - Notify. Credit the reporter, add the GHSA link to the GitHub release notes, and comment on related public issues.
| Version | Support |
|---|---|
| Latest minor of the current major | Bug and security fixes |
| 2.x, after v3.0 is released | Security fixes only, see below |
| Older minors and majors | None |
Until v3.0 is released, only the latest 2.x release is supported.
After v3.0 is released, 2.x gets security fixes until 2027-02-28 or 90 days
after the v3.0 release, whichever is later. They ship as a new release on the
latest 2.x line. scripts/release.sh only releases from main, so this needs
a v2 maintenance branch and a script change when v3.0 ships.
scripts/release.sh (npm run release) cuts a release. Its optional argument
is the version bump: patch (default), minor, or major, as in
scripts/release.sh minor or npm run release -- minor. Any other argument,
or more than one, prints the usage and stops the script before it checks
anything.
- It checks that
ghis installed, the working tree is clean, the branch ismain, andHEADmatchesorigin/main. It refuses to release when no commit since the last tag references a PR (#123), and exits without a release whenHEADis already the current tag. - It bumps the version (
npm version patch,minor, ormajor). If the current version is tagged locally but not on the remote, it releases that tag instead, without a bump, and ignores the argument. That path skips the PR-reference check, step 3, and the commit and tag in step 4. Useminorormajorrather than tagging a version by hand: a hand-made tag's commit must also bumpserver.json(version, the npm packageversion, and the image tag; the MCP Registry publish fails on a mismatch), pin thenpxexamples to the previous release, regeneratedocs/tools/*.mdwithmake docs, and passnpm run release:mcp-registry -- --check. - It syncs the version into
server.jsonand pins thenpxexamples inREADME*.mdanddocs/to the previous release, the last known-good version. It runs the MCP Registry preflight (npm run release:mcp-registry -- --check) andmake docs. It then mergeschangelog.d/*.mdfragments (exceptREADME.md) into a new#### [vX.Y.Z]section ofCHANGELOG.mdand deletes those files. No fragments is a no-op, so a release with an emptychangelog.d/is unchanged from the previous path. - It commits
chore(release): vX.Y.Z, tagsvX.Y.Z, pushes both, and creates the GitHub release with generated notes.
The GitHub release notes list the PRs referenced since the previous tag, grouped
by PR title prefix (feat, fix, docs, ...), followed by contributors. The
script reads at most 20 PR numbers, so check the notes when more PRs merged. PRs
opened by github-actions[bot] (Homebrew formula, tool docs, Star History) are
left out. Dependabot PRs stay in. Releases below v3.0.0 also carry the
#815 deprecation notice.
Each pull request that a user can see adds a file under changelog.d/, named
after the branch with - for /. The
Changelog workflow fails a PR that adds
neither a fragment nor a CHANGELOG.md edit, unless it has the no-changelog
label. scripts/release.sh moves those items into CHANGELOG.md under the new
version heading. See changelog.d/README.md.
Publishing a stable GitHub release starts these workflows, directly or in turn. A pre-release skips them; see Pre-release builds.
npm-publish.ymlbuilds the tag and publishes@zereight/mcp-gitlabto npm with trusted publishing (OIDC) and provenance. The workflow uses no npm token. Itssync-homebrewjob then opens anautomation/homebrew-formulaPR that updatesFormula/zereight-mcp-gitlab.rb.docker-publish.ymlbuildslinux/amd64andlinux/arm64images with provenance and SBOM attestations, pusheszereight050/gitlab-mcp:<version>andlatestto Docker Hub with theDOCKERHUB_*secrets, and updates the Docker Hub README.mcp-registry-publish.ymlstarts when the release's Docker Publish run succeeds (workflow_run), not on the release itself. It checks out the release commit, waits until the npm version and the Docker tag are visible, then publishesserver.jsonto the MCP Registry asio.github.zereight/gitlab-mcp, authenticated with GitHub OIDC. If Docker Publish fails, the registry publish is skipped.mcpb-release.ymlpacks the Claude Desktop bundle and uploadsgitlab-mcp.mcpbto the GitHub release.
Retry a failed step in GitHub Actions, not from a local checkout: re-run the
failed workflow run, or dispatch docker-publish.yml,
mcp-registry-publish.yml, mcpb-release.yml, or homebrew-formula.yml by
hand. npm-publish.yml has no manual trigger, so a failed npm publish is
retried by re-running its run; the re-run skips the publish when the version is
already on npm. A release runs the workflow file at its tag, so the skip only
applies to releases tagged after it was added. Re-running a release's Docker
Publish run starts the registry publish again once it passes. Dispatch
docker-publish.yml from the release tag, as in
gh workflow run docker-publish.yml --ref v2.3.6 -f tag=2.3.6. Dispatched from
main, it builds main and overwrites the release image. A dispatched Docker
Publish doesn't start the registry publish, so dispatch
mcp-registry-publish.yml after it with the tag as its ref input, as in
gh workflow run mcp-registry-publish.yml -f ref=v2.3.6. That runs main's
workflow file and checks out the tag.
After a release:
- Check that the release workflows passed (
gh run list --event release), and the MCP Registry publish (gh run list --workflow mcp-registry-publish.yml --event workflow_run). - Merge the
automation/homebrew-formulaPR. - Comment on linked issues with the published version. Close them after the reporter confirms.
npm-publish.yml also publishes pre-release builds to the npm next dist-tag,
so a reporter can try a merged fix before the next stable release with
npx -y @zereight/mcp-gitlab@next.
- A push to
mainthat changes the package (TypeScript sources outsidetest/,scripts/, andmcp-server/,package.json,package-lock.json, or the TypeScript config) publishes<next patch>-next.<run number>, for example2.3.5-next.123.chore(release):commits and pushes bygithub-actions[bot]are skipped. - A GitHub pre-release publishes its tag version, which must look like
vX.Y.Z-rc.1. - The version is set in the job workspace only and never committed back. The
publish uses trusted publishing (OIDC) and provenance like
latest, always with--tag next.nextruns one at a time, so overlapping pushes don't race. - A pre-release doesn't publish
latestto npm, sync the Homebrew formula, push Docker images, update the MCP Registry, or upload the MCP bundle.scripts/release.shignores pre-release tags and versions when it picks the previous tag and the docs pin. - Right after a stable release,
nextcan be older thanlatestuntil the next push tomainpublishes a new build.