feat(api): Add programmatic Python API for custom workflows by bilelomrani1 · Pull Request #1408 · python-semantic-release/python-semantic-release

Summary

Related issue: #1407

This PR adds a first-class programmatic Python API that allows users to import and use semantic release functionality directly from Python code without going through the CLI.

The API consists of two new modules:

  • context.py: A SemanticReleaseContext dataclass that encapsulates all configuration, with a from_config_file() factory method for loading from pyproject.toml
  • operations.py: Core functions (build_release_history, render_changelog, render_release_notes, compute_next_version)

This enables use cases like posting release notes to Slack, custom monorepo release orchestration, and unit testing release configuration.

Scope

This PR introduces the API surface only. It does not refactor the CLI to use this API internally. The implementation reuses existing internal functions from cli/config.py and cli/changelog_writer.py.

This is intentionally the first in a series of stacked PRs. Keeping the API introduction separate from the internal refactoring makes review easier and allows the API design to be validated before deeper changes are made. Subsequent PRs will refactor the CLI to use this API internally, consolidating duplicated logic. So this PR indeed adds a decent amount of LOC but I'm expecting the future ones to remove a lot of duplicated code.

Future refactoring opportunities

Once this API is validated, the CLI could be refactored to use it internally:

  1. RuntimeContext inheritance: RuntimeContext could extend SemanticReleaseContext, adding only CLI-specific fields (global_cli_options, masker)

  2. CLI as thin wrapper: Commands like changelog, version, and publish could become thin wrappers that call the core API functions

  3. Shared config building: The config loading logic in _from_raw_config mirrors RuntimeContext.from_raw_config and could be consolidated

  4. Template rendering: render_changelog and render_release_notes currently import from cli/changelog_writer.py; this logic could be moved to operations.py with the CLI importing from it instead

  5. Test simplification: Tests could directly instantiate SemanticReleaseContext with mock objects instead of setting up config files or mocking CLI invocations. Unit tests for changelog rendering, version computation, and release history building could call API functions directly, making tests more focused and easier to maintain. We can keep the existing test infra for testing the CLI itself

Example usage

from semantic_release import (
    SemanticReleaseContext,
    build_release_history,
    render_release_notes,
    render_changelog,
    compute_next_version,
)

# Load configuration from pyproject.toml
ctx = SemanticReleaseContext.from_config_file()

# Build release history from git
history = build_release_history(ctx)
print(f"Found {len(history.released)} releases")

# Compute what the next version would be
next_version = compute_next_version(ctx)
current_version = list(history.released.keys())[0]

# Require manual approval for major version bumps
if next_version.major > current_version.major:
    if not confirm_major_release(next_version):
        sys.exit(1)

# Render release notes and post to Slack
notes = render_release_notes(ctx, history, current_version)
slack_client.post_message(
    channel="#releases",
    text=f"*v{current_version}* has been released!\n\n{notes}"
)

# Generate full changelog for documentation site
changelog = render_changelog(ctx, history)
docs_path = Path("docs/changelog.md")
docs_path.write_text(changelog)

PR Completion Checklist

  • Reviewed & followed the Contributor Guidelines

  • Changes Implemented & Validation pipeline succeeds

  • Commits follow the Conventional Commits standard
    and are separated into the proper commit type and scope (recommended order: test, build, feat/fix, docs)

  • Appropriate Unit tests added/updated

  • Appropriate End-to-End tests added/updated

  • Appropriate Documentation added/updated and syntax validated for sphinx build (see Contributor Guidelines)