CI/CD Integration
Integrate Hotspots into your CI/CD pipeline to catch complexity regressions before they reach production.
Overview
Hotspots provides first-class CI/CD support:
- Zero-config GitHub Action for seamless integration
- Policy enforcement to block risky changes
- Delta analysis comparing changes vs. baseline
- PR comments with actionable insights
- HTML reports as workflow artifacts
- Exit codes for build failure control
Supported CI Systems: GitHub Actions, GitLab CI, CircleCI, Travis CI, Jenkins, Bitbucket Pipelines
GitHub Actions (Recommended)
Quick Start
Create .github/workflows/hotspots.yml:
name: Hotspots
on:
pull_request:
push:
branches: [main]
jobs:
analyze:
runs-on: ubuntu-latest
permissions:
contents: read
pull-requests: write # For PR comments
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0 # Required for delta analysis
- uses: Stephen-Collins-tech/hotspots-action@v1
with:
github-token: ${{ secrets.GITHUB_TOKEN }}That's it! The action will:
- Detect PRs automatically and run delta analysis
- Post results as PR comments
- Generate HTML reports as artifacts
- Fail the build on policy violations
See GitHub Action Guide for complete documentation.
Action Inputs
| Input | Description | Default |
|---|---|---|
path | Path to analyze | . (repo root) |
policy | Policy mode | critical-introduction |
min-lrs | Min LRS threshold | - |
config | Path to config file | Auto-discover |
fail-on | When to fail: error, warn, never | error |
version | Hotspots version | latest |
github-token | Token for PR comments | github.token (auto) |
post-comment | Post PR comment | true |
Action Outputs
| Output | Description |
|---|---|
violations | JSON array of violations |
passed | true/false |
summary | Markdown summary |
report-path | HTML report path |
json-output | JSON output path |
Example Configurations
Strict Policy (No Regressions)
- uses: Stephen-Collins-tech/hotspots-action@v1
with:
policy: strict
fail-on: error
github-token: ${{ secrets.GITHUB_TOKEN }}Custom Threshold
- uses: Stephen-Collins-tech/hotspots-action@v1
with:
min-lrs: 8.0 # Only flag functions with LRS ≥ 8
fail-on: warn
github-token: ${{ secrets.GITHUB_TOKEN }}With Configuration File
- uses: Stephen-Collins-tech/hotspots-action@v1
with:
config: .hotspots.ci.json
github-token: ${{ secrets.GITHUB_TOKEN }}Create .hotspots.ci.json:
{
"exclude": ["**/*.test.ts", "**/__tests__/**"],
"min_lrs": 5.0,
"thresholds": {
"moderate": 5.0,
"high": 8.0,
"critical": 10.0
}
}Multi-Path Analysis
- uses: Stephen-Collins-tech/hotspots-action@v1
with:
path: src/
github-token: ${{ secrets.GITHUB_TOKEN }}
- uses: Stephen-Collins-tech/hotspots-action@v1
with:
path: lib/
github-token: ${{ secrets.GITHUB_TOKEN }}Upload HTML Report
- uses: Stephen-Collins-tech/hotspots-action@v1
id: hotspots
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
- uses: actions/upload-artifact@v4
if: always()
with:
name: hotspots-report
path: ${{ steps.hotspots.outputs.report-path }}
retention-days: 30Use Outputs in Subsequent Steps
- uses: Stephen-Collins-tech/hotspots-action@v1
id: hotspots
with:
fail-on: never # Don't fail, handle manually
github-token: ${{ secrets.GITHUB_TOKEN }}
- name: Check Results
run: |
if [ "${{ steps.hotspots.outputs.passed }}" == "false" ]; then
echo "::warning::Hotspots found violations"
echo "${{ steps.hotspots.outputs.summary }}"
fiGitLab CI
Installation
Add to .gitlab-ci.yml:
stages:
- analyze
hotspots:
stage: analyze
image: rust:latest
before_script:
- cargo install hotspots-cli --version 1.0.0
script:
- hotspots analyze src/ --mode delta --policy --format json
artifacts:
reports:
junit: hotspots-report.xml
paths:
- .hotspots/report.html
expire_in: 1 week
rules:
- if: '$CI_PIPELINE_SOURCE == "merge_request_event"'With Configuration File
hotspots:
stage: analyze
image: rust:latest
before_script:
- cargo install hotspots-cli
script:
- hotspots analyze src/ --config .hotspots.ci.json --mode delta --policy
artifacts:
paths:
- .hotspots/report.html
only:
- merge_requestsDocker Image (Faster)
hotspots:
stage: analyze
image: ghcr.io/stephen-collins-tech/hotspots:latest
script:
- hotspots analyze src/ --mode delta --policy --format json
artifacts:
paths:
- .hotspots/report.htmlCircleCI
Configuration
Add to .circleci/config.yml:
version: 2.1
jobs:
hotspots:
docker:
- image: rust:latest
steps:
- checkout
- run:
name: Install Hotspots
command: cargo install hotspots-cli
- run:
name: Run Analysis
command: hotspots analyze src/ --mode delta --policy --format json
- store_artifacts:
path: .hotspots/report.html
destination: hotspots-report
workflows:
analyze:
jobs:
- hotspots:
filters:
branches:
only: mainWith Caching
jobs:
hotspots:
docker:
- image: rust:latest
steps:
- checkout
- restore_cache:
keys:
- cargo-v1-{{ checksum "Cargo.lock" }}
- cargo-v1-
- run:
name: Install Hotspots
command: |
if ! command -v hotspots &> /dev/null; then
cargo install hotspots-cli
fi
- save_cache:
key: cargo-v1-{{ checksum "Cargo.lock" }}
paths:
- ~/.cargo
- run:
name: Run Analysis
command: hotspots analyze src/ --mode delta --policyTravis CI
Configuration
Add to .travis.yml:
language: rust
rust:
- stable
install:
- cargo install hotspots-cli
script:
- hotspots analyze src/ --mode delta --policy --format json
after_success:
- echo "Hotspots analysis passed"PR-Only Analysis
jobs:
include:
- stage: analyze
if: type = pull_request
script:
- hotspots analyze src/ --mode delta --policyJenkins
Pipeline Script
pipeline {
agent any
stages {
stage('Setup') {
steps {
sh 'cargo install hotspots-cli || true'
}
}
stage('Analyze') {
steps {
sh 'hotspots analyze src/ --mode delta --policy --format json > hotspots-output.json'
}
}
stage('Report') {
steps {
archiveArtifacts artifacts: '.hotspots/report.html', fingerprint: true
publishHTML([
reportDir: '.hotspots',
reportFiles: 'report.html',
reportName: 'Hotspots Report'
])
}
}
}
post {
failure {
echo 'Hotspots found blocking violations'
}
}
}Freestyle Project
Build Steps:
# Install Hotspots (first time)
cargo install hotspots-cli
# Run analysis
hotspots analyze src/ --mode delta --policy --format jsonPost-build Actions:
- Archive artifacts:
.hotspots/report.html - Publish HTML reports:
.hotspots/report.html
Bitbucket Pipelines
Configuration
Add to bitbucket-pipelines.yml:
pipelines:
default:
- step:
name: Hotspots Analysis
image: rust:latest
caches:
- cargo
script:
- cargo install hotspots-cli
- hotspots analyze src/ --mode delta --policy --format json
artifacts:
- .hotspots/report.html
pull-requests:
'**':
- step:
name: Hotspots PR Check
image: rust:latest
script:
- cargo install hotspots-cli
- hotspots analyze src/ --mode delta --policyDocker Integration
Using Pre-built Image
docker run --rm -v $(pwd):/workspace ghcr.io/stephen-collins-tech/hotspots:latest \
analyze /workspace/src --mode delta --policy --format jsonCustom Dockerfile
FROM rust:latest as builder
RUN cargo install hotspots-cli
FROM debian:bookworm-slim
COPY --from=builder /usr/local/cargo/bin/hotspots /usr/local/bin/
WORKDIR /workspace
ENTRYPOINT ["hotspots"]Build and use:
docker build -t hotspots:custom .
docker run --rm -v $(pwd):/workspace hotspots:custom analyze /workspace/srcEnvironment Variables
Hotspots respects standard git and CI environment variables:
Git Context
GIT_DIR- Override.gitdirectoryGIT_WORK_TREE- Override working directory
PR Detection (for delta mode)
GitHub Actions:
GITHUB_EVENT_NAME=pull_request
GitLab CI:
CI_MERGE_REQUEST_IID
CircleCI:
CIRCLE_PULL_REQUEST
Travis CI:
TRAVIS_PULL_REQUEST
When detected, delta mode compares vs. merge-base instead of direct parent.
Exit Codes
| Code | Meaning | Triggered By |
|---|---|---|
0 | Success | No violations, or only warnings |
1 | Failure | Blocking policy violations |
Control exit behavior:
# Fail on errors only (default)
hotspots analyze src/ --mode delta --policy
# Fail on warnings too
hotspots analyze src/ --mode delta --policy --fail-on warnings
# Never fail (for reporting only)
hotspots analyze src/ --mode delta --policy --fail-on neverIn CI scripts:
# Ignore exit code (always pass)
hotspots analyze src/ --mode delta --policy || true
# Custom handling
if ! hotspots analyze src/ --mode delta --policy; then
echo "Hotspots found violations, but continuing..."
fiCaching Strategies
GitHub Actions
- name: Cache Hotspots Binary
uses: actions/cache@v4
with:
path: ~/.cargo/bin/hotspots
key: hotspots-${{ runner.os }}-v1.0.0
- name: Install or Use Cached Hotspots
run: |
if ! command -v hotspots &> /dev/null; then
cargo install hotspots-cli --version 1.0.0
fiGitLab CI
cache:
key: hotspots-${CI_COMMIT_REF_SLUG}
paths:
- ~/.cargo/bin/hotspots
- .hotspots/snapshots/CircleCI
- restore_cache:
keys:
- hotspots-v1-{{ .Branch }}
- hotspots-v1-
- save_cache:
key: hotspots-v1-{{ .Branch }}
paths:
- ~/.cargo/bin/hotspotsMulti-Language Projects
Analyze Multiple Directories
- name: Analyze TypeScript
run: hotspots analyze src/ --mode delta --policy
- name: Analyze Go
run: hotspots analyze backend/ --mode delta --policy
- name: Analyze Python
run: hotspots analyze scripts/ --mode delta --policyCombined Report
# Analyze all at once (recommended)
hotspots analyze . --config .hotspotsrc.json --mode delta --policy.hotspotsrc.json:
{
"include": [
"src/**/*.ts",
"backend/**/*.go",
"scripts/**/*.py"
],
"exclude": [
"**/*.test.*",
"**/node_modules/**"
]
}Troubleshooting
"not in a git repository"
Cause: Missing .git directory or shallow clone.
Fix: Use fetch-depth: 0 in checkout:
- uses: actions/checkout@v4
with:
fetch-depth: 0"failed to extract git context"
Cause: Missing git metadata (commit SHA, parents).
Fix: Ensure full git history:
git fetch --unshallow # If shallow
git fetch origin main:main # Fetch base branch"merge-base not found"
Cause: PR base branch not available.
Fix (GitHub Actions):
- uses: actions/checkout@v4
with:
fetch-depth: 0
ref: ${{ github.event.pull_request.head.ref }}Fix (GitLab CI):
before_script:
- git fetch origin $CI_MERGE_REQUEST_TARGET_BRANCH_NAMEExit Code Always 0
Cause: Using || true or fail-on: never.
Fix: Remove exit code overrides:
# Bad
hotspots analyze src/ --mode delta --policy || true
# Good
hotspots analyze src/ --mode delta --policyPre-commit Hooks
Block pushes with complexity regressions before they reach CI using hotspots init --hooks.
Setup
# 1. Seed a baseline snapshot (required — delta mode compares against this)
hotspots analyze . --mode snapshot
# 2. Print hook templates
hotspots init --hooksOption 1: pre-commit Framework
Add the printed YAML block to .pre-commit-config.yaml:
repos:
- repo: local
hooks:
- id: hotspots
name: hotspots risk check
language: system
entry: hotspots analyze . --mode delta --policy --format text
pass_filenames: false
stages: [pre-push]Then install:
pre-commit install --hook-type pre-pushOption 2: Raw Shell Hook
Copy the printed shell block (starting with #!/usr/bin/env sh) to .git/hooks/pre-push:
hotspots init --hooks 2>/dev/null | sed -n '/^#!/,$ p' > .git/hooks/pre-push
chmod +x .git/hooks/pre-pushOr copy manually — the hook content is:
#!/usr/bin/env sh
set -e
hotspots analyze . --mode delta --policy --format textHow It Works
- Runs on
pre-push, notpre-commit, to avoid slowing down every commit - Delta mode compares the working tree against the last snapshot
- Exits non-zero if blocking policy violations are found, preventing the push
- If no baseline snapshot exists yet, policy evaluation is silently skipped
GitHub Code Scanning (SARIF)
Upload SARIF output to GitHub to get inline annotations on pull requests and alerts in the Security tab.
Workflow
name: Hotspots SARIF
on:
push:
branches: [main]
pull_request:
jobs:
scan:
runs-on: ubuntu-latest
permissions:
contents: read
security-events: write # Required for upload-sarif
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Run Hotspots (snapshot)
run: hotspots analyze . --mode snapshot --format sarif --output .hotspots/results.sarif
- name: Upload SARIF to GitHub
uses: github/codeql-action/upload-sarif@v3
with:
sarif_file: .hotspots/results.sarifRisk Band → SARIF Level Mapping
| Hotspots band | SARIF level | GitHub annotation |
|---|---|---|
critical | error | Red |
high | warning | Yellow |
moderate | note | Blue |
low | — | Not emitted |
Custom Output Path
hotspots analyze . --mode snapshot --format sarif --output sarif-results/hotspots.sarif- name: Upload SARIF
uses: github/codeql-action/upload-sarif@v3
with:
sarif_file: sarif-results/hotspots.sarifSee SARIF Format for complete output format documentation.
Best Practices
1. Use Delta Mode in CI
# ✅ Good - Delta mode for PRs
- run: hotspots analyze src/ --mode delta --policy
# ❌ Bad - Snapshot mode without comparison
- run: hotspots analyze src/ --format json2. Separate Dev and CI Configs
Development: .hotspotsrc.json
{
"min_lrs": 0.0,
"top": 20
}CI: .hotspots.ci.json
{
"min_lrs": 5.0,
"thresholds": {
"moderate": 5.0,
"high": 8.0,
"critical": 10.0
}
}Use in CI:
- run: hotspots analyze src/ --config .hotspots.ci.json --mode delta --policy3. Archive Reports
- uses: actions/upload-artifact@v4
if: always()
with:
name: hotspots-report-${{ github.sha }}
path: .hotspots/report.html
retention-days: 304. Post PR Comments
- uses: Stephen-Collins-tech/hotspots-action@v1
with:
post-comment: true # Default for PRs
github-token: ${{ secrets.GITHUB_TOKEN }}5. Gradual Rollout
Start with warnings only:
- run: hotspots analyze src/ --mode delta --policy --fail-on neverThen tighten:
- run: hotspots analyze src/ --mode delta --policy --fail-on errorRelated Documentation
- GitHub Action Guide - Complete GitHub Action documentation
- Configuration - Config file format and options
- CLI Reference - All CLI commands and flags
- Output Formats - JSON schema, HTML reports, SARIF
- Policy Engine - Policy rules and enforcement
Examples Repository
See examples/ci-cd/ for complete working examples:
- GitHub Actions
- GitLab CI
- CircleCI
- Jenkins
- Docker
Need help? Open an issue on GitHub.