Skip to content

Commit 54ac16d

Browse files
MaxGhenisclaude
andcommitted
Migrate to pyproject.toml and towncrier fragments
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent da12b33 commit 54ac16d

7 files changed

Lines changed: 177 additions & 66 deletions

File tree

.github/bump_version.py

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
"""Infer semver bump from towncrier fragment types and update version."""
2+
3+
import re
4+
import sys
5+
from pathlib import Path
6+
7+
8+
def get_current_version(pyproject_path: Path) -> str:
9+
text = pyproject_path.read_text()
10+
match = re.search(
11+
r'^version\s*=\s*"(\d+\.\d+\.\d+)"', text, re.MULTILINE
12+
)
13+
if not match:
14+
print(
15+
"Could not find version in pyproject.toml",
16+
file=sys.stderr,
17+
)
18+
sys.exit(1)
19+
return match.group(1)
20+
21+
22+
def infer_bump(changelog_dir: Path) -> str:
23+
fragments = [
24+
f
25+
for f in changelog_dir.iterdir()
26+
if f.is_file() and f.name != ".gitkeep"
27+
]
28+
if not fragments:
29+
print("No changelog fragments found", file=sys.stderr)
30+
sys.exit(1)
31+
32+
categories = {f.suffix.lstrip(".") for f in fragments}
33+
for f in fragments:
34+
parts = f.stem.split(".")
35+
if len(parts) >= 2:
36+
categories.add(parts[-1])
37+
38+
if "breaking" in categories:
39+
return "major"
40+
if "added" in categories or "removed" in categories:
41+
return "minor"
42+
return "patch"
43+
44+
45+
def bump_version(version: str, bump: str) -> str:
46+
major, minor, patch = (int(x) for x in version.split("."))
47+
if bump == "major":
48+
return f"{major + 1}.0.0"
49+
elif bump == "minor":
50+
return f"{major}.{minor + 1}.0"
51+
else:
52+
return f"{major}.{minor}.{patch + 1}"
53+
54+
55+
def update_file(path: Path, old_version: str, new_version: str):
56+
text = path.read_text()
57+
updated = text.replace(
58+
f'version = "{old_version}"',
59+
f'version = "{new_version}"',
60+
)
61+
if updated != text:
62+
path.write_text(updated)
63+
print(f" Updated {path}")
64+
65+
66+
def main():
67+
root = Path(__file__).resolve().parent.parent
68+
pyproject = root / "pyproject.toml"
69+
changelog_dir = root / "changelog.d"
70+
71+
current = get_current_version(pyproject)
72+
bump = infer_bump(changelog_dir)
73+
new = bump_version(current, bump)
74+
75+
print(f"Version: {current} -> {new} ({bump})")
76+
77+
update_file(pyproject, current, new)
78+
79+
80+
if __name__ == "__main__":
81+
main()

.github/workflows/pr.yml

Lines changed: 12 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -18,25 +18,20 @@ jobs:
1818
with:
1919
options: ". -l 79 --check"
2020
version: "24.3.0"
21-
check-version:
22-
name: Check version
21+
check-changelog:
22+
name: Check changelog fragment
2323
runs-on: ubuntu-latest
2424
steps:
25-
- uses: actions/checkout@v3
26-
with:
27-
fetch-depth: 0
28-
repository: ${{ github.event.pull_request.head.repo.full_name }}
29-
ref: ${{ github.event.pull_request.head.ref }}
30-
- name: Set up Python
31-
uses: actions/setup-python@v4
32-
with:
33-
python-version: "3.12"
34-
- name: Build changelog
35-
run: pip install yaml-changelog && make changelog
36-
- name: Preview changelog update
37-
run: ".github/get-changelog-diff.sh"
38-
- name: Check version number has been properly updated
39-
run: ".github/is-version-number-acceptable.sh"
25+
- uses: actions/checkout@v4
26+
- name: Check for changelog fragment
27+
run: |
28+
FRAGMENTS=$(find changelog.d -type f ! -name '.gitkeep' | wc -l)
29+
if [ "$FRAGMENTS" -eq 0 ]; then
30+
echo "::error::No changelog fragment found in changelog.d/"
31+
echo "Add one with: echo 'Description.' > changelog.d/\$(git branch --show-current).<type>.md"
32+
echo "Types: added, changed, fixed, removed, breaking"
33+
exit 1
34+
fi
4035
test:
4136
name: Test
4237
runs-on: ubuntu-latest

