Skip to content
Open
Show file tree
Hide file tree
Changes from 2 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
299 changes: 299 additions & 0 deletions .github/workflows/bump-version.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,299 @@
name: Bump Version

on:
workflow_dispatch:
inputs:
force_bump_type:
description: 'Force a specific bump type (leave empty for AI auto-detection)'
required: false
type: choice
options:
- ''
- major
- minor
- patch
ai_provider:
description: 'AI provider to use for analysis (default: gemini)'
required: false
type: choice
default: 'gemini'
options:
- gemini
- openai
- anthropic
schedule:
# Run weekly on Monday at 9:00 AM UTC
- cron: '0 9 * * 1'

jobs:
analyze-and-bump:
runs-on: ubuntu-latest
permissions:
contents: write
pull-requests: write
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
fetch-depth: 0 # Need full history to analyze commits
submodules: false

- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.10'

- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install google-generativeai openai anthropic

- name: Get current version
id: current-version
run: |
CURRENT_VERSION=$(cat version.txt | tr -d '[:space:]')
echo "version=${CURRENT_VERSION}" >> $GITHUB_OUTPUT
echo "Current version: ${CURRENT_VERSION}"

- name: Check for existing bump PR
id: check-existing-pr
env:
GH_TOKEN: ${{ github.token }}
run: |
# Check if there's already an open PR for version bump
EXISTING_PR=$(gh pr list --state open --label "version-bump" --json number --jq '.[0].number' || echo "")
if [ -n "$EXISTING_PR" ]; then
echo "has_existing_pr=true" >> $GITHUB_OUTPUT
echo "existing_pr_number=${EXISTING_PR}" >> $GITHUB_OUTPUT
echo "⚠️ Existing version bump PR found: #${EXISTING_PR}"
else
echo "has_existing_pr=false" >> $GITHUB_OUTPUT
echo "βœ“ No existing version bump PR found"
fi
Comment on lines +64 to +72
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Guard against null from gh pr list to avoid false positives.

Current test treats "null" as a PR number. Use // empty.

Apply:

-          EXISTING_PR=$(gh pr list --state open --label "version-bump" --json number --jq '.[0].number' || echo "")
+          EXISTING_PR=$(gh pr list --state open --label "version-bump" --json number --jq '.[0].number // empty' || echo "")
           if [ -n "$EXISTING_PR" ]; then
πŸ“ Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
EXISTING_PR=$(gh pr list --state open --label "version-bump" --json number --jq '.[0].number' || echo "")
if [ -n "$EXISTING_PR" ]; then
echo "has_existing_pr=true" >> $GITHUB_OUTPUT
echo "existing_pr_number=${EXISTING_PR}" >> $GITHUB_OUTPUT
echo "⚠️ Existing version bump PR found: #${EXISTING_PR}"
else
echo "has_existing_pr=false" >> $GITHUB_OUTPUT
echo "βœ“ No existing version bump PR found"
fi
EXISTING_PR=$(gh pr list --state open --label "version-bump" --json number --jq '.[0].number // empty' || echo "")
if [ -n "$EXISTING_PR" ]; then
echo "has_existing_pr=true" >> $GITHUB_OUTPUT
echo "existing_pr_number=${EXISTING_PR}" >> $GITHUB_OUTPUT
echo "⚠️ Existing version bump PR found: #${EXISTING_PR}"
else
echo "has_existing_pr=false" >> $GITHUB_OUTPUT
echo "βœ“ No existing version bump PR found"
fi
πŸ€– Prompt for AI Agents
.github/workflows/bump-version.yml around lines 55 to 63: the current GH CLI jq
expression can return the string "null" which gets treated as a non-empty PR
number; update the jq part to coerce null to an empty string (use .[0].number //
empty) so EXISTING_PR will be empty when no PR exists, leaving the rest of the
if-check unchanged.


- name: Analyze commits with AI
id: analyze
if: steps.check-existing-pr.outputs.has_existing_pr == 'false'
env:
GEMINI_API_KEY: ${{ secrets.GEMINI_API_KEY }}
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
run: |
# Determine which AI provider to use
# Priority: manual input > default (gemini)
if [ -n "${{ inputs.ai_provider }}" ]; then
AI_PROVIDER="${{ inputs.ai_provider }}"
else
AI_PROVIDER="gemini"
fi

