Introduction

Effective versioning, release management, and changelog generation are crucial aspects of maintaining a Python package. These tasks can be time-consuming and error-prone if done manually. In this article, we'll explore how to streamline the release process using Semantic Release and GitHub Actions, ensuring automated versioning, changelog generation, and PyPI release.

What is Semantic Release?

Semantic Release is a tool that automates versioning, generates changelogs, and releases packages based on the Semantic Versioning specification. It analyzes commit messages using conventional commit to determine the type of version bump (major, minor, or patch) and generates a new version number, release the version, and create changelogs accordingly.

Semantic Versioning 2.0.0
Semantic Versioning spec and website
Conventional Commits
A specification for adding human and machine readable meaning to commit messages

Understanding Semantic Versioning

Semantic Versioning, or SemVer, is a versioning system that brings clarity and consistency to the way software versions are assigned and incremented. It follows a three-part version number format: MAJOR.MINOR.PATCH. Each component has a specific meaning:

  1. MAJOR version: Increased for each incompatible API changes introduced.
  2. MINOR version: Added for each backward-compatible functionality enhancements.
  3. PATCH version: Incremented for backward-compatible bug fixes without additional functionality added.

Understanding Conventional Commit

Conventional Commits, as the name suggests, adhere to a convention when structuring commit messages. The specification follows a simple yet powerful pattern:

<type>([optional scope]): <description>

[optional body]

[optional footer]
  • Type: Describes the purpose of the commit (e.g., feat for a new feature, fix for a bug fix).
  • Scope: Indicates the module or area of the project impacted by the commit.
  • Description: A concise and clear statement of what the commit accomplishes.
  • Body: Additional details providing context and reasoning behind the change (optional).
  • Footer: Used to reference issues, breaking changes, or other metadata (optional).

Example 1: New Feature with Scope and Breaking Change

feat(api): add support for GraphQL queries

BREAKING CHANGE: The existing REST endpoints are deprecated and 
will be removed in the next release. 
Update your clients to use the new GraphQL queries.

Example 2: Bug Fix with Detailed Explanation

fix(database): resolve null pointer exception in query parsing

The previous implementation did not handle null values in query parameters,
leading to a null pointer exception during query parsing. 
This fix addresses the issue #5 and ensures proper handling of null values.

Example Workflow:

Let's consider a practical example of applying Conventional Commits in a Python project:

1. Initialize Project Folder

We will use poetry to initialize our Python package using poetry new <package_name> command with simple greet function

2. Initialize Github Repository

# Change to <package_name> directory
cd <package_name>

git init

Create a Python file named <package_name>/greet.py with the following content:

def greet(name):
    return f"Hello, {name}!"

3. Add Semantic Release Github Action

Create a github workflow file and save it as .github/workflows/ci.yml on your repository

name: CI
on:
  push:
    branches: [main]
  pull_request:
    branches: [main]

jobs:
  Release:
    if: github.event_name == 'push' && github.ref == 'refs/heads/main' && !contains(github.event.head_commit.message, 'chore(release):')
    runs-on: ubuntu-latest
    steps:
      - uses: actions/setup-python@v3
        with:
          python-version: 3.8.17
      - name: Checkout Code
        uses: actions/checkout@v3
      - name: Install Python Poetry
        uses: abatilo/actions-poetry@v2.1.0
        with:
          poetry-version: 1.5.1
      - name: Semantic Release
        uses: bjoluc/semantic-release-config-poetry@v2
        with:
          GITHUB_TOKEN: ${{ secrets.GH_TOKEN }}
          PYPI_TOKEN: ${{ secrets.PYPI_TOKEN }}
          RELEASE_BRANCH: main

This GitHub Actions workflow triggers on every push and pull request to the main branch. It sets up Python, installs dependencies (if any), and runs Semantic Release.

Your directory should look like this now

.
├── .github
│   └── workflows
│       └── ci.yml
├── README.md
├── pyproject.toml
├── semver_example
│   ├── __init__.py
│   └── greet.py
└── tests
    └── __init__.py

4 directories, 6 files

4. Configure GitHub Secrets and Workflow Permission

Create an API token for PyPI and GitHub personal API token with sufficient privilege

To securely store your GitHub and PyPI tokens, go to your GitHub repository, navigate to Settings > Secrets, and add GH_TOKEN and PYPI_TOKEN secret using token value created from previous step.

Make sure workflow permissions (Settings > Code and automation > Action > General > Workflow permissions) has been set to read and write permissions

5. Push your package to Github

git init
git add *
git commit -m "feat: add greet function"

git branch -M main
git remote add origin https://github.com/<YOUR_REPOSITORY>
git push -u origin main

Soon you will see a release with changelogs generated from our commit message. Pretty cool right?

6. Add Changes to Your Repository

Let's create another function and save it under <package_name>/bye.py

def bye(name):
    return f"Goodbye, {name}!"
# pull auto generated files from github actions first
git pull
git add *
git commit -m "feat: add bye function"
git push -u origin main

You will then notice we have another release with minor version number increased as we create new functionality without breaking any backward compatibility

Moving forward, we can continue to use conventional commits in our repository to automate versioning, changelog generation, and PyPI releases. Check out this sample repository to see them in action

GitHub - im-perativa/streamlit-calendar: A Streamlit component to show calendar view using FullCalendar
A Streamlit component to show calendar view using FullCalendar - GitHub - im-perativa/streamlit-calendar: A Streamlit component to show calendar view using FullCalendar

Conclusion

By integrating Semantic Release and GitHub Actions into your Python package development workflow, you can automate versioning, changelog generation, and PyPI releases. This ensures a consistent and error-free release process, allowing you to focus on coding rather than managing releases. Embrace automation and enhance the reliability of your package releases today!

Share this post