Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
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
1 change: 1 addition & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ Thumbs.db
docs/
*.md
README*
!README.md

# Tests
tests/
Expand Down
94 changes: 94 additions & 0 deletions .github/bump_version.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
"""Infer semver bump from towncrier fragment types and update version."""

from __future__ import annotations

import re
import sys
from pathlib import Path


class VersioningError(Exception):
"""Raised when version bump inference cannot be completed."""


def get_current_version(pyproject_path: Path) -> str:
text = pyproject_path.read_text()
match = re.search(r'^version\s*=\s*"(\d+\.\d+\.\d+)"', text, re.MULTILINE)
if not match:
raise VersioningError("Could not find version in pyproject.toml")
return match.group(1)


def get_fragment_category(fragment_path: Path) -> str | None:
parts = fragment_path.name.split(".")
if len(parts) >= 3:
return parts[-2]
if fragment_path.suffix and fragment_path.suffix != ".md":
return fragment_path.suffix.lstrip(".")
return None


def infer_bump(changelog_dir: Path) -> str:
fragments = [
fragment
for fragment in changelog_dir.iterdir()
if fragment.is_file() and fragment.name != ".gitkeep"
]
if not fragments:
raise VersioningError("No changelog fragments found")

categories = {
category
for fragment in fragments
if (category := get_fragment_category(fragment))
}

if "breaking" in categories:
return "major"
if "added" in categories or "removed" in categories:
return "minor"
return "patch"


def bump_version(version: str, bump: str) -> str:
major, minor, patch = (int(x) for x in version.split("."))
if bump == "major":
return f"{major + 1}.0.0"
if bump == "minor":
return f"{major}.{minor + 1}.0"
return f"{major}.{minor}.{patch + 1}"


def update_file(path: Path, old_version: str, new_version: str) -> bool:
text = path.read_text()
updated = text.replace(
f'version = "{old_version}"',
f'version = "{new_version}"',
)
if updated == text:
return False
path.write_text(updated)
print(f" Updated {path}")
return True


def main(root: Path | None = None) -> str:
root = root or Path(__file__).resolve().parent.parent
pyproject = root / "pyproject.toml"
changelog_dir = root / "changelog.d"

try:
current = get_current_version(pyproject)
bump = infer_bump(changelog_dir)
except VersioningError as exc:
print(str(exc), file=sys.stderr)
raise SystemExit(1) from exc

new = bump_version(current, bump)
print(f"Version: {current} -> {new} ({bump})")
update_file(pyproject, current, new)
return new


if __name__ == "__main__":
raise SystemExit(main())
8 changes: 0 additions & 8 deletions .github/changelog_template.md

This file was deleted.

2 changes: 0 additions & 2 deletions .github/get-changelog-diff.sh

This file was deleted.

33 changes: 0 additions & 33 deletions .github/is-version-number-acceptable.sh

This file was deleted.

14 changes: 12 additions & 2 deletions .github/publish-git-tag.sh
Original file line number Diff line number Diff line change
@@ -1,4 +1,14 @@
#! /usr/bin/env bash

git tag `python setup.py --version`
git push --tags || true # update the repository version
set -euo pipefail

version=$(python -c 'import tomllib; from pathlib import Path; print(tomllib.loads(Path("pyproject.toml").read_text())["project"]["version"])')

if git rev-parse --verify --quiet "refs/tags/$version" >/dev/null
then
echo "Tag $version already exists."
exit 0
fi

git tag "$version"
git push origin "$version"
23 changes: 23 additions & 0 deletions .github/scripts/build-and-push-image.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
#!/bin/bash

set -euo pipefail

ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
cd "$ROOT"

ARTIFACT_TAG="${IMAGE_NAME:?}:${GITHUB_SHA:?}"
GHCR_SHA_TAG="ghcr.io/${GITHUB_REPOSITORY:?}:${GITHUB_SHA}"
GHCR_LATEST_TAG="ghcr.io/${GITHUB_REPOSITORY:?}:latest"

docker build \
-f ./gcp/policyengine_household_api/Dockerfile.production \
-t "${ARTIFACT_TAG}" \
.
docker push "${ARTIFACT_TAG}"

if [ "${GITHUB_EVENT_NAME:-}" = "push" ] && [ "${GITHUB_REF:-}" = "refs/heads/main" ]; then
docker tag "${ARTIFACT_TAG}" "${GHCR_SHA_TAG}"
docker push "${GHCR_SHA_TAG}"
docker tag "${ARTIFACT_TAG}" "${GHCR_LATEST_TAG}"
docker push "${GHCR_LATEST_TAG}"
fi
17 changes: 17 additions & 0 deletions .github/scripts/check-changelog-fragment.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
#!/bin/bash

set -euo pipefail

ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
cd "$ROOT"

FRAGMENTS=$(find changelog.d -type f ! -name ".gitkeep" | wc -l | tr -d ' ')

if [ "$FRAGMENTS" -eq 0 ]; then
echo "::error::No changelog fragment found in changelog.d/"
echo "Add one with: echo 'Description.' > changelog.d/\$(git branch --show-current).<type>.md"
echo "Types: added, changed, fixed, removed, breaking"
exit 1
fi

echo "Found $FRAGMENTS changelog fragment(s)."
78 changes: 47 additions & 31 deletions .github/scripts/check_updates.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,14 @@
Check for PolicyEngine package updates and generate PR summary.

