HelixCommit includes five built-in formatters for rendering changelogs: Markdown, HTML, plain text, JSON, and YAML.
Feature Markdown HTML Text JSON YAML GitHub releases Best Works Plain No No Websites Good Best No API API Email Varies Best Universal No No Terminal Good No Best Parse Good Programmatic use Parse Parse Parse Best Best CI/CD pipelines Good Limited Good Best Best
Generate GitHub-compatible Markdown.
render_markdown()
from helixcommit.formatters.markdown import render_markdown
markdown = render_markdown(changelog: Changelog) -> str
Structured changelog object to render.
Returns: Formatted Markdown string
Example
from helixcommit.formatters.markdown import render_markdown
markdown = render_markdown(changelog)
# Save to file
Path( "RELEASE_NOTES.md" ).write_text(markdown)
# Print to console
print (markdown)
## Release v2.0.0
_Released on 2025-11-13_
### Breaking Changes
- **api** : Change authentication flow (abc123)
### Features
- **ui** : Add dashboard component (def456)
- **api** : Implement rate limiting (ghi789)
### Bug Fixes
- Fix memory leak in parser (jkl012)
[ View full changelog ]( https://github.com/owner/repo/compare/v1.0.0...v2.0.0 )
Generate styled HTML output.
render_html()
from helixcommit.formatters.html import render_html
html = render_html(changelog: Changelog) -> str
Structured changelog object to render.
Returns: Formatted HTML string with embedded CSS
Example
from helixcommit.formatters.html import render_html
html = render_html(changelog)
# Save to file
Path( "changelog.html" ).write_text(html)
# Serve on website
return Response(html, mimetype = 'text/html' )
<! DOCTYPE html >
< html >
< head >
< meta charset = "utf-8" >
< title > Release v2.0.0 </ title >
< style >
/* Embedded CSS styles */
</ style >
</ head >
< body >
< div class = "release" >
< h2 > Release v2.0.0 </ h2 >
< p class = "date" > Released on November 13, 2025 </ p >
<!-- Sections and items -->
</ div >
</ body >
</ html >
Text formatter
Generate plain text output.
render_text()
from helixcommit.formatters.text import render_text
text = render_text(changelog: Changelog) -> str
Structured changelog object to render.
Returns: Plain text string
Example
from helixcommit.formatters.text import render_text
text = render_text(changelog)
# Save to file
Path( "CHANGES.txt" ).write_text(text)
# Send via email
send_email( to = "team@example.com" , body = text)
# Post to Slack
slack_client.post_message( channel = "#releases" , text = text)
================================================================================
Release v2.0.0
Released on 2025-11-13
================================================================================
BREAKING CHANGES
----------------
* api: Change authentication flow (abc123)
FEATURES
--------
* ui: Add dashboard component (def456)
* api: Implement rate limiting (ghi789)
BUG FIXES
---------
* Fix memory leak in parser (jkl012)
Full changelog: https://github.com/owner/repo/compare/v1.0.0...v2.0.0
Generate machine-readable JSON output for programmatic use.
render_json()
from helixcommit.formatters.json import render_json
json_output = render_json(changelog: Changelog, * , indent: int = 2 ) -> str
Structured changelog object to render.
Number of spaces for JSON indentation.
Returns: JSON string representation of the changelog
Example
from helixcommit.formatters.json import render_json
json_output = render_json(changelog)
# Save to file
Path( "changelog.json" ).write_text(json_output)
# Parse and process
import json
data = json.loads(json_output)
for section in data[ "sections" ]:
print ( f " { section[ 'title' ] } : { len (section[ 'items' ]) } items" )
{
"version" : "v2.0.0" ,
"date" : "2025-11-13T12:00:00+00:00" ,
"sections" : [
{
"title" : "Features" ,
"items" : [
{
"title" : "Add dashboard component" ,
"type" : "feat" ,
"scope" : "ui" ,
"breaking" : false ,
"references" : {
"commit" : "def456" ,
"pr" : "https://github.com/owner/repo/pull/42"
}
}
]
}
],
"metadata" : {
"compare_url" : "https://github.com/owner/repo/compare/v1.0.0...v2.0.0"
}
}
Generate human-readable YAML output for CI/CD pipelines.
render_yaml()
from helixcommit.formatters.yaml import render_yaml
yaml_output = render_yaml(changelog: Changelog, * , default_flow_style: bool = False ) -> str
Structured changelog object to render.
If True, use flow style (inline) for collections.
Returns: YAML string representation of the changelog
Example
from helixcommit.formatters.yaml import render_yaml
yaml_output = render_yaml(changelog)
# Save to file
Path( "changelog.yaml" ).write_text(yaml_output)
# Use with CI/CD tools
with open ( ".release-data.yaml" , "w" ) as f:
f.write(yaml_output)
version : v2.0.0
date : '2025-11-13T12:00:00+00:00'
sections :
- title : Features
items :
- title : Add dashboard component
type : feat
scope : ui
breaking : false
references :
commit : def456
pr : https://github.com/owner/repo/pull/42
metadata :
compare_url : https://github.com/owner/repo/compare/v1.0.0...v2.0.0
Generate output in all formats:
from pathlib import Path
from helixcommit.formatters import markdown, html, text, json, yaml
# Generate all formats
markdown_output = markdown.render_markdown(changelog)
html_output = html.render_html(changelog)
text_output = text.render_text(changelog)
json_output = json.render_json(changelog)
yaml_output = yaml.render_yaml(changelog)
# Save to files
Path( "RELEASE_NOTES.md" ).write_text(markdown_output)
Path( "changelog.html" ).write_text(html_output)
Path( "CHANGES.txt" ).write_text(text_output)
Path( "changelog.json" ).write_text(json_output)
Path( "changelog.yaml" ).write_text(yaml_output)
print ( "✓ Generated release notes in all formats" )
Create your own formatter by implementing a rendering function:
from helixcommit.models import Changelog, ChangelogSection
def custom_formatter ( changelog : Changelog) -> str :
"""Generate custom format release notes."""
lines = []
# Header
lines.append( f "# Release { changelog.version } " )
lines.append( f "Date: { changelog.release_date.strftime( '%Y-%m- %d ' ) } " )
lines.append( "" )
# Sections
for section in changelog.sections:
lines.append( f "## { section.title } " )
for item in section.items:
# Format: "- [TYPE] Description (SHA)"
commit_type = item.commit_type.upper()
sha_short = item.commit_sha[: 7 ]
lines.append( f "- [ { commit_type } ] { item.description } ( { sha_short } )" )
lines.append( "" )
return " \n " .join(lines)
# Use custom formatter
output = custom_formatter(changelog)
print (output)
For more complex customization, use Jinja2 templates:
from jinja2 import Template
from helixcommit.models import Changelog
# Define template
template_string = """
# {{ changelog.version }}
Released: {{ changelog.release_date.strftime('%B %d , %Y') }}
{% for section in changelog.sections %}
## {{ section.title }}
{% for item in section.items %}
- {{ item.description }}
{%- if item.scope %} ({{ item.scope }}){% endif %}
{%- if item.breaking %} ⚠️ BREAKING{% endif %}
{% endfor %}
{% endfor %}
"""
def template_formatter ( changelog : Changelog) -> str :
"""Render using Jinja2 template."""
template = Template(template_string)
return template.render( changelog = changelog)
# Use template formatter
output = template_formatter(changelog)
Complete example
from datetime import datetime
from pathlib import Path
from helixcommit.changelog import ChangelogBuilder
from helixcommit.formatters import markdown, html, text, json, yaml
from helixcommit.git_client import CommitRange, GitRepository
def generate_all_formats ( repo_path : Path, since : str , until : str ):
"""Generate release notes in all formats."""
# Get commits
repo = GitRepository(repo_path)
commits = list (repo.iter_commits(
CommitRange( since = since, until = until)
))
# Build changelog
builder = ChangelogBuilder( include_scopes = True )
changelog = builder.build(
version = until,
release_date = datetime.now(),
commits = commits,
commit_prs = {},
pr_index = {}
)
# Generate all formats
formats = {
"RELEASE_NOTES.md" : markdown.render_markdown(changelog),
"changelog.html" : html.render_html(changelog),
"CHANGES.txt" : text.render_text(changelog),
"changelog.json" : json.render_json(changelog),
"changelog.yaml" : yaml.render_yaml(changelog)
}
# Save files
output_dir = repo_path / "releases"
output_dir.mkdir( exist_ok = True )
for filename, content in formats.items():
output_file = output_dir / filename
output_file.write_text(content)
print ( f "✓ Generated { filename } " )
# Usage
generate_all_formats(
repo_path = Path( "." ),
since = "v1.0.0" ,
until = "v2.0.0"
)
Next steps
Changelog builder Build changelogs from commits
Output formats Learn about format selection
Python API Complete API reference
Examples See formatters in action