echo "Using AI provider: ${AI_PROVIDER}"
echo "Analyzing commits to determine version bump..."

# Run AI analysis with specified provider
python scripts/ai_determine_version_bump.py \
--provider ${AI_PROVIDER} \
Comment on lines +94 to +95
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | πŸ”΄ Critical

🧩 Analysis chain

Verify that the Python script accepts the --provider parameter.

The workflow passes --provider ${AI_PROVIDER} to scripts/ai_determine_version_bump.py. If the script doesn't accept this argument, it will fail with "unrecognized arguments" error.

Run this script to verify the parameter is supported:


🏁 Script executed:

#!/bin/bash
# Check if ai_determine_version_bump.py accepts --provider
if [ -f scripts/ai_determine_version_bump.py ]; then
  python scripts/ai_determine_version_bump.py --help | grep -E '\-\-provider'
  if [ $? -eq 0 ]; then
    echo "βœ“ Script accepts --provider flag"
  else
    echo "βœ— Script does NOT accept --provider flag"
  fi
else
  echo "βœ— Script file not found"
fi

Length of output: 40


Now let me examine the actual Python script and workflow file directly:


🏁 Script executed:

#!/bin/bash
# Check the Python script and workflow file
echo "=== Checking Python script ==="
if [ -f scripts/ai_determine_version_bump.py ]; then
  head -100 scripts/ai_determine_version_bump.py
else
  echo "Script not found"
fi

echo ""
echo "=== Checking workflow context (lines 70-120) ==="
if [ -f .github/workflows/bump-version.yml ]; then
  sed -n '70,120p' .github/workflows/bump-version.yml
else
  echo "Workflow not found"
fi

Length of output: 5241


Let me view the complete Python script to see the argparse definitions:


🏁 Script executed:

#!/bin/bash
# View the full Python script to check argparse setup
cat scripts/ai_determine_version_bump.py | grep -A 50 "argparse\|ArgumentParser\|add_argument" | head -80

Length of output: 2139


Remove unsupported --provider flag; script auto-detects provider from environment variables.

The Python script does not accept --provider argument. It determines the AI provider by checking which API key environment variables are set (GEMINI_API_KEY, ANTHROPIC_API_KEY, OPENAI_API_KEY) and tries them in order: OpenAI β†’ Claude β†’ Gemini β†’ Fallback. Remove the --provider ${AI_PROVIDER} flag from line 95.

Additionally, the 2>&1 redirection on line 97 will mix --verbose stderr output with stdout JSON, corrupting the JSON that jq parses on lines 100-102. Redirect only stdout to the file: > /tmp/analysis.json (without 2>&1).

πŸ€– Prompt for AI Agents
In .github/workflows/bump-version.yml around lines 94–102, the workflow passes
an unsupported --provider ${AI_PROVIDER} flag to the Python script and redirects
stderr into stdout (2>&1), which corrupts the JSON consumed by jq; remove the
entire --provider ${AI_PROVIDER} argument from the python
scripts/ai_determine_version_bump.py invocation and change the output
redirection so only stdout is written to /tmp/analysis.json (remove the 2>&1),
leaving stderr on the console so jq can safely parse the JSON file.

--output-format json \
--verbose > /tmp/analysis.json 2>&1

# Extract results
BUMP_TYPE=$(jq -r '.bump_type' /tmp/analysis.json)
NEW_VERSION=$(jq -r '.new_version' /tmp/analysis.json)
REASONING=$(jq -r '.reasoning' /tmp/analysis.json)

echo "bump_type=${BUMP_TYPE}" >> $GITHUB_OUTPUT
echo "new_version=${NEW_VERSION}" >> $GITHUB_OUTPUT
echo "ai_provider=${AI_PROVIDER}" >> $GITHUB_OUTPUT

# Save reasoning and key changes for PR body
jq -r '.reasoning' /tmp/analysis.json > /tmp/reasoning.txt
jq -r '.key_changes[]' /tmp/analysis.json > /tmp/key_changes.txt || echo "" > /tmp/key_changes.txt

