Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 19 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
.PHONY: install sync test lint format typecheck check generate docs clean build
.PHONY: install sync test lint format typecheck check generate docs clean build bump release

# Bootstrap: install + dev deps, build the local venv.
install sync:
Expand Down Expand Up @@ -73,3 +73,21 @@ clean:
# Build wheel + sdist via hatchling.
build:
uv build

# Bump the version (pyproject.toml + src/zenrows/__version__.py) and relock.
# PART defaults to patch; pass PART=minor, PART=major, or an explicit X.Y.Z.
# Local-only: stages the change but does not commit, push, or tag.
PART ?= patch
bump:
uv run python scripts/bump_version.py $(PART)
uv lock
git add pyproject.toml src/zenrows/__version__.py uv.lock

# Bump, commit, push to main, and cut the GitHub release that triggers the
# PyPI publish workflow (.github/workflows/release.yml). Pushes to main and
# publishes a public release — confirm the diff before running this.
release: bump
$(eval NEW_VERSION := $(shell grep -m1 '^version = ' pyproject.toml | sed -E 's/version = "(.*)"/\1/'))
git commit -m "chore: bump version to $(NEW_VERSION)"
git push origin main
gh release create "v$(NEW_VERSION)" --title "v$(NEW_VERSION)" --generate-notes
68 changes: 68 additions & 0 deletions scripts/bump_version.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
"""Bump the SDK version in pyproject.toml and src/zenrows/__version__.py.

Usage:
uv run python scripts/bump_version.py patch|minor|major|<explicit-version>

Keeps the two hardcoded version strings in sync (there's no dynamic
versioning set up — hatchling reads pyproject.toml's `version` directly).
"""

import re
import sys
from pathlib import Path

ROOT = Path(__file__).resolve().parent.parent
PYPROJECT = ROOT / "pyproject.toml"
VERSION_FILE = ROOT / "src" / "zenrows" / "__version__.py"

VERSION_RE = re.compile(r"^(\d+)\.(\d+)\.(\d+)$")


def current_version() -> str:
text = PYPROJECT.read_text()
match = re.search(r'^version = "(.*)"$', text, flags=re.MULTILINE)
if not match:
raise SystemExit("could not find version = \"...\" in pyproject.toml")
return match.group(1)


def next_version(current: str, bump: str) -> str:
match = VERSION_RE.match(current)
if match:
major, minor, patch = (int(part) for part in match.groups())
if bump == "major":
return f"{major + 1}.0.0"
if bump == "minor":
return f"{major}.{minor + 1}.0"
if bump == "patch":
return f"{major}.{minor}.{patch + 1}"
# Not patch/minor/major, or current version isn't plain semver: treat
# the argument as an explicit version string.
if not VERSION_RE.match(bump):
raise SystemExit(f"'{bump}' is not 'patch', 'minor', 'major', or a X.Y.Z version")
return bump


def write_version(path: Path, pattern: str, replacement: str) -> None:
text = path.read_text()
new_text, count = re.subn(pattern, replacement, text, count=1, flags=re.MULTILINE)
if count != 1:
raise SystemExit(f"could not find version string to replace in {path}")
path.write_text(new_text)


def main() -> None:
if len(sys.argv) != 2:
raise SystemExit(__doc__)

old = current_version()
new = next_version(old, sys.argv[1])

write_version(PYPROJECT, r'^version = ".*"$', f'version = "{new}"')
write_version(VERSION_FILE, r'^__version__ = ".*"$', f'__version__ = "{new}"')

print(f"{old} -> {new}")


if __name__ == "__main__":
main()
Loading