gitlab-changelog-release
Skill4 starsChanged 13 days agoMITView on GitHub
Manually generate a CHANGELOG.md and cut a GitLab release, using either git-cliff (Conventional Commits) or GitLab's native Changelog API (Git trailers) depending on which convention the repo already uses. Use when the user asks to "generar changelog", "cortar un release", "sacar un tag", "hacer release notes", or wants a CHANGELOG.md set up/updated for a project on GitLab.com. This is the ONLY supported flow — run it by hand every time you cut a release. Do NOT wire this into GitLab CI/CD tag-triggered jobs; see "Why manual, not CI" below.
- Installs packages
- Commits and pushes
In this repository37 other files it gives its agents.
- blast-radiusSkill
- elementflow-kbSkill
- gitlab-ci-quality-gatesSkill
- gitlab-project-bootstrapSkill
- gitlab-security-setupSkill
- gitlab-wiki-bootstrapSkill
- lando-checkSkill
- lando-img-placeholderSkill
- lando-mail-smtpSkill
- lando-setupSkill
- mallard-releaseSkill
- mr-flowSkill
- no-commentsSkill
- paginated-report-pdfSkill
- panda-kbSkill
- ploi-prestashop-deploySkill
- prestashop-kbSkill
- ps-consoleSkill
- ps-css-buildSkill
- ps-demo-userSkill
- ps-email-previewSkill
- ps-image-regenSkill
- ps-invoice-previewSkill
- ps-new-projectSkill
- ps-one-page-checkoutSkill
- ps-project-docSkill
- ps-security-auditSkill
- ps-storefront-polishSkill
- ps-theme-cacheSkill
- ps-translateSkill
- ps-view-transitionsSkill
- ps-watchSkill
- reflectSkill
- show-me-your-workSkill
- skill-authorSkill
- task-contextSkill
- verify-ps-projectSkill
The file
We split this skill into the rules it gives an agent
9 rules. About 0 are common, 5 are distinctive, and 4 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈5
Distinctive · estimate
This author's own choices. Worth a look.
4
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Distinctive: line 73
Create at repo root:
- Very specific: line 129
If the repo has no tags yet, every commit lands under ## [Unreleased].
- Distinctive: line 159
Commit the updated CHANGELOG.md:
- Distinctive: line 174
Create the GitLab Release with the extracted notes:
- Very specific: line 198
Use only if the repo's commits already end with a Changelog: <type> trailer (added, fixed, changed, deprecated, removed, security, other) — …
- Distinctive: line 203
No local tool needed — everything runs through GitLab's own API, including the CHANGELOG.md commit itself (no git clone/push, no job-token …
- Very specific: line 208
Create a Project Access Token with the api scope: repo → Settings → Access Tokens.
- Distinctive: line 234
Create the GitLab Release:
- Very specific: line 249
Add .gitlab/changelog_config.yml to rename categories or change the template — see the Changelogs docs for the full templating language.
≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
GitLab Changelog + Release (manual flow)
Two valid approaches — pick based on what the repo's commit history already uses, don't mix them:
| Repo's commit convention | Use |
Conventional Commits (feat:, fix:, docs:...) — most Cinetic repos | Path A — git-cliff |
Changelog: added/fixed/changed/... Git trailers | Path B — GitLab native Changelog API |
They are not interchangeable. GitLab's native Changelog API
(/repository/changelog) and glab changelog generate ONLY read Changelog:
trailers — they do NOT parse feat:/fix: prefixes at all. Point either one
at a repo that only has Conventional Commits and it silently finds nothing
(tested live: returned "No changes." on a repo with 187 feat:/fix:
commits and zero trailers). Verified against
GitLab's own Changelogs docs —
no config exists to make the native API match commit-title prefixes instead
of trailers. Check git log --oneline -20 before picking a path: if you see
feat(...)/fix(...) etc, use Path A; if commits end with a Changelog:
added line, use Path B.
Why manual, not CI
GitLab.com tag-push pipeline creation was tested exhaustively on a real
Cinetic project and found completely non-functional — not a config bug on
our side. Proof: a job with no rules: at all (unconditional, should
always run) still produced zero pipeline objects on a plain push to the
default branch. That rules out YAML, rules:, job-token permissions, and
any known GitLab CI_JOB_TOKEN-push restriction (that one only applies to
pushes authenticated by a job token, not normal SSH/HTTPS pushes). Also
learned: glab api projects/<id>/ci/lint --dry-run is unreliable — it
reports "resulting pipeline would have been empty" even for an unconditional
job, so don't trust it as a signal either way. The remaining explanation
requires Owner-level access to Usage Quotas / Audit Events to confirm
(compute minutes, namespace restriction, etc) — until someone with that
access resolves it, do not build release automation on GitLab CI tag
triggers. Do the steps below by hand every time instead.
Path A — git-cliff (Conventional Commits)
Prerequisites
- Commits in the repo follow Conventional Commits (
feat:,fix:,docs:,chore:,refactor:, etc). git-cliff groups by these prefixes — non-conventional commits get silently skipped from the changelog. git-cliffinstalled locally:brew install git-cliff(Rust binary, zero npm dependencies/postinstall scripts — deliberately NOTconventional-changelog-cli, which is deprecated with 54 transitive deps).glabCLI authenticated (glab auth status) — used to create the GitLab Release without touching the UI.
Step 1 — cliff.toml config (once per project)
Create at repo root:
[changelog]
header = """
# Changelog\n
Todos los cambios notables de este proyecto se documentan aquí.\n
"""
body = """
{% if version %}\
## [{{ version | trim_start_matches(pat="v") }}]{% if timestamp %} - {{ timestamp | date(format="%Y-%m-%d") }}{% endif %}
{% else %}\
## [Unreleased]
{% endif %}\
{% for group, commits in commits | group_by(attribute="group") %}
### {{ group | striptags | trim | upper_first }}
{% for commit in commits %}
- {{ commit.message | upper_first }} ([{{ commit.id | truncate(length=7, end="") }}](../../commit/{{ commit.id }}))\
{% endfor %}
{% endfor %}\n
"""
trim = true
[git]
conventional_commits = true
filter_unconventional = true
split_commits = false
commit_parsers = [
{ message = "^feat", group = "Features" },
{ message = "^fix", group = "Bug Fixes" },
{ message = "^docs", group = "Documentation" },
{ message = "^style", group = "Styling" },
{ message = "^refactor", group = "Refactor" },
{ message = "^perf", group = "Performance" },
{ message = "^test", group = "Testing" },
{ message = "^chore\\(deps\\)", group = "Dependencies" },
{ message = "^chore", group = "Miscellaneous" },
{ message = "^ci", group = "CI/CD" },
{ message = "^build", group = "Build" },
{ message = "^revert", group = "Reverts" },
]
filter_commits = true
tag_pattern = "v[0-9]*"
ignore_tags = ""
topo_order = false
sort_commits = "oldest"Adjust commit_parsers groups/order to taste — this is the full default set.
Step 2 — first CHANGELOG.md (once, covers all history)
git-cliff -c cliff.toml -o CHANGELOG.mdIf the repo has no tags yet, every commit lands under ## [Unreleased].
That's expected and fine — the next tag you cut will split it out properly.
git-cliff prints a warning like N commit(s) were skipped due to parse
error(s) for non-conventional commits — that's normal, not a bug.
Commit it:
git add cliff.toml CHANGELOG.md
git commit -m "feat(changelog): add changelog generation with git-cliff"
git push origin <default-branch>Step 3 — cutting a release (repeat every release)
- Decide the version (semver:
vMAJOR.MINOR.PATCH). Bump MAJOR for breaking changes, MINOR for features, PATCH for fixes only — same rule regardless of whether commits used exact Conventional Commit types. Regenerate the full changelog and extract just this version's notes:
git-cliff -c cliff.toml -o CHANGELOG.md git-cliff -c cliff.toml --unreleased --tag vX.Y.Z --strip header -o release_notes.md--unreleased --tag vX.Y.Ztells git-cliff "treat everything since the last tag as if it were tagged vX.Y.Z" — this works BEFORE the tag exists, so you get the right notes without a chicken-and-egg problem.Commit the updated CHANGELOG.md:
git add CHANGELOG.md git commit -m "docs(changelog): update for vX.Y.Z" git push origin <default-branch>Tag and push:
git tag -a vX.Y.Z -m "vX.Y.Z" git push origin vX.Y.ZCreate the GitLab Release with the extracted notes:
glab release create vX.Y.Z -F release_notes.md --repo <group/subgroup/project>Or without
glab, via API:glab api -X POST "projects/<PROJECT_ID>/releases" \ -f "tag_name=vX.Y.Z" \ -f "description=$(cat release_notes.md)"Clean up the local scratch file:
rm release_notes.md
Path B — GitLab native Changelog API (Git trailers)
Use only if the repo's commits already end with a Changelog: <type> trailer
(added, fixed, changed, deprecated, removed, security, other) —
not Conventional Commit prefixes. Official GitLab tutorial:
Automate releases and release notes with GitLab.
No local tool needed — everything runs through GitLab's own API, including
the CHANGELOG.md commit itself (no git clone/push, no job-token setup).
Setup (once per project)
- Create a Project Access Token with the
apiscope: repo → Settings → Access Tokens. - Store it as an env var / password manager entry (this is a manual flow, not a CI/CD variable — no CI job reads it).
Cutting a release (repeat every release)
- Commits destined for the changelog must have a trailer, e.g.:
For squash-merged MRs, put the trailer on the MR's squash/merge commit message (edit it in the "Ready to merge" box before merging).Add ChatBot Changelog: added - Tag the release:
git tag -a vX.Y.Z -m "vX.Y.Z" && git push origin vX.Y.Z - Generate release notes from the API (writes markdown, does NOT touch the
repo):
curl -H "PRIVATE-TOKEN: $TOKEN" \ "https://gitlab.com/api/v4/projects/<PROJECT_ID>/repository/changelog?version=vX.Y.Z" \ | jq -r .notes > release_notes.md - Create the GitLab Release:
glab release create vX.Y.Z -F release_notes.md --repo <group/subgroup/project> - Optional — persist the same notes into a
CHANGELOG.mdfile in the repo. Same call with-X POSTinstead of GET — it commits directly to the default branch via the API:curl -H "PRIVATE-TOKEN: $TOKEN" -X POST \ "https://gitlab.com/api/v4/projects/<PROJECT_ID>/repository/changelog?version=vX.Y.Z"
Customizing categories/template
Add .gitlab/changelog_config.yml to rename categories or change the
template — see the Changelogs docs
for the full templating language. Also lets you override tag_regex if tags
don't follow vMAJOR.MINOR.PATCH.
Checklist
Path A (git-cliff):
cliff.tomlpresent at repo root,commit_parsersmatches this project's Conventional Commit types in useCHANGELOG.mdgenerated and committed- Version decided (semver, based on change content not commit count)
release_notes.mdgenerated with--unreleased --tag vX.Y.Z- Tag created + pushed
- GitLab Release created via
glab release createwith the notes - Scratch
release_notes.mdremoved
Path B (native API):
- Project access token (scope
api) available (env var / password manager, not a CI/CD variable) - Commits destined for the changelog carry a
Changelog: <type>trailer - Tag created + pushed
release_notes.mdfetched via GET, GitLab Release created from it- (optional) CHANGELOG.md persisted via the
-X POSTvariant
Both paths — do NOT:
- Wire
changelog/releasejobs into.gitlab-ci.ymlgated onrules: if: $CI_COMMIT_TAGand assume they'll fire. Confirmed non-functional on at least one GitLab.com project even with an unconditional (no-rules) job. Revisit only if a group Owner confirms the root cause (suspected compute-minutes quota) and fixes it.
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