This script checks PyPI for newer versions of PolicyEngine packages,
updates setup.py if needed, and generates changelog summaries.
updates pyproject.toml if needed, and generates changelog summaries.
"""

import os
import re
import sys
import tomllib
from pathlib import Path

import requests

Expand All @@ -24,14 +26,21 @@ def parse_version(version_str):
return tuple(map(int, version_str.split(".")))


def get_current_versions(setup_content):
"""Extract current package versions from setup.py content."""
def get_current_versions(pyproject_content):
"""Extract current package versions from pyproject.toml content."""
current_versions = {}
dependencies = tomllib.loads(pyproject_content)["project"].get(
"dependencies", []
)
for pkg in PACKAGES:
pattern = rf'{pkg.replace("_", "[-_]")}==([0-9]+\.[0-9]+\.[0-9]+)'
match = re.search(pattern, setup_content)
if match:
current_versions[pkg] = match.group(1)
package_names = (pkg, pkg.replace("_", "-"))
for dependency in dependencies:
for package_name in package_names:
if dependency.startswith(f"{package_name}=="):
current_versions[pkg] = dependency.split("==", 1)[1]
break
if pkg in current_versions:
break
return current_versions


Expand Down Expand Up @@ -59,12 +68,19 @@ def find_updates(current_versions, latest_versions):
return updates


def update_setup_content(setup_content, updates):
"""Update setup.py content with new versions."""
new_content = setup_content
def update_pyproject_content(pyproject_content, updates):
"""Update pyproject.toml content with new versions."""
new_content = pyproject_content
for pkg, versions in updates.items():
pattern = rf'({pkg.replace("_", "[-_]")}==)[0-9]+\.[0-9]+\.[0-9]+'
new_content = re.sub(pattern, rf'\g<1>{versions["new"]}', new_content)
pattern = (
rf'("{pkg.replace("_", "[-_]")}==)'
rf'{re.escape(versions["old"])}(")'
)
new_content = re.sub(
pattern,
rf"\g<1>{versions['new']}\g<2>",
new_content,
)
return new_content


Expand Down Expand Up @@ -214,14 +230,10 @@ def generate_summary(updates):
return "\n\n".join(summary_parts)


def generate_changelog_entry(updates):
"""Generate changelog entry for this repo."""
def generate_changelog_fragment(updates):
"""Generate towncrier changelog fragment content for this repo."""
new_version = updates["policyengine_us"]["new"]
return f"""- bump: patch
changes:
changed:
- Update PolicyEngine US to {new_version}
"""
return f"Update PolicyEngine US to {new_version}.\n"


def write_github_output(key, value):
Expand All @@ -234,11 +246,11 @@ def write_github_output(key, value):

def main():
"""Main entry point for the script."""
# Read current versions from setup.py
with open("setup.py", "r") as f:
setup_content = f.read()
# Read current versions from pyproject.toml
with open("pyproject.toml", "r") as f:
pyproject_content = f.read()

current_versions = get_current_versions(setup_content)
current_versions = get_current_versions(pyproject_content)
print(f"Current versions: {current_versions}")

# Get latest versions from PyPI
Expand All @@ -255,20 +267,24 @@ def main():

print(f"Updates available: {updates}")

# Update setup.py
new_setup_content = update_setup_content(setup_content, updates)
with open("setup.py", "w") as f:
f.write(new_setup_content)
# Update pyproject.toml
new_pyproject_content = update_pyproject_content(
pyproject_content, updates
)
with open("pyproject.toml", "w") as f:
f.write(new_pyproject_content)

# Generate and save PR summary
full_summary = generate_summary(updates)
with open("pr_summary.md", "w") as f:
f.write(full_summary)

# Create changelog entry
changelog_entry = generate_changelog_entry(updates)
with open("changelog_entry.yaml", "w") as f:
f.write(changelog_entry)
# Create changelog fragment
changelog_dir = Path("changelog.d")
changelog_dir.mkdir(exist_ok=True)
new_version = updates["policyengine_us"]["new"]
fragment_path = changelog_dir / f"policyengine-us-{new_version}.changed.md"
fragment_path.write_text(generate_changelog_fragment(updates))

# Set outputs
write_github_output("has_updates", "true")
Expand Down
24 changes: 24 additions & 0 deletions .github/scripts/commit-weekly-update.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
#!/bin/bash

set -euo pipefail

ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
cd "$ROOT"

UPDATES_SUMMARY="${UPDATES_SUMMARY:?UPDATES_SUMMARY must be set}"
BRANCH_NAME="bot/weekly-us-update"

git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git switch -C "$BRANCH_NAME"

shopt -s nullglob
FRAGMENTS=(changelog.d/*.md)
if [ "${#FRAGMENTS[@]}" -eq 0 ]; then
echo "Expected at least one changelog fragment in changelog.d/"
exit 1
fi

git add pyproject.toml uv.lock "${FRAGMENTS[@]}"
git commit -m "Update policyengine-us (${UPDATES_SUMMARY})"
git push -f origin "$BRANCH_NAME"
9 changes: 9 additions & 0 deletions .github/scripts/delete-appengine-version.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
#!/bin/bash

set -euo pipefail

SERVICE_NAME="${SERVICE_NAME:?SERVICE_NAME must be set}"
VERSION="${VERSION:?VERSION must be set}"

echo "Deleting App Engine version ${VERSION} from service ${SERVICE_NAME}"
gcloud app versions delete "${VERSION}" --service="${SERVICE_NAME}" --quiet
Loading
Loading