name: Publish Release
on:
push:
tags:
- 'v*.*.*'
release:
types: [published]
workflow_dispatch:
inputs:
tag:
description: 'Tag to publish (e.g., v1.0.0)'
required: true
type: string
permissions:
contents: write
packages: read
jobs:
wait-for-tests:
name: Wait for Test Completion
runs-on: ubuntu-latest
if: github.repository == 'teknologika/deckbuilder'
steps:
- name: Wait for test workflow
if: github.event_name == 'push'
uses: lewagon/wait-on-check-action@v1.3.1
with:
ref: ${{ github.ref }}
check-name: 'Test Deckbuilder Engine (3.11)'
repo-token: ${{ secrets.GITHUB_TOKEN }}
wait-interval: 30
allowed-conclusions: success
- name: Confirm tests passed
run: |
echo "β
Tests completed successfully, proceeding with release"
publish-pypi:
name: Publish to PyPI
runs-on: ubuntu-latest
needs: wait-for-tests
if: github.repository == 'teknologika/deckbuilder'
steps:
- uses: actions/checkout@v4
- name: Set up Python 3.11
uses: actions/setup-python@v5
with:
python-version: "3.11"
- name: Cache pip dependencies
uses: actions/cache@v4
with:
path: ~/.cache/pip
key: ${{ runner.os }}-pip-release-${{ hashFiles('**/requirements.txt', '**/pyproject.toml') }}
restore-keys: |
${{ runner.os }}-pip-release-
${{ runner.os }}-pip-
- name: Install build dependencies
run: |
python -m pip install --upgrade pip
pip install build twine
- name: Extract version from tag
run: |
if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
# Manual dispatch with tag input
TAG_NAME="${{ github.event.inputs.tag }}"
else
# Automatic tag push
TAG_NAME=${GITHUB_REF#refs/tags/}
fi
# Extract version from tag (v1.0.0 -> 1.0.0)
VERSION=${TAG_NAME#v}
echo "RELEASE_VERSION=${VERSION}" >> $GITHUB_ENV
echo "TAG_NAME=${TAG_NAME}" >> $GITHUB_ENV
echo "π·οΈ Release tag: ${TAG_NAME}"
echo "π¦ Release version: ${VERSION}"
- name: Update version in pyproject.toml
run: |
echo "π§ Updating pyproject.toml with release version ${RELEASE_VERSION}"
sed -i "s/version = \".*\"/version = \"${RELEASE_VERSION}\"/" pyproject.toml
echo "β
Version updated in pyproject.toml"
- name: Verify version update
run: |
echo "π Verifying version in pyproject.toml:"
grep "version = " pyproject.toml
- name: Validate version format
run: |
# Ensure version follows semantic versioning (includes pre-release)
if [[ ! "${RELEASE_VERSION}" =~ ^[0-9]+\.[0-9]+\.[0-9]+(a[0-9]+|b[0-9]+|rc[0-9]+)?$ ]]; then
echo "β Invalid version format: ${RELEASE_VERSION}"
echo "Expected format: X.Y.Z or X.Y.ZbN (e.g., 1.0.0, 1.0.0b1, 1.0.0a1, 1.0.0rc1)"
exit 1
fi
echo "β
Version format validated: ${RELEASE_VERSION}"
- name: Build package
run: |
echo "π¨ Building release package..."
python -m build
echo "π Package contents:"
ls -la dist/
- name: Verify package integrity
run: |
echo "π Checking package integrity..."
twine check dist/*
echo "β
Package integrity verified"
- name: Test package installation
run: |
echo "π§ͺ Testing package installation..."
# Create clean environment for testing
python -m venv release_test_env
source release_test_env/bin/activate
# Install the built package
pip install dist/*.whl
# Test basic functionality
deckbuilder --help > /dev/null
python -c "from deckbuilder.core.engine import get_deckbuilder_client; print('β
Package import test passed')"
# Cleanup
deactivate
rm -rf release_test_env
- name: Publish to PyPI
env:
TWINE_USERNAME: __token__
TWINE_PASSWORD: ${{ secrets.PYPI_API_TOKEN }}
run: |
echo "π Publishing to PyPI..."
twine upload dist/*
echo "β
Successfully published ${RELEASE_VERSION} to PyPI"
- name: Create GitHub Release Notes
if: github.event_name == 'push'
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
echo "π·οΈ Creating GitHub release for ${TAG_NAME}..."
# Create release notes content
cat > release_notes.md << 'EOF'
# Deckbuilder v${{ env.RELEASE_VERSION }}
This release has been automatically published to PyPI.
## Installation
```bash
pip install deckbuilder==${{ env.RELEASE_VERSION }}
```
## Quick Start
```bash
# CLI usage
deckbuilder create presentation.md
deckbuilder --help
# Python usage
from deckbuilder import Deckbuilder
db = Deckbuilder()
```
## Links
- π¦ [PyPI Package](https://pypi.org/project/deckbuilder/${{ env.RELEASE_VERSION }}/)
- π [Documentation](https://github.com/teknologika/deckbuilder#readme)
- π [Report Issues](https://github.com/teknologika/deckbuilder/issues)
---
π€ This release was automatically generated by GitHub Actions.
EOF
# Create the release using GitHub CLI
gh release create "${TAG_NAME}" \
--title "Release ${RELEASE_VERSION}" \
--notes-file release_notes.md \
--latest \
dist/*
echo "β
GitHub release ${TAG_NAME} created successfully"
- name: Upload release artifacts
uses: actions/upload-artifact@v4
with:
name: pypi-release-${{ env.RELEASE_VERSION }}
path: |
dist/
pyproject.toml
retention-days: 90
- name: Generate release summary
run: |
echo "π Production release published successfully!" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "π¦ **Version:** ${RELEASE_VERSION}" >> $GITHUB_STEP_SUMMARY
echo "π **PyPI:** https://pypi.org/project/deckbuilder/${RELEASE_VERSION}/" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "## Installation" >> $GITHUB_STEP_SUMMARY
echo "\`\`\`bash" >> $GITHUB_STEP_SUMMARY
echo "pip install deckbuilder==${RELEASE_VERSION}" >> $GITHUB_STEP_SUMMARY
echo "\`\`\`" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "## CLI Usage" >> $GITHUB_STEP_SUMMARY
echo "\`\`\`bash" >> $GITHUB_STEP_SUMMARY
echo "deckbuilder create presentation.md" >> $GITHUB_STEP_SUMMARY
echo "deckbuilder --help" >> $GITHUB_STEP_SUMMARY
echo "\`\`\`" >> $GITHUB_STEP_SUMMARY
- name: Notify release completion
run: |
echo "π Release ${RELEASE_VERSION} published successfully!"
echo "π¦ PyPI: https://pypi.org/project/deckbuilder/${RELEASE_VERSION}/"
echo "π₯ Install: pip install deckbuilder==${RELEASE_VERSION}"
echo "π GitHub Release: https://github.com/teknologika/deckbuilder/releases/tag/${TAG_NAME}"