Pydantic AI Release Watcher #112
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Pydantic AI Release Watcher | |
| on: | |
| schedule: | |
| - cron: '0 9 * * *' # Every day at 9am UTC | |
| workflow_dispatch: | |
| inputs: | |
| force: | |
| description: 'Force create issue even if version unchanged' | |
| required: false | |
| default: 'false' | |
| jobs: | |
| check-release: | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: read | |
| issues: write | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v6 | |
| - name: Fetch latest pydantic-ai version | |
| id: fetch-version | |
| run: | | |
| LATEST=$(curl -fsSL https://pypi.org/pypi/pydantic-ai/json | python3 -c "import sys,json; print(json.load(sys.stdin)['info']['version'])") | |
| echo "latest=$LATEST" >> "$GITHUB_OUTPUT" | |
| echo "Latest pydantic-ai version: $LATEST" | |
| - name: Read stored version | |
| id: stored-version | |
| run: | | |
| STORED=$(cat .github/pydantic-ai-version.txt | tr -d '[:space:]') | |
| echo "stored=$STORED" >> "$GITHUB_OUTPUT" | |
| echo "Stored version: $STORED" | |
| - name: Check for new release and duplicates | |
| id: check | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| run: | | |
| LATEST="${{ steps.fetch-version.outputs.latest }}" | |
| STORED="${{ steps.stored-version.outputs.stored }}" | |
| FORCE="${{ github.event.inputs.force }}" | |
| if [[ "$LATEST" == "$STORED" && "$FORCE" != "true" ]]; then | |
| echo "No new release (still $LATEST). Exiting." | |
| echo "should_run=false" >> "$GITHUB_OUTPUT" | |
| exit 0 | |
| fi | |
| echo "New release detected: $STORED -> $LATEST" | |
| # Check for existing open issue for this version | |
| EXISTING=$(gh api "repos/${{ github.repository }}/issues?state=open&labels=pydantic-ai-update" \ | |
| --jq ".[] | select(.title | contains(\"v${LATEST}\")) | .number" 2>/dev/null | head -1) | |
| if [[ -n "$EXISTING" ]]; then | |
| echo "Issue already exists for v${LATEST}: #${EXISTING} — skipping." | |
| echo "should_run=false" >> "$GITHUB_OUTPUT" | |
| exit 0 | |
| fi | |
| echo "should_run=true" >> "$GITHUB_OUTPUT" | |
| - name: Analyze release | |
| if: steps.check.outputs.should_run == 'true' | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| run: | | |
| OLD="${{ steps.stored-version.outputs.stored }}" | |
| NEW="${{ steps.fetch-version.outputs.latest }}" | |
| chmod +x .github/scripts/analyze-pydantic-release.sh | |
| .github/scripts/analyze-pydantic-release.sh "$OLD" "$NEW" > /tmp/release-summary.md | |
| - name: Create issue and assign Copilot | |
| if: steps.check.outputs.should_run == 'true' | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| run: | | |
| OLD="${{ steps.stored-version.outputs.stored }}" | |
| NEW="${{ steps.fetch-version.outputs.latest }}" | |
| OLD="$OLD" \ | |
| NEW_VERSION="$NEW" \ | |
| SUMMARY="$(cat /tmp/release-summary.md)" \ | |
| python3 .github/scripts/create-issue-body.py | |
| RESPONSE=$(gh api repos/${{ github.repository }}/issues \ | |
| --method POST \ | |
| --field title="chore: port pydantic-ai v${NEW} changes" \ | |
| --field body=@/tmp/issue-body.md \ | |
| --field labels[]="pydantic-ai-update") | |
| ISSUE_NUMBER=$(echo "$RESPONSE" | python3 -c "import sys,json; print(json.load(sys.stdin)['number'])") | |
| ISSUE_URL=$(echo "$RESPONSE" | python3 -c "import sys,json; print(json.load(sys.stdin)['html_url'])") | |
| echo "Opened issue: $ISSUE_URL" | |
| # Assign Copilot via GraphQL using PAT (GITHUB_TOKEN lacks bot-assignee permission) | |
| ISSUE_NODE_ID=$(echo "$RESPONSE" | python3 -c "import sys,json; print(json.load(sys.stdin)['node_id'])") | |
| GH_TOKEN=${{ secrets.VIBES_GH_PAT }} gh api graphql -f query=" | |
| mutation { | |
| addAssigneesToAssignable(input: { | |
| assignableId: \"${ISSUE_NODE_ID}\", | |
| assigneeIds: [\"BOT_kgDOC9w8XQ\"] | |
| }) { | |
| assignable { ... on Issue { number assignees(first: 3) { nodes { login } } } } | |
| } | |
| } | |
| " |