Skip to content
Merged
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
225 changes: 225 additions & 0 deletions .github/workflows/release-alt.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,225 @@
name: Release to PyPI (Alternative)

on:
push:
branches: [main]
workflow_dispatch:

concurrency:
group: release
cancel-in-progress: false

permissions:
contents: write
pull-requests: read

jobs:
determine-version:
# Only run when the push is a merged PR (has associated PR number),
# or always when triggered manually via workflow_dispatch
if: github.event_name == 'workflow_dispatch' || (github.event.head_commit.message != '' && contains(github.event.head_commit.message, '#'))
runs-on: ubuntu-latest

outputs:
current_version: ${{ steps.current.outputs.version }}
new_version: ${{ steps.newver.outputs.version }}
bump_type: ${{ steps.newver.outputs.bump }}
has_pr: ${{ steps.pr.outputs.number != '' }}

steps:
- uses: actions/checkout@v4

- uses: actions/setup-python@v5
with:
python-version: "3.12"

- name: Find merged PR
id: pr
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
pr_json=$(gh pr list --state merged --base main --json number,title,body,labels --limit 1 --search "${{ github.sha }}")
number=$(echo "$pr_json" | python -c "import json,sys; d=json.load(sys.stdin); print(d[0]['number'] if d else '')")
if [ -z "$number" ]; then
echo "No merged PR found for this push, skipping release"
echo "number=" >> "$GITHUB_OUTPUT"
exit 0
fi
title=$(echo "$pr_json" | python -c "import json,sys; print(json.load(sys.stdin)[0]['title'])")
body=$(echo "$pr_json" | python -c "import json,sys; print(json.load(sys.stdin)[0]['body'])")
labels=$(echo "$pr_json" | python -c "import json,sys; print(json.dumps([l['name'] for l in json.load(sys.stdin)[0]['labels']]))")
echo "number=$number" >> "$GITHUB_OUTPUT"
echo "title=$title" >> "$GITHUB_OUTPUT"
{
echo "body<<PREOF"
echo "$body"
echo "PREOF"
} >> "$GITHUB_OUTPUT"
echo "labels=$labels" >> "$GITHUB_OUTPUT"

- name: Get current version
if: steps.pr.outputs.number != ''
id: current
run: |
version=$(python -c "import tomllib; print(tomllib.load(open('pyproject.toml','rb'))['project']['version'])")
echo "version=$version" >> "$GITHUB_OUTPUT"

- name: Determine version bump from PR labels
if: steps.pr.outputs.number != ''
id: labels
run: |
labels='${{ steps.pr.outputs.labels }}'
if echo "$labels" | grep -q '"major"'; then
echo "bump=major" >> "$GITHUB_OUTPUT"
elif echo "$labels" | grep -q '"minor"'; then
echo "bump=minor" >> "$GITHUB_OUTPUT"
elif echo "$labels" | grep -q '"patch"'; then
echo "bump=patch" >> "$GITHUB_OUTPUT"
else
echo "bump=" >> "$GITHUB_OUTPUT"
fi

- name: Fallback to patch if no label
id: bump
if: steps.pr.outputs.number != '' && steps.labels.outputs.bump == ''
run: |
# Default to patch bump if no version label is found
echo "bump_type=patch" >> "$GITHUB_OUTPUT"
echo "No version label found, defaulting to patch bump"

- name: Calculate new version
if: steps.pr.outputs.number != ''
id: newver
run: |
current="${{ steps.current.outputs.version }}"
bump="${{ steps.labels.outputs.bump || steps.bump.outputs.bump_type }}"
bump="$(echo "$bump" | tr -d '[:space:]' | tr '[:upper:]' '[:lower:]')"

echo "Current version: $current"
echo "Bump type: $bump"

IFS='.' read -r major minor patch <<< "$current"
echo "Parsed version parts - major: $major, minor: $minor, patch: $patch"

case "$bump" in
major)
major=$((major + 1)); minor=0; patch=0
echo "Performing major version bump"
;;
minor)
minor=$((minor + 1)); patch=0
echo "Performing minor version bump"
;;
patch)
patch=$((patch + 1))
echo "Performing patch version bump"
;;
*)
echo "Warning: Unknown bump type '$bump', defaulting to patch"
patch=$((patch + 1))
bump="patch"
;;
esac

new_version="${major}.${minor}.${patch}"
echo "version=$new_version" >> "$GITHUB_OUTPUT"
echo "bump=$bump" >> "$GITHUB_OUTPUT"
echo "Successfully calculated version bump: $current → $new_version ($bump)"

