From 0c31e91b537ff7c0293500243b4d260764f71fd5 Mon Sep 17 00:00:00 2001 From: UncleCode Date: Thu, 24 Jul 2025 20:58:43 +0800 Subject: [PATCH] feat: Add CI/CD workflows for automated PyPI and Docker releases --- .github/workflows/release.yml | 137 +++++++++++++++++++++++++++++ .github/workflows/test-release.yml | 103 ++++++++++++++++++++++ 2 files changed, 240 insertions(+) create mode 100644 .github/workflows/release.yml create mode 100644 .github/workflows/test-release.yml diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 00000000..bf13a870 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,137 @@ +name: Release Pipeline +on: + push: + tags: + - 'v*' + - '!test-v*' # Exclude test tags + +jobs: + release: + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: '3.12' + + - name: Extract version from tag + id: get_version + run: | + TAG_VERSION=${GITHUB_REF#refs/tags/v} + echo "VERSION=$TAG_VERSION" >> $GITHUB_OUTPUT + echo "Releasing version: $TAG_VERSION" + + - name: Check version consistency + run: | + TAG_VERSION=${{ steps.get_version.outputs.VERSION }} + PACKAGE_VERSION=$(python -c "from crawl4ai.__version__ import __version__; print(__version__)") + + echo "Tag version: $TAG_VERSION" + echo "Package version: $PACKAGE_VERSION" + + if [ "$TAG_VERSION" != "$PACKAGE_VERSION" ]; then + echo "โŒ Version mismatch! Tag: $TAG_VERSION, Package: $PACKAGE_VERSION" + echo "Please update crawl4ai/__version__.py to match the tag version" + exit 1 + fi + echo "โœ… Version check passed: $TAG_VERSION" + + - name: Install build dependencies + run: | + python -m pip install --upgrade pip + pip install build twine + + - name: Build package + run: python -m build + + - name: Check package + run: twine check dist/* + + - name: Upload to PyPI + env: + TWINE_USERNAME: __token__ + TWINE_PASSWORD: ${{ secrets.PYPI_TOKEN }} + run: | + echo "๐Ÿ“ฆ Uploading to PyPI..." + twine upload dist/* + echo "โœ… Package uploaded to https://pypi.org/project/crawl4ai/" + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Log in to Docker Hub + uses: docker/login-action@v3 + with: + username: ${{ secrets.DOCKER_USERNAME }} + password: ${{ secrets.DOCKER_TOKEN }} + + - name: Extract major and minor versions + id: versions + run: | + VERSION=${{ steps.get_version.outputs.VERSION }} + MAJOR=$(echo $VERSION | cut -d. -f1) + MINOR=$(echo $VERSION | cut -d. -f1-2) + echo "MAJOR=$MAJOR" >> $GITHUB_OUTPUT + echo "MINOR=$MINOR" >> $GITHUB_OUTPUT + + - name: Build and push Docker images + uses: docker/build-push-action@v5 + with: + context: . + push: true + tags: | + ${{ secrets.DOCKER_USERNAME }}/crawl4ai:${{ steps.get_version.outputs.VERSION }} + ${{ secrets.DOCKER_USERNAME }}/crawl4ai:${{ steps.versions.outputs.MINOR }} + ${{ secrets.DOCKER_USERNAME }}/crawl4ai:${{ steps.versions.outputs.MAJOR }} + ${{ secrets.DOCKER_USERNAME }}/crawl4ai:latest + platforms: linux/amd64,linux/arm64 + + - name: Create GitHub Release + uses: actions/create-release@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + tag_name: v${{ steps.get_version.outputs.VERSION }} + release_name: Release v${{ steps.get_version.outputs.VERSION }} + body: | + ## ๐ŸŽ‰ Crawl4AI v${{ steps.get_version.outputs.VERSION }} Released! + + ### ๐Ÿ“ฆ Installation + + **PyPI:** + ```bash + pip install crawl4ai==${{ steps.get_version.outputs.VERSION }} + ``` + + **Docker:** + ```bash + docker pull ${{ secrets.DOCKER_USERNAME }}/crawl4ai:${{ steps.get_version.outputs.VERSION }} + docker pull ${{ secrets.DOCKER_USERNAME }}/crawl4ai:latest + ``` + + ### ๐Ÿ“ What's Changed + See [CHANGELOG.md](https://github.com/${{ github.repository }}/blob/main/CHANGELOG.md) for details. + draft: false + prerelease: false + + - name: Summary + run: | + echo "## ๐Ÿš€ Release Complete!" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "### ๐Ÿ“ฆ PyPI Package" >> $GITHUB_STEP_SUMMARY + echo "- Version: ${{ steps.get_version.outputs.VERSION }}" >> $GITHUB_STEP_SUMMARY + echo "- URL: https://pypi.org/project/crawl4ai/" >> $GITHUB_STEP_SUMMARY + echo "- Install: \`pip install crawl4ai==${{ steps.get_version.outputs.VERSION }}\`" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "### ๐Ÿณ Docker Images" >> $GITHUB_STEP_SUMMARY + echo "- \`${{ secrets.DOCKER_USERNAME }}/crawl4ai:${{ steps.get_version.outputs.VERSION }}\`" >> $GITHUB_STEP_SUMMARY + echo "- \`${{ secrets.DOCKER_USERNAME }}/crawl4ai:${{ steps.versions.outputs.MINOR }}\`" >> $GITHUB_STEP_SUMMARY + echo "- \`${{ secrets.DOCKER_USERNAME }}/crawl4ai:${{ steps.versions.outputs.MAJOR }}\`" >> $GITHUB_STEP_SUMMARY + echo "- \`${{ secrets.DOCKER_USERNAME }}/crawl4ai:latest\`" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "### ๐Ÿ“‹ GitHub Release" >> $GITHUB_STEP_SUMMARY + echo "https://github.com/${{ github.repository }}/releases/tag/v${{ steps.get_version.outputs.VERSION }}" >> $GITHUB_STEP_SUMMARY \ No newline at end of file diff --git a/.github/workflows/test-release.yml b/.github/workflows/test-release.yml new file mode 100644 index 00000000..c7ddf623 --- /dev/null +++ b/.github/workflows/test-release.yml @@ -0,0 +1,103 @@ +name: Test Release Pipeline +on: + push: + tags: + - 'test-v*' + +jobs: + test-release: + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: '3.12' + + - name: Extract version from tag + id: get_version + run: | + TAG_VERSION=${GITHUB_REF#refs/tags/test-v} + echo "VERSION=$TAG_VERSION" >> $GITHUB_OUTPUT + echo "Testing with version: $TAG_VERSION" + + - name: Check version consistency + run: | + TAG_VERSION=${{ steps.get_version.outputs.VERSION }} + PACKAGE_VERSION=$(python -c "from crawl4ai.__version__ import __version__; print(__version__)") + + echo "Tag version: $TAG_VERSION" + echo "Package version: $PACKAGE_VERSION" + + if [ "$TAG_VERSION" != "$PACKAGE_VERSION" ]; then + echo "โŒ Version mismatch! Tag: $TAG_VERSION, Package: $PACKAGE_VERSION" + echo "Please update crawl4ai/__version__.py to match the tag version" + exit 1 + fi + echo "โœ… Version check passed: $TAG_VERSION" + + - name: Install build dependencies + run: | + python -m pip install --upgrade pip + pip install build twine + + - name: Build package + run: python -m build + + - name: Check package + run: twine check dist/* + + - name: Upload to Test PyPI + env: + TWINE_USERNAME: __token__ + TWINE_PASSWORD: ${{ secrets.TEST_PYPI_TOKEN }} + run: | + echo "๐Ÿ“ฆ Uploading to Test PyPI..." + twine upload --repository testpypi dist/* + echo "โœ… Package uploaded to https://test.pypi.org/project/crawl4ai/" + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Log in to Docker Hub + uses: docker/login-action@v3 + with: + username: ${{ secrets.DOCKER_USERNAME }} + password: ${{ secrets.DOCKER_TOKEN }} + + - name: Build and push Docker test images + uses: docker/build-push-action@v5 + with: + context: . + push: true + tags: | + ${{ secrets.DOCKER_USERNAME }}/crawl4ai:test-${{ steps.get_version.outputs.VERSION }} + ${{ secrets.DOCKER_USERNAME }}/crawl4ai:test-latest + platforms: linux/amd64,linux/arm64 + + - name: Summary + run: | + echo "## ๐ŸŽ‰ Test Release Complete!" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "### ๐Ÿ“ฆ Test PyPI Package" >> $GITHUB_STEP_SUMMARY + echo "- Version: ${{ steps.get_version.outputs.VERSION }}" >> $GITHUB_STEP_SUMMARY + echo "- URL: https://test.pypi.org/project/crawl4ai/" >> $GITHUB_STEP_SUMMARY + echo "- Install: \`pip install -i https://test.pypi.org/simple/ crawl4ai==${{ steps.get_version.outputs.VERSION }}\`" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "### ๐Ÿณ Docker Test Images" >> $GITHUB_STEP_SUMMARY + echo "- \`${{ secrets.DOCKER_USERNAME }}/crawl4ai:test-${{ steps.get_version.outputs.VERSION }}\`" >> $GITHUB_STEP_SUMMARY + echo "- \`${{ secrets.DOCKER_USERNAME }}/crawl4ai:test-latest\`" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "### ๐Ÿงน Cleanup Commands" >> $GITHUB_STEP_SUMMARY + echo "\`\`\`bash" >> $GITHUB_STEP_SUMMARY + echo "# Remove test tag" >> $GITHUB_STEP_SUMMARY + echo "git tag -d test-v${{ steps.get_version.outputs.VERSION }}" >> $GITHUB_STEP_SUMMARY + echo "git push origin :test-v${{ steps.get_version.outputs.VERSION }}" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "# Remove Docker test images" >> $GITHUB_STEP_SUMMARY + echo "docker rmi ${{ secrets.DOCKER_USERNAME }}/crawl4ai:test-${{ steps.get_version.outputs.VERSION }}" >> $GITHUB_STEP_SUMMARY + echo "docker rmi ${{ secrets.DOCKER_USERNAME }}/crawl4ai:test-latest" >> $GITHUB_STEP_SUMMARY + echo "\`\`\`" >> $GITHUB_STEP_SUMMARY \ No newline at end of file