Feature/improved auto workflows #3
Workflow file for this run
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: Build & Deploy | |
| on: | |
| push: | |
| branches: | |
| - main | |
| pull_request: | |
| branches: | |
| - main | |
| workflow_dispatch: | |
| inputs: | |
| promote: | |
| description: 'Promote to production tags after build' | |
| required: false | |
| type: boolean | |
| default: false | |
| jobs: | |
| build: | |
| name: Build Images | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: read | |
| packages: write | |
| strategy: | |
| matrix: | |
| include: | |
| - python_version: "3.9" | |
| debian_version: "bullseye" | |
| - python_version: "3.10" | |
| debian_version: "bullseye" | |
| - python_version: "3.11" | |
| debian_version: "bookworm" | |
| - python_version: "3.12" | |
| debian_version: "bookworm" | |
| - python_version: "3.13" | |
| debian_version: "bookworm" | |
| - python_version: "3.14" | |
| debian_version: "bookworm" | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@v6 | |
| - name: Set up QEMU | |
| uses: docker/setup-qemu-action@v3 | |
| with: | |
| platforms: all | |
| - name: Set up Docker Buildx | |
| uses: docker/setup-buildx-action@v3 | |
| - name: Log in to GitHub Container Registry | |
| uses: docker/login-action@v3 | |
| with: | |
| registry: ghcr.io | |
| username: ${{ github.actor }} | |
| password: ${{ secrets.GITHUB_TOKEN }} | |
| - name: Check if image already exists | |
| id: check | |
| run: | | |
| IMAGE="ghcr.io/${{ github.repository_owner }}/python-container-builder:${{ matrix.python_version }}-${{ github.sha }}" | |
| echo "Checking if ${IMAGE} already exists..." | |
| if docker manifest inspect "${IMAGE}" >/dev/null 2>&1; then | |
| echo "✅ Image already exists, skipping build" | |
| echo "exists=true" >> $GITHUB_OUTPUT | |
| else | |
| echo "❌ Image does not exist, will build" | |
| echo "exists=false" >> $GITHUB_OUTPUT | |
| fi | |
| - name: Extract metadata | |
| if: steps.check.outputs.exists == 'false' | |
| id: meta | |
| uses: docker/metadata-action@v5 | |
| with: | |
| images: ghcr.io/${{ github.repository_owner }}/python-container-builder | |
| tags: | | |
| type=sha,prefix=${{ matrix.python_version }}-,format=long | |
| type=ref,event=pr,prefix=pr-,suffix=-${{ matrix.python_version }} | |
| - name: Build and push Docker images | |
| if: steps.check.outputs.exists == 'false' | |
| uses: docker/build-push-action@v6 | |
| with: | |
| context: . | |
| file: Dockerfile | |
| platforms: linux/amd64,linux/arm64 | |
| push: true | |
| build-args: | | |
| DEBIAN_VERSION=${{ matrix.debian_version }} | |
| PYTHON_VERSION=${{ matrix.python_version }} | |
| tags: ${{ steps.meta.outputs.tags }} | |
| labels: ${{ steps.meta.outputs.labels }} | |
| cache-from: type=gha,scope=python-${{ matrix.python_version }} | |
| cache-to: type=gha,mode=max,scope=python-${{ matrix.python_version }} | |
| provenance: true | |
| sbom: true | |
| outputs: type=image,name=python-container-builder,annotation-index.org.opencontainers.image.description=Build your Python distroless containers with this | |
| promote: | |
| name: Promote to Production | |
| runs-on: ubuntu-latest | |
| needs: [build, test, security-scan] | |
| # Promote on: | |
| # 1. Normal merge to main (not force push) | |
| # 2. Manual workflow dispatch with promote flag enabled | |
| # CRITICAL: Only runs if build, test, AND security-scan all succeed | |
| if: | | |
| (github.event_name == 'push' && github.ref == 'refs/heads/main' && !github.event.forced) || | |
| (github.event_name == 'workflow_dispatch' && inputs.promote == true) | |
| permissions: | |
| contents: read | |
| packages: write | |
| strategy: | |
| matrix: | |
| python_version: ["3.9", "3.10", "3.11", "3.12", "3.13", "3.14"] | |
| steps: | |
| - name: Log in to GitHub Container Registry | |
| uses: docker/login-action@v3 | |
| with: | |
| registry: ghcr.io | |
| username: ${{ github.actor }} | |
| password: ${{ secrets.GITHUB_TOKEN }} | |
| - name: Promote commit SHA to version tag | |
| run: | | |
| # Get the full commit SHA | |
| COMMIT_SHA="${{ github.sha }}" | |
| # Source image with commit SHA | |
| SOURCE_IMAGE="ghcr.io/${{ github.repository_owner }}/python-container-builder:${{ matrix.python_version }}-${COMMIT_SHA}" | |
| # Destination tags | |
| VERSION_TAG="ghcr.io/${{ github.repository_owner }}/python-container-builder:${{ matrix.python_version }}" | |
| echo "Promoting ${SOURCE_IMAGE} to ${VERSION_TAG}" | |
| # Re-tag the existing image (no rebuild) | |
| docker buildx imagetools create \ | |
| "${SOURCE_IMAGE}" \ | |
| --tag "${VERSION_TAG}" | |
| echo "✅ Successfully promoted ${{ matrix.python_version }} to production" | |
| - name: Promote latest tag | |
| if: matrix.python_version == '3.14' | |
| run: | | |
| COMMIT_SHA="${{ github.sha }}" | |
| SOURCE_IMAGE="ghcr.io/${{ github.repository_owner }}/python-container-builder:${{ matrix.python_version }}-${COMMIT_SHA}" | |
| LATEST_TAG="ghcr.io/${{ github.repository_owner }}/python-container-builder:latest" | |
| echo "Promoting ${SOURCE_IMAGE} to ${LATEST_TAG}" | |
| docker buildx imagetools create \ | |
| "${SOURCE_IMAGE}" \ | |
| --tag "${LATEST_TAG}" | |
| echo "✅ Successfully promoted latest tag" | |
| security-scan: | |
| name: Security Scan | |
| runs-on: ubuntu-latest | |
| needs: build | |
| permissions: | |
| contents: read | |
| security-events: write | |
| steps: | |
| - name: Run Trivy vulnerability scanner | |
| uses: aquasecurity/trivy-action@master | |
| with: | |
| image-ref: ghcr.io/${{ github.repository_owner }}/python-container-builder:3.14-${{ github.sha }} | |
| format: 'sarif' | |
| output: 'trivy-results.sarif' | |
| severity: 'CRITICAL,HIGH' | |
| - name: Upload Trivy results to GitHub Security | |
| uses: github/codeql-action/upload-sarif@v4 | |
| if: always() | |
| with: | |
| sarif_file: 'trivy-results.sarif' | |
| - name: Print Trivy results summary | |
| uses: aquasecurity/trivy-action@master | |
| if: always() | |
| with: | |
| image-ref: ghcr.io/${{ github.repository_owner }}/python-container-builder:3.14-${{ github.sha }} | |
| format: 'table' | |
| severity: 'CRITICAL,HIGH' | |
| test: | |
| name: Test Images | |
| runs-on: ubuntu-latest | |
| needs: build | |
| permissions: | |
| contents: read | |
| strategy: | |
| matrix: | |
| python_version: ["3.9", "3.10", "3.11", "3.12", "3.13", "3.14"] | |
| steps: | |
| - name: Test Python version | |
| run: | | |
| docker run --rm ghcr.io/${{ github.repository_owner }}/python-container-builder:${{ matrix.python_version }}-${{ github.sha }} python --version | |
| - name: Test uv is installed | |
| run: | | |
| docker run --rm ghcr.io/${{ github.repository_owner }}/python-container-builder:${{ matrix.python_version }}-${{ github.sha }} uv --version | |
| - name: Test poetry is installed | |
| run: | | |
| docker run --rm ghcr.io/${{ github.repository_owner }}/python-container-builder:${{ matrix.python_version }}-${{ github.sha }} poetry --version | |
| - name: Test pipenv is installed | |
| run: | | |
| docker run --rm ghcr.io/${{ github.repository_owner }}/python-container-builder:${{ matrix.python_version }}-${{ github.sha }} pipenv --version | |
| - name: Test pdm is installed | |
| run: | | |
| docker run --rm ghcr.io/${{ github.repository_owner }}/python-container-builder:${{ matrix.python_version }}-${{ github.sha }} pdm --version | |
| - name: Test venv is created | |
| run: | | |
| docker run --rm ghcr.io/${{ github.repository_owner }}/python-container-builder:${{ matrix.python_version }}-${{ github.sha }} sh -c 'test -d /.venv && echo "venv exists"' | |
| - name: Test package installation with uv | |
| run: | | |
| docker run --rm ghcr.io/${{ github.repository_owner }}/python-container-builder:${{ matrix.python_version }}-${{ github.sha }} sh -c 'uv pip install requests && /.venv/bin/python -c "import requests; print(f\"requests {requests.__version__} imported successfully\")"' | |
| - name: Test package installation with pip | |
| run: | | |
| docker run --rm ghcr.io/${{ github.repository_owner }}/python-container-builder:${{ matrix.python_version }}-${{ github.sha }} sh -c '/.venv/bin/pip install click && /.venv/bin/python -c "import click; print(f\"click {click.__version__} imported successfully\")"' |