chore: init from code-review-graph-main snapshot (v2.3.7)
This commit is contained in:
@@ -0,0 +1,190 @@
|
||||
# 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)
|
||||
|
||||
```yaml
|
||||
# .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/[email protected]
|
||||
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:
|
||||
|
||||
```yaml
|
||||
- uses: tirth8205/[email protected]
|
||||
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](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`](../.github/workflows/pr-review.yml) and
|
||||
[`.github/workflows/pr-review-comment.yml`](../.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](https://securitylab.github.com/resources/github-actions-preventing-pwn-requests/)).
|
||||
|
||||
## Dogfooding
|
||||
|
||||
This repository runs the action on its own PRs via
|
||||
[`.github/workflows/pr-review.yml`](../.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`](../.github/workflows/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`](../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:
|
||||
|
||||
```bash
|
||||
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
|
||||
```
|
||||
Reference in New Issue
Block a user