echo "AI Analysis Result (${AI_PROVIDER}):"
echo " Bump type: ${BUMP_TYPE}"
echo " New version: ${NEW_VERSION}"
echo " Reasoning: ${REASONING}"

- name: Override with manual input
id: final-decision
if: steps.check-existing-pr.outputs.has_existing_pr == 'false'
run: |
# Use manual input if provided, otherwise use AI result
if [ -n "${{ inputs.force_bump_type }}" ]; then
BUMP_TYPE="${{ inputs.force_bump_type }}"
echo "Using manual bump type: ${BUMP_TYPE}"

# Calculate new version
CURRENT_VERSION="${{ steps.current-version.outputs.version }}"
if [ "$BUMP_TYPE" = "major" ]; then
MAJOR=$(echo $CURRENT_VERSION | cut -d'.' -f1)
NEW_VERSION="$((MAJOR + 1)).0.0"
elif [ "$BUMP_TYPE" = "minor" ]; then
MAJOR=$(echo $CURRENT_VERSION | cut -d'.' -f1)
MINOR=$(echo $CURRENT_VERSION | cut -d'.' -f2)
NEW_VERSION="${MAJOR}.$((MINOR + 1)).0"
elif [ "$BUMP_TYPE" = "patch" ]; then
MAJOR=$(echo $CURRENT_VERSION | cut -d'.' -f1)
MINOR=$(echo $CURRENT_VERSION | cut -d'.' -f2)
PATCH=$(echo $CURRENT_VERSION | cut -d'.' -f3 | sed 's/[^0-9].*//')
NEW_VERSION="${MAJOR}.${MINOR}.$((PATCH + 1))"
fi

echo "Manual override - reasoning" > /tmp/reasoning.txt
else
BUMP_TYPE="${{ steps.analyze.outputs.bump_type }}"
NEW_VERSION="${{ steps.analyze.outputs.new_version }}"
echo "Using AI-determined bump type: ${BUMP_TYPE}"
fi

echo "bump_type=${BUMP_TYPE}" >> $GITHUB_OUTPUT
echo "new_version=${NEW_VERSION}" >> $GITHUB_OUTPUT

echo "Final decision:"
echo " Bump type: ${BUMP_TYPE}"
echo " New version: ${NEW_VERSION}"

- name: Check if bump needed
id: check-bump
if: steps.check-existing-pr.outputs.has_existing_pr == 'false'
run: |
BUMP_TYPE="${{ steps.final-decision.outputs.bump_type }}"

if [ "$BUMP_TYPE" = "none" ]; then
echo "needs_bump=false" >> $GITHUB_OUTPUT
echo "ℹ️ No version bump needed"
else
echo "needs_bump=true" >> $GITHUB_OUTPUT
echo "βœ“ Version bump needed: ${BUMP_TYPE}"
fi

- name: Update version.txt
if: steps.check-existing-pr.outputs.has_existing_pr == 'false' && steps.check-bump.outputs.needs_bump == 'true'
run: |
NEW_VERSION="${{ steps.final-decision.outputs.new_version }}"
echo "${NEW_VERSION}" > version.txt
echo "βœ“ Updated version.txt to ${NEW_VERSION}"

- name: Generate PR body
id: pr-body
if: steps.check-existing-pr.outputs.has_existing_pr == 'false' && steps.check-bump.outputs.needs_bump == 'true'
run: |
BUMP_TYPE="${{ steps.final-decision.outputs.bump_type }}"
NEW_VERSION="${{ steps.final-decision.outputs.new_version }}"
CURRENT_VERSION="${{ steps.current-version.outputs.version }}"
AI_PROVIDER="${{ steps.analyze.outputs.ai_provider }}"

# Read AI reasoning
REASONING=$(cat /tmp/reasoning.txt || echo "Manual version bump")

# Read key changes
KEY_CHANGES=$(cat /tmp/key_changes.txt || echo "")

# Generate PR body
cat > /tmp/pr_body.md << 'EOF'
## Version Bump: v$CURRENT_VERSION β†’ v$NEW_VERSION

This PR bumps the version from **v$CURRENT_VERSION** to **v$NEW_VERSION** ($BUMP_TYPE_UPPER bump).

### AI Analysis$AI_PROVIDER_TEXT

