ML model manifest: single source + drift guard + CI de-dup (#917) #795
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: Docker Build & Test | |
| on: | |
| push: | |
| branches: [ "main", "release/2.4", "release/2.5", "release/2.6" ] | |
| paths: | |
| - 'docker/pipeline/Dockerfile' | |
| - '.dockerignore' | |
| - 'pyproject.toml' | |
| - '**.py' | |
| - '.github/workflows/docker.yml' | |
| # Fast Docker build runs on PRs when Docker-related files change | |
| # Full Docker validation still runs on merge to main | |
| pull_request: | |
| branches: [ "main", "release/2.4", "release/2.5", "release/2.6" ] | |
| paths: | |
| - 'docker/pipeline/Dockerfile' | |
| - '.dockerignore' | |
| - 'pyproject.toml' | |
| - '**.py' | |
| - '.github/workflows/docker.yml' | |
| # Manual trigger so build-logic changes (free-disk steps, buildx opts) can be | |
| # validated without touching a Dockerfile/source path. | |
| workflow_dispatch: | |
| permissions: | |
| contents: read | |
| packages: write # push/pull the GHCR registry build cache (ghcr.io/<owner>/podcast-scraper-buildcache) | |
| # Newer push to same (workflow, ref) cancels the in-progress run — Docker | |
| # build is expensive (ML variant ~17 min) and the old run's image is | |
| # obsolete the moment the new commit lands. | |
| concurrency: | |
| group: ${{ github.workflow }}-${{ github.ref }} | |
| cancel-in-progress: true | |
| jobs: | |
| # Fast Docker build for PRs (LLM-only variant, sub-3 min target) | |
| # Builds LLM-only variant for fast feedback on PRs | |
| # Most code changes affect both variants, so LLM-only is sufficient for PR validation | |
| docker-build-fast: | |
| if: github.event_name == 'pull_request' | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v6 | |
| - name: Free disk space | |
| # #703: cold pipeline (ml) builds tip over the runner's free-disk | |
| # threshold even with dotnet/android/ghc removed. Also nuke | |
| # /opt/hostedtoolcache (CodeQL, Java, Swift, Boost) — pure docker | |
| # builds don't need any hosted-tool runtimes. | |
| run: | | |
| echo "before:" | |
| df -h / | |
| sudo rm -rf /usr/share/dotnet /usr/local/lib/android /opt/ghc /home/linuxbrew/.linuxbrew \ | |
| /opt/hostedtoolcache /usr/share/swift /usr/local/share/boost \ | |
| /usr/local/lib/node_modules /usr/local/.ghcup /usr/lib/google-cloud-sdk \ | |
| /usr/local/share/powershell /usr/local/share/chromium /opt/microsoft /opt/az || true | |
| sudo swapoff -a || true | |
| sudo rm -f /swapfile /mnt/swapfile || true | |
| sudo apt-get clean || true | |
| sudo rm -rf /var/lib/apt/lists/* || true | |
| docker image prune -af || true | |
| echo "after:" | |
| df -h / | |
| - name: Set up Docker Buildx | |
| uses: docker/setup-buildx-action@v4 | |
| with: | |
| driver-opts: | | |
| image=moby/buildkit:latest | |
| - name: Log in to GHCR (registry build cache) | |
| uses: docker/login-action@v4 | |
| with: | |
| registry: ghcr.io | |
| username: ${{ github.actor }} | |
| password: ${{ secrets.GITHUB_TOKEN }} | |
| - name: Build Docker image (LLM-only variant, fast) | |
| uses: docker/build-push-action@v7 | |
| with: | |
| context: . | |
| file: docker/pipeline/Dockerfile | |
| build-args: | | |
| INSTALL_EXTRAS= | |
| push: false | |
| load: true | |
| tags: podcast-scraper:test-llm | |
| # Build cache lives in GHCR (registry), not GHA — keeps the ~9 GB ml-models GHA | |
| # cache from being evicted under the 10 GB cap (#915). cache-from only here; | |
| # docker-build-full maintains the cache. | |
| cache-from: type=registry,ref=ghcr.io/${{ github.repository_owner }}/podcast-scraper-buildcache:llm-only | |
| - name: Test Docker image (help command) | |
| run: | | |
| docker run --rm podcast-scraper:test-llm --help | |
| - name: Test Docker image (version check) | |
| run: | | |
| docker run --rm podcast-scraper:test-llm --version | |
| - name: Test Docker image (no args - should show config file error) | |
| run: | | |
| output=$(docker run --rm podcast-scraper:test-llm 2>&1 || true) | |
| echo "$output" | grep -q "Config file not found" | |
| - name: Validate Dockerfile with hadolint | |
| run: | | |
| docker run --rm -v "$(pwd):/workspace" -w /workspace hadolint/hadolint:latest hadolint docker/pipeline/Dockerfile | |
| # Full Docker build for main branch (both variants in parallel) | |
| # Builds both LLM-only and ML-enabled variants to ensure full validation | |
| docker-build-full: | |
| if: github.event_name == 'push' && (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/release/2.4' || github.ref == 'refs/heads/release/2.5' || github.ref == 'refs/heads/release/2.6') | |
| runs-on: ubuntu-latest | |
| strategy: | |
| matrix: | |
| include: | |
| - name: llm-only | |
| tag: podcast-scraper:test-llm | |
| build_args: "INSTALL_EXTRAS=" | |
| - name: ml | |
| tag: podcast-scraper:test | |
| build_args: "INSTALL_EXTRAS=ml\nPRELOAD_ML_MODELS=true" | |
| steps: | |
| - uses: actions/checkout@v6 | |
| - name: Free disk space | |
| # #703: cold pipeline (ml) builds tip over the runner's free-disk | |
| # threshold even with dotnet/android/ghc removed. Also nuke | |
| # /opt/hostedtoolcache (CodeQL, Java, Swift, Boost) — pure docker | |
| # builds don't need any hosted-tool runtimes. | |
| run: | | |
| echo "before:" | |
| df -h / | |
| sudo rm -rf /usr/share/dotnet /usr/local/lib/android /opt/ghc /home/linuxbrew/.linuxbrew \ | |
| /opt/hostedtoolcache /usr/share/swift /usr/local/share/boost \ | |
| /usr/local/lib/node_modules /usr/local/.ghcup /usr/lib/google-cloud-sdk \ | |
| /usr/local/share/powershell /usr/local/share/chromium /opt/microsoft /opt/az || true | |
| sudo swapoff -a || true | |
| sudo rm -f /swapfile /mnt/swapfile || true | |
| sudo apt-get clean || true | |
| sudo rm -rf /var/lib/apt/lists/* || true | |
| docker image prune -af || true | |
| echo "after:" | |
| df -h / | |
| - name: Set up Docker Buildx | |
| uses: docker/setup-buildx-action@v4 | |
| with: | |
| driver-opts: | | |
| image=moby/buildkit:latest | |
| - name: Log in to GHCR (registry build cache) | |
| uses: docker/login-action@v4 | |
| with: | |
| registry: ghcr.io | |
| username: ${{ github.actor }} | |
| password: ${{ secrets.GITHUB_TOKEN }} | |
| - name: Build Docker image (${{ matrix.name }}) | |
| uses: docker/build-push-action@v7 | |
| with: | |
| context: . | |
| file: docker/pipeline/Dockerfile | |
| build-args: | | |
| ${{ matrix.build_args }} | |
| push: false | |
| load: true | |
| tags: ${{ matrix.tag }} | |
| # Build cache lives in GHCR (registry), not GHA, so the ~9 GB ml-models GHA cache | |
| # isn't evicted under the 10 GB cap (#915). This job maintains the cache (snyk-docker | |
| # reuses it read-only). mode=min keeps the export small. | |
| cache-from: type=registry,ref=ghcr.io/${{ github.repository_owner }}/podcast-scraper-buildcache:${{ matrix.name }} | |
| cache-to: type=registry,mode=min,ref=ghcr.io/${{ github.repository_owner }}/podcast-scraper-buildcache:${{ matrix.name }} | |
| - name: Test Docker image (${{ matrix.name }} - help command) | |
| run: | | |
| docker run --rm ${{ matrix.tag }} --help | |
| - name: Test Docker image (${{ matrix.name }} - version check) | |
| run: | | |
| docker run --rm ${{ matrix.tag }} --version | |
| - name: Test Docker image (${{ matrix.name }} - no args - should show config file error) | |
| run: | | |
| output=$(docker run --rm ${{ matrix.tag }} 2>&1 || true) | |
| echo "$output" | grep -q "Config file not found" | |
| - name: Validate Dockerfile with hadolint | |
| if: matrix.name == 'llm-only' | |
| run: | | |
| docker run --rm -v "$(pwd):/workspace" -w /workspace hadolint/hadolint:latest hadolint docker/pipeline/Dockerfile |