diff --git a/.github/capture-version.sh b/.github/capture-version.sh new file mode 100755 index 00000000..d36d9857 --- /dev/null +++ b/.github/capture-version.sh @@ -0,0 +1,15 @@ +#!/usr/bin/env bash +# +# Capture the installed policyengine version as a GitHub Actions step output. +# +# Environment: +# GITHUB_OUTPUT Step output file provided by GitHub Actions. +set -euo pipefail + +VERSION=$(python .github/fetch_version.py) +if ! [[ "$VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then + echo "Unexpected version output: ${VERSION}" >&2 + exit 1 +fi +echo "version=${VERSION}" >> "$GITHUB_OUTPUT" +echo "Captured version ${VERSION}" diff --git a/.github/dispatch-policyengine-release.sh b/.github/dispatch-policyengine-release.sh new file mode 100755 index 00000000..2b487cab --- /dev/null +++ b/.github/dispatch-policyengine-release.sh @@ -0,0 +1,28 @@ +#!/usr/bin/env bash +# +# Notify consumer repositories that a policyengine release is available on +# PyPI by sending a policyengine-release repository dispatch, which triggers +# their update workflows to open single-version bump PRs. +# +# Environment: +# VERSION Exact policyengine version that was released. +# GH_TOKEN PolicyEngine GitHub App token with Contents write access to the +# consumer repositories. +set -euo pipefail + +if [[ -z "${VERSION:-}" ]]; then + echo "ERROR: VERSION must be set." >&2 + exit 1 +fi + +CONSUMER_REPOS=( + policyengine-api + policyengine-api-v2 +) + +for repo in "${CONSUMER_REPOS[@]}"; do + gh api "repos/PolicyEngine/${repo}/dispatches" \ + -f event_type=policyengine-release \ + -f "client_payload[version]=${VERSION}" + echo "Dispatched policyengine-release ${VERSION} to PolicyEngine/${repo}." +done diff --git a/.github/wait-for-pypi.sh b/.github/wait-for-pypi.sh new file mode 100755 index 00000000..f33ec603 --- /dev/null +++ b/.github/wait-for-pypi.sh @@ -0,0 +1,27 @@ +#!/usr/bin/env bash +# +# Wait until the given policyengine release is visible on PyPI, so consumers +# notified afterwards can resolve the exact version. +# +# Environment: +# VERSION Exact policyengine version to wait for. +set -euo pipefail + +if [[ -z "${VERSION:-}" ]]; then + echo "ERROR: VERSION must be set." >&2 + exit 1 +fi + +ATTEMPTS=30 +DELAY=20 +for attempt in $(seq 1 "$ATTEMPTS"); do + if curl -fsSL -o /dev/null "https://pypi.org/pypi/policyengine/${VERSION}/json"; then + echo "policyengine ${VERSION} is visible on PyPI." + exit 0 + fi + echo "Attempt ${attempt}/${ATTEMPTS}: policyengine ${VERSION} not visible on PyPI yet; retrying in ${DELAY}s." + sleep "$DELAY" +done + +echo "ERROR: Timed out waiting for policyengine ${VERSION} on PyPI." >&2 +exit 1 diff --git a/.github/workflows/push.yaml b/.github/workflows/push.yaml index de05657c..b5a51d62 100644 --- a/.github/workflows/push.yaml +++ b/.github/workflows/push.yaml @@ -6,6 +6,9 @@ # # Phase 2 (sentinel commit): Publish to PyPI + GitHub Release # Skips Lint + Test since the code is identical to Phase 1. +# After Publish, NotifyConsumers waits for the release to be visible on +# PyPI and sends a policyengine-release repository dispatch to consumer +# repos so they open version-bump PRs. name: Push to main @@ -139,6 +142,8 @@ jobs: if: github.event.head_commit.message == 'Update package version' env: GH_TOKEN: ${{ github.token }} + outputs: + version: ${{ steps.version.outputs.version }} steps: - name: Checkout repo uses: actions/checkout@v6 @@ -152,6 +157,9 @@ jobs: run: uv pip install -e .[dev] --system - name: Install policyengine run: uv pip install policyengine --system + - name: Capture published version + id: version + run: bash .github/capture-version.sh - name: Publish a git tag run: ".github/publish-git-tag.sh" - name: Build package @@ -183,3 +191,29 @@ jobs: "release-assets/policyengine-bundle-$VERSION.constraints.txt" \ "release-assets/policyengine-bundle-$VERSION.citation.txt" \ "release-assets/policyengine-bundle-$VERSION.verification.json" + + # ── Phase 2: Open consumer update PRs (after Publish) ───── + NotifyConsumers: + name: Open consumer update PRs + runs-on: ubuntu-latest + needs: [Publish] + steps: + - name: Checkout repo + uses: actions/checkout@v6 + - name: Wait for release to be visible on PyPI + env: + VERSION: ${{ needs.Publish.outputs.version }} + run: bash .github/wait-for-pypi.sh + - name: Generate GitHub App token + id: app-token + uses: actions/create-github-app-token@v1 + with: + app-id: ${{ secrets.APP_ID }} + private-key: ${{ secrets.APP_PRIVATE_KEY }} + owner: PolicyEngine + repositories: policyengine-api,policyengine-api-v2 + - name: Dispatch policyengine-release to consumer repos + env: + GH_TOKEN: ${{ steps.app-token.outputs.token }} + VERSION: ${{ needs.Publish.outputs.version }} + run: bash .github/dispatch-policyengine-release.sh diff --git a/changelog.d/notify-consumers-on-release.changed.md b/changelog.d/notify-consumers-on-release.changed.md new file mode 100644 index 00000000..0bd98656 --- /dev/null +++ b/changelog.d/notify-consumers-on-release.changed.md @@ -0,0 +1 @@ +Notify consumer repositories (policyengine-api, policyengine-api-v2) after each PyPI release via a policyengine-release repository dispatch, so they open version-bump PRs without polling PyPI.