Feature/calendar (#4) #20
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: Publish to PyPI | |
| on: | |
| push: | |
| branches: | |
| - main | |
| tags: | |
| - "v[0-9]+.[0-9]+.[0-9]+*" # Triggers on branches AND version tags | |
| permissions: | |
| contents: write # Required for GitHub Releases | |
| id-token: write # Required for PyPI trusted publishing | |
| jobs: | |
| # ------------------------------------------------------------------ | |
| # JOB 1: QUALITY ASSURANCE (runs on branches AND tags) | |
| # ------------------------------------------------------------------ | |
| test: | |
| name: Run Tests | |
| runs-on: ubuntu-latest | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| python-version: ["3.11", "3.12", "3.13"] | |
| steps: | |
| - name: Checkout Code | |
| uses: actions/checkout@v4 | |
| - name: Install uv | |
| uses: astral-sh/setup-uv@v5 | |
| with: | |
| version: "latest" | |
| - name: Set up Python ${{ matrix.python-version }} | |
| run: uv python install ${{ matrix.python-version }} | |
| - name: Install Project & Dependencies | |
| run: uv sync --all-extras --dev | |
| - name: Run Pytest | |
| run: uv run pytest | |
| # ✅ SAFE debug output (never fails the job) | |
| - name: Debug Event Info | |
| run: | | |
| echo "ref: ${{ github.ref }}" | |
| echo "ref_type: ${{ github.ref_type }}" | |
| echo "ref_name: ${{ github.ref_name }}" | |
| if [[ "${{ github.ref_type }}" == "tag" ]]; then | |
| echo "✅ TAG EVENT - Will trigger build/publish" | |
| else | |
| echo "ℹ️ BRANCH EVENT - Skipping build/publish" | |
| fi | |
| # ------------------------------------------------------------------ | |
| # JOB 2: BUILD ARTIFACTS (ONLY on tags) | |
| # ------------------------------------------------------------------ | |
| build: | |
| name: Build Distribution | |
| needs: test | |
| if: github.ref_type == 'tag' # ONLY run on actual tag pushes | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Checkout Code | |
| uses: actions/checkout@v4 | |
| with: | |
| ref: ${{ github.ref }} | |
| - name: Set up Python | |
| uses: actions/setup-python@v5 | |
| with: | |
| python-version: "3.12" | |
| - name: Install Build Tools | |
| run: pip install build hatchling | |
| - name: Build Distributions | |
| run: python -m build | |
| - name: Verify Artifacts | |
| run: ls -lh dist/ | |
| - name: Upload Artifacts | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: python-package-distributions | |
| path: dist/ | |
| retention-days: 7 | |
| # ✅ Version verification ONLY on tags (safe!) | |
| - name: Verify Version Consistency | |
| run: | | |
| # Install package to verify __version__ | |
| pip install dist/*.whl | |
| python -c "from exmailer import __version__; print(f'Version: {__version__}'); assert __version__ != '0.0.0-dev', 'Version is dev build!'" | |
| # ------------------------------------------------------------------ | |
| # JOB 3: CREATE GITHUB RELEASE (ONLY on tags) | |
| # ------------------------------------------------------------------ | |
| create-release: | |
| name: Create GitHub Release | |
| needs: build # ✅ FIXED: depends on actual 'build' job | |
| if: github.ref_type == 'tag' | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Checkout Code | |
| uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 0 # Required for changelog generation | |
| - name: Download Artifacts | |
| uses: actions/download-artifact@v4 | |
| with: | |
| name: python-package-distributions | |
| path: dist/ | |
| - name: Generate Changelog | |
| id: changelog | |
| run: | | |
| PREV_TAG=$(git describe --tags --abbrev=0 --exclude "$(git tag --points-at HEAD)" 2>/dev/null || echo "") | |
| if [ -z "$PREV_TAG" ]; then | |
| echo "body<<EOF" >> $GITHUB_OUTPUT | |
| echo "# 🚀 Initial Release" >> $GITHUB_OUTPUT | |
| echo "" >> $GITHUB_OUTPUT | |
| echo "First stable release of ExMailer." >> $GITHUB_OUTPUT | |
| echo "EOF" >> $GITHUB_OUTPUT | |
| else | |
| echo "body<<EOF" >> $GITHUB_OUTPUT | |
| echo "# 📦 Version ${{ github.ref_name }}" >> $GITHUB_OUTPUT | |
| echo "" >> $GITHUB_OUTPUT | |
| echo "## ✨ Changes since $PREV_TAG" >> $GITHUB_OUTPUT | |
| echo "" >> $GITHUB_OUTPUT | |
| git log $PREV_TAG..HEAD --pretty=format:"- %s" --reverse | \ | |
| grep -E "^(feat|fix|chore|docs|refactor|perf)" | \ | |
| sed -E 's/^(feat.*)/✨ \1/; s/^(fix.*)/🐛 \1/; s/^(docs.*)/📚 \1/; s/^(chore.*)/🔧 \1/; s/^(refactor.*)/♻️ \1/; s/^(perf.*)/⚡ \1/' || echo "- Various improvements" >> $GITHUB_OUTPUT | |
| echo "" >> $GITHUB_OUTPUT | |
| echo "## 📦 Installation" >> $GITHUB_OUTPUT | |
| echo "" >> $GITHUB_OUTPUT | |
| echo '```bash' >> $GITHUB_OUTPUT | |
| echo 'pip install exmailer' >> $GITHUB_OUTPUT | |
| echo '```' >> $GITHUB_OUTPUT | |
| echo "EOF" >> $GITHUB_OUTPUT | |
| fi | |
| - name: Create GitHub Release | |
| uses: softprops/action-gh-release@v2 | |
| with: | |
| tag_name: ${{ github.ref_name }} | |
| name: "ExMailer ${{ github.ref_name }}" | |
| body: ${{ steps.changelog.outputs.body }} | |
| draft: false | |
| prerelease: false | |
| files: | | |
| dist/*.whl | |
| dist/*.tar.gz | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| # ------------------------------------------------------------------ | |
| # JOB 4: PUBLISH TO PYPI (ONLY on tags) | |
| # ------------------------------------------------------------------ | |
| publish: | |
| name: Publish to PyPI | |
| needs: build | |
| if: github.ref_type == 'tag' | |
| runs-on: ubuntu-latest | |
| environment: | |
| name: pypi | |
| url: https://pypi.org/p/exmailer | |
| steps: | |
| - name: Download Artifacts | |
| uses: actions/download-artifact@v4 | |
| with: | |
| name: python-package-distributions | |
| path: dist/ | |
| - name: Publish to PyPI | |
| uses: pypa/gh-action-pypi-publish@release/v1 |