Adding dependency graph analysis to a CI pipeline is a different integration problem than adding a linter or a test runner. Linters and test runners are stateless — they look at the code in the PR branch and produce a pass/fail. Dependency analysis is stateful: it needs an up-to-date graph of your service relationships to reason about the impact of a specific change. That state lives outside the individual pipeline run, which changes how you structure the integration.
This walkthrough covers the three most common CI environments we see in platform engineering teams: GitHub Actions, CircleCI, and Jenkins. The mechanics differ, but the architecture is the same: authenticate, submit the changed service context, receive the risk assessment, report it as a named status check.
Prerequisites and initial setup
Before any CI integration, you need a buildpath.yaml in the root of each service repository (or in the monorepo root if you're scanning multiple services). The minimum viable config looks like this:
scan_targets:
- service: payments-api
path: ./services/payments-api
language: go
- service: order-service
path: ./services/order-service
language: typescript
risk_thresholds:
low: 30
medium: 65
# above 65 = HIGH
notifications:
slack_channel: "#platform-alerts"
The scan_targets list tells the scanner which directories to analyze and what language parser to use. The risk_thresholds map score ranges to severity bands. These thresholds are the most team-specific part of the config; the defaults above work for teams starting out, but you'll want to recalibrate after your first few weeks of data.
GitHub Actions integration
GitHub Actions is the most straightforward integration path because the workflow file lives in the repository and branch protection rules connect directly to named checks.
Create .github/workflows/buildpath-check.yml:
name: Buildpath Risk Check
on:
pull_request:
branches: [main, staging]
jobs:
risk-analysis:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Run Buildpath analysis
uses: buildpathio/buildpath-action@v1
with:
api-token: ${{ secrets.BUILDPATH_API_TOKEN }}
config-path: ./buildpath.yaml
pr-number: ${{ github.event.pull_request.number }}
fail-on: HIGH
The fail-on: HIGH flag causes the step to exit with code 1 when the analysis returns a HIGH risk score. This is what triggers the "checks failed" state in the PR UI. You can also set fail-on: MEDIUM for stricter gates, or omit it entirely to report scores without blocking — useful when you're first calibrating thresholds and don't want to block engineers while you tune.
After the workflow runs once, navigate to your repository's Settings → Branches → Branch protection rules and add buildpath/risk-score as a required status check. This is what prevents the merge button from activating on HIGH-risk PRs without an override.
CircleCI configuration
CircleCI's orb ecosystem makes this a single-command integration once you've set up the API token in your CircleCI environment variables.
version: 2.1
orbs:
buildpath: buildpathio/[email protected]
workflows:
pr-analysis:
jobs:
- buildpath/check:
api-token: BUILDPATH_API_TOKEN
config-path: ./buildpath.yaml
context: platform-secrets
The context: platform-secrets references a CircleCI context that contains your API token. If your organization uses project-level environment variables instead of contexts, remove that line and ensure BUILDPATH_API_TOKEN is set at the project level.
One CircleCI-specific consideration: if you're running multiple service pipelines in parallel using dynamic config or continuation orbs, each pipeline should pass the specific service name in the analysis payload so the graph update applies to the correct node. The orb handles this automatically when config-path points to a buildpath.yaml with a single scan target, but for multi-service configs you may want to scope each pipeline to a specific service.
Jenkins pipeline step
Jenkins pipelines that don't have a marketplace of pre-built steps benefit from the Buildpathio CLI, which is a single binary that handles authentication and reporting.
pipeline {
agent any
environment {
BUILDPATH_TOKEN = credentials('buildpath-api-token')
}
stages {
stage('Buildpath Risk Analysis') {
steps {
sh '''
curl -sSL https://get.buildpathio.com/cli | sh -s -- --version 1.4
buildpath check \
--token $BUILDPATH_TOKEN \
--config ./buildpath.yaml \
--pr-id ${env.CHANGE_ID} \
--output json > buildpath-result.json
SCORE=$(cat buildpath-result.json | python3 -c \
"import sys,json; print(json.load(sys.stdin)['score'])")
if [ "$SCORE" -gt 65 ]; then
echo "HIGH risk score: $SCORE. Review downstream impact."
exit 1
fi
'''
}
}
}
}
The CHANGE_ID environment variable is set by the Jenkins GitHub Branch Source plugin when building PR branches. If you're using a different SCM plugin, check its documentation for the equivalent variable that exposes the PR number.
Keeping analysis latency low
The practical success of a CI-integrated risk check depends on how long it adds to the total pipeline time. If the analysis step adds 8 minutes to a 6-minute pipeline, engineers will flag it as overhead and push for it to be removed or made non-blocking.
Several factors control analysis latency: graph size (larger graphs take longer to traverse), cache freshness (warm caches on the analysis server are faster), and network round trips. For teams with fewer than 50 services, the analysis step should run in under 90 seconds. For larger graphs, batching the static analysis locally and sending only the diff context to the API — rather than the full source tree — reduces both latency and token usage significantly. The CLI's --diff-only flag enables this mode.
We're not saying every team needs sub-60-second analysis. We're saying that if analysis latency exceeds your test suite runtime, you should profile where the time is going before assuming the integration needs to be made optional.