Skip to content

Commit 0f8a60d

Browse files
authored
Release version 0.0.1a2 (#112)
1. Adds a script to do the relasee 2. Adds RELEASE.md 3. Adds a workflow to push to pypi.
1 parent ad56e0f commit 0f8a60d

File tree

11 files changed

+317
-13
lines changed

11 files changed

+317
-13
lines changed

.github/workflows/release.yml

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
# yaml-language-server: $schema=https://json.schemastore.org/github-workflow.json
2+
3+
name: "Release"
4+
5+
on:
6+
push:
7+
tags:
8+
- "*"
9+
10+
# Declare default permissions as read only.
11+
permissions: read-all
12+
13+
jobs:
14+
build:
15+
name: Build and Publish
16+
runs-on: ubuntu-latest
17+
environment:
18+
# Create this environment in the GitHub repository under Settings -> Environments
19+
name: release
20+
permissions:
21+
actions: read
22+
contents: read
23+
id-token: write
24+
25+
steps:
26+
- name: Harden Runner
27+
uses: step-security/harden-runner@0080882f6c36860b6ba35c610c98ce87d4e2f26f # v2.10.2
28+
with:
29+
egress-policy: audit
30+
31+
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
32+
33+
- name: Set up Python
34+
uses: actions/setup-python@0b93645e9fea7318ecaed2b359559ac225c90a2b # v5.3.0
35+
with:
36+
python-version: "3.12"
37+
38+
- name: Install uv
39+
run: |
40+
curl -LsSf https://astral.sh/uv/0.5.24/install.sh -o install.sh
41+
echo "f476e445f4a56234fcc12ed478289f80e8e97b230622d8ce2f2406ebfeeb2620 install.sh" > checksum.txt
42+
sha256sum --check checksum.txt
43+
chmod +x install.sh
44+
./install.sh
45+
uv --version
46+
47+
- name: Install dependencies
48+
run: |
49+
python -m pip install --upgrade pip
50+
uv sync --frozen --all-packages
51+
52+
- name: Build packages
53+
run: |
54+
uv build --all-packages
55+
56+
- name: Publish to PyPI
57+
run: |
58+
uv publish --trusted-publishing always

RELEASE.md

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
# Release steps
2+
3+
1. Run `uv run scripts/release.py <bump_type>`. See [Bump types](#bump-types) for available options.
4+
2. This should bump all the versions for the packages and also created a release branch.
5+
3. Create a PR and get it merged.
6+
4. Now go to https://github.com/microsoft/teams.py/releases/new and create a new release.
7+
5. This will automatically kick off a release workflow that needs to be aproved.
8+
6. Once approved, the release will be published to PyPI.
9+
10+
## Appendix
11+
12+
# Bump types
13+
Version bump types:
14+
major - Increment major version (1.0.0 -> 2.0.0)
15+
minor - Increment minor version (1.0.0 -> 1.1.0)
16+
patch - Increment patch version (1.0.0 -> 1.0.1)
17+
stable - Remove pre-release suffix (1.0.0a1 -> 1.0.0)
18+
alpha - Add/increment alpha pre-release (1.0.0 -> 1.0.0a1)
19+
beta - Add/increment beta pre-release (1.0.0 -> 1.0.0b1)
20+
rc - Add/increment release candidate (1.0.0 -> 1.0.0rc1)
21+
post - Add/increment post-release (1.0.0 -> 1.0.0.post1)
22+
dev - Add/increment dev release (1.0.0 -> 1.0.0.dev1)
23+

packages/api/pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[project]
22
name = "microsoft-teams-api"
3-
version = "0.0.1-alpha.1"
3+
version = "0.0.1a2"
44
description = "API package for Microsoft Teams"
55
readme = "README.md"
66
repository = "https://github.com/microsoft/teams.py"

packages/apps/pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[project]
22
name = "microsoft-teams-apps"
3-
version = "0.0.1-alpha.1"
3+
version = "0.0.1a2"
44
description = "The app package for a Microsoft Teams agent"
55
authors = [{ name = "Microsoft", email = "[email protected]" }]
66
readme = "README.md"

packages/cards/pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[project]
22
name = "microsoft-teams-cards"
3-
version = "0.0.1-alpha.1"
3+
version = "0.0.1a2"
44
description = "Cards package for Microsoft Teams"
55
readme = "README.md"
66
repository = "https://github.com/microsoft/teams.py"

packages/common/pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[project]
22
name = "microsoft-teams-common"
3-
version = "0.0.1-alpha.1"
3+
version = "0.0.1a2"
44
description = "Common package for Microsoft Teams"
55
readme = "README.md"
66
repository = "https://github.com/microsoft/teams.py"

packages/devtools/pyproject.toml

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[project]
22
name = "microsoft-teams-devtools"
3-
version = "0.0.1-alpha.1"
3+
version = "0.0.1a2"
44
description = "Local tool to streamline testing and development"
55
authors = [{ name = "Microsoft", email = "[email protected]" }]
66
readme = "README.md"
@@ -16,6 +16,10 @@ dependencies = [
1616
"pydantic>=2.0.0",
1717
]
1818

19+
[tool.uv.sources]
20+
"microsoft-teams-api" = { workspace = true }
21+
"microsoft-teams-apps" = { workspace = true }
22+
1923
[project.urls]
2024
Homepage = "https://github.com/microsoft/teams.py/tree/main/packages/devtools/src/microsoft/teams/devtools"
2125

packages/graph/pyproject.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[project]
22
name = "microsoft-teams-graph"
3-
version = "0.0.1-alpha.1"
3+
version = "0.0.1a2"
44
description = "The Graph package for a Microsoft Teams agent"
55
readme = "README.md"
66
license = { text = "MIT" }
@@ -36,4 +36,4 @@ packages = ["src/microsoft"]
3636
include = ["src"]
3737

3838
[tool.uv.sources]
39-
microsoft-teams-common = { workspace = true }
39+
microsoft-teams-common = { workspace = true }

scripts/release.py

Lines changed: 213 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,213 @@
1+
#!/usr/bin/env python3
2+
"""
3+
Copyright (c) Microsoft Corporation. All rights reserved.
4+
Licensed under the MIT License.
5+
"""
6+
7+
import argparse
8+
import subprocess
9+
import sys
10+
from pathlib import Path
11+
from typing import Dict, List
12+
13+
import tomllib
14+
15+
16+
def get_packages_dir() -> Path:
17+
"""Get the packages directory relative to the script location."""
18+
script_dir = Path(__file__).parent
19+
return script_dir.parent / "packages"
20+
21+
22+
def find_packages() -> List[Path]:
23+
"""Find all package directories containing pyproject.toml."""
24+
packages_dir = get_packages_dir()
25+
packages: List[Path] = []
26+
27+
for item in packages_dir.iterdir():
28+
if item.is_dir() and (item / "pyproject.toml").exists():
29+
packages.append(item)
30+
31+
return sorted(packages)
32+
33+
34+
def dry_run_version_bump(package_path: Path, bump_type: str) -> str:
35+
"""Run a dry-run version bump to see what the new version would be."""
36+
try:
37+
result = subprocess.run(
38+
["uv", "version", "--bump", bump_type, "--dry-run"],
39+
cwd=package_path,
40+
capture_output=True,
41+
text=True,
42+
check=True,
43+
)
44+
# Extract the version from the output
45+
# Handle multiple formats:
46+
# Format 1: "Would bump version from X.Y.Z to A.B.C"
47+
# Format 2: "package-name X.Y.Z => A.B.C"
48+
# Format 3: Just "A.B.C"
49+
output = result.stdout.strip()
50+
51+
if " to " in output:
52+
return output.split(" to ")[-1]
53+
elif " => " in output:
54+
return output.split(" => ")[-1]
55+
else:
56+
# Fallback: extract version from the end of the output
57+
return output.split()[-1]
58+
except subprocess.CalledProcessError as e:
59+
print(f" ✗ Failed to dry-run bump {package_path.name}: {e.stderr}")
60+
sys.exit(1)
61+
62+
63+
def bump_package_version(package_path: Path, bump_type: str, verbose: bool = False) -> str:
64+
"""Bump the version of a package and return the new version."""
65+
print(f"Bumping {package_path.name} version ({bump_type})...")
66+
67+
try:
68+
result = subprocess.run(
69+
["uv", "version", "--bump", bump_type],
70+
cwd=package_path,
71+
capture_output=not verbose,
72+
text=True,
73+
check=True,
74+
)
75+
print(f" ✓ {package_path.name}: {result.stdout.strip()}")
76+
return get_package_version(package_path)
77+
except subprocess.CalledProcessError as e:
78+
print(f" ✗ Failed to bump {package_path.name}: {e.stderr}")
79+
sys.exit(1)
80+
81+
82+
def get_package_version(package_path: Path) -> str:
83+
"""Extract version from pyproject.toml."""
84+
pyproject_path = package_path / "pyproject.toml"
85+
86+
try:
87+
with open(pyproject_path, "rb") as f:
88+
data = tomllib.load(f)
89+
return data["project"]["version"]
90+
except (KeyError, tomllib.TOMLDecodeError, OSError) as e:
91+
print(f"Error reading version from {pyproject_path}: {e}")
92+
sys.exit(1)
93+
94+
95+
def create_release_branch(version: str, verbose: bool = False) -> str:
96+
"""Create a new release branch."""
97+
branch_name = f"release_{version}"
98+
99+
try:
100+
# Create and switch to new branch
101+
subprocess.run(["git", "checkout", "-b", branch_name], check=True, capture_output=not verbose)
102+
print(f"Created and switched to branch: {branch_name}")
103+
104+
# Add all changes
105+
subprocess.run(["git", "add", "."], check=True, capture_output=not verbose)
106+
107+
# Commit changes
108+
subprocess.run(["git", "commit", "-m", f"Release version {version}"], check=True, capture_output=not verbose)
109+
print(f"Committed changes for release {version}")
110+
111+
return branch_name
112+
except subprocess.CalledProcessError as e:
113+
print(f"Error creating release branch: {e}")
114+
sys.exit(1)
115+
116+
117+
def main() -> None:
118+
"""Main script entry point."""
119+
parser = argparse.ArgumentParser(
120+
description="Release script for Microsoft Teams Python SDK",
121+
formatter_class=argparse.RawDescriptionHelpFormatter,
122+
epilog="""
123+
Version bump types:
124+
major - Increment major version (1.0.0 -> 2.0.0)
125+
minor - Increment minor version (1.0.0 -> 1.1.0)
126+
patch - Increment patch version (1.0.0 -> 1.0.1)
127+
stable - Remove pre-release suffix (1.0.0a1 -> 1.0.0)
128+
alpha - Add/increment alpha pre-release (1.0.0 -> 1.0.0a1)
129+
beta - Add/increment beta pre-release (1.0.0 -> 1.0.0b1)
130+
rc - Add/increment release candidate (1.0.0 -> 1.0.0rc1)
131+
post - Add/increment post-release (1.0.0 -> 1.0.0.post1)
132+
dev - Add/increment dev release (1.0.0 -> 1.0.0.dev1)
133+
""",
134+
)
135+
136+
parser.add_argument(
137+
"bump_type",
138+
choices=["major", "minor", "patch", "stable", "alpha", "beta", "rc", "post", "dev"],
139+
help="Type of version bump to perform",
140+
)
141+
parser.add_argument(
142+
"-v",
143+
"--verbose",
144+
action="store_true",
145+
help="Show detailed output from commands",
146+
)
147+
148+
args = parser.parse_args()
149+
150+
# Find all packages
151+
packages = find_packages()
152+
if not packages:
153+
print("No packages found in packages/ directory")
154+
sys.exit(1)
155+
156+
print(f"Found {len(packages)} packages:")
157+
for pkg in packages:
158+
print(f" - {pkg.name}")
159+
print()
160+
161+
# First, do a dry-run to check all packages would have the same version
162+
print("Running dry-run to check version consistency...")
163+
dry_run_versions: Dict[str, str] = {}
164+
for package in packages:
165+
new_version = dry_run_version_bump(package, args.bump_type)
166+
dry_run_versions[package.name] = new_version
167+
print(f" {package.name}: {get_package_version(package)} -> {new_version}")
168+
169+
# Check if all packages would have the same version
170+
unique_dry_run_versions = set(dry_run_versions.values())
171+
if len(unique_dry_run_versions) != 1:
172+
print("\n❌ ERROR: Packages would have different versions after bump:")
173+
for pkg, ver in dry_run_versions.items():
174+
print(f" {pkg}: {ver}")
175+
print("\nAll packages must have the same version. Please fix version inconsistencies first.")
176+
sys.exit(1)
177+
178+
target_version = next(iter(unique_dry_run_versions))
179+
print(f"\n✓ All packages will be bumped to: {target_version}")
180+
print("\nProceeding with actual version bump...")
181+
182+
# Now do the actual version bump
183+
versions: Dict[str, str] = {}
184+
for package in packages:
185+
new_version = bump_package_version(package, args.bump_type, args.verbose)
186+
versions[package.name] = new_version
187+
188+
# Verify all packages have the same version (should always pass now)
189+
unique_versions = set(versions.values())
190+
if len(unique_versions) != 1:
191+
print("❌ CRITICAL ERROR: Packages have different versions after bump (this should not happen):")
192+
for pkg, ver in versions.items():
193+
print(f" {pkg}: {ver}")
194+
sys.exit(1)
195+
196+
# Use the first version as the release version
197+
release_version = next(iter(unique_versions))
198+
print(f"\nAll packages bumped to version: {release_version}")
199+
200+
# Ask user about creating branch
201+
response = input("\nWould you like to create a release branch (y/N): ").strip().lower()
202+
203+
if response in ("y", "yes"):
204+
branch_name = create_release_branch(release_version, args.verbose)
205+
print(f"\n✓ Release {release_version} is ready!")
206+
print(f" Branch: {branch_name}")
207+
else:
208+
print(f"\nVersion bump complete. Release version: {release_version}")
209+
print("You can manually commit and create a branch/PR when ready.")
210+
211+
212+
if __name__ == "__main__":
213+
main()

tests/echo/pyproject.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ requires-python = ">=3.12"
77
dependencies = [
88
"dotenv>=0.9.9",
99
"microsoft-teams-apps",
10+
"microsoft-teams-api",
11+
"microsoft-teams-devtools"
1012
]
1113

1214
[tool.uv.sources]

0 commit comments

Comments
 (0)