agentleFS
educlopezeduclopez/mallard/skills/gitlab-changelog-release/SKILL.md

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

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
  1. Distinctive:

    Create at repo root:

    line 73
  2. Very specific:

    If the repo has no tags yet, every commit lands under ## [Unreleased].

    line 129
  3. Distinctive:

    Commit the updated CHANGELOG.md:

    line 159
  4. Distinctive:

    Create the GitLab Release with the extracted notes:

    line 174
  5. Very specific:

    Use only if the repo's commits already end with a Changelog: <type> trailer (added, fixed, changed, deprecated, removed, security, other) — …

    line 198
  6. Distinctive:

    No local tool needed — everything runs through GitLab's own API, including the CHANGELOG.md commit itself (no git clone/push, no job-token …

    line 203
  7. Very specific:

    Create a Project Access Token with the api scope: repo → Settings → Access Tokens.

    line 208
  8. Distinctive:

    Create the GitLab Release:

    line 234
  9. Very specific:

    Add .gitlab/changelog_config.yml to rename categories or change the template — see the Changelogs docs for the full templating language.

    line 249

≈ “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.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
SKILL.md

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 conventionUse
Conventional Commits (feat:, fix:, docs:...) — most Cinetic reposPath A — git-cliff
Changelog: added/fixed/changed/... Git trailersPath 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-cliff installed locally: brew install git-cliff (Rust binary, zero npm dependencies/postinstall scripts — deliberately NOT conventional-changelog-cli, which is deprecated with 54 transitive deps).
  • glab CLI 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.md

If 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)

  1. 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.
  2. 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.Z tells 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.

  3. Commit the updated CHANGELOG.md:

    git add CHANGELOG.md
    git commit -m "docs(changelog): update for vX.Y.Z"
    git push origin <default-branch>
  4. Tag and push:

    git tag -a vX.Y.Z -m "vX.Y.Z"
    git push origin vX.Y.Z
  5. Create 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)"
  6. 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)

  1. Create a Project Access Token with the api scope: repo → Settings → Access Tokens.
  2. 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)

  1. Commits destined for the changelog must have a trailer, e.g.:
    Add ChatBot
    
    Changelog: added
    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).
  2. Tag the release: git tag -a vX.Y.Z -m "vX.Y.Z" && git push origin vX.Y.Z
  3. 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
  4. Create the GitLab Release:
    glab release create vX.Y.Z -F release_notes.md --repo <group/subgroup/project>
  5. Optional — persist the same notes into a CHANGELOG.md file in the repo. Same call with -X POST instead 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.toml present at repo root, commit_parsers matches this project's Conventional Commit types in use
  • CHANGELOG.md generated and committed
  • Version decided (semver, based on change content not commit count)
  • release_notes.md generated with --unreleased --tag vX.Y.Z
  • Tag created + pushed
  • GitLab Release created via glab release create with the notes
  • Scratch release_notes.md removed

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.md fetched via GET, GitLab Release created from it
  • (optional) CHANGELOG.md persisted via the -X POST variant

Both paths — do NOT:

  • Wire changelog/release jobs into .gitlab-ci.yml gated on rules: if: $CI_COMMIT_TAG and 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.

Download now (ZIP)

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
What did you use it for? e.g. Reviewing pull requests in a Rails app

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.