Release Process
June 8, 2026 · View on GitHub
This document describes how to release packages for the Teams SDK for Python. It assumes you have required entitlements in Azure DevOps for triggering releases.
This project uses Nerdbank.GitVersioning for automatic version management.
Prerequisites
The .NET SDK and nbgv CLI are required for publishing but optional for local development. Without them, packages fall back to version 0.0.0 so you can still build and test locally.
CI pipelines set NBGV_REQUIRED=1 to ensure builds fail if nbgv is unavailable.
# Optional for local dev, required for releases
dotnet tool install -g nbgv
Branch Strategy
| Branch | Versions | Published |
|---|---|---|
main | 2.0.1.dev1, 2.0.1.dev2, ... | No |
release | 2.0.0 | Yes |
release is a long-lived branch — it already exists. You don't create it; you bring main into it.
Workflow
Development happens on main. When ready to release:
- Open a PR from a feature branch (e.g.
<you>/release-2.0.13) into the existingreleasebranch - The PR should make
releaseequalmainplus the version bump inversion.json - Merge the PR, then run the publish pipeline
Preparing the release branch
Start from main, bump the version, then use git merge -s ours origin/release to mark release as merged without pulling in any of its content.
-
Start a branch from latest
main:git fetch origin git checkout -b <you>/prep-release-<version> origin/main -
Edit
version.jsonand change"version"from"X.Y.Z-dev.{height}"to"X.Y.Z"(the stable version you're releasing), then commit:git add version.json git commit -m "Release <version>: set version to <version> stable" -
Merge
releaseinto your branch using-s ours(records release as a parent but keeps main's tree):git merge -s ours origin/release -m "Release <version>: merge main into release" -
Push and open a PR targeting
release:git push -u origin <you>/prep-release-<version> gh pr create --base release --title "Release <version>: merge main into release"
Versioning
Versions are managed by Nerdbank.GitVersioning via version.json.
Current Configuration (main)
{
"version": "2.0.13-dev.{height}",
"versionHeightOffset": 1
}
Builds on main produce dev versions like 2.0.1.dev1, 2.0.1.dev2, etc. These are not published.
Example Package Names
| Branch | Package Name |
|---|---|
main | microsoft_teams_apps-2.0.1.dev2.tar.gz |
release | microsoft_teams_apps-2.0.0.tar.gz |
Note: Running the pipeline on a branch not in
publicReleaseRefSpec(e.g., a feature branch) produces versions with the commit hash appended, like2.0.1.dev5+g1a2b3c4. This is expected and useful for testing.
Producing a Stable Release
The version on release should be a plain stable string (e.g. 2.0.0, no -dev suffix). The PR opened in the Workflow section above already handles this — just edit version.json before pushing:
{
"version": "2.0.0",
"versionHeightOffset": 1
}
After the PR merges, run the publish pipeline with Public to release to PyPI.
Publishing
The publish pipeline (.azdo/publish.yml) is manually triggered and requires selecting a Publish Type: Internal or Public.
- Go to Pipelines > teams.py in ADO
- Click Run pipeline
- Select the
releasebranch - Choose a Publish Type:
- Internal — publishes unsigned packages to the Azure Artifacts
TeamsSDKPreviewsfeed. No approval required. Packages are available immediately. - Public — signs packages via ESRP and publishes to PyPI. Requires approval via the
teams-sdk-publishADO environment before the ESRP release proceeds.
- Internal — publishes unsigned packages to the Azure Artifacts
- Pipeline runs: Build > Test > Publish
Note: The pipeline filters out packages matching the
ExcludePackageFoldersvariable. Prerelease versions are taggednexton PyPI; stable versions are taggedlatest.
Tagging and GitHub Release
After the publish pipeline finishes and packages land on PyPI, tag the release and create a GitHub Release page:
# Create a draft release at the release branch tip
gh release create v<version> -R microsoft/teams.py \
--target release --title "v<version>" --draft \
--generate-notes --notes-start-tag v<previous-version>
Note: GitHub's auto-generated notes walk back from the release branch tip. Because the release PR is squash-merged, the auto-list shows only the merge PR. To get the real PR delta from main, query by date:
# Note: macOS has no `tac` — the awk one-liner below works everywhere
gh api -X GET search/issues \
-f q='repo:microsoft/teams.py is:pr is:merged base:main merged:>=<previous-release-publish-date>' \
--jq '.items[] | "* \(.title) by @\(.user.login) in \(.html_url)"' \
| awk '{ a[NR] = \$0 } END { for (i = NR; i >= 1; i--) print a[i] }' \
> /tmp/notes.md
Paste the curated list into the draft, then publish from the GitHub UI (or via gh release edit --draft=false) to create the tag.
Approvers
The teams-sdk-publish environment in Azure DevOps controls who can approve public releases. To modify approvers:
- Go to Pipelines > Environments in ADO
- Select teams-sdk-publish
- Click the three dots menu > Approvals and checks
- Add/remove approvers as needed