CI Integration
pydepgate is designed to run as an artifact gate in CI.
The usual pattern is:
- Build the package artifact.
- Scan the artifact before publishing or deploying it.
- Fail the pipeline if pydepgate finds HIGH or CRITICAL startup-vector behavior.
- Optionally store the scan evidence as a CI artifact for later review.
pydepgate supports CI integration through direct CLI invocation, Docker, pre-commit hooks, JSON output, SARIF output, and the local evidence database.
Exit code contract
CI integrations should rely on pydepgate’s public exit-code contract.
| Code | Meaning |
|---|---|
0 | Clean. No findings at or above --min-severity. |
1 | Findings present, but none HIGH or CRITICAL. |
2 | At least one HIGH or CRITICAL finding. |
3 | Tool error. The scan could not complete. |
The standard CI pattern is to fail the build on exit code 2.
Exit code 1 means pydepgate found lower-severity signals, but not enough to block the pipeline under the active threshold. Use --strict-exit when you want any finding to fail the build.
Recommended CI policy
For most projects:
pydepgate scan --ci --min-severity high dist/*.whl
This blocks on HIGH and CRITICAL findings while allowing lower-severity informational signals to be reviewed separately.
--ci is an output-mode flag. It does not change severity behavior.
When enabled, --ci:
- forces JSON output unless another format is explicitly selected
- disables color unless color was explicitly configured
- preserves the normal exit-code contract
Pair --ci with --min-severity high when you want CI to block only on HIGH and CRITICAL findings.
Direct CLI invocation
Direct CLI invocation is the simplest integration path when the CI job already has Python available.
GitHub Actions
name: pydepgate scan
on:
push:
pull_request:
jobs:
scan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.12"
- name: Build wheel
run: |
python -m pip install --upgrade pip
python -m pip install build
python -m build --wheel
- name: Install pydepgate
run: python -m pip install "pydepgate>=0.5,<0.6"
- name: Scan wheel
run: pydepgate scan --ci --min-severity high dist/*.whl
Use a bounded version range for CI if you want patch releases without accepting a future breaking change. Use an exact version pin when you need fully reproducible pipeline behavior.
Example:
python -m pip install "pydepgate==0.5.0"
GitLab CI
stages:
- build
- scan
build-wheel:
stage: build
image: python:3.12-slim
script:
- python -m pip install --upgrade pip
- python -m pip install build
- python -m build --wheel
artifacts:
paths:
- dist/
scan-wheel:
stage: scan
image: python:3.12-slim
script:
- python -m pip install --upgrade pip
- python -m pip install "pydepgate>=0.5,<0.6"
- pydepgate scan --ci --min-severity high dist/*.whl
dependencies:
- build-wheel
Scanning installed packages
Artifact scanning is preferred for packages you build yourself, because it scans the exact wheel or source distribution that would be published.
Installed-package scanning is useful when you want to inspect a third-party package already present in the environment.
- name: Install target package
run: python -m pip install some-package
- name: Scan installed package
run: pydepgate scan --ci --min-severity high some-package
Use this for dependency intake or local environment review. For release gating, scan the built artifact.
Docker image
The official Docker image is published at:
ghcr.io/nuclear-treestump/pydepgate
The image is intended for CI pipelines that should not install pydepgate into the build environment directly.
Published tags include:
docker pull ghcr.io/nuclear-treestump/pydepgate:latest
docker pull ghcr.io/nuclear-treestump/pydepgate:0.5
docker pull ghcr.io/nuclear-treestump/pydepgate:0.5.0
Use latest for quick local testing. Use a version tag or digest pin in CI.
See Guide: Docker Image for image properties, supported platforms, digest pinning, signatures, attestations, and reproducibility notes.
Scan one artifact with Docker
docker run --rm \
-v "$(pwd)/dist:/scan:ro" \
ghcr.io/nuclear-treestump/pydepgate:0.5 \
scan --ci --min-severity high /scan/some-package-1.0.0-py3-none-any.whl
The :ro mount is recommended for ordinary scans. pydepgate does not need write access to the scanned artifact.
Scan multiple artifacts with Docker
Docker does not expand globs unless a shell is used inside the container. For wildcard scans, override the entrypoint and invoke sh -c:
docker run --rm \
--entrypoint sh \
-v "$(pwd)/dist:/scan:ro" \
ghcr.io/nuclear-treestump/pydepgate:0.5 \
-c 'pydepgate scan --ci --min-severity high /scan/*.whl'
This keeps glob expansion inside the container, where /scan/*.whl actually exists.
GitHub Actions with Docker
name: pydepgate docker scan
on:
push:
pull_request:
jobs:
scan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.12"
- name: Build wheel
run: |
python -m pip install --upgrade pip
python -m pip install build
python -m build --wheel
- name: Scan wheel with pydepgate container
run: |
docker run --rm \
--entrypoint sh \
-v "$/dist:/scan:ro" \
ghcr.io/nuclear-treestump/pydepgate:0.5 \
-c 'pydepgate scan --ci --min-severity high /scan/*.whl'
For locked-down pipelines, replace the version tag with an image digest.
docker run --rm \
--entrypoint sh \
-v "$/dist:/scan:ro" \
ghcr.io/nuclear-treestump/pydepgate@sha256:<digest> \
-c 'pydepgate scan --ci --min-severity high /scan/*.whl'
GitLab CI with Docker
scan-wheel:
stage: scan
image:
name: ghcr.io/nuclear-treestump/pydepgate:0.5
entrypoint: [""]
script:
- pydepgate scan --ci --min-severity high dist/*.whl
dependencies:
- build-wheel
The entrypoint: [""] line overrides the image’s pydepgate entrypoint so GitLab can run its normal shell wrapper before executing the script section.
Multi-stage Dockerfile
For projects that already build inside Docker, use pydepgate as a scan stage.
FROM python:3.12-slim AS build
WORKDIR /src
COPY . .
RUN python -m pip install --upgrade pip \
&& python -m pip install build \
&& python -m build --wheel
FROM ghcr.io/nuclear-treestump/pydepgate:0.5 AS scan
COPY --from=build /src/dist/*.whl /scan/
RUN pydepgate scan --ci --min-severity high /scan/*.whl
The scan stage fails if blocking findings are present, stopping the build before downstream stages receive the artifact.
Environment variables in Docker
All PYDEPGATE_* environment variables work inside the container.
docker run --rm \
-v "$(pwd)/dist:/scan:ro" \
-e PYDEPGATE_PEEK=1 \
-e PYDEPGATE_MIN_SEVERITY=high \
-e PYDEPGATE_FORMAT=json \
ghcr.io/nuclear-treestump/pydepgate:0.5 \
scan /scan/some-package-1.0.0-py3-none-any.whl
Use environment variables when your CI system manages policy through job configuration rather than command-line arguments.
Saving scan evidence in CI
pydepgate can store scan evidence in its local evidence database.
This is useful when you want the CI job to preserve:
- artifact identity and hashes
- active findings
- finding locations
- decoded payload trees
- CVE matches
- later
pydepgate db explainoutput
Set XDG_DATA_HOME to a workspace directory, run the scan with --save-to-db, and upload that directory as a CI artifact.
GitHub Actions evidence artifact
- name: Scan wheel and save evidence
env:
XDG_DATA_HOME: $/.pydepgate-data
run: pydepgate scan --ci --min-severity high --save-to-db dist/*.whl
- name: Upload pydepgate evidence
if: always()
uses: actions/upload-artifact@v4
with:
name: pydepgate-evidence
path: .pydepgate-data/
if: always() ensures the evidence is uploaded even when pydepgate exits with code 2.
Inspecting evidence after CI
After downloading the artifact, use:
pydepgate db list-runs
pydepgate db explain --run-id <run-id>
See CLI Reference: db.
CVE scanning in CI
pydepgate scan and pydepgate cvescan answer different questions.
pydepgate scan asks:
Does this artifact contain suspicious startup-vector behavior?
pydepgate cvescan asks:
Does this artifact identify as a package version known to be vulnerable or malicious in the local CVE database?
A CI job can run both.
- name: Install pydepgate
run: python -m pip install "pydepgate>=0.5,<0.6"
- name: Update local CVE database
run: pydepgate cvedb update
- name: Static startup-vector scan
run: pydepgate scan --ci --min-severity high dist/*.whl
- name: Known vulnerability scan
run: pydepgate cvescan --ci --min-severity high dist/*.whl
For offline or hermetic CI, build or cache the CVE database in an earlier trusted job and reuse it in the scan job.
See CLI Reference: cvedb and CLI Reference: cvescan.
SARIF upload
Use SARIF when you want pydepgate findings to appear in GitHub Code Scanning or another SARIF-compatible system.
- name: Run pydepgate SARIF scan
run: |
pydepgate scan \
--format sarif \
--sarif-srcroot "$GITHUB_WORKSPACE" \
--min-severity low \
dist/*.whl > pydepgate.sarif
- name: Upload SARIF
uses: github/codeql-action/upload-sarif@v3
with:
sarif_file: pydepgate.sarif
SARIF scans are usually run with a lower display threshold than blocking CI scans because code scanning is useful for review, not just pass/fail gating.
pre-commit hooks
pydepgate ships two pre-commit hook IDs.
pydepgate runs pydepgate scan --single against staged .py files. It defaults to --min-severity high so commits are not blocked by informational density signals. The threshold is tunable via args.
pydepgate-pth scans staged .pth files. .pth files are startup-vector files, so any finding there warrants review.
Add pydepgate to pre-commit
In .pre-commit-config.yaml:
repos:
- repo: https://github.com/nuclear-treestump/pydepgate
rev: v0.5.0
hooks:
- id: pydepgate
- id: pydepgate-pth
Install and run the hooks:
pre-commit install
pre-commit run --all-files
Tune the hook threshold
Catch MEDIUM and above:
- id: pydepgate
args: [--min-severity, medium, --no-color]
Enable payload peeking:
- id: pydepgate
args: [--min-severity, high, --no-color, --peek]
Exclude generated or vendored directories:
- id: pydepgate
exclude: ^(generated/|vendor/|third_party/)
pre-commit in CI
The same hook configuration works in CI.
GitHub Actions:
- uses: pre-commit/action@v3.0.1
GitLab CI:
pre-commit:
image: python:3.12-slim
script:
- python -m pip install pre-commit
- pre-commit run --all-files
pre-commit is best used as a commit-time safety net. Release pipelines should still scan built artifacts.
Recommended thresholds
| Context | Recommended flag | Rationale |
|---|---|---|
| CI artifact gate | --ci --min-severity high | Blocks on HIGH/CRITICAL findings only. |
| Strict CI artifact gate | --ci --strict-exit | Blocks on any finding. |
| SARIF review scan | --format sarif --min-severity low | Emits lower-severity context for review surfaces. |
pre-commit .py hook | --min-severity high | Avoids blocking commits on informational density signals. |
pre-commit .pth hook | no severity filter | Any finding in a .pth file warrants review. |
| Interactive investigation | --min-severity low or no filter | Preserves full signal visibility. |
| Evidence-producing scan | --save-to-db | Stores reproducible scan records for later explanation. |
JSON output for downstream processing
Use JSON when a later CI step needs to apply custom policy.
Do not use || true if you need the original pydepgate exit code. Capture it explicitly.
set +e
pydepgate scan --format json --min-severity low dist/*.whl > scan-results.json
status=$?
set -e
python scripts/evaluate-pydepgate-results.py scan-results.json
exit "$status"
This preserves pydepgate’s exit code while still allowing custom processing.
If your custom policy fully replaces pydepgate’s exit-code behavior, exit with your policy result instead:
set +e
pydepgate scan --format json --min-severity low dist/*.whl > scan-results.json
scan_status=$?
set -e
python scripts/evaluate-pydepgate-results.py scan-results.json
policy_status=$?
if [ "$scan_status" -eq 3 ]; then
exit 3
fi
exit "$policy_status"
See Output Formats for the JSON schema.
Minimal copy-paste recipes
Block HIGH/CRITICAL findings on built wheels
python -m pip install "pydepgate>=0.5,<0.6"
pydepgate scan --ci --min-severity high dist/*.whl
Strict zero-finding gate
python -m pip install "pydepgate>=0.5,<0.6"
pydepgate scan --ci --strict-exit dist/*.whl
Containerized scan
docker run --rm \
--entrypoint sh \
-v "$(pwd)/dist:/scan:ro" \
ghcr.io/nuclear-treestump/pydepgate:0.5 \
-c 'pydepgate scan --ci --min-severity high /scan/*.whl'
Static scan plus CVE scan
python -m pip install "pydepgate>=0.5,<0.6"
pydepgate cvedb update
pydepgate scan --ci --min-severity high dist/*.whl
pydepgate cvescan --ci --min-severity high dist/*.whl
Save evidence for later explanation
export XDG_DATA_HOME="$PWD/.pydepgate-data"
pydepgate scan --ci --min-severity high --save-to-db dist/*.whl
pydepgate db list-runs