Delivery + curation arc (epic #1413) #1277
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: Snyk Security Scan | |
| on: | |
| push: | |
| branches: [ "main", "release/2.4", "release/2.5", "release/2.6" ] | |
| paths: | |
| - '**.py' | |
| - 'pyproject.toml' | |
| - 'docker/pipeline/Dockerfile' | |
| - '.dockerignore' | |
| - '.github/workflows/snyk.yml' | |
| pull_request: | |
| branches: [ "main", "release/2.4", "release/2.5", "release/2.6" ] | |
| paths: | |
| - '**.py' | |
| - 'pyproject.toml' | |
| - 'docker/pipeline/Dockerfile' | |
| - '.dockerignore' | |
| - '.github/workflows/snyk.yml' | |
| schedule: | |
| # Run weekly on Mondays at 00:00 UTC | |
| - cron: '0 0 * * 1' | |
| workflow_dispatch: | |
| permissions: | |
| contents: read | |
| security-events: write | |
| packages: read # pull the GHCR registry build cache that docker-build-full maintains (#915) | |
| # Cancel in-progress scan when a newer push to the same ref arrives. | |
| # Snyk runs are 3-18 min; old run's findings are superseded the moment a | |
| # new commit lands. | |
| concurrency: | |
| group: ${{ github.workflow }}-${{ github.ref }} | |
| cancel-in-progress: true | |
| jobs: | |
| # Scan Python dependencies | |
| snyk-dependencies: | |
| name: Snyk - Dependencies | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v7 | |
| - name: Free disk space | |
| run: | | |
| sudo rm -rf /usr/share/dotnet /usr/local/lib/android /opt/ghc /home/linuxbrew/.linuxbrew | |
| docker image prune -af || true | |
| sudo apt-get clean | |
| sudo rm -rf /var/lib/apt/lists/* | |
| df -h | |
| - name: Set up Python 3.11 | |
| uses: actions/setup-python@v7 | |
| with: | |
| python-version: "3.11.8" | |
| cache: "pip" | |
| cache-dependency-path: pyproject.toml | |
| - name: Install dependencies | |
| run: | | |
| python -m pip install --upgrade pip | |
| # Retry: spaCy model wheels are direct GitHub release URLs; transient 502s happen. | |
| for n in 1 2 3 4 5; do | |
| if pip install -e ".[dev,ml,llm,search]"; then | |
| pip cache purge || true | |
| exit 0 | |
| fi | |
| echo "pip install failed (attempt $n/5), retry in 45s..." | |
| sleep 45 | |
| done | |
| exit 1 | |
| # Associate scans with the GitHub repo so public OSS repos are not billed as private CLI tests. | |
| - name: Run Snyk to check for vulnerabilities | |
| uses: snyk/actions/python@master | |
| continue-on-error: true | |
| env: | |
| SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }} | |
| with: | |
| args: --file=pyproject.toml --package-manager=pip --severity-threshold=high --sarif-file-output=snyk.sarif --remote-repo-url=${{ github.server_url }}/${{ github.repository }} | |
| - name: Upload Snyk results to GitHub Code Scanning | |
| uses: github/codeql-action/upload-sarif@v4 | |
| if: always() && hashFiles('snyk.sarif') != '' | |
| with: | |
| sarif_file: snyk.sarif | |
| wait-for-processing: true | |
| continue-on-error: true | |
| # Scan Docker image (aligned with docker.yml: fast LLM-only on PRs; on main, same ML+preload | |
| # args as docker-build-full `ml` matrix so the scanned image matches prod bake) | |
| snyk-docker: | |
| name: Snyk - Docker | |
| runs-on: ubuntu-latest | |
| # ML+preload bake can run ~17 min; do not cancel mid-build when another push lands. | |
| timeout-minutes: 45 | |
| if: github.event_name != 'schedule' | |
| concurrency: | |
| group: snyk-docker-${{ github.ref }} | |
| cancel-in-progress: false | |
| steps: | |
| - uses: actions/checkout@v7 | |
| # Match docker.yml (#703): hosted toolchains are unused for pure docker builds. | |
| - name: Free disk space | |
| 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, read-only) | |
| uses: docker/login-action@v4 | |
| with: | |
| registry: ghcr.io | |
| username: ${{ github.actor }} | |
| password: ${{ secrets.GITHUB_TOKEN }} | |
| # Snyk reuses the GHCR build cache that docker-build-full maintains — cache-from | |
| # only (no cache-to), so it no longer exports a duplicate of the ~5 GB model layer (#915). | |
| - name: Build Docker image (PR — LLM-only, fast) | |
| if: github.event_name == 'pull_request' | |
| uses: docker/build-push-action@v7 | |
| with: | |
| context: . | |
| file: docker/pipeline/Dockerfile | |
| build-args: | | |
| INSTALL_EXTRAS= | |
| push: false | |
| load: true | |
| tags: podcast-scraper:snyk-scan | |
| cache-from: type=registry,ref=ghcr.io/${{ github.repository_owner }}/podcast-scraper-buildcache:llm-only | |
| # Push / workflow_dispatch: same ML args as docker-build-full `ml`, reusing its GHCR | |
| # build cache READ-ONLY (no cache-to) — the duplicate ~5 GB model-layer export is gone. | |
| - name: Build Docker image (main — ML + preload) | |
| if: github.event_name != 'pull_request' | |
| uses: docker/build-push-action@v7 | |
| with: | |
| context: . | |
| file: docker/pipeline/Dockerfile | |
| build-args: | | |
| INSTALL_EXTRAS=ml | |
| PRELOAD_ML_MODELS=true | |
| push: false | |
| load: true | |
| tags: podcast-scraper:snyk-scan | |
| cache-from: type=registry,ref=ghcr.io/${{ github.repository_owner }}/podcast-scraper-buildcache:ml | |
| - name: Verify Docker image exists | |
| run: | | |
| docker images | grep podcast-scraper || echo "Image not found" | |
| docker inspect podcast-scraper:snyk-scan || echo "Image inspection failed" | |
| - name: Run Snyk to check Docker image for vulnerabilities | |
| uses: snyk/actions/docker@master | |
| continue-on-error: true | |
| env: | |
| SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }} | |
| with: | |
| image: podcast-scraper:snyk-scan | |
| args: --severity-threshold=high --sarif-file-output=snyk-docker.sarif | |
| - name: Upload Snyk Docker results to GitHub Code Scanning | |
| uses: github/codeql-action/upload-sarif@v4 | |
| if: | | |
| always() | |
| && hashFiles('snyk-docker.sarif') != '' | |
| && (github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository) | |
| with: | |
| sarif_file: snyk-docker.sarif | |
| wait-for-processing: true | |
| continue-on-error: true | |
| # Monitor dependencies (for PRs and scheduled runs) | |
| snyk-monitor: | |
| name: Snyk - Monitor | |
| runs-on: ubuntu-latest | |
| if: github.event_name == 'pull_request' || github.event_name == 'schedule' | |
| steps: | |
| - uses: actions/checkout@v7 | |
| - name: Free disk space | |
| run: | | |
| sudo rm -rf /usr/share/dotnet /usr/local/lib/android /opt/ghc /home/linuxbrew/.linuxbrew | |
| docker image prune -af || true | |
| sudo apt-get clean | |
| sudo rm -rf /var/lib/apt/lists/* | |
| df -h | |
| - name: Set up Python 3.11 | |
| uses: actions/setup-python@v7 | |
| with: | |
| python-version: "3.11.8" | |
| cache: "pip" | |
| cache-dependency-path: pyproject.toml | |
| - name: Install dependencies | |
| run: | | |
| python -m pip install --upgrade pip | |
| # Retry: spaCy model wheels are direct GitHub release URLs; transient 502s happen. | |
| for n in 1 2 3 4 5; do | |
| if pip install -e ".[dev,ml,llm,search]"; then | |
| pip cache purge || true | |
| exit 0 | |
| fi | |
| echo "pip install failed (attempt $n/5), retry in 45s..." | |
| sleep 45 | |
| done | |
| exit 1 | |
| - name: Run Snyk to monitor project | |
| uses: snyk/actions/python@master | |
| continue-on-error: true | |
| env: | |
| SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }} | |
| with: | |
| command: monitor | |
| args: --file=pyproject.toml --package-manager=pip --remote-repo-url=${{ github.server_url }}/${{ github.repository }} |