Skip to main content
Version: Next

Python Package Build Workflow

View Source

Build Python package distributions (wheel and source distribution).

Overviewโ€‹

This workflow builds Python package artifacts using modern Python packaging tools, creating both wheel (.whl) and source distribution (.tar.gz) files ready for publishing to PyPI or other package indexes.

When to Useโ€‹

  • โœ… You need to build Python package distributions
  • โœ… You're preparing for PyPI publication
  • โœ… You want to create distributable package artifacts
  • โœ… You need to validate package build process

Workflow Architectureโ€‹

Inputsโ€‹

Required Inputsโ€‹

None - uses repository defaults

Optional Inputsโ€‹

InputTypeDefaultDescription
python_versionstring'3.11'Python version for building
build_toolstring'build'Build tool (build, setuptools, poetry)
build_argsstring''Additional build arguments
validate_packagebooleantrueRun package validation checks

Outputsโ€‹

OutputDescription
wheel_pathPath to built wheel file
sdist_pathPath to built source distribution
package_versionExtracted package version
artifact_nameName of uploaded artifact

Usage Examplesโ€‹

Basic Usageโ€‹

name: Build

on: [push, pull_request]

jobs:
build:
uses: Chisanan232/GitHub-Action_Reusable_Workflows-Python/.github/workflows/rw_python_package.yaml@master

With Custom Python Versionโ€‹

jobs:
build:
uses: Chisanan232/GitHub-Action_Reusable_Workflows-Python/.github/workflows/rw_python_package.yaml@master
with:
python_version: '3.12'

Using Poetryโ€‹

jobs:
build:
uses: Chisanan232/GitHub-Action_Reusable_Workflows-Python/.github/workflows/rw_python_package.yaml@master
with:
build_tool: poetry
python_version: '3.11'

Complete Build and Publish Pipelineโ€‹

name: Build and Publish

on:
push:
tags:
- 'v*'

jobs:
build:
uses: Chisanan232/GitHub-Action_Reusable_Workflows-Python/.github/workflows/rw_python_package.yaml@master
with:
python_version: '3.11'
validate_package: true

publish:
needs: build
uses: Chisanan232/GitHub-Action_Reusable_Workflows-Python/.github/workflows/rw_push_pypi.yaml@master
secrets:
pypi_token: ${{ secrets.PYPI_API_TOKEN }}

How It Worksโ€‹

Step 1: Environment Setupโ€‹

Sets up Python and installs build tools:

- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: ${{ inputs.python_version }}

- name: Install build tools
run: |
python -m pip install --upgrade pip
pip install build twine check-manifest

Step 2: Build Source Distributionโ€‹

Creates source distribution (.tar.gz):

python -m build --sdist

Step 3: Build Wheelโ€‹

Creates wheel distribution (.whl):

python -m build --wheel

Step 4: Package Validationโ€‹

Validates built packages:

twine check dist/*
check-manifest

Step 5: Upload Artifactsโ€‹

Uploads built packages as artifacts:

- uses: actions/upload-artifact@v4
with:
name: python-package-distributions
path: dist/

Build Toolsโ€‹

Python Build (Default)โ€‹

Modern PEP 517 compliant build tool:

build_tool: build

Advantages:

  • Standard Python packaging
  • PEP 517/518 compliant
  • Works with any build backend

Requirements:

[build-system]
requires = ["setuptools>=45", "wheel", "setuptools_scm[toml]>=6.2"]
build-backend = "setuptools.build_meta"

Poetryโ€‹

Poetry-based package building:

build_tool: poetry

Advantages:

  • Integrated dependency management
  • Lock file support
  • Modern packaging

Requirements:

[build-system]
requires = ["poetry-core>=1.0.0"]
build-backend = "poetry.core.masonry.api"

Setuptoolsโ€‹

Traditional setuptools building:

build_tool: setuptools

Advantages:

  • Wide compatibility
  • Well-established
  • Flexible configuration

Package Configurationโ€‹

Modern Python packaging configuration:

[build-system]
requires = ["setuptools>=45", "wheel"]
build-backend = "setuptools.build_meta"

[project]
name = "my-package"
version = "1.0.0"
description = "My Python package"
authors = [
{name = "Your Name", email = "you@example.com"}
]
dependencies = [
"requests>=2.28.0",
]
requires-python = ">=3.9"

[project.optional-dependencies]
dev = [
"pytest>=7.0.0",
"pytest-cov>=4.0.0",
]

setup.py (Legacy)โ€‹

Traditional setup.py configuration:

from setuptools import setup, find_packages

setup(
name="my-package",
version="1.0.0",
packages=find_packages(where="src"),
package_dir={"": "src"},
install_requires=[
"requests>=2.28.0",
],
python_requires=">=3.9",
)

Package Validationโ€‹

Twine Checkโ€‹

Validates package metadata and structure:

twine check dist/*

Checks:

  • README rendering
  • Metadata completeness
  • Package structure
  • File integrity

Check-Manifestโ€‹

Verifies MANIFEST.in completeness:

check-manifest

Ensures all necessary files are included in source distribution.

Package Structure Validationโ€‹

Validates package can be installed:

pip install dist/*.whl
python -c "import my_package; print(my_package.__version__)"

Best Practicesโ€‹

1. Version Managementโ€‹

Use dynamic versioning:

[project]
dynamic = ["version"]

[tool.setuptools.dynamic]
version = {attr = "my_package.__version__"}

Or use setuptools_scm:

[build-system]
requires = ["setuptools>=45", "setuptools_scm[toml]>=6.2"]

[tool.setuptools_scm]
write_to = "src/my_package/_version.py"

2. Include Necessary Filesโ€‹

Create MANIFEST.in:

include README.md
include LICENSE
include pyproject.toml
recursive-include src *.py
recursive-include tests *.py

3. Package Metadataโ€‹

Provide complete metadata:

[project]
name = "my-package"
version = "1.0.0"
description = "A short description"
readme = "README.md"
license = {text = "MIT"}
authors = [{name = "Your Name", email = "you@example.com"}]
keywords = ["keyword1", "keyword2"]
classifiers = [
"Development Status :: 4 - Beta",
"Intended Audience :: Developers",
"License :: OSI Approved :: MIT License",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
]

4. Clean Build Environmentโ€‹

Ensure clean builds:

# Remove old builds
rm -rf dist/ build/ *.egg-info/

# Build fresh
python -m build

Troubleshootingโ€‹

Build Failsโ€‹

Symptoms:

  • Build command fails
  • Missing dependencies

Solutions:

  1. Check pyproject.toml syntax:

    python -m build --sdist --wheel
  2. Verify build dependencies:

    [build-system]
    requires = ["setuptools>=45", "wheel"]
  3. Review build logs for errors

Package Validation Failsโ€‹

Symptoms:

  • Twine check fails
  • Invalid metadata

Solutions:

  1. Fix README rendering:

    [project]
    readme = {file = "README.md", content-type = "text/markdown"}
  2. Complete required metadata:

    [project]
    name = "my-package"
    version = "1.0.0"
    description = "Required description"
  3. Check file includes in MANIFEST.in

Missing Files in Distributionโ€‹

Symptoms:

  • Files missing from built package
  • Import errors after installation

Solutions:

  1. Update MANIFEST.in:

    recursive-include src *.py
    include README.md LICENSE
  2. Use check-manifest:

    check-manifest
  3. Verify package_data in pyproject.toml:

    [tool.setuptools.package-data]
    my_package = ["data/*.json", "templates/*.html"]

Package Distribution Typesโ€‹

Wheel Distribution (.whl)โ€‹

Binary distribution format:

Advantages:

  • Faster installation
  • No build step required
  • Platform-specific optimizations

File naming:

my_package-1.0.0-py3-none-any.whl

Source Distribution (.tar.gz)โ€‹

Source code distribution:

Advantages:

  • Platform independent
  • Includes all source files
  • Can be built on any platform

File naming:

my-package-1.0.0.tar.gz

Additional Resourcesโ€‹