From b2a28c098efdfbddce7e656e49e152d8df35a50b Mon Sep 17 00:00:00 2001 From: mochan Date: Mon, 27 Apr 2026 21:36:17 +0530 Subject: [PATCH] feat(ci): codegraph PR review scaffold (template + dry-run script + dependabot) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The PR-review workflow lives at .github/ci-templates/pr-review.workflow.yml (outside .github/workflows/ so a token without the `workflow` OAuth scope can still push the repo). Activation is a one-time copy + push: gh auth refresh -h github.com -s workflow cp .github/ci-templates/pr-review.workflow.yml .github/workflows/pr-review.yml git add .github/workflows/pr-review.yml && git commit && git push Once active, every PR against main: 1. Worktree-checkout origin/main, build, save baseline 2. Build PR head graph 3. codegraph review --fail-on high --format markdown --baseline ... 4. Sticky PR comment via peter-evans/create-or-update-comment (comment-tag: codegraph-review — replaces, doesn't stack) 5. Fail check if findings exceed threshold Concurrency group on PR number with cancel-in-progress; review.md + comment.md + graph.db uploaded as a 7-day artifact for debugging. First-PR graceful path: if no baseline can be saved from main, post a friendly "first-time review" comment and pass. Also adds: - .github/PULL_REQUEST_TEMPLATE.md with a codegraph-signals section - .github/dependabot.yml — weekly pip + github-actions - scripts/test-pr-review-locally.sh — local dry-run that emulates the workflow exactly (worktree-add origin/main, baseline save, review) - README "PR review CI" section with activation + dry-run docs No production code changed; 467 tests still pass; ruff + mypy --strict clean. --- .github/PULL_REQUEST_TEMPLATE.md | 16 +++ .github/ci-templates/pr-review.workflow.yml | 136 ++++++++++++++++++++ .github/dependabot.yml | 19 +++ README.md | 41 ++++++ scripts/test-pr-review-locally.sh | 65 ++++++++++ 5 files changed, 277 insertions(+) create mode 100644 .github/PULL_REQUEST_TEMPLATE.md create mode 100644 .github/ci-templates/pr-review.workflow.yml create mode 100644 .github/dependabot.yml create mode 100755 scripts/test-pr-review-locally.sh diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 0000000..c4ee1f5 --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,16 @@ +## What this PR does + + + +## Why + + + +## codegraph signals + + diff --git a/.github/ci-templates/pr-review.workflow.yml b/.github/ci-templates/pr-review.workflow.yml new file mode 100644 index 0000000..8386ea9 --- /dev/null +++ b/.github/ci-templates/pr-review.workflow.yml @@ -0,0 +1,136 @@ +# codegraph PR review workflow — TEMPLATE +# +# This file lives OUTSIDE .github/workflows/ on purpose: GitHub rejects +# pushes to .github/workflows/* from OAuth tokens that lack the `workflow` +# scope. By keeping the template here, the repo can be cloned + pushed-to +# with the default `repo` scope; activating the workflow is a one-time +# manual step (see README → "PR review CI"). +# +# To activate: +# gh auth refresh -h github.com -s workflow +# cp .github/ci-templates/pr-review.workflow.yml .github/workflows/pr-review.yml +# git add .github/workflows/pr-review.yml +# git commit -m "ci: activate codegraph PR review" +# git push + +name: codegraph PR review + +on: + pull_request: + branches: [main] + types: [opened, synchronize, reopened] + +permissions: + contents: read + pull-requests: write + +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number }} + cancel-in-progress: true + +jobs: + review: + runs-on: ubuntu-latest + timeout-minutes: 15 + + steps: + - name: Checkout PR head + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Setup Python + uses: actions/setup-python@v5 + with: + python-version: "3.10" + cache: pip + + - name: Install codegraph (PR head) + run: pip install -e . + + - name: Build baseline graph from main + run: | + set -euxo pipefail + mkdir -p .baseline-checkout + git worktree add .baseline-checkout origin/main + ( + cd .baseline-checkout + pip install -e . --quiet + codegraph build --no-incremental + codegraph baseline save --output ../.codegraph/baseline.db + ) + if [ ! -f .codegraph/baseline.db ]; then + echo "BASELINE_MISSING=1" >> "$GITHUB_ENV" + echo "::warning::No baseline could be saved from main; running first-time review." + fi + + - name: Build PR head graph + run: codegraph build --no-incremental + + - name: Run codegraph review + run: | + set -uo pipefail + if [ -n "${BASELINE_MISSING:-}" ]; then + cat > review.md <<'EOF' + ## codegraph PR review + + No baseline graph from `main` was available for this run, so a + full diff review was skipped. The PR head was still parsed + successfully — codegraph review will activate from the next PR. + EOF + exit 0 + fi + set +e + codegraph review \ + --format markdown \ + --output review.md \ + --fail-on high \ + --baseline .codegraph/baseline.db + rc=$? + set -e + if [ "$rc" -ne 0 ]; then + echo "REVIEW_FAILED=1" >> "$GITHUB_ENV" + fi + + - name: Wrap review in comment template + env: + RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} + run: | + set -e + { + echo "## codegraph PR review" + echo + echo "
" + echo "Diff vs main · severity ≤ high" + echo + cat review.md + echo + echo "
" + echo + echo "Triggered by codegraph CI · last run" + } > comment.md + + - name: Post or update PR comment + uses: peter-evans/create-or-update-comment@v4 + with: + issue-number: ${{ github.event.pull_request.number }} + body-file: comment.md + edit-mode: replace + comment-tag: codegraph-review + + - name: Upload review artifact + if: always() + uses: actions/upload-artifact@v4 + with: + name: codegraph-review + path: | + review.md + comment.md + .codegraph/graph.db + retention-days: 7 + + - name: Fail if findings exceed threshold + if: env.REVIEW_FAILED == '1' + run: | + echo "::error::codegraph review found high-or-critical findings. See PR comment for details." + exit 1 diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..d19d09c --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,19 @@ +version: 2 +updates: + - package-ecosystem: "pip" + directory: "/" + schedule: + interval: "weekly" + open-pull-requests-limit: 5 + labels: + - "dependencies" + - "python" + + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "weekly" + open-pull-requests-limit: 3 + labels: + - "dependencies" + - "ci" diff --git a/README.md b/README.md index 35799e3..03e8af0 100644 --- a/README.md +++ b/README.md @@ -417,6 +417,47 @@ node --test tests/*.js # 55 JS tests --- +## PR review CI (dogfood) + +`codegraph` ships its own PR-review workflow as a template. Once activated, +every PR opened against `main` runs codegraph against itself, posts the diff +as a sticky PR comment, and fails the check on high-severity findings. + +The workflow lives at +[`.github/ci-templates/pr-review.workflow.yml`](.github/ci-templates/pr-review.workflow.yml) +(outside `.github/workflows/` so the repo can be cloned and pushed-to with a +token that lacks the `workflow` OAuth scope). Activate with: + +```bash +gh auth refresh -h github.com -s workflow +cp .github/ci-templates/pr-review.workflow.yml .github/workflows/pr-review.yml +git add .github/workflows/pr-review.yml +git commit -m "ci: activate codegraph PR review" +git push +``` + +Once active, the workflow: + +1. Builds a graph from `origin/main` and saves it as a baseline. +2. Builds a graph from the PR head. +3. Runs `codegraph review --format markdown --fail-on high` against the diff. +4. Posts the result as a sticky PR comment (replaced on each push, no spam). +5. Fails the check if any high-or-critical findings appear. + +**Local dry-run** before opening a PR: + +```bash +git fetch origin main +./scripts/test-pr-review-locally.sh +# writes review.md + comment.md, exits non-zero if findings exceed --fail-on high +``` + +First-PR graceful path: if no baseline can be saved from `main` (brand-new +repo, empty default branch), the workflow posts a friendly "first-time +review" comment and passes — codegraph review activates from the next PR. + +--- + ## Acknowledgements `codegraph` stands on: diff --git a/scripts/test-pr-review-locally.sh b/scripts/test-pr-review-locally.sh new file mode 100755 index 0000000..87ad070 --- /dev/null +++ b/scripts/test-pr-review-locally.sh @@ -0,0 +1,65 @@ +#!/usr/bin/env bash +# Dry-run the PR review workflow locally so you can validate it before pushing. +# +# Requires: +# - clean repo (no uncommitted changes) +# - origin/main fetched (`git fetch origin main`) +# - codegraph installed in editable mode (`pip install -e .`) +# +# Produces ./review.md and ./comment.md exactly like the CI does, but skips +# the GitHub-API steps (comment posting, artifact upload, exit code). +set -euo pipefail + +REPO_ROOT=$(git rev-parse --show-toplevel) +cd "$REPO_ROOT" + +if ! git diff-index --quiet HEAD --; then + echo "[WARN] working tree has uncommitted changes; review will reflect them" >&2 +fi + +if ! command -v codegraph >/dev/null 2>&1; then + echo "[ERROR] codegraph not on PATH — run \`pip install -e .\` first" >&2 + exit 1 +fi + +echo "==> Building baseline from origin/main" +rm -rf /tmp/cg-baseline-checkout +git worktree add /tmp/cg-baseline-checkout origin/main +trap 'git worktree remove --force /tmp/cg-baseline-checkout 2>/dev/null || true' EXIT + +( + cd /tmp/cg-baseline-checkout + codegraph build --no-incremental + codegraph baseline save --output "$REPO_ROOT/.codegraph/baseline.db" +) + +echo "==> Building PR-head graph" +codegraph build --no-incremental + +echo "==> Running codegraph review" +set +e +codegraph review \ + --format markdown \ + --output review.md \ + --fail-on high \ + --baseline .codegraph/baseline.db +rc=$? +set -e + +echo "==> Wrapping into comment.md" +{ + echo "## codegraph PR review" + echo + echo "
" + echo "Diff vs main · severity ≤ high" + echo + cat review.md + echo + echo "
" +} > comment.md + +echo +echo "----" +echo "review.md and comment.md written. Review exit code: $rc" +echo "(Non-zero = high/critical findings; CI would fail on this.)" +exit "$rc"