Makefile

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -29,13 +29,9 @@ deploy: ## Deploy to GCP
2929
rm Dockerfile
3030
rm .gac.json
3131

32-
changelog: ## Build changelog
33-
build-changelog changelog.yaml --output changelog.yaml --update-last-date --start-from 0.1.0 --append-file changelog_entry.yaml
34-
build-changelog changelog.yaml --org PolicyEngine --repo policyengine-household-api --output CHANGELOG.md --template .github/changelog_template.md
35-
bump-version changelog.yaml setup.py policyengine_household_api/constants.py
36-
rm changelog_entry.yaml || true
37-
touch changelog_entry.yaml
38-
32+
changelog:
33+
python .github/bump_version.py
34+
towncrier build --yes --version $$(python -c "import re; print(re.search(r'version = \"(.+?)\"', open('pyproject.toml').read()).group(1))")
3935
COMPOSE_FILE ?= docker/docker-compose.yml
4036
COMPOSE_EXTERNAL_FILE ?= docker/docker-compose.external.yml
4137
DOCKER_IMG ?= policyengine:policyengine-household-api
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Migrated from changelog_entry.yaml to towncrier fragments to eliminate merge conflicts.

pyproject.toml

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
[project]
2+
name = "policyengine-household-api"
3+
version = "0.13.3"
4+
description = "PolicyEngine Household API"
5+
readme = "README.md"
6+
authors = [
7+
{ name = "PolicyEngine", email = "hello@policyengine.org" }
8+
]
9+
requires-python = ">=3.12"
10+
dependencies = [
11+
"Authlib>=1.3.1",
12+
"Flask-Caching==2.0.2",
13+
"Flask-Limiter",
14+
"anthropic",
15+
"black==24.3.0",
16+
"cloud-sql-python-connector",
17+
"flask-cors>=3",
18+
"flask-sqlalchemy>=3",
19+
"flask>=2.2",
20+
"google-cloud-logging",
21+
"google-cloud-storage",
22+
"gunicorn",
23+
"inflect",
24+
"policyengine-il==0.1.0",
25+
"policyengine-ng==0.5.1",
26+
"policyengine_canada==0.96.3",
27+
"policyengine_uk==2.31.0",
28+
"policyengine_us==1.532.0",
29+
"pydantic",
30+
"pyjwt",
31+
"pymysql",
32+
"python-dotenv",
33+
"urllib3<1.27,>=1.21.1",
34+
]
35+
36+
[build-system]
37+
requires = ["hatchling"]
38+
build-backend = "hatchling.build"
39+
40+
[project.optional-dependencies]
41+
dev = [
42+
"pytest-timeout", "towncrier>=24.8.0",
43+
44+
]
45+
46+
[tool.hatch.build.targets.wheel]
47+
packages = ["policyengine_household_api"]
48+
49+
[tool.towncrier]
50+
package = "policyengine_household_api"
51+
directory = "changelog.d"
52+
filename = "CHANGELOG.md"
53+
title_format = "## [{version}] - {project_date}"
54+
issue_format = ""
55+
underlines = ["", "", ""]
56+
57+
[[tool.towncrier.type]]
58+
directory = "breaking"
59+
name = "Breaking changes"
60+
showcontent = true
61+
62+
[[tool.towncrier.type]]
63+
directory = "added"
64+
name = "Added"
65+
showcontent = true
66+
67+
[[tool.towncrier.type]]
68+
directory = "changed"
69+
name = "Changed"
70+
showcontent = true
71+
72+
[[tool.towncrier.type]]
73+
directory = "fixed"
74+
name = "Fixed"
75+
showcontent = true
76+
77+
[[tool.towncrier.type]]
78+
directory = "removed"
79+
name = "Removed"
80+
showcontent = true

setup.py

Lines changed: 0 additions & 42 deletions
This file was deleted.

0 commit comments

Comments
 (0)