$REASONING

EOF

if [ -n "$KEY_CHANGES" ]; then
echo "" >> /tmp/pr_body.md
echo "### Key Changes" >> /tmp/pr_body.md
echo "" >> /tmp/pr_body.md
while IFS= read -r change; do
echo "- $change" >> /tmp/pr_body.md
done < /tmp/key_changes.txt
fi

cat >> /tmp/pr_body.md << 'EOF'

### Semantic Versioning Rules

According to our [CONTRIBUTING.md](../blob/main/CONTRIBUTING.md):

- **Major**: Incompatible API changes
- **Minor**: Added functionality that is backwards-compatible
- **Patch**: Backwards-compatible bug fixes

### Next Steps

After merging this PR:
1. Create a git tag: `git tag v$NEW_VERSION`
2. Run the [release workflow](../actions/workflows/release.yml) with tag `v$NEW_VERSION`

---

πŸ€– Auto-generated by [bump-version workflow](../actions/runs/$RUN_ID)
EOF

# Replace variables
sed -i "s/\$CURRENT_VERSION/$CURRENT_VERSION/g" /tmp/pr_body.md
sed -i "s/\$NEW_VERSION/$NEW_VERSION/g" /tmp/pr_body.md
sed -i "s/\$BUMP_TYPE_UPPER/$(echo $BUMP_TYPE | tr '[:lower:]' '[:upper:]')/g" /tmp/pr_body.md
sed -i "s|\$REASONING|$REASONING|g" /tmp/pr_body.md
sed -i "s/\$RUN_ID/${{ github.run_id }}/g" /tmp/pr_body.md

# Add AI provider info if available
if [ -n "$AI_PROVIDER" ]; then
sed -i "s/\$AI_PROVIDER_TEXT/ (using $AI_PROVIDER)/g" /tmp/pr_body.md
else
sed -i "s/\$AI_PROVIDER_TEXT//g" /tmp/pr_body.md
fi

cat /tmp/pr_body.md

- name: Create Pull Request
if: steps.check-existing-pr.outputs.has_existing_pr == 'false' && steps.check-bump.outputs.needs_bump == 'true'
uses: peter-evans/create-pull-request@v6
with:
token: ${{ secrets.FLASHINFER_BOT_TOKEN }}
commit-message: "release: bump version to ${{ steps.final-decision.outputs.new_version }}"
title: "Release: Bump version to v${{ steps.final-decision.outputs.new_version }}"
body-path: /tmp/pr_body.md
branch: bump-version-${{ steps.final-decision.outputs.new_version }}
delete-branch: true
labels: |
version-bump
automated
release
committer: flashinfer-bot <flashinfer-bot@users.noreply.github.com>
author: flashinfer-bot <flashinfer-bot@users.noreply.github.com>

- name: Summary
if: steps.check-existing-pr.outputs.has_existing_pr == 'false'
run: |
echo "## Version Bump Summary" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY

if [ "${{ steps.check-bump.outputs.needs_bump }}" = "true" ]; then
echo "βœ… Version bump PR created" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "- **Current version**: ${{ steps.current-version.outputs.version }}" >> $GITHUB_STEP_SUMMARY
echo "- **New version**: ${{ steps.final-decision.outputs.new_version }}" >> $GITHUB_STEP_SUMMARY
echo "- **Bump type**: ${{ steps.final-decision.outputs.bump_type }}" >> $GITHUB_STEP_SUMMARY

# Add AI provider info if available
if [ -n "${{ steps.analyze.outputs.ai_provider }}" ]; then
echo "- **AI provider**: ${{ steps.analyze.outputs.ai_provider }}" >> $GITHUB_STEP_SUMMARY
fi
else
echo "ℹ️ No version bump needed" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "No significant changes detected since the last release." >> $GITHUB_STEP_SUMMARY
fi

- name: Already has open PR
if: steps.check-existing-pr.outputs.has_existing_pr == 'true'
run: |
echo "## Version Bump Summary" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "⚠️ A version bump PR already exists: #${{ steps.check-existing-pr.outputs.existing_pr_number }}" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "Please review and merge the existing PR before creating a new one." >> $GITHUB_STEP_SUMMARY

exit 0
Loading