CityRP Documentation Repository

Commit Message Conventions

Last updated

Internal commits

Internal (non-player-facing) commits are written in Conventional Commits style: a lowercase type, a colon, then a description — fix: Permanent bans., feat: Add migrations library., chore: update photon.. Common types in use here are fix, feat, chore, docs, refactor, style, tests, and ci. Pick the type that matches what changed, not what the change was for.

Public Changelogs

Player-facing commits are marked with a public= prefix. Everything from public= onward is captured as the changelog entry — including any lines after the first, so a commit body written under a public= subject line is part of the entry, not just the subject line itself.

Where the entry goes

Pull requests currently land as merge commits, and the changelog webhook parses every commit that reaches master as its own entry. The entry goes on the last line of the merge commit message, typed by whoever merges:

Merge pull request 'fix(vehicles): reset FPIS grille bodygroups on None' (#1624) from dev/... into master

Reviewed-on: https://gitea.limelightgaming.net/limelight-development/cityrp2/pulls/1624
Reviewed-by: ...
public=Fixed FPIS grill lights, front and rear sides from duplicating.

The draft lives in a ## Changelog section at the head of the PR body, in a fenced block the merger copies from. The changelog skill writes it. An internal change has no entry, so its PR body has no ## Changelog section at all.

Warning

Keep public= out of the PR title. Gitea quotes the title into the merge subject, and the capture runs to the end of the message, so the entry is published with ' (#1416) from dev/... into master Reviewed-on: ... attached.

Warning

Keep public= off every leaf commit. A merge commit carries each leaf onto master, where it is parsed as an entry of its own, in addition to the merge's. Leaf commits and PR titles use the internal Conventional Commits style above.

Warning

Put public= last. Anything after it in the same message, a trailer or a second paragraph, becomes part of the entry.

An entry is at most 200 characters, counting everything after public=, hashtags included. The limit is hard: the database column and the console command that carries the entry both stop there.

A public= commit looks like:

public=Chefs return to Evocity. Chefs now receive 10% extra EXP when cooking on a stove. #type:update #thread:28936 #thread:28602

Feedback close message

A pull request body has a second, optional public= slot, unrelated to the changelog. When a merged pull request closes a feedback-synced issue (defined below), the website reads a close message from the pull request description and shows it to the player on their Feedback item, attached to its resolved status change:

public=We've added this, should be live soon.

The website reads it by these rules:

  • A public= inside a fenced or inline code block is ignored.
  • Outside code, the first public= wins, and everything after it to the end of the body is the message. The allowlisted changelog hashtags (#type:, #thread: and the rest) are stripped.
  • It only counts when whoever merges holds feedback.triage on the website.

So a pull request body has two slots, and they never collide:

Slot Where Read by
Changelog entry Inside the fence in the ## Changelog section at the head of the body The merger, who copies it into the merge commit message
Close message Unfenced, starting at public=, and the last thing in the body The website, on merge

The changelog entry stays inside its fence, which is what keeps the website from mistaking it for a close message. The close message goes below everything else: Closes #n, the test plan, gate notes and any tool attribution footer. Anything after it is shown to the player. A pull request that closes no feedback-synced issue has no close message, and no unfenced public= anywhere in its body.

A close message runs from a sentence to one paragraph, sized to the request; the changelog skill drafts it.

An issue is feedback-synced when its body ends with a Synced from: line linking https://limelightgaming.net/feedback/<id>. Check for that line, not the issue's labels: player bug reports arrive through the sync with only the repo bug label.

Hashtags

Hashtags are #tag:value, and by convention go at the end of the message, after the player-facing prose.

Tag Meaning Multiple values
#cmd:text A command to highlight Arbitrary text — /slashcommand and !bangcommand are both fine, it's just displayed as-is. One value per tag; repeat the tag for more than one command.
#contrib:name Credit a contributor Name or forum user ID. Comma-separated for multiple, and deduplicated case-insensitively.
#map:name Affected map Resolved to a display name and link. One value per tag; repeat the tag for more than one map.
#rev:id Related revision One value per tag; repeat the tag for more than one revision — a comma-joined value is not split apart, it just gets treated (and linked) as a single id.
#thread:id Forum thread Comma-separated values are each turned into their own link. Repeating the tag also works.
#image:url Image Comma-separated values are each turned into their own thumbnail. Repeating the tag also works.
#type:value Category Not rendered as its own entry — it picks the accent colour, see below. One value only.

#type: accepts info, new, update, fix, exploit, or remove.

So #contrib: is the one tag that's reliably comma-separated and deduplicated everywhere it's read. #thread: and #image: accept comma-separated values too. For everything else (#cmd:, #map:, #rev:), don't comma-join — repeat the tag once per value instead:

public=... #rev:114 #rev:115 #rev:116 #contrib:name 1,name 2 #cmd:/newcommand

Accent colour

Each changelog entry gets an accent colour — used for its row on the changelog page and for its embed color where it's posted elsewhere. It's picked from keywords anywhere in the captured text (case-insensitive, first match wins, checked in this order):

  1. exploit → exploit
  2. remove → remove
  3. bug / fix / hotfix / resolv / recover → fix
  4. added / new / release / approved → new
  5. otherwise → the default, update

An explicit #type:value tag overrides whatever the keyword cascade picked.

This means word choice in the description silently affects the accent colour unless #type: is set explicitly. For example, public=Fixed the new maintenance minigame hits the fix branch (checked before the new branch), not new, because "Fixed" matches first. If the intended category doesn't match what the keyword cascade would pick, set #type: explicitly rather than relying on wording.