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 actions: read steps: - uses: actions/checkout@v4 with: token: ${{ secrets.GITHUB_TOKEN }} - name: Check CI status run: | echo "ℹ️ This workflow will download build artifacts from the latest CI run." echo " CI must have completed successfully on the current commit." 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@v5 with: python-version: '3.13' - 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: Get CI run ID id: get-ci-run run: | # Get the latest successful CI run on the previous commit (before version bump) COMMIT_SHA=$(git rev-parse HEAD~1) RUN_ID=$(gh run list \ --workflow="CI - Build and Test" \ --status=success \ --commit=$COMMIT_SHA \ --json databaseId \ --jq '.[0].databaseId') if [ -z "$RUN_ID" ]; then echo "❌ No successful CI run found for commit $COMMIT_SHA" echo "" echo "This usually means:" echo "1. CI hasn't run on the latest commit yet" echo "2. CI failed on the latest commit" echo "" echo "Please ensure CI passes on main branch before releasing." exit 1 fi echo "✅ Found CI run: $RUN_ID" echo "run-id=$RUN_ID" >> $GITHUB_OUTPUT env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - name: Download artifacts from CI run: | echo "📦 Downloading artifacts from CI run ${{ steps.get-ci-run.outputs.run-id }}..." # Download all wheel artifacts gh run download ${{ steps.get-ci-run.outputs.run-id }} \ --pattern "wheels-*" \ --dir ./dist-downloads # Consolidate all wheels into packages/*/dist/ mkdir -p packages/leann-core/dist mkdir -p packages/leann-backend-hnsw/dist mkdir -p packages/leann-backend-diskann/dist mkdir -p packages/leann/dist find ./dist-downloads -name "*.whl" -exec cp {} ./packages/ \; # Move wheels to correct package directories for wheel in packages/*.whl; do if [[ $wheel == *"leann_core"* ]]; then mv "$wheel" packages/leann-core/dist/ elif [[ $wheel == *"leann_backend_hnsw"* ]]; then mv "$wheel" packages/leann-backend-hnsw/dist/ elif [[ $wheel == *"leann_backend_diskann"* ]]; then mv "$wheel" packages/leann-backend-diskann/dist/ elif [[ $wheel == *"leann-"* ]] && [[ $wheel != *"backend"* ]] && [[ $wheel != *"core"* ]]; then mv "$wheel" packages/leann/dist/ fi done # List downloaded wheels echo "✅ Downloaded wheels:" find packages/*/dist -name "*.whl" -type f | sort env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - name: Test on TestPyPI (optional) if: inputs.test_pypi continue-on-error: true env: TWINE_USERNAME: __token__ TWINE_PASSWORD: ${{ secrets.TEST_PYPI_API_TOKEN }} run: | if [ -z "$TWINE_PASSWORD" ]; then echo "⚠️ TEST_PYPI_API_TOKEN not configured, skipping TestPyPI upload" echo " To enable TestPyPI testing, add TEST_PYPI_API_TOKEN to repository secrets" exit 0 fi pip install twine echo "📦 Uploading to TestPyPI..." twine upload --repository testpypi packages/*/dist/* --verbose || { echo "⚠️ TestPyPI upload failed, but continuing with release" echo " This is optional and won't block the release" exit 0 } 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."