ci: add comprehensive GitHub Actions pipeline #6
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
| # SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. | |
| # SPDX-License-Identifier: Apache-2.0 | |
| # | |
| # Licensed under the Apache License, Version 2.0 (the "License"); | |
| # you may not use this file except in compliance with the License. | |
| # You may obtain a copy of the License at | |
| # | |
| # http://www.apache.org/licenses/LICENSE-2.0 | |
| # | |
| # Unless required by applicable law or agreed to in writing, software | |
| # distributed under the License is distributed on an "AS IS" BASIS, | |
| # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| # See the License for the specific language governing permissions and | |
| # limitations under the License. | |
| name: CI | |
| on: | |
| pull_request: | |
| push: | |
| branches: | |
| - develop | |
| - "release/**" | |
| workflow_dispatch: | |
| permissions: | |
| contents: read | |
| concurrency: | |
| group: ${{ github.workflow }}-${{ github.ref }} | |
| cancel-in-progress: true | |
| env: | |
| PYTHONDONTWRITEBYTECODE: "1" | |
| PYTHONUNBUFFERED: "1" | |
| UV_CACHE_DIR: ${{ github.workspace }}/.cache/uv | |
| UV_LINK_MODE: copy | |
| jobs: | |
| validate: | |
| name: Validate lockfile and configuration | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Checkout with submodules | |
| uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 | |
| with: | |
| fetch-depth: 0 | |
| persist-credentials: false | |
| submodules: recursive | |
| - name: Set up uv | |
| uses: astral-sh/setup-uv@37802adc94f370d6bfd71619e3f0bf239e1f3b78 | |
| with: | |
| version: "0.9.15" | |
| enable-cache: true | |
| - name: Install Python and dependencies | |
| run: | | |
| uv python install 3.13 | |
| uv sync --locked --group dev | |
| - name: Check lockfile | |
| run: uv lock --check | |
| - name: Compile Python sources | |
| run: uv run python -m compileall -q src tests scripts ci | |
| - name: Validate NAT configurations | |
| env: | |
| NVIDIA_API_KEY: dummy # pragma: allowlist secret | |
| TAVILY_API_KEY: dummy # pragma: allowlist secret | |
| run: | | |
| for config_file in src/vuln_analysis/configs/*.yml; do | |
| uv run nat validate --config_file "${config_file}" | |
| done | |
| lint: | |
| name: Lint and repository checks | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Checkout history | |
| uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 | |
| with: | |
| fetch-depth: 0 | |
| persist-credentials: false | |
| - name: Set up uv | |
| uses: astral-sh/setup-uv@37802adc94f370d6bfd71619e3f0bf239e1f3b78 | |
| with: | |
| version: "0.9.15" | |
| enable-cache: true | |
| - name: Run Flake8 | |
| run: | | |
| uvx --python 3.13 --from flake8==7.3.0 --with flake8-pyproject==1.2.3 \ | |
| flake8 src/vuln_analysis | |
| - name: Run ShellCheck | |
| run: shellcheck deploy/*.sh scripts/*.sh | |
| - name: Check copyright headers on changed files | |
| env: | |
| BEFORE_SHA: ${{ github.event.before }} | |
| PR_BASE_SHA: ${{ github.event.pull_request.base.sha }} | |
| run: | | |
| base_sha="${PR_BASE_SHA:-${BEFORE_SHA:-}}" | |
| if [[ -z "${base_sha}" || "${base_sha}" =~ ^0+$ ]]; then | |
| base_sha="$(git rev-parse HEAD^)" | |
| fi | |
| uv run --no-project --python 3.13 python ci/scripts/copyright.py \ | |
| --git-diff-commits "${base_sha}" "${GITHUB_SHA}" \ | |
| --verify-apache-v2 \ | |
| --exclude '^aiq/' \ | |
| --exclude '^\.secrets\.baseline$' | |
| unit_tests: | |
| name: Unit tests | |
| needs: validate | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Checkout with submodules | |
| uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 | |
| with: | |
| persist-credentials: false | |
| submodules: recursive | |
| - name: Set up uv | |
| uses: astral-sh/setup-uv@37802adc94f370d6bfd71619e3f0bf239e1f3b78 | |
| with: | |
| version: "0.9.15" | |
| enable-cache: true | |
| - name: Install Python and dependencies | |
| run: | | |
| uv python install 3.13 | |
| uv sync --locked --group dev | |
| - name: Run tests with coverage | |
| run: | | |
| mkdir -p reports/junit | |
| uv run python -X faulthandler -m pytest tests -v \ | |
| --cov=src/vuln_analysis \ | |
| --cov-fail-under=70 \ | |
| --cov-report=term \ | |
| --cov-report=xml:coverage.xml \ | |
| --cov-report=html:htmlcov \ | |
| --junitxml=reports/junit/all.xml | |
| - name: Upload test reports | |
| if: always() | |
| uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 | |
| with: | |
| name: unit-test-reports | |
| path: | | |
| reports/junit/all.xml | |
| coverage.xml | |
| htmlcov/ | |
| retention-days: 14 | |
| security: | |
| name: Source security checks | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 | |
| with: | |
| persist-credentials: false | |
| - name: Set up uv | |
| uses: astral-sh/setup-uv@37802adc94f370d6bfd71619e3f0bf239e1f3b78 | |
| with: | |
| version: "0.9.15" | |
| enable-cache: true | |
| - name: Scan tracked files | |
| run: | | |
| git ls-files -z | xargs -0 \ | |
| uvx --from detect-secrets==1.5.0 detect-secrets-hook --baseline .secrets.baseline --exclude-files '^aiq$' --exclude-files '^\.env\.example$' -- | |
| - name: Run Bandit | |
| run: uvx --from 'bandit[toml]==1.8.6' bandit -c pyproject.toml -r src/vuln_analysis | |
| dependency_audit: | |
| name: Dependency audit (advisory) | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Checkout with submodules | |
| uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 | |
| with: | |
| persist-credentials: false | |
| submodules: recursive | |
| - name: Set up uv | |
| uses: astral-sh/setup-uv@37802adc94f370d6bfd71619e3f0bf239e1f3b78 | |
| with: | |
| version: "0.9.15" | |
| enable-cache: true | |
| - name: Export locked runtime dependencies | |
| run: | | |
| uv export --locked --no-dev --no-emit-project --no-hashes \ | |
| --output-file requirements-audit.txt | |
| # The current lockfile has known findings that need remediation before this | |
| # can become a blocking gate. Keep producing a reviewable report meanwhile. | |
| - name: Audit dependencies | |
| id: audit | |
| continue-on-error: true | |
| run: | | |
| uvx --from pip-audit==2.9.0 pip-audit \ | |
| --requirement requirements-audit.txt \ | |
| --no-deps --disable-pip --progress-spinner off \ | |
| --format json --output dependency-audit.json | |
| - name: Upload dependency report | |
| if: always() | |
| uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 | |
| with: | |
| name: dependency-audit | |
| path: dependency-audit.json | |
| if-no-files-found: warn | |
| retention-days: 14 | |
| - name: Summarize advisory result | |
| if: always() | |
| env: | |
| AUDIT_OUTCOME: ${{ steps.audit.outcome }} | |
| run: | | |
| echo "Dependency audit outcome (currently advisory): ${AUDIT_OUTCOME}" >> "${GITHUB_STEP_SUMMARY}" | |
| infrastructure: | |
| name: Container and Compose validation | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 | |
| with: | |
| persist-credentials: false | |
| - name: Run Hadolint | |
| run: | | |
| docker run --rm \ | |
| --volume "$PWD:/workspace" \ | |
| --workdir /workspace \ | |
| --entrypoint "" \ | |
| hadolint/hadolint:v2.14.0-alpine@sha256:7aba693c1442eb31c0b015c129697cb3b6cb7da589d85c7562f9deb435a6657c \ | |
| hadolint --ignore DL3008 --ignore DL3013 deploy/Dockerfile | |
| - name: Render base Compose configuration | |
| run: docker compose -f deploy/docker-compose.yml config > compose.yml | |
| - name: Render Compose configuration with NIM overlay | |
| run: | | |
| docker compose \ | |
| -f deploy/docker-compose.yml \ | |
| -f deploy/docker-compose.nim.yml \ | |
| config > compose-nim.yml | |
| - name: Upload rendered Compose configurations | |
| if: always() | |
| uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 | |
| with: | |
| name: compose-rendered | |
| path: | | |
| compose.yml | |
| compose-nim.yml | |
| retention-days: 14 | |
| package: | |
| name: Build package | |
| needs: validate | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Checkout with submodules | |
| uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 | |
| with: | |
| fetch-depth: 0 | |
| persist-credentials: false | |
| submodules: recursive | |
| - name: Set up uv | |
| uses: astral-sh/setup-uv@37802adc94f370d6bfd71619e3f0bf239e1f3b78 | |
| with: | |
| version: "0.9.15" | |
| enable-cache: true | |
| - name: Install Python | |
| run: uv python install 3.13 | |
| - name: Build wheel and source distribution | |
| run: uv build --out-dir dist | |
| - name: Smoke-test wheel metadata and import | |
| run: | | |
| uv venv --python 3.13 /tmp/vuln-analysis-wheel-smoke | |
| uv pip install --python /tmp/vuln-analysis-wheel-smoke/bin/python \ | |
| --no-deps dist/*.whl | |
| /tmp/vuln-analysis-wheel-smoke/bin/python - <<'PY' | |
| import importlib.metadata | |
| import vuln_analysis | |
| scripts = importlib.metadata.entry_points(group="console_scripts") | |
| assert any(entry.name == "vuln-analysis" for entry in scripts) | |
| PY | |
| - name: Upload package | |
| uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 | |
| with: | |
| name: python-package | |
| path: dist/ | |
| retention-days: 14 | |
| container_build: | |
| name: Build and scan container | |
| if: github.event_name != 'pull_request' | |
| needs: | |
| - unit_tests | |
| - infrastructure | |
| - package | |
| - security | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Checkout with submodules | |
| uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 | |
| with: | |
| fetch-depth: 0 | |
| persist-credentials: false | |
| submodules: recursive | |
| - name: Build runtime image | |
| run: | | |
| docker build \ | |
| --file deploy/Dockerfile \ | |
| --target runtime \ | |
| --tag vuln-analysis:ci . | |
| - name: Generate image SBOM | |
| run: | | |
| docker run --rm \ | |
| --volume /var/run/docker.sock:/var/run/docker.sock \ | |
| --volume "$PWD:/workspace" \ | |
| aquasec/trivy:0.70.0@sha256:be1190afcb28352bfddc4ddeb71470835d16462af68d310f9f4bca710961a41e image \ | |
| --format cyclonedx \ | |
| --output /workspace/vuln-analysis-sbom.cdx.json \ | |
| vuln-analysis:ci | |
| # Report the existing image findings without blocking releases until an | |
| # image-specific allowlist and remediation baseline are established. | |
| - name: Scan image vulnerabilities | |
| id: image_scan | |
| continue-on-error: true | |
| run: | | |
| docker run --rm \ | |
| --volume /var/run/docker.sock:/var/run/docker.sock \ | |
| --volume "$PWD:/workspace" \ | |
| aquasec/trivy:0.70.0@sha256:be1190afcb28352bfddc4ddeb71470835d16462af68d310f9f4bca710961a41e image \ | |
| --severity HIGH,CRITICAL \ | |
| --ignore-unfixed \ | |
| --format json \ | |
| --output /workspace/vuln-analysis-trivy.json \ | |
| --exit-code 1 \ | |
| vuln-analysis:ci | |
| - name: Upload image security reports | |
| if: always() | |
| uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 | |
| with: | |
| name: image-security-reports | |
| path: | | |
| vuln-analysis-sbom.cdx.json | |
| vuln-analysis-trivy.json | |
| if-no-files-found: warn | |
| retention-days: 14 |