This document explains the entire automated release pipeline for Hazelbean, from creating a GitHub release to having your package available on both PyPI and conda-forge.
What you do:
- Create a git tag (e.g.,
v1.7.7) - Push the tag to GitHub
- Create a GitHub Release
- Review and merge the auto-generated conda-forge PR (usually within 24 hours)
What happens automatically:
- GitHub Actions builds wheels and uploads to PyPI
- conda-forge bot detects the PyPI release
- conda-forge bot creates a PR to update the conda recipe
- After you merge: conda-forge builds and publishes to Anaconda.org
Do you need to change anything?
- ✅ No changes needed to your repository! The automation is already set up.
- ✅ You just need to be a maintainer on the conda-forge feedstock (which you are now).
# Decide on your version number
VERSION="1.7.7"
# Create and push the tag
git tag -a "v${VERSION}" -m "Release version ${VERSION}"
git push origin "v${VERSION}"Why? Your project uses setuptools_scm, which automatically determines the package version from git tags. The tag IS your version number.
Note: The v prefix is conventional (e.g., v1.7.7), but setuptools_scm strips it to create version 1.7.7.
- Go to https://github.com/jandrewjohnson/hazelbean_dev/releases
- Click "Draft a new release"
- Select your tag from the dropdown
- Add release notes (what's new, what's fixed)
- Click "Publish release"
When you click "Publish release", the build-python.yml workflow automatically:
A) Builds Binary Wheels (the build-wheels job):
- Runs on Ubuntu, Windows, and macOS
- Builds for Python 3.10, 3.11, 3.12, and 3.13
- Compiles your Cython extensions for each platform
- Creates
.whlfiles
B) Builds Source Distribution (the build-sdist job):
- Creates a
.tar.gzsource archive - Includes all source code and setup files
C) Uploads to PyPI:
- name: Upload to pypi
if: github.event_name == 'release'
run: twine upload --username="__token__" --password=${{secrets.PYPI_APIKEY}} dist/*.tar.gzD) Attaches Files to GitHub Release:
- All wheels and source distributions are attached to your GitHub release for direct download
E) Updates CHANGELOG.md (the changelog.yml workflow):
- Automatically generates changelog from commits
- Commits back to the
mainbranch
Timeline: Usually completes in 15-30 minutes.
What happens:
- The
regro-cf-autotick-botmonitors PyPI for new releases of packages in conda-forge - When it detects your new version on PyPI (usually within 1-24 hours), it springs into action
The bot checks:
- Your new version number
- The new source tarball URL on PyPI
- The SHA256 hash of the tarball
- Any changes in your dependencies
Where: https://github.com/conda-forge/hazelbean-feedstock
The bot creates a PR that updates recipe/meta.yaml:
# OLD (example from 1.7.6)
{% set version = "1.7.6" %}
source:
url: https://pypi.io/packages/source/h/hazelbean/hazelbean-1.7.6.tar.gz
sha256: abc123def456...
# NEW (what the bot changes to 1.7.7)
{% set version = "1.7.7" %}
source:
url: https://pypi.io/packages/source/h/hazelbean/hazelbean-1.7.7.tar.gz
sha256: xyz789uvw012...Special Note: Currently, the hazelbean feedstock includes a patch file (1.7.6-utils-fstring-patch.diff) that fixes a syntax error. See CONDA_FORGE_FSTRING_FIX.md for details. Once you release 1.7.7+ with the fix, you can remove this patch from the feedstock.
Your responsibilities as a maintainer:
- Check the PR - GitHub will notify you
- Verify changes:
- Version number is correct
- SHA256 hash matches PyPI (bot calculates this automatically)
- Dependencies are still correct
- Run CI checks:
- conda-forge automatically builds on Linux, macOS, Windows
- Tests run in each environment
- You can see build logs before merging
- Merge when green ✅
Common scenarios:
- ✅ Everything auto-updates correctly → Just merge
⚠️ New dependencies added → You may need to manually add them tometa.yaml⚠️ Build constraints changed → Update build requirements if needed⚠️ Patches need updating → Remove obsolete patches (like the f-string fix)
After you merge:
- conda-forge CI rebuilds your package for all platforms
- Packages are uploaded to https://anaconda.org/conda-forge/hazelbean
- Users can now install via:
conda install -c conda-forge hazelbean
Timeline: Build and publish usually completes within 1-2 hours of merging.
Your pyproject.toml configures automatic versioning:
[tool.setuptools_scm]
version_scheme = "post-release"
local_scheme = "node-and-date"What this means:
- Git tag
v1.7.7→ Package version1.7.7 - Commits after tag → Version becomes
1.7.7.post1.dev0+g1234567(development version) - No manual version editing needed - it's all automatic!
Your repository needs this secret configured (already set up):
PYPI_APIKEY: API token for uploading to PyPI- Type: PyPI API token
- Permissions: Upload packages
- Set in: Repository Settings → Secrets → Actions
The feedstock repository contains:
hazelbean-feedstock/
├── recipe/
│ ├── meta.yaml # Main recipe file
│ ├── build.sh # Unix build script
│ ├── bld.bat # Windows build script
│ └── patches/ # Temporary patches
│ └── 1.7.6-utils-fstring-patch.diff
└── README.md
Key file: recipe/meta.yaml defines:
- Package version and source URL
- Build requirements (Cython, numpy, etc.)
- Runtime requirements (gdal, geopandas, etc.)
- Test commands
- Metadata (license, maintainers, etc.)
Use this checklist for each release:
- All tests passing locally
- Version number decided
- CHANGELOG.md updated (or will auto-update)
- Any breaking changes documented
- Fix any issues noted in previous conda-forge builds
- Create git tag:
git tag -a v1.X.Y -m "Release 1.X.Y" - Push tag:
git push origin v1.X.Y - Create GitHub release with notes
- Wait for GitHub Actions to complete (~20 min)
- Verify wheels uploaded to PyPI
- Test pip install:
pip install hazelbean==1.X.Y
- Wait for bot PR (usually < 24 hours)
- Review bot's PR on conda-forge/hazelbean-feedstock
- Check if patches need updating/removing
- Verify dependencies are correct
- Wait for CI builds to pass
- Merge PR
- Wait for publication (~1-2 hours)
- Test conda install:
conda install -c conda-forge hazelbean==1.X.Y
- Update documentation if needed
- Announce on relevant channels
- Close milestone/issues for this release
Symptom: GitHub Action fails at "Upload to pypi" step
Common causes:
- Version already exists on PyPI - You can't re-upload the same version
- Fix: Increment version and create new tag
- PYPI_APIKEY expired or invalid
- Fix: Regenerate token on PyPI, update GitHub secret
- Package metadata issues
- Fix: Check
pyproject.tomlandsetup.pyfor errors
- Fix: Check
Symptom: 48+ hours, no PR on feedstock
Common causes:
- PyPI release not visible - Check https://pypi.org/project/hazelbean/
- Bot is temporarily down - Check conda-forge status
- Manual trigger needed
Manual solution:
# Fork conda-forge/hazelbean-feedstock
# Clone your fork
git clone https://github.com/YOUR_USERNAME/hazelbean-feedstock.git
cd hazelbean-feedstock
# Update recipe/meta.yaml
# - Change version number
# - Update source URL
# - Update sha256 hash (get from PyPI)
# Commit and create PR
git checkout -b update-1.7.7
git add recipe/meta.yaml
git commit -m "Update to 1.7.7"
git push origin update-1.7.7
# Create PR on GitHubSymptom: CI fails on feedstock PR
Common causes:
- Missing dependencies - Add to
meta.yamlrequirements - Cython compilation errors - Check build constraints
- Test failures - Package tests run during build
- Platform-specific issues - Check each platform's log
Where to look:
- Each platform (Linux/macOS/Windows) has separate logs
- Click on the failing check to see detailed output
- Look for error messages in the build log
Current situation: The feedstock has a patch for the f-string bug in 1.7.6
When to update:
- After releasing 1.7.7+ with the fix applied
- Edit
recipe/meta.yamlto remove the patch reference:# Remove these lines: patches: - 1.7.6-utils-fstring-patch.diff
- Delete the patch file
- Submit PR with these changes
- Pros:
- Faster updates (within minutes of release)
- Wider reach (anyone with pip)
- Direct from source
- Cons:
- Users must handle system dependencies (GDAL, etc.)
- Binary wheels only for common platforms
- Can have dependency conflicts
- Pros:
- All dependencies included (GDAL, PROJ, etc.)
- Binary packages for all platforms
- Better reproducibility
- No system package requirements
- Cons:
- Slower updates (24h+ delay)
- Requires conda/mamba
- Build process is more complex
Best practice: Maintain both! Different users have different needs.
- Main docs: https://conda-forge.org/docs/
- Maintainer guide: https://conda-forge.org/docs/maintainer/
- Updating packages: https://conda-forge.org/docs/maintainer/updating_pkgs.html
- Source code: https://github.com/jandrewjohnson/hazelbean_dev
- conda-forge feedstock: https://github.com/conda-forge/hazelbean-feedstock
- PyPI page: https://pypi.org/project/hazelbean/
- PyPI downloads: https://pepy.tech/project/hazelbean
- conda-forge downloads: https://anaconda.org/conda-forge/hazelbean
- GitHub Actions: https://github.com/jandrewjohnson/hazelbean_dev/actions
A: You only need to review and merge the bot's PR. The bot does 95% of the work automatically.
A: Yes, but it's not recommended. Many users prefer conda because it handles system dependencies. Just don't merge the bot's PR if you want to skip it.
A: The PyPI release is nearly instant (20 minutes). conda-forge will be slower (24-48 hours total), but that's usually acceptable for hotfixes.
A: Yes! The feedstock CI builds test packages. You can download them from the PR's "Artifacts" section and test locally before merging.
A: Follow Semantic Versioning:
1.7.7→1.7.8for bug fixes1.7.7→1.8.0for new features1.7.7→2.0.0for breaking changes
A: You can edit the bot's PR before merging! Just push commits to the bot's branch to fix any issues.
Possible enhancements to consider:
-
Automated testing before release:
- Add a "pre-release" workflow that runs extended tests
- Gate releases on passing tests
-
Release notes automation:
- Auto-generate more detailed release notes from commit messages
- Include contributor acknowledgments
-
Multi-stage releases:
- Release to PyPI test server first
- Promote to production after validation
-
Conda-forge co-maintenance:
- Add more maintainers to the feedstock
- Faster PR reviews and merges
The beauty of this setup: Once configured (which it already is!), releasing Hazelbean to both PyPI and conda-forge requires minimal manual work:
- 30 seconds: Create and push a git tag
- 2 minutes: Create GitHub release with notes
- 5 minutes: Review conda-forge bot PR (when it arrives)
Everything else is automated. Your job is to make great software - the infrastructure handles distribution! 🚀