Skip to content
Open
Show file tree
Hide file tree
Changes from all 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
98 changes: 98 additions & 0 deletions .github/workflows/auto-tag-and-release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
name: Auto Tag and Release

on:
pull_request:
Copy link
Contributor

Choose a reason for hiding this comment

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

I would be careful with automatically tagging. We want to make sure the tagged commit has a clean internal CI.

Copy link
Contributor

Choose a reason for hiding this comment

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

We could read a summary of the CI report and map it to the commit.

Copy link
Collaborator

Choose a reason for hiding this comment

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

The automatic tagging seems to happen if and only if a PR with a version-bump label gets merged, which should only get merged after the CI has been reviewed. The PR seems to remove the additional step of manually tagging & releasing.

Usually a version bump PR like 1921 is more of an empty PR that updates version.txt -- the internal CI should have been triggered on the PR and the report should have already been reviewed before checking the PR in.

Copy link
Contributor

Choose a reason for hiding this comment

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

Agreed, but I believe the thinking is that we can have PRs in between the version bump and the actual tagged version, for agility reasons.

types: [closed]
branches:
- main

jobs:
auto-tag-and-release:
# Only run if the PR was merged and has the version-bump label
if: github.event.pull_request.merged == true && contains(github.event.pull_request.labels.*.name, 'version-bump')
runs-on: ubuntu-latest
permissions:
contents: write
actions: write
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
ref: main
fetch-depth: 0

Comment on lines +19 to +23
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

Fetch tags to make tag-existence check reliable.

Without fetching tags, local git may miss remote tags. Fetch tags explicitly.

       uses: actions/checkout@v4
       with:
         ref: main
-        fetch-depth: 0
+        fetch-depth: 0
+        fetch-tags: true
πŸ“ 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
uses: actions/checkout@v4
with:
ref: main
fetch-depth: 0
uses: actions/checkout@v4
with:
ref: main
fetch-depth: 0
fetch-tags: true
πŸ€– Prompt for AI Agents
.github/workflows/auto-tag-and-release.yml around lines 19-23: the workflow
currently checks out the repo but doesn't guarantee remote tags are present
locally; update the checkout step to explicitly fetch tags (or add a short step
immediately after checkout to run a git fetch --tags) so subsequent
tag-existence checks are reliable. Ensure the change either enables the checkout
input that fetches tags or adds a dedicated git fetch tags step.

- name: Get version from version.txt
id: get-version
run: |
VERSION=$(cat version.txt | tr -d '[:space:]')
echo "version=${VERSION}" >> $GITHUB_OUTPUT
echo "tag=v${VERSION}" >> $GITHUB_OUTPUT
echo "Version from version.txt: ${VERSION}"
echo "Will create tag: v${VERSION}"

- name: Check if tag already exists
id: check-tag
run: |
TAG="v${{ steps.get-version.outputs.version }}"
if git rev-parse "$TAG" >/dev/null 2>&1; then
echo "exists=true" >> $GITHUB_OUTPUT
echo "⚠️ Tag $TAG already exists"
else
echo "exists=false" >> $GITHUB_OUTPUT
echo "βœ“ Tag $TAG does not exist yet"
fi
Comment on lines +33 to +43
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

Check tag existence on remote, not only locally.

git rev-parse can miss remote tags and lead to duplicate tag attempts.

-      - name: Check if tag already exists
+      - name: Check if tag already exists (remote)
         id: check-tag
         run: |
           TAG="v${{ steps.get-version.outputs.version }}"
-          if git rev-parse "$TAG" >/dev/null 2>&1; then
+          if git ls-remote --tags origin "refs/tags/$TAG" | grep -q "$TAG"; then
             echo "exists=true" >> $GITHUB_OUTPUT
             echo "⚠️  Tag $TAG already exists"
           else
             echo "exists=false" >> $GITHUB_OUTPUT
             echo "βœ“ Tag $TAG does not exist yet"
           fi
πŸ€– Prompt for AI Agents
.github/workflows/auto-tag-and-release.yml around lines 33 to 43: the current
check uses git rev-parse which only inspects local refs and can miss tags that
exist on the remote; update the step to check the remote (either run git fetch
--tags --no-recurse-submodules first, then test with git rev-parse, or call git
ls-remote --tags origin "refs/tags/$TAG" and treat a non-empty result as
existing) and write exists=true/false to $GITHUB_OUTPUT accordingly, preserving
the existing log messages.


- name: Create and push tag
if: steps.check-tag.outputs.exists == 'false'
env:
GH_TOKEN: ${{ github.token }}
run: |
TAG="v${{ steps.get-version.outputs.version }}"

# Configure git
git config --local user.name "github-actions[bot]"
git config --local user.email "41898282+github-actions[bot]@users.noreply.github.com"

# Create annotated tag
git tag -a "$TAG" -m "Release $TAG"

# Push tag
git push origin "$TAG"

echo "βœ… Created and pushed tag: $TAG"

- name: Trigger release workflow
if: steps.check-tag.outputs.exists == 'false'
env:
GH_TOKEN: ${{ secrets.FLASHINFER_BOT_TOKEN }}
run: |
TAG="v${{ steps.get-version.outputs.version }}"

# Trigger the release workflow
gh workflow run release.yml \
-f tag="$TAG"

echo "βœ… Triggered release workflow for tag: $TAG"

- name: Summary
run: |
echo "## Auto Tag and Release Summary" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY

if [ "${{ steps.check-tag.outputs.exists }}" = "true" ]; then
echo "⚠️ Tag v${{ steps.get-version.outputs.version }} already exists, skipped" >> $GITHUB_STEP_SUMMARY
else
echo "βœ… Successfully created tag and triggered release workflow" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "- **Version**: ${{ steps.get-version.outputs.version }}" >> $GITHUB_STEP_SUMMARY
echo "- **Tag**: v${{ steps.get-version.outputs.version }}" >> $GITHUB_STEP_SUMMARY
echo "- **PR**: #${{ github.event.pull_request.number }}" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "πŸ”— [View Release Workflow Runs](../../actions/workflows/release.yml)" >> $GITHUB_STEP_SUMMARY
fi

- name: Tag already exists
if: steps.check-tag.outputs.exists == 'true'
run: |
echo "Tag v${{ steps.get-version.outputs.version }} already exists, skipping tag creation and release"
exit 0
243 changes: 243 additions & 0 deletions .github/workflows/bump-version.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,243 @@
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: |
python scripts/generate_bump_pr_body.py \
--current-version "${{ steps.current-version.outputs.version }}" \
--new-version "${{ steps.final-decision.outputs.new_version }}" \
--bump-type "${{ steps.final-decision.outputs.bump_type }}" \
--ai-provider "${{ steps.analyze.outputs.ai_provider }}" \
--reasoning-file /tmp/reasoning.txt \
--key-changes-file /tmp/key_changes.txt \
--run-id "${{ github.run_id }}" \
--output /tmp/pr_body.md

echo "Generated PR body:"
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 <[email protected]>
author: flashinfer-bot <[email protected]>

- 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