Below are pragmatic, tooling-friendly approaches to ensure every changelog line carries a JIRA reference that links back to your tracker. All examples assume JIRA keys like OBS-123 (adjust the regex if your project key differs).
- Commit convention (Conventional Commits + JIRA at end):
feat(api): add distributed tracing [OBS-123]
or:
feat(api): add distributed tracing
Refs: OBS-123
- Changelog entry (Keep a Changelog):
### Added
- Add distributed tracing ([OBS-123](https://jira.example.com/browse/OBS-123))Pros: Zero extra tools.
Cons: Manual discipline; no auto-linking.
1) Add a cliff.toml at repo root:
[changelog]
header = "# Changelog"
body = """
{% for group, commits in commits | group_by(attribute="group") %}
### {{ group | upper_first }}
{% for commit in commits %}
- {{ commit.message | first_line | trim }}{% if commit.footers %} ({{ commit.footers | join(sep=", ") }}){% endif %}{% if commit.jira %} ({{ commit.jira | join(sep=", ") }}){% endif %}
{% endfor %}
{% endfor %}
"""
footer = ""
[git]
conventional_commits = true
# Extract JIRA keys from subject, body, or footers
# Matches OBS-123, CORE-42, etc. Adjust to your project(s).
filter_unconventional = false
[commit_parsers]
# Map types to Keep a Changelog sections
"feat" = { group = "Added" }
"fix" = { group = "Fixed" }
"perf" = { group = "Changed" }
"refactor" = { group = "Changed" }
"docs" = { group = "Changed" }
"build" = { group = "Changed" }
"ci" = { group = "Changed" }
"chore" = { group = "Changed" }
"style" = { group = "Changed" }
"test" = { group = "Changed" }
[regex]
# Capture JIRA keys anywhere in the commit
jira = "(?i)\\b([A-Z][A-Z0-9]+-\\d+)\\b"
[remote.github]
# not used for JIRA; keep if you also want GH links
[templates]
# Render matched JIRA keys as full links
jira = "[{{ key }}](https://jira.example.com/browse/{{ key }})"2) Generate changelog:
git cliff -o CHANGELOG.mdExpected output snippet:
### Added
- add distributed tracing ([OBS-123](https://jira.example.com/browse/OBS-123))Notes:
jiraregex pulls keys from subject/body/footers.templates.jiracontrols link format.- Maps Conventional Commit
feat→ “Added”,fix→ “Fixed”, etc., matching Keep a Changelog.
1) Enforce commit format with JIRA in subject or footer.
Example subject:
feat(auth): allow login using email alias [OBS-789]
Or footer:
Refs: OBS-789
2) .cz.toml (or .czrc) to standardize commits:
[tool.commitizen]
name = "cz_conventional_commits"
version = "0.0.0"
tag_format = "v$version"
bump_message = "chore(release): $current_version → $new_version"
changelog_file = "CHANGELOG.md"
update_changelog_on_bump = true
[tool.commitizen.customize]
# Optional: guardrail hint, not strict enforcement
message_template = "{commit_type}{scope}: {subject} [JIRA-KEY]"3) commitlint (optional) to require a JIRA key
If you use commitlint with Husky:
// commitlint.config.cjs
module.exports = {
extends: ['@commitlint/config-conventional'],
rules: {
'footer-max-line-length': [2, 'always', 200],
'references-empty': [0, 'never'],
'subject-case': [0],
// Require JIRA key either in subject [...] or footers:
'subject-jira-key': [2, 'always']
},
plugins: [
{
rules: {
'subject-jira-key': ({ subject, footer }) => {
const re = /\b[A-Z][A-Z0-9]+-\d+\b/;
const ok = re.test(subject || '') || re.test(footer || '');
return [
ok ? 0 : 2,
ok ? 'ok' : 'Commit must contain a JIRA key like OBS-123'
];
}
}
}
]
};4) conventional-changelog
Most generators can be extended via writer options, but it’s simpler to:
- Keep the key in the subject, and
- Post-process output (a small script) to auto-link
OBS-123→https://jira.example.com/browse/OBS-123.
Example post-process (Node):
// scripts/link-jira.js
const fs = require('fs');
const path = 'CHANGELOG.md';
let s = fs.readFileSync(path, 'utf8');
s = s.replace(/\b([A-Z][A-Z0-9]+-\d+)\b/g, '[$1](https://jira.example.com/browse/$1)');
fs.writeFileSync(path, s);Then:
conventional-changelog -p angular -i CHANGELOG.md -s
node scripts/link-jira.jsIf you prefer curated entries:
- Put JIRA in the news fragment filename or content, e.g.
123.featurecontaining:Add distributed tracing. (OBS-123)
towncrier buildwill assembleCHANGELOG.md.- After build, run a small Python script to auto-link JIRA keys:
# scripts/link_jira.py import re, pathlib p = pathlib.Path("CHANGELOG.md") s = p.read_text() s = re.sub(r"\b([A-Z][A-Z0-9]+-\d+)\b", r"[\1](https://jira.example.com/browse/\1)", s) p.write_text(s)
- Put the JIRA key in every commit (subject
[...]or footerRefs:). - Generate changelog with
git-cliffand a shortcliff.tomlthat:- Maps Conventional Commit types to Keep a Changelog sections,
- Auto-detects JIRA keys via regex,
- Renders them as links.
- Keep entries user-focused; the JIRA link is appended at the end of each bullet.
Resulting changelog excerpt:
## [Unreleased]
### Added
- Add distributed tracing ( [OBS-123](https://jira.example.com/browse/OBS-123) )
### Fixed
- Correct negative number handling in parser ( [OBS-456](https://jira.example.com/browse/OBS-456) )This gives you reliable observability from changelog → JIRA with minimal friction and excellent team hygiene.