Skip to main content
HelixCommit includes five built-in formatters for rendering changelogs: Markdown, HTML, plain text, JSON, and YAML.

Formatter comparison

FeatureMarkdownHTMLTextJSONYAML
GitHub releasesBestWorksPlainNoNo
WebsitesGoodBestNoAPIAPI
EmailVariesBestUniversalNoNo
TerminalGoodNoBestParseGood
Programmatic useParseParseParseBestBest
CI/CD pipelinesGoodLimitedGoodBestBest

Markdown formatter

Generate GitHub-compatible Markdown.

render_markdown()

from helixcommit.formatters.markdown import render_markdown

markdown = render_markdown(changelog: Changelog) -> str
changelog
Changelog
required
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)

Output format

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

HTML formatter

Generate styled HTML output.

render_html()

from helixcommit.formatters.html import render_html

html = render_html(changelog: Changelog) -> str
changelog
Changelog
required
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')

Output format

<!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
changelog
Changelog
required
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)

Output format

================================================================================
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

JSON formatter

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
changelog
Changelog
required
Structured changelog object to render.
indent
int
default:"2"
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")

Output format

{
  "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"
  }
}

YAML formatter

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
changelog
Changelog
required
Structured changelog object to render.
default_flow_style
bool
default:"False"
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)

Output format

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

Using all formatters

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

Custom formatters

Create your own formatter by implementing a rendering function:

Basic custom formatter

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)

Template-based formatting

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