Publish Wheels #5
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: | |
| tags: | |
| - "v*.*.*" # Trigger on version tags like v1.0.0 | |
| workflow_dispatch: # Allow manual trigger | |
| # Cancel in-progress runs when a new run is triggered | |
| concurrency: | |
| group: ${{ github.workflow }}-${{ github.ref }} | |
| cancel-in-progress: true | |
| jobs: | |
| # First job: Run tests | |
| test: | |
| name: Test | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 30 | |
| strategy: | |
| matrix: | |
| python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"] | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Set up Python ${{ matrix.python-version }} | |
| uses: actions/setup-python@v5 | |
| with: | |
| python-version: ${{ matrix.python-version }} | |
| - name: Set up Rust | |
| uses: dtolnay/rust-toolchain@v1 | |
| with: | |
| toolchain: stable | |
| - name: Cache Rust dependencies | |
| uses: actions/cache@v4 | |
| with: | |
| path: | | |
| ~/.cargo/registry | |
| ~/.cargo/git | |
| target | |
| key: ${{ runner.os }}-rust-${{ hashFiles('**/Cargo.lock') }} | |
| restore-keys: | | |
| ${{ runner.os }}-rust- | |
| - name: Install uv | |
| uses: astral-sh/setup-uv@v6 | |
| with: | |
| python-version: ${{ matrix.python-version }} | |
| - name: Install dependencies and build | |
| run: | | |
| uv sync --dev | |
| uv run maturin develop | |
| - name: Run tests | |
| run: uv run pytest tests/ -v | |
| # Second job: Security audit | |
| security: | |
| name: Security audit | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 15 | |
| needs: test | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Set up Rust | |
| uses: dtolnay/rust-toolchain@v1 | |
| with: | |
| toolchain: stable | |
| - name: Cache Rust dependencies | |
| uses: actions/cache@v4 | |
| with: | |
| path: | | |
| ~/.cargo/registry | |
| ~/.cargo/git | |
| target | |
| key: ${{ runner.os }}-rust-${{ hashFiles('**/Cargo.lock') }} | |
| restore-keys: | | |
| ${{ runner.os }}-rust- | |
| - name: Cache cargo-audit | |
| uses: actions/cache@v4 | |
| with: | |
| path: ~/.cargo/bin/cargo-audit | |
| key: ${{ runner.os }}-cargo-audit-${{ hashFiles('**/Cargo.lock') }} | |
| restore-keys: | | |
| ${{ runner.os }}-cargo-audit- | |
| - name: Install cargo-audit | |
| run: | | |
| if ! command -v cargo-audit &> /dev/null; then | |
| cargo install cargo-audit | |
| fi | |
| - name: Run security audit | |
| run: | | |
| echo "Running security audit..." | |
| cargo audit --format json > audit-report.json || true | |
| cargo audit | |
| - name: Upload security audit report | |
| if: always() | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: security-audit-report | |
| path: audit-report.json | |
| # Third job: Build wheels for multiple platforms | |
| build: | |
| name: Build wheels | |
| runs-on: ${{ matrix.os }} | |
| timeout-minutes: 45 | |
| needs: [test, security] | |
| strategy: | |
| matrix: | |
| os: [ubuntu-latest, windows-latest, macos-latest] | |
| python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"] | |
| exclude: | |
| # Exclude some combinations to reduce build time | |
| - os: windows-latest | |
| python-version: "3.9" | |
| - os: macos-latest | |
| python-version: "3.9" | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Set up Python ${{ matrix.python-version }} | |
| uses: actions/setup-python@v5 | |
| with: | |
| python-version: ${{ matrix.python-version }} | |
| - name: Set up Rust | |
| uses: dtolnay/rust-toolchain@v1 | |
| with: | |
| toolchain: stable | |
| - name: Cache Rust dependencies | |
| uses: actions/cache@v4 | |
| with: | |
| path: | | |
| ~/.cargo/registry | |
| ~/.cargo/git | |
| target | |
| key: ${{ runner.os }}-rust-${{ hashFiles('**/Cargo.lock') }} | |
| restore-keys: | | |
| ${{ runner.os }}-rust- | |
| - name: Install uv | |
| uses: astral-sh/setup-uv@v6 | |
| with: | |
| python-version: ${{ matrix.python-version }} | |
| - name: Install maturin | |
| run: uv sync --dev | |
| - name: Build wheels | |
| run: | | |
| echo "Building wheels for ${{ matrix.os }} Python ${{ matrix.python-version }}..." | |
| uv run maturin build --release --strip | |
| echo "Build completed successfully!" | |
| ls -la target/wheels/ | |
| - name: Upload wheels | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: wheels-${{ matrix.os }}-${{ matrix.python-version }} | |
| path: target/wheels/*.whl | |
| # Fourth job: Build source distribution | |
| build-sdist: | |
| name: Build source distribution | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 20 | |
| needs: [test, security] | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Set up Python | |
| uses: actions/setup-python@v5 | |
| with: | |
| python-version: "3.11" | |
| - name: Set up Rust | |
| uses: dtolnay/rust-toolchain@v1 | |
| with: | |
| toolchain: stable | |
| - name: Cache Rust dependencies | |
| uses: actions/cache@v4 | |
| with: | |
| path: | | |
| ~/.cargo/registry | |
| ~/.cargo/git | |
| target | |
| key: ${{ runner.os }}-rust-${{ hashFiles('**/Cargo.lock') }} | |
| restore-keys: | | |
| ${{ runner.os }}-rust- | |
| - name: Install uv | |
| uses: astral-sh/setup-uv@v6 | |
| - name: Install maturin | |
| run: uv sync --dev | |
| - name: Build sdist | |
| run: | | |
| echo "Building source distribution..." | |
| uv run maturin sdist | |
| echo "Source distribution built successfully!" | |
| ls -la target/wheels/ | |
| - name: Upload sdist | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: sdist | |
| path: target/wheels/*.tar.gz | |
| # Fifth job: Publish to PyPI | |
| publish: | |
| name: Publish to PyPI | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 20 | |
| needs: [test, security, build, build-sdist] | |
| if: startsWith(github.ref, 'refs/tags/') | |
| environment: | |
| name: pypi | |
| url: https://pypi.org/project/logxide/ | |
| permissions: | |
| id-token: write # For trusted publishing | |
| steps: | |
| - name: Download all artifacts | |
| uses: actions/download-artifact@v4 | |
| with: | |
| path: dist/ | |
| merge-multiple: true | |
| - name: Prepare release assets | |
| run: | | |
| echo "Preparing release assets..." | |
| find dist/ -name "*.whl" -o -name "*.tar.gz" | sort | |
| echo "Total assets: $(find dist/ -type f \( -name "*.whl" -o -name "*.tar.gz" \) | wc -l)" | |
| - name: List artifacts | |
| run: | | |
| echo "Downloaded artifacts:" | |
| find dist/ -type f -name "*.whl" -o -name "*.tar.gz" | sort | |
| echo "Total files: $(find dist/ -type f \( -name "*.whl" -o -name "*.tar.gz" \) | wc -l)" | |
| - name: Validate packages before publishing | |
| run: | | |
| echo "Validating packages before publishing..." | |
| python -m pip install --upgrade uv | |
| uv pip install --system twine | |
| uv run twine check dist/* | |
| echo "✅ All packages passed validation!" | |
| - name: Publish to PyPI | |
| uses: pypa/gh-action-pypi-publish@release/v1 | |
| with: | |
| packages-dir: dist/ | |
| verbose: true | |
| print-hash: true | |
| skip-existing: false | |
| - name: Create job summary | |
| run: | | |
| echo "## 🚀 Publication Summary" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "### 📦 Published Packages" >> $GITHUB_STEP_SUMMARY | |
| echo "| Package | Version | Type |" >> $GITHUB_STEP_SUMMARY | |
| echo "|---------|---------|------|" >> $GITHUB_STEP_SUMMARY | |
| VERSION=${GITHUB_REF#refs/tags/v} | |
| echo "| logxide | $VERSION | PyPI Package |" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "### 🔗 Links" >> $GITHUB_STEP_SUMMARY | |
| echo "- [PyPI Package](https://pypi.org/project/logxide/$VERSION/)" >> $GITHUB_STEP_SUMMARY | |
| echo "- [GitHub Release](https://github.com/${{ github.repository }}/releases/tag/${{ github.ref_name }})" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "### 📊 Build Stats" >> $GITHUB_STEP_SUMMARY | |
| echo "- **Wheels built**: $(find dist/ -name "*.whl" | wc -l)" >> $GITHUB_STEP_SUMMARY | |
| echo "- **Source distribution**: $(find dist/ -name "*.tar.gz" | wc -l)" >> $GITHUB_STEP_SUMMARY | |
| echo "- **Total files**: $(find dist/ -type f | wc -l)" >> $GITHUB_STEP_SUMMARY | |
| - name: Verify PyPI publication | |
| run: | | |
| echo "Waiting for PyPI to update..." | |
| sleep 30 | |
| VERSION=${GITHUB_REF#refs/tags/v} | |
| echo "Checking if logxide version $VERSION is available on PyPI..." | |
| python -c "import requests; r = requests.get('https://pypi.org/pypi/logxide/$VERSION/json'); print('✅ Published successfully!' if r.status_code == 200 else '❌ Not found on PyPI yet')" | |
| # Sixth job: Create GitHub release | |
| release: | |
| name: Create GitHub Release | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 15 | |
| needs: [publish] | |
| if: startsWith(github.ref, 'refs/tags/') | |
| permissions: | |
| contents: write | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Get version from tag | |
| id: get_version | |
| run: echo "VERSION=${GITHUB_REF#refs/tags/v}" >> $GITHUB_OUTPUT | |
| - name: Download all artifacts | |
| uses: actions/download-artifact@v4 | |
| with: | |
| path: dist/ | |
| merge-multiple: true | |
| - name: Create Release | |
| uses: softprops/action-gh-release@v1 | |
| with: | |
| name: LogXide v${{ steps.get_version.outputs.VERSION }} | |
| draft: false | |
| prerelease: false | |
| generate_release_notes: true | |
| files: | | |
| dist/*.whl | |
| body: | | |
| ## LogXide v${{ steps.get_version.outputs.VERSION }} | |
| High-performance, Rust-powered drop-in replacement for Python's logging module. | |
| ### Installation | |
| ```bash | |
| pip install logxide==${{ steps.get_version.outputs.VERSION }} | |
| ``` | |
| ### Quick Start | |
| ```python | |
| import logxide | |
| logxide.install() | |
| import logging | |
| logging.basicConfig(level=logging.INFO) | |
| logger = logging.getLogger(__name__) | |
| logger.info("Hello from LogXide!") | |
| ``` | |
| See the [CHANGELOG](https://github.com/${{ github.repository }}/blob/main/CHANGELOG.md) for detailed release notes. | |
| ### Build Information | |
| - **Platforms**: Ubuntu, Windows, macOS | |
| - **Python versions**: 3.9, 3.10, 3.11, 3.12, 3.13 | |
| - **Security audit**: ✅ Passed | |
| - **Tests**: ✅ All tests passed | |
| - **Package validation**: ✅ All packages validated with twine | |
| - name: Create release summary | |
| run: | | |
| echo "## 🎉 Release Summary" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "### 📋 Release Details" >> $GITHUB_STEP_SUMMARY | |
| echo "- **Version**: ${{ steps.get_version.outputs.VERSION }}" >> $GITHUB_STEP_SUMMARY | |
| echo "- **Tag**: ${{ github.ref_name }}" >> $GITHUB_STEP_SUMMARY | |
| echo "- **Commit**: ${{ github.sha }}" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "### 🔗 Quick Links" >> $GITHUB_STEP_SUMMARY | |
| echo "- [📦 PyPI Package](https://pypi.org/project/logxide/${{ steps.get_version.outputs.VERSION }}/)" >> $GITHUB_STEP_SUMMARY | |
| echo "- [📋 GitHub Release](https://github.com/${{ github.repository }}/releases/tag/${{ github.ref_name }})" >> $GITHUB_STEP_SUMMARY | |
| echo "- [📚 Documentation](https://Indosaram.readthedocs.io/logxide)" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "### ✅ Quality Checks" >> $GITHUB_STEP_SUMMARY | |
| echo "- Security audit: ✅ Passed" >> $GITHUB_STEP_SUMMARY | |
| echo "- All tests: ✅ Passed" >> $GITHUB_STEP_SUMMARY | |
| echo "- Package validation: ✅ Passed" >> $GITHUB_STEP_SUMMARY | |
| echo "- Build verification: ✅ Passed" >> $GITHUB_STEP_SUMMARY | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| # Final job: Send notification | |
| notify: | |
| name: Send Notification | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 5 | |
| needs: [release] | |
| if: always() && startsWith(github.ref, 'refs/tags/') | |
| steps: | |
| - name: Get version from tag | |
| id: get_version | |
| run: echo "VERSION=${GITHUB_REF#refs/tags/v}" >> $GITHUB_OUTPUT | |
| - name: Create final notification | |
| run: | | |
| echo "## 🚀 LogXide v${{ steps.get_version.outputs.VERSION }} Release Complete!" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "### 📊 Workflow Status" >> $GITHUB_STEP_SUMMARY | |
| echo "- **Security**: ${{ needs.release.result == 'success' && '✅ Passed' || '❌ Failed' }}" >> $GITHUB_STEP_SUMMARY | |
| echo "- **Tests**: ${{ needs.release.result == 'success' && '✅ Passed' || '❌ Failed' }}" >> $GITHUB_STEP_SUMMARY | |
| echo "- **Build**: ${{ needs.release.result == 'success' && '✅ Passed' || '❌ Failed' }}" >> $GITHUB_STEP_SUMMARY | |
| echo "- **Publish**: ${{ needs.release.result == 'success' && '✅ Passed' || '❌ Failed' }}" >> $GITHUB_STEP_SUMMARY | |
| echo "- **Release**: ${{ needs.release.result == 'success' && '✅ Passed' || '❌ Failed' }}" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "### 🔗 Installation" >> $GITHUB_STEP_SUMMARY | |
| echo '```bash' >> $GITHUB_STEP_SUMMARY | |
| echo "pip install logxide==${{ steps.get_version.outputs.VERSION }}" >> $GITHUB_STEP_SUMMARY | |
| echo '```' >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "### 📚 Next Steps" >> $GITHUB_STEP_SUMMARY | |
| echo "- Check [PyPI](https://pypi.org/project/logxide/${{ steps.get_version.outputs.VERSION }}/) for package availability" >> $GITHUB_STEP_SUMMARY | |
| echo "- View [GitHub Release](https://github.com/${{ github.repository }}/releases/tag/${{ github.ref_name }}) for detailed notes" >> $GITHUB_STEP_SUMMARY | |
| echo "- Update documentation if needed" >> $GITHUB_STEP_SUMMARY | |
| if [ "${{ needs.release.result }}" == "success" ]; then | |
| echo "✅ All systems go! LogXide v${{ steps.get_version.outputs.VERSION }} is now available!" | |
| else | |
| echo "❌ Release encountered issues. Please check the logs above." | |
| exit 1 | |
| fi |