diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 7cfa627..28705d4 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -111,24 +111,20 @@ to a GitHub pre-release. Releases are driven by a git tag. The release workflow builds binary wheels and attaches them to a GitHub pre-release for validation before PyPI publishing. -The version must be kept in sync across two files: -- `pyproject.toml` — `[project] version` -- `crates/fastly-compute-py/Cargo.toml` — `[package] version` +The version must be kept in sync across several files including `pyproject.toml`, `crates/fastly-compute-py/Cargo.toml`, and all workspace `uv.lock` / lockfiles. `make lint` checks these are in sync. ### Steps -1. Bump `version` in both files above to the new version (e.g. `0.2.0`). - -2. Verify locally: +1. Use the automated version bump helper to update versions and synchronize all cargo/uv lockfiles across the workspace and examples: ```bash - make lint + make bump-version VERSION=0.2.0 ``` -3. PR the changes and land into main. +2. PR the changes and land into main. -4. Push tag (make sure you are on the right sha first) +3. Push tag (make sure you are on the right sha first) ``` git tag v0.2.0 git push origin v0.2.0 @@ -138,6 +134,9 @@ The version must be kept in sync across two files: on any mismatch) → parallel wheel + sdist builds → `collect-artifacts` → `create-github-release`. -5. (Pending) If the release is built successfully, it will make its way to PyPI - via trusted publishing. +5. If the release workflow succeeds, a new pre-release will be available on + GitHub. Afer review and update of the generated changelog, use the + GitHub UI to transition the release to no longer being a pre-release. + This will trigger the publish workflow and push a [new release to PyPI][pypi]. +[pypi]: https://pypi.org/project/fastly-compute/ diff --git a/Cargo.lock b/Cargo.lock index 9ac50b9..8aa1096 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -882,7 +882,7 @@ dependencies = [ [[package]] name = "fastly-compute-py" -version = "0.1.1" +version = "0.1.2" dependencies = [ "anyhow", "cargo_metadata", diff --git a/Makefile b/Makefile index e56e18c..b142d39 100644 --- a/Makefile +++ b/Makefile @@ -96,7 +96,7 @@ lint: fastly_compute/runtime_patching/patches.py | $(STUBS_DIR) @echo "Checking version synchronization..." uv run python scripts/check_version_sync.py @echo "Linting Python code..." - uv run --extra dev ruff check . + uv run --extra dev --extra test --extra examples ruff check . uv run --extra dev --extra test pyrefly check @echo "Linting Rust code..." cd crates/fastly-compute-py && cargo clippy --release --no-default-features --features binary -- -D warnings @@ -119,6 +119,12 @@ format-check: @echo "Checking Rust formatting..." cd crates/fastly-compute-py && cargo fmt --check +# Bump version numbers across the project for a new release +bump-version: + @test -n "$(VERSION)" || (echo "Error: VERSION is required. Example: make bump-version VERSION=0.2.0" && exit 1) + uv run python scripts/bump_version.py $(VERSION) + $(MAKE) lint + # Help target help: @echo "Fastly Compute Python SDK" @@ -132,6 +138,7 @@ help: @echo " serve [EXAMPLE=name] Serve example (default: $(EXAMPLE))" @echo " test Run integration tests (builds all examples)" @echo " test-update-snapshots Update snapshot test baselines" + @echo " bump-version VERSION=vX.Y.Z Bump version across pyproject.toml and Cargo.toml" @echo " build-all Build all examples (alias for 'all')" @echo " list-examples List available examples" @echo " clean Clean all build artifacts (including Rust)" @@ -156,4 +163,4 @@ help: @echo "" @echo "Available examples: $(EXAMPLES)" -.PHONY: all serve test test-update-snapshots list-examples build-all clean lint lint-fix format format-check help +.PHONY: all serve test test-update-snapshots list-examples build-all clean lint lint-fix format format-check help bump-version diff --git a/crates/fastly-compute-py/Cargo.toml b/crates/fastly-compute-py/Cargo.toml index b3d809d..b30dcf6 100644 --- a/crates/fastly-compute-py/Cargo.toml +++ b/crates/fastly-compute-py/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "fastly-compute-py" -version = "0.1.1" +version = "0.1.2" edition = "2021" [lib] diff --git a/examples/backend-requests/uv.lock b/examples/backend-requests/uv.lock index 07d68cc..236a760 100644 --- a/examples/backend-requests/uv.lock +++ b/examples/backend-requests/uv.lock @@ -28,7 +28,7 @@ wheels = [ [[package]] name = "fastly-compute" -version = "0.1.1" +version = "0.1.2" source = { editable = "../../" } [package.metadata] diff --git a/examples/bottle-app/uv.lock b/examples/bottle-app/uv.lock index ae7a15e..b17dace 100644 --- a/examples/bottle-app/uv.lock +++ b/examples/bottle-app/uv.lock @@ -28,7 +28,7 @@ requires-dist = [ [[package]] name = "fastly-compute" -version = "0.1.1" +version = "0.1.2" source = { editable = "../../" } [package.metadata] diff --git a/examples/flask-app/uv.lock b/examples/flask-app/uv.lock index 6c7045b..dd56f28 100644 --- a/examples/flask-app/uv.lock +++ b/examples/flask-app/uv.lock @@ -34,7 +34,7 @@ wheels = [ [[package]] name = "fastly-compute" -version = "0.1.1" +version = "0.1.2" source = { editable = "../../" } [package.metadata] diff --git a/examples/game-of-life/uv.lock b/examples/game-of-life/uv.lock index 32f5bd2..33189b1 100644 --- a/examples/game-of-life/uv.lock +++ b/examples/game-of-life/uv.lock @@ -34,7 +34,7 @@ wheels = [ [[package]] name = "fastly-compute" -version = "0.1.1" +version = "0.1.2" source = { editable = "../../" } [package.metadata] diff --git a/pyproject.toml b/pyproject.toml index 2f9821b..6ea7523 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "fastly-compute" -version = "0.1.1" +version = "0.1.2" description = "Python SDK for Fastly Compute" readme = "README.md" requires-python = ">=3.12" diff --git a/scripts/bump_version.py b/scripts/bump_version.py new file mode 100755 index 0000000..9b47426 --- /dev/null +++ b/scripts/bump_version.py @@ -0,0 +1,85 @@ +#!/usr/bin/env python3 +"""Bump version numbers across the project for a new release. + +Updates the version in: + - pyproject.toml + - crates/fastly-compute-py/Cargo.toml + +And then runs `cargo metadata` to update Cargo.lock and +`uv lock` in the base and for all examples. +""" + +import argparse +import re +import subprocess +import sys +from pathlib import Path + + +def update_file_version(file_path: Path, pattern: str, replacement: str) -> None: + """Read a file, replace a version pattern, and write it back.""" + content = file_path.read_text() + new_content, count = re.subn(pattern, replacement, content, flags=re.MULTILINE) + if count == 0: + raise ValueError(f"Could not find version pattern in {file_path}") + file_path.write_text(new_content, encoding="utf-8") + print(f"✓ Updated version in {file_path.relative_to(file_path.parents[1])}") + + +def main() -> int: + """Parse arguments, validate the version, and update all configuration files.""" + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument( + "version", + help="The new version string (e.g., 0.2.0 or 0.1.1)", + ) + args = parser.parse_args() + + # Validate version format (semantic versioning: X.Y.Z) + new_version = args.version.lstrip("v") + if not re.match(r"^\d+\.\d+\.\d+(?:-\w+)?$", new_version): + print( + f"Error: version '{args.version}' is not a valid semantic version", + file=sys.stderr, + ) + return 1 + + root_dir = Path(__file__).parent.parent + pyproject_path = root_dir / "pyproject.toml" + cargo_path = root_dir / "crates" / "fastly-compute-py" / "Cargo.toml" + + new_version_line = f'version = "{new_version}"' + + # pyrpoject.toml: Matches `version = "X.Y.Z"` under [project] + update_file_version(pyproject_path, r'^version = ".*$', new_version_line) + + # Cargo.toml: Matches `version = "X.Y.Z"` under [package] + update_file_version(cargo_path, r'^version = ".*$', new_version_line) + + # Update Cargo.lock + print("Updating Cargo.lock...") + subprocess.check_call( + ["cargo", "metadata", "--format-version=1"], stdout=subprocess.DEVNULL + ) + print("✓ Updated Cargo.lock") + + # Update workspace uv.lock + print("Updating workspace uv.lock...") + subprocess.check_call(["uv", "lock"]) + print("✓ Updated workspace uv.lock") + + # Update example uv.lock files + print("Updating example uv.lock files...") + examples_dir = root_dir / "examples" + for example_path in examples_dir.iterdir(): + if example_path.is_dir() and (example_path / "pyproject.toml").exists(): + print(f" - Updating {example_path.name}/uv.lock...") + subprocess.check_call(["uv", "lock"], cwd=example_path) + print("✓ Updated all example uv.lock files") + + print(f"\nSuccessfully bumped version to {new_version} across the project!") + return 0 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/uv.lock b/uv.lock index f73a41f..f0bac82 100644 --- a/uv.lock +++ b/uv.lock @@ -125,7 +125,7 @@ wheels = [ [[package]] name = "fastly-compute" -version = "0.1.1" +version = "0.1.2" source = { editable = "." } [package.optional-dependencies]