Skip to main content
Recipes

Recipe: Add Travsr to CI

Run Travsr in your CI pipeline to catch blast-radius regressions, enforce dependency rules, or generate change impact reports on every PR.

GitHub Actions example

# .github/workflows/travsr.yml
name: Travsr graph check

on:
pull_request:

jobs:
blast-radius:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 2 # need HEAD~1 for delta index

- name: Install Travsr
run: npm install -g @travsr.com/travsr

- name: Index repository
# --semantic runs Phase B (call edges) before returning so the graph is
# query-ready immediately. The git hook is installed but never fires in CI.
run: travsr init --semantic

- name: Check blast radius of changed files
run: |
CHANGED=$(git diff --name-only HEAD~1 HEAD | grep -E '\.(ts|rs|py|go)$' || true)
if [ -z "$CHANGED" ]; then
echo "No source files changed."
exit 0
fi

for FILE in $CHANGED; do
echo "=== Blast radius: $FILE ==="
# --direction callers follows incoming edges: everything that depends on this file.
travsr graph "$FILE" --direction callers --format json \
| jq -r '.nodes | length as $n | "\($n) affected nodes"'
done

- name: Fail if a change affects more than 50 nodes
run: |
for FILE in $(git diff --name-only HEAD~1 HEAD | grep -E '\.(ts|rs)$'); do
COUNT=$(travsr graph "$FILE" --direction callers --format json | jq '.nodes | length')
if [ "$COUNT" -gt 50 ]; then
echo "HIGH BLAST RADIUS: $FILE affects $COUNT nodes (> 50)"
exit 1
fi
done

Caching the graph between runs

For large repositories, cache .travsr/graph.db between CI runs to avoid re-indexing from scratch:

- uses: actions/cache@v4
with:
path: .travsr/graph.db
key: travsr-${{ runner.os }}-${{ hashFiles('**/*.ts', '**/*.rs') }}
restore-keys: travsr-${{ runner.os }}-

- name: Index repository
# travsr init is idempotent: it refreshes an existing graph or builds one
# from scratch. With a cached .travsr/graph.db only changed files are re-parsed.
run: travsr init --semantic

Dependency rule enforcement

Enforce architectural rules (e.g. "controllers must not import db directly"):

# --direction deps follows outgoing edges: what this file imports / depends on.
travsr graph "src/controllers/UserController.ts" --direction deps --format json \
| jq -e '.nodes[] | select(.path | startswith("src/db/"))' >/dev/null \
&& { echo "VIOLATION: Controller imports db directly"; exit 1; } \
|| echo "OK: no direct db import"