Release Process¶
This document describes how a new version of azure-functions-doctor reaches PyPI.
Releases are automated by Release Please. It decides the version, writes the changelog, and cuts the tag. It does not publish — publication is a separate, gated step.
Who owns what¶
| Component | Owns |
|---|---|
release-please.yml |
version calculation, CHANGELOG.md, the Release PR, the tag, the GitHub Release |
e2e-azure.yml |
real-Azure certification of one exact commit |
publish-pypi.yml |
the only path that uploads to PyPI (workflow_dispatch only) |
Do not hand-edit src/azure_functions_doctor/__init__.py version strings, CHANGELOG.md, or
.release-please-manifest.json. Release Please maintains all three.
Step 1: Merge Conventional Commits¶
Version bumps are derived from commit messages on main:
| Commit | Bump |
|---|---|
fix: |
patch (0.20.0 → 0.20.1) |
feat: |
minor (0.20.0 → 0.21.0) |
feat!: / fix!: / BREAKING CHANGE: footer |
minor while pre-1.0 (see below) |
While this package is pre-1.0, bump-minor-pre-major is enabled, so a breaking change moves to the
next minor (0.21.0) rather than jumping to 1.0.0. Going to 1.0 is a deliberate, separate decision.
Use scopes for more context: fix(scope): short imperative summary
Changelog categories¶
| Prefix | Changelog section |
|---|---|
feat: |
Features |
fix: |
Bug Fixes |
docs: |
Documentation |
refactor: |
Refactor |
style: |
Styling |
test: |
Testing |
perf: |
Performance |
ci: / chore: |
Miscellaneous Tasks |
build: |
Other |
These are configured in release-please-config.json under changelog-sections.
Step 2: Merge the Release PR¶
Release Please keeps an open Release PR titled like chore(main): release 0.21.0. It contains the
version bump and the changelog entry.
Merging that PR is the act of cutting a release. On merge, Release Please tags the release commit
(v0.21.0) and publishes the GitHub Release.
Release Please runs with RELEASE_PLEASE_TOKEN (a fine-grained PAT) rather than the default
GITHUB_TOKEN, so the required status checks run on the Release PR and it is merged under the same
branch-protection rules as any other PR. The PAT expires; regenerate it and update the secret before
it does, or no Release PR will appear.
Step 3: The release workflow runs¶
The tag starts publish-pypi.yml. Everything else is automatic:
| Tier | Catches |
|---|---|
build |
tag/__version__ mismatch; produces the one artifact that is later uploaded |
lib-tests |
library unit regressions |
doctor-runtime-gate |
installs the candidate wheel, asserts the installed version matches the release, then runs the package's own CLI (azure-functions-doctor doctor --path examples/v2/http-trigger --profile minimal) from the installed wheel. Unlike a host-boot smoke, this imports and exercises this package's primary runtime surface (the diagnostic CLI) end to end. |
azure-e2e |
cloud-only drift — deploys to real Azure, runs the live e2e suite, uploads an azure-cert record |
publish |
uploads the exact artifact build produced; it never rebuilds |
azure-e2e calls e2e-azure.yml as a reusable workflow at the same ref being published, so
certification covers the exact published commit by construction. There is no separate certification
step to dispatch and no freshness window to expire.
Publication uses PyPI Trusted Publishing (OIDC); there is no API token to manage.
To re-run after a failed gate (nothing was uploaded, so the version is still free):
What is no longer used¶
| Retired | Replacement |
|---|---|
make release-patch / release-minor / release-major / release |
merge the Release PR |
make changelog / make commit-changelog (git-cliff) |
Release Please writes CHANGELOG.md |
make tag-release |
Release Please creates the tag |
make publish-pypi (local hatch publish) |
the publish job in publish-pypi.yml |
cliff.toml |
release-please-config.json |
| manual Azure certification dispatch | the azure-e2e job in publish-pypi.yml |
These Makefile targets are deleted, not merely discouraged, so nothing can create a second writer for the version, changelog, or tags — or push an ungated artifact to PyPI.
make publish-test (TestPyPI) is unaffected.
Recovery¶
| Situation | Action |
|---|---|
| Any gate failed | Nothing was uploaded. Fix the cause and re-run publish-pypi.yml on the same tag, or fix forward on main and let the next Release PR cut a new version. Never move or reuse a tag. |
| Tag exists but was never published | A valid resting state. Re-run publish, or abandon the version and let the next release take the following number. |
| Release PR stopped appearing | Check for a stale autorelease: pending label on an already-merged Release PR. Release Please treats that as a release in flight and will not open another. This failure is silent — the workflow still reports success. |
| Automation unavailable (break-glass) | Bump __version__, match .release-please-manifest.json, commit, tag, push. The tag starts the same gated workflow — never bypass it. |
Still-current Makefile commands¶
| Task | Command |
|---|---|
| Build distributions | make build |
| Publish to TestPyPI | make publish-test |
| Show current version | make version |
To test a local build: