feat: implement comprehensive CI/CD pipeline with two-stage release

- Add ci.yml for continuous integration on every commit
  - Test builds on Ubuntu/macOS with Python 3.9/3.10/3.11
  - Ensure code quality before any release

- Add release-manual.yml for controlled releases
  - Manual trigger prevents accidental releases
  - Version validation and tag creation
  - Optional TestPyPI testing before production
  - Only creates tag after validation passes

- Keep build-and-publish.yml for automated PyPI deployment
  - Triggered by new tags (separation of concerns)
  - Handles multi-platform wheel building
  - Allows retry if PyPI upload fails

- Update RELEASE.md with clear prerequisites and workflow

This setup ensures:
1. Every commit is tested (CI)
2. Releases are deliberate (manual trigger)
3. Failed CI won't create broken tags
4. PyPI publish can be retried independently
This commit is contained in:
Andy Lee
2025-07-24 13:29:21 -07:00
parent e3defbca84
commit 0a17d2c9d8
3 changed files with 266 additions and 9 deletions

124
.github/workflows/release-manual.yml vendored Normal file
View File

@@ -0,0 +1,124 @@
name: Manual Release
on:
workflow_dispatch:
inputs:
version:
description: 'Version to release (e.g., 0.1.1)'
required: true
type: string
test_pypi:
description: 'Test on TestPyPI first'
required: false
type: boolean
default: true
jobs:
validate-and-release:
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- uses: actions/checkout@v4
with:
token: ${{ secrets.GITHUB_TOKEN }}
- name: Check CI status
run: |
echo " This workflow assumes CI has already passed on the current commit."
echo " If not, please wait for CI to complete before releasing."
echo ""
- name: Validate version format
run: |
if ! [[ "${{ inputs.version }}" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
echo "❌ Invalid version format. Use semantic versioning (e.g., 0.1.1)"
exit 1
fi
echo "✅ Version format valid: ${{ inputs.version }}"
- name: Check if version already exists
run: |
if git tag | grep -q "^v${{ inputs.version }}$"; then
echo "❌ Version v${{ inputs.version }} already exists!"
exit 1
fi
echo "✅ Version is new"
- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: '3.11'
- name: Install uv
run: |
curl -LsSf https://astral.sh/uv/install.sh | sh
echo "$HOME/.cargo/bin" >> $GITHUB_PATH
- name: Update versions
run: |
./scripts/bump_version.sh ${{ inputs.version }}
git config user.name "GitHub Actions"
git config user.email "actions@github.com"
git add packages/*/pyproject.toml
git commit -m "chore: release v${{ inputs.version }}"
- name: Build packages for TestPyPI
run: |
echo "🔨 Building packages for testing..."
./scripts/build_and_test.sh all
echo "✅ Build successful! (Already tested on multiple platforms)"
- name: Test on TestPyPI (optional)
if: inputs.test_pypi
env:
TWINE_USERNAME: __token__
TWINE_PASSWORD: ${{ secrets.TEST_PYPI_API_TOKEN }}
run: |
pip install twine
echo "📦 Uploading to TestPyPI..."
twine upload --repository testpypi packages/*/dist/* --verbose
echo "✅ Test upload successful!"
echo "📋 Check packages at: https://test.pypi.org/user/your-username/"
echo ""
echo "To test installation:"
echo "pip install -i https://test.pypi.org/simple/ leann"
- name: Create and push tag
run: |
git tag "v${{ inputs.version }}"
git push origin main
git push origin "v${{ inputs.version }}"
echo "✅ Tag v${{ inputs.version }} created and pushed"
- name: Create GitHub Release
uses: softprops/action-gh-release@v1
with:
tag_name: v${{ inputs.version }}
name: Release v${{ inputs.version }}
body: |
## 🚀 Release v${{ inputs.version }}
### What's Changed
See the [full changelog](https://github.com/${{ github.repository }}/compare/...v${{ inputs.version }})
### Installation
```bash
pip install leann==${{ inputs.version }}
```
### Test Installation (if using TestPyPI)
```bash
pip install -i https://test.pypi.org/simple/ leann==${{ inputs.version }}
```
draft: false
prerelease: false
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Trigger PyPI publish
run: |
echo "🚀 Triggering PyPI publish workflow..."
# The existing build-and-publish.yml will be triggered by the tag push
echo "✅ Release process completed! The publish workflow will run automatically."