publish:
needs: determine-version
if: needs.determine-version.outputs.new_version != ''
runs-on: ubuntu-latest
environment: pypi

steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
token: ${{ secrets.GITHUB_TOKEN }}

- uses: actions/setup-python@v5
with:
python-version: "3.12"

- name: Install dependencies
run: |
pip install build twine
# Try to install with dev dependencies, fallback to basic install
if pip install -e ".[dev]"; then
echo "Installed with dev dependencies"
else
echo "Dev dependencies not found, installing basic package"
pip install -e .
fi

- name: Install test dependencies
run: |
# Install pytest if not already installed
pip install pytest

- name: Run tests
run: |
# Run tests if they exist
if [ -d "tests" ] && [ "$(ls -A tests/*.py 2>/dev/null)" ]; then
echo "Running tests..."
python -m pytest tests/ -v
else
echo "No tests found, skipping test step"
fi

- name: Update version for build only
run: |
echo "Temporarily updating version in pyproject.toml for build..."
echo "Current version: ${{ needs.determine-version.outputs.current_version }}"
echo "New version: ${{ needs.determine-version.outputs.new_version }}"

# Update version temporarily for building
sed -i "s/^version = \".*\"/version = \"${{ needs.determine-version.outputs.new_version }}\"/" pyproject.toml

# Verify the version was updated
new_version_check=$(python -c "import tomllib; print(tomllib.load(open('pyproject.toml','rb'))['project']['version'])")
echo "Updated version for build: $new_version_check"

- name: Build package
run: |
echo "Building Python package with version ${{ needs.determine-version.outputs.new_version }}..."
python -m build

# Verify build artifacts were created
if [ ! -d "dist" ] || [ -z "$(ls -A dist/)" ]; then
echo "Error: Build failed - no artifacts found in dist/"
exit 1
fi

echo "Build successful. Generated files:"
ls -la dist/

- name: Publish to PyPI
env:
TWINE_USERNAME: __token__
TWINE_PASSWORD: ${{ secrets.PYPI_API_TOKEN }}
run: |
echo "Publishing package to PyPI..."
twine upload dist/* --verbose

- name: Create git tag
run: |
echo "Creating git tag for version ${{ needs.determine-version.outputs.new_version }}..."
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"

git tag "v${{ needs.determine-version.outputs.new_version }}"
git push origin "v${{ needs.determine-version.outputs.new_version }}"

echo "Successfully created and pushed tag v${{ needs.determine-version.outputs.new_version }}"

- name: Create GitHub Release
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
echo "Creating GitHub release..."
gh release create "v${{ needs.determine-version.outputs.new_version }}" \
--title "Release ${{ needs.determine-version.outputs.new_version }}" \
--notes "Release ${{ needs.determine-version.outputs.new_version }}" \
--verify-tag
52 changes: 44 additions & 8 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -204,18 +204,54 @@ jobs:
echo "Publishing package to PyPI..."
twine upload dist/* --verbose

- name: Tag and push version bump
- name: Create version bump pull request
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
echo "Configuring git user..."
echo "Creating version bump pull request..."

# Create a new branch for version bump
version_branch="release/bump-version-${{ needs.determine-version.outputs.new_version }}"
git checkout -b "$version_branch"

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

echo "Committing version bump..."
# Commit version change
git add pyproject.toml
git commit -m "Bump version to ${{ needs.determine-version.outputs.new_version }}"

# Push the branch
git push origin "$version_branch"

# Create pull request and auto-merge it
pr_url=$(gh pr create \
--base main \
--head "$version_branch" \
--title "🚀 Bump version to ${{ needs.determine-version.outputs.new_version }}" \
--body "Automated version bump after release ${{ needs.determine-version.outputs.new_version }} was published to PyPI.")

echo "Created version bump PR: $pr_url"

# Wait a moment for PR to be created
sleep 2

# Auto-merge the PR (requires admin/maintain permissions on the repo)
echo "Attempting to auto-merge version bump PR..."
if gh pr merge "$version_branch" --squash --auto; then
echo "Version bump PR set to auto-merge"
else
echo "Could not auto-merge version bump PR. It will need manual review."
fi

echo "Creating and pushing git tag..."
- name: Create git tag
run: |
echo "Creating git tag for version ${{ needs.determine-version.outputs.new_version }}..."
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"

git tag "v${{ needs.determine-version.outputs.new_version }}"
git push origin main --tags

echo "Successfully tagged and pushed version v${{ needs.determine-version.outputs.new_version }}"
git push origin "v${{ needs.determine-version.outputs.new_version }}"
echo "Successfully created and pushed tag v${{ needs.determine-version.outputs.new_version }}"
Loading
Loading