Skip to content

Latest commit

 

History

History
190 lines (157 loc) · 8.38 KB

File metadata and controls

190 lines (157 loc) · 8.38 KB

GitHub Action: Risk-Scored PR Review

code-review-graph ships a composite GitHub Action (action.yml at the repo root) that posts a risk-scored, graph-aware review comment on every pull request — think of it as a hosted AI review bot (Greptile-style), except the analysis is local-first: the knowledge graph is built and queried entirely on your CI runner, and no source code is sent to any external service.

On each PR run the action:

  1. Installs code-review-graph from PyPI.
  2. Restores the cached .code-review-graph/ SQLite graph (or builds it from scratch on a cache miss) and incrementally re-parses the files changed by the PR.
  3. Runs code-review-graph detect-changes --base origin/<base-branch> to get risk-scored functions, affected execution flows, and test gaps.
  4. Renders a markdown report (via scripts/render_pr_comment.py) and upserts a single sticky PR comment — the same comment is updated on every push, so the PR thread is never spammed.
  5. Optionally fails the job when the overall risk score crosses a threshold (fail-on-risk).

Quick start (external repositories)

# .github/workflows/code-review-graph.yml
name: code-review-graph

on:
  pull_request:

permissions:
  contents: read
  pull-requests: write

jobs:
  review:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v7
      - uses: tirth8205/code-review-graph@v2.3.6
        with:
          github-token: ${{ secrets.GITHUB_TOKEN }}

That is the whole setup. The default GITHUB_TOKEN provided by Actions is sufficient — no PAT, no API key, no third-party service.

Self-hosted runners must be version 2.327.1 or newer. The composite action uses Node 24-based GitHub actions, including actions/setup-python@v6, actions/cache@v6, and the recommended actions/checkout@v7 example.

To turn the review into a merge gate:

      - uses: tirth8205/code-review-graph@v2.3.6
        with:
          github-token: ${{ secrets.GITHUB_TOKEN }}
          fail-on-risk: high

Inputs

Input Required Default Description
github-token yes Token used to post the sticky PR comment via the GitHub API. The workflow's default GITHUB_TOKEN works when the job has pull-requests: write.
comment no true Post (and keep updated) the sticky PR comment. Set to false to run analysis/gating without commenting.
fail-on-risk no none Fail the job when the overall risk score reaches a level: none (never fail), high (risk ≥ 0.70), critical (risk ≥ 0.85).
python-version no 3.12 Python version used to run code-review-graph (3.10+ supported).

Outputs

Output Description
comment-file Runner-local path to the rendered markdown report. Use with comment: false when a separate trusted workflow will publish it.

Risk levels

detect-changes produces a 0.0–1.0 overall risk score (max across changed functions; see code_review_graph/changes.py:compute_risk_score for the scoring factors: flow participation, community crossing, test coverage, security-sensitive names, caller count). The action maps it to levels:

Level Score
low < 0.40
medium 0.40 – 0.69
high 0.70 – 0.84
critical ≥ 0.85

What the comment contains

  • Overall risk score and level, with counts of changed functions, affected flows, and test gaps.
  • Risk-scored changes — a table of the top changed symbols ordered by risk, with file:line locations and test-coverage status.
  • Affected execution flows — which entry-point flows the change touches, ordered by criticality.
  • Test gaps — changed functions with no direct test coverage.
  • Token savings — how many tokens the graph-backed report saved versus reading every changed file in full. This is the same context_savings estimate the CLI's Token Savings panel shows (a chars / 4 approximation labelled estimated: true — see REPRODUCING.md for the calibration methodology).
  • A Powered by code-review-graph footer.

The comment starts with a hidden HTML marker (<!-- code-review-graph-report -->). The action looks the marker up via gh api on each run and PATCHes the existing comment instead of creating a new one (a "sticky" comment).

Cache behavior

The action caches the .code-review-graph/ directory (the SQLite graph database) with actions/cache:

  • Key: code-review-graph-schema9-<runner.os>-<hashFiles(lockfiles)>, where the lockfile hash covers common Python/JS/Go/Rust/Ruby/PHP lockfiles (uv.lock, poetry.lock, requirements*.txt, package-lock.json, go.sum, Cargo.lock, …).
  • Schema segment: schema9 tracks the database schema version (LATEST_VERSION in code_review_graph/migrations.py). It is bumped when the schema changes so stale caches are never restored across incompatible versions.
  • Restore keys: fall back to any cache for the same OS and schema, so a lockfile change still reuses the previous graph.
  • On cache hit: the action runs code-review-graph update --base origin/<base-branch>, which re-parses only the files that differ from the PR's base ref. If the restored database turns out to be unusable, it falls back to a full build.
  • On cache miss: a full code-review-graph build runs (one-time cost; subsequent PR runs are incremental).

Security notes

  • Token scope: direct commenting needs contents: read for checkout and pull-requests: write to post the comment. In the split fork-safe setup, the analysis workflow needs only contents: read; the trusted commenter needs only actions: read and pull-requests: write. Grant exactly those permissions in each workflow.
  • Local-first: analysis runs entirely on the runner. No code, diff, or metadata leaves GitHub's infrastructure; there is no external API, account, or key.
  • Untrusted input: all dynamic values (github.base_ref, the PR number, action inputs) are passed to scripts through environment variables, never interpolated into shell commands. The markdown renderer escapes table/markup characters and strips control characters from symbol names and file paths before they reach the comment body, on top of the server-side _sanitize_name() sanitization.
  • Pinning: when consuming the action from another repository, pin uses: to a release tag or commit SHA rather than @main.
  • Fork PRs: pull_request runs from forks receive a read-only GITHUB_TOKEN, so they cannot post the comment directly. Use an unprivileged pull_request workflow with comment: false, upload the comment-file as an artifact, and publish it from a separate trusted workflow_run workflow. See .github/workflows/pr-review.yml and .github/workflows/pr-review-comment.yml. GitHub loads the workflow_run workflow from the default branch, so the trusted commenting half becomes active only after that workflow is merged. The privileged workflow must verify the source event and analyzed commit, extract only under runner.temp, cap and validate the artifact, and add its own sticky marker before posting. Avoid pull_request_target with a checkout of PR code because it can execute untrusted code with a privileged token (details).

Dogfooding

This repository runs the action on its own PRs via .github/workflows/pr-review.yml, which runs the local action.yml without write permissions and uploads the rendered report. The trusted pr-review-comment.yml workflow validates that artifact and posts the sticky comment without checking out or executing PR-controlled code.

Rendering script

The markdown rendering and risk gating logic lives in scripts/render_pr_comment.py (stdlib only, unit-tested in tests/test_action_render.py) rather than inline YAML, so it can be tested and reused:

code-review-graph detect-changes --base origin/main | \
  python scripts/render_pr_comment.py            # markdown to stdout

python scripts/render_pr_comment.py --input report.json \
  --fail-on-risk high --quiet                    # gate only: exit 3 on breach