Complete step-by-step guide for updating versions and deploying to PyPI using GitHub Actions.
- All tests passing locally
- Code changes committed to git
-
PYPI_API_TOKENsecret configured in GitHub repository - Ready to create a new release
The primary deployment method uses GitHub Actions for automatic publishing to PyPI when you push a git tag.
- Set up PyPI API Token in GitHub Secrets:
- Go to https://pypi.org/manage/account/token/
- Create new token with scope "Entire account" or "llmcosts" project
- Go to https://github.com/llmcosts/llmcosts-python/settings/secrets/actions
- Add secret named
PYPI_API_TOKENwith your PyPI token
Update the version in TWO places:
-
pyproject.toml(line ~3):version = "X.Y.Z"
-
llmcosts/__init__.py(line ~3):__version__ = "X.Y.Z"
Version Strategy:
- Patch:
0.1.0→0.1.1(bug fixes) - Minor:
0.1.0→0.2.0(new features, backward compatible) - Major:
0.1.0→1.0.0(breaking changes)
Edit CHANGELOG.md:
-
Add new version section at the top (after the header):
## [X.Y.Z] - 2024-MM-DD ### Added - New features ### Changed - Changes to existing functionality ### Fixed - Bug fixes ### Removed - Removed features
-
Add version link at the bottom:
[X.Y.Z]: https://github.com/llmcosts/llmcosts-python/releases/tag/vX.Y.Z
Commit the version and changelog updates:
# Stage changes
git add pyproject.toml llmcosts/__init__.py CHANGELOG.md
# Commit with version message
git commit -m "Release version X.Y.Z"
git push origin mainThis step triggers the automated deployment:
# Create tag (triggers GitHub Actions deployment)
git tag vX.Y.Z
git push origin --tags🚀 GitHub Actions will now automatically:
- Build the package (
python -m build) - Upload to PyPI using the
PYPI_API_TOKENsecret - Handle any upload conflicts with
skip-existing: true
- Check GitHub Actions: https://github.com/llmcosts/llmcosts-python/actions
- Verify deployment status in the "Publish package to PyPI" workflow
- Check PyPI page: https://pypi.org/project/llmcosts/
Test the new version is available:
# In a fresh environment (wait 2-3 minutes for PyPI to update)
pip install llmcosts==X.Y.Z
python -c "import llmcosts; print(f'PyPI Version: {llmcosts.__version__}')"Create a GitHub release for visibility:
- Go to https://github.com/llmcosts/llmcosts-python/releases
- Click "Create a new release"
- Choose the tag
vX.Y.Z - Use changelog content as release notes
- Publish release
For experienced users, the entire automated process:
# Update versions in pyproject.toml and llmcosts/__init__.py
# Update CHANGELOG.md
git add pyproject.toml llmcosts/__init__.py CHANGELOG.md
git commit -m "Release version X.Y.Z"
git tag vX.Y.Z
git push origin main --tags
# GitHub Actions handles the rest!- Cause: Missing or incorrect
PYPI_API_TOKENsecret - Solution:
- Check the secret exists at https://github.com/llmcosts/llmcosts-python/settings/secrets/actions
- Regenerate PyPI token if needed
- Ensure token has proper scope (entire account or project-specific)
- Cause: Trying to upload same version twice (GitHub Actions retrying)
- Solution: Version conflict is handled automatically with
skip-existing: true - Note: If you need to re-release the same version, increment to next version
- Cause: Tag doesn't start with
v*or not pushed to origin - Solution:
# Ensure tag format is correct git tag v0.2.2 # Good git tag 0.2.2 # Bad - missing 'v' prefix # Push tags to origin git push origin --tags
- Cause: Forgot to update version in both files
- Solution: Ensure both
pyproject.tomland__init__.pyhave same version
- Cause: Missing dependencies or circular imports
- Solution: Check
pyproject.tomldependencies, test locally first
# Check current versions
grep "version = " pyproject.toml
grep "__version__ = " llmcosts/__init__.py
# Verify they match
python -c "import configparser, ast;
config = configparser.ConfigParser();
config.read('pyproject.toml');
pyproject_version = config['project']['version'].strip('\"');
with open('llmcosts/__init__.py') as f:
for line in f:
if '__version__' in line:
init_version = ast.literal_eval(line.split('=')[1].strip());
break;
print(f'pyproject.toml: {pyproject_version}');
print(f'__init__.py: {init_version}');
print(f'Match: {pyproject_version == init_version}')"If GitHub Actions fails or you need to deploy manually:
-
Clean and build locally:
rm -rf dist/ build/ *.egg-info/ uv run python -m build -
Test the build:
# Create test environment python -m venv test_env source test_env/bin/activate pip install dist/llmcosts-X.Y.Z-py3-none-any.whl python -c "import llmcosts; print(f'Version: {llmcosts.__version__}')" deactivate && rm -rf test_env
-
Upload to PyPI:
uv run twine upload dist/* # Enter username: __token__ # Enter password: <your-pypi-api-token>
The automated deployment is configured in .github/workflows/publish.yml:
name: Publish package to PyPI
on:
push:
tags:
- 'v*' # Triggers on any tag starting with 'v'
jobs:
build-and-publish:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.11'
- name: Install build tools
run: pip install build
- name: Build package
run: python -m build
- name: Publish to PyPI
uses: pypa/gh-action-pypi-publish@release/v1
with:
password: ${{ secrets.PYPI_API_TOKEN }}
skip-existing: true🎯 Wheel Package (what users install):
- All Python modules in
llmcosts/ - Type hints (
py.typed) - License file
📦 Source Distribution:
- Everything above plus:
README.md,CHANGELOG.md- All test files in
tests/ - Build configuration files
❌ Not included: docs/ folder, GitHub workflows, development files
For enhanced automation, consider:
bump2version- Automated version bumpingsemantic-release- Automated releases based on commit messagesrelease-please- Google's automated release tool
- GitHub Actions PyPI Publishing
- PyPI API Tokens
- Semantic Versioning
- Keep a Changelog
- Python Packaging Documentation
Primary deployment process:
# 1. Update versions and changelog
# 2. Commit and tag
git add pyproject.toml llmcosts/__init__.py CHANGELOG.md
git commit -m "Release version X.Y.Z"
git tag vX.Y.Z
git push origin main --tags
# 3. GitHub Actions automatically deploys to PyPIMonitor deployment:
- GitHub Actions: https://github.com/llmcosts/llmcosts-python/actions
- PyPI releases: https://pypi.org/project/llmcosts/
Need help? Check the troubleshooting section above or the manual deployment backup method.