Skip to content

feat(actions): add reusable code-review composite action#5

Open
mabry1985 wants to merge 1 commit into
mainfrom
feat/code-review-composite-action
Open

feat(actions): add reusable code-review composite action#5
mabry1985 wants to merge 1 commit into
mainfrom
feat/code-review-composite-action

Conversation

@mabry1985
Copy link
Copy Markdown
Contributor

@mabry1985 mabry1985 commented May 22, 2026

Bundles the async-parallel PR review flow that consumes review-code as a single GitHub Action so downstream protoLabs repos don't each re-implement the workflow plumbing.

What's in here

  • .github/actions/code-review/action.yml — composite action that runs review-code init → map → run → report through the gateway and posts findings as a sticky PR comment. continue-on-error: true on the review step keeps it non-blocking. Marker-based dedup on the comment so re-runs PATCH the existing comment rather than stacking new ones.
  • .github/actions/code-review/post-findings.mjs — sticky comment poster used by the composite action.
  • README — documents the action under "Model-backed code review" with inputs table, behavior notes, and required secrets.

Why a composite action instead of leaving each repo to write its own

protoMaker already shipped a workflow that did exactly this (PR #3615 in protoMaker); quinn, AionUi, and any other consumer would copy-paste the same YAML. The sticky-comment script, the env-var translation, the timeout config, the continue-on-error choice — none of it is repo-specific. Owning the plumbing here keeps it consistent across consumers and lets bug fixes flow on a version bump instead of per-repo PRs.

Usage

# .github/workflows/code-review.yml in a consuming repo
name: Code Review

on:
  pull_request:
    branches: [main]

permissions:
  contents: read
  pull-requests: write

jobs:
  review:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with: { fetch-depth: 0 }
      - uses: actions/setup-node@v4
        with: { node-version: '22' }
      - uses: protoLabsAI/release-tools/.github/actions/code-review@main
        with:
          gateway-api-key: ${{ secrets.GATEWAY_API_KEY }}
          gateway-base-url: ${{ secrets.GATEWAY_BASE_URL }}

Inputs

Input Required Default Description
gateway-api-key yes Bearer token for the gateway. Passed as OPENAI_API_KEY.
gateway-base-url no https://api.proto-labs.ai Gateway base URL without /v1 — appended automatically.
model no protolabs/smart (review-code default) Override the gateway model alias.
timeout-ms no 300000 Per-request timeout.
pr-number no from event PR number to comment on; skip the sticky comment if empty.
github-token no ${{ github.token }} For posting the sticky comment.
release-tools-ref no main release-tools ref to install (branch, tag, commit).

Validation

  • YAML lints cleanly (js-yaml parse).
  • node --check clean on post-findings.mjs.
  • review-code itself already validated against the gateway from protoMaker (PR #3615) — local end-to-end run with the same secrets this action expects.
  • Live PR test — happens after merge, when a consuming repo wires it in.

Follow-up

protoMaker will get a separate slim PR that drops its bespoke code-review.yml (54 lines + sticky-comment script) and replaces it with a 5-line workflow calling this action.

Summary by CodeRabbit

New Features

  • Added automated code review workflow that analyzes pull request changes and generates findings reports
  • Code review findings are now automatically posted as sticky comments on pull requests for easier tracking and discussion

Review Change Stack

Bundles the async-parallel PR review flow that consumes `review-code`
as a single GitHub Action so downstream protoLabs repos don't each
re-implement the workflow plumbing.

## What's in here

- `.github/actions/code-review/action.yml` — composite action that runs
  `review-code init → map → run → report` through the gateway and posts
  findings as a sticky PR comment. `continue-on-error: true` on the
  review step keeps it non-blocking. Marker-based dedup on the comment
  so re-runs `PATCH` the existing comment rather than stacking new ones.
- `.github/actions/code-review/post-findings.mjs` — sticky comment poster
  used by the composite action.
- README: documents the action under "Model-backed code review" with
  inputs table, behavior notes, and required secrets.

## Why a composite action instead of leaving each repo to write its own

protoMaker already shipped a workflow that did exactly this; quinn,
AionUi, and any other consumer would copy-paste the same YAML. The
sticky-comment script, the env-var translation, the timeout config,
the `continue-on-error` choice — none of it is repo-specific. Owning
the plumbing here keeps it consistent across consumers and lets bug
fixes flow on a version bump instead of per-repo PRs.

## Usage

```yaml
- uses: protoLabsAI/release-tools/.github/actions/code-review@main
  with:
    gateway-api-key: ${{ secrets.GATEWAY_API_KEY }}
    gateway-base-url: ${{ secrets.GATEWAY_BASE_URL }}
```

Inputs: gateway-api-key (required), gateway-base-url, model, timeout-ms,
pr-number, github-token, release-tools-ref. Outputs: findings,
report-path.

## Validation

- YAML lints cleanly (js-yaml parse).
- `node --check` clean on post-findings.mjs.
- review-code itself is already validated against the gateway from
  protoMaker (PR #3615) — local end-to-end run with the same secrets
  this action expects.
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 22, 2026

Walkthrough

This PR introduces a new composite GitHub Action for protoLabsAI/release-tools that orchestrates LLM-backed code review via the review-code CLI. The action initializes repository features, runs async review, generates markdown reports, and posts findings as sticky PR comments using a Node.js helper script.

Changes

Code Review Workflow Action

Layer / File(s) Summary
Action metadata and public interface
.github/actions/code-review/action.yml
Defines the action name, description, branding, configurable inputs for gateway API key, base URL, model, timeout, GitHub token, PR number, and release-tools ref, plus public outputs for findings count and report path.
Review execution workflow
.github/actions/code-review/action.yml
Initializes review state, maps repository features, and executes async-parallel review via review-code CLI with gateway credentials and optional model override; extracts findings count from run log and writes to GitHub output with continue-on-error: true for non-blocking execution.
Report generation and sticky comment posting
.github/actions/code-review/action.yml, .github/actions/code-review/post-findings.mjs
Generates markdown report file from review CLI output, conditionally invokes post-findings.mjs to post or update findings as a sticky PR comment (matching via HTML marker), appends report contents to GitHub Actions step summary with findings header. Node script validates environment variables, reads report, wraps gh CLI to query and update existing comments or create new ones, with all failures logged but non-blocking.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~12 minutes

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat(actions): add reusable code-review composite action' directly and clearly summarizes the main change: adding a new reusable GitHub composite action for code review.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/code-review-composite-action

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.github/actions/code-review/action.yml:
- Around line 80-83: Replace the fixed /tmp/code-review.md with a unique per-run
temp file and update the subsequent existence check and GITHUB_OUTPUT write to
use that variable; specifically change the npx review-code command invocation
(the line calling "npx ... review-code report --output /tmp/code-review.md") to
write to a temp file created at runtime (e.g., via mktemp or using
RUNNER_TEMP/GITHUB_RUN_ID) and then change the if check and the echo that sets
report_path to reference that same temp variable instead of the hard-coded path,
optionally also ensure the script verifies the file is non-empty before
exporting report_path to avoid using stale/empty files.

In @.github/actions/code-review/post-findings.mjs:
- Around line 56-58: The current code assigns existingId = match.id from
gh(['pr','view', prNumber, '--json', 'comments']) but then PATCHes
/repos/${owner}/${repo}/issues/comments/${existingId}, which requires the REST
numeric comment ID; update the lookup so existingId is the REST comment id:
replace the gh pr view comments fetch with a call to the REST API (e.g., gh api
repos/:owner/:repo/issues/:issue_number/comments) and find the comment whose
body includes MARKER, then set existingId to that comment's id before calling
the PATCH endpoint; ensure the variables referenced are existingId, match (or
the found comment), and the PATCH URL
/repos/${owner}/${repo}/issues/comments/${existingId}.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 2ed5236c-dc70-490b-afa7-fb08dee1ed11

📥 Commits

Reviewing files that changed from the base of the PR and between 3fafad5 and c2f9f93.

⛔ Files ignored due to path filters (1)
  • README.md is excluded by !*.md
📒 Files selected for processing (2)
  • .github/actions/code-review/action.yml
  • .github/actions/code-review/post-findings.mjs

Comment on lines +80 to +83
npx --yes -p "github:protoLabsAI/release-tools#${{ inputs.release-tools-ref }}" review-code report --output /tmp/code-review.md 2>&1 | tee /tmp/report.log || true
if [ -f /tmp/code-review.md ]; then
echo "report_path=/tmp/code-review.md" >> "$GITHUB_OUTPUT"
fi
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Avoid stale report reuse by using a unique temp file per run.

Line 80 writes to a fixed /tmp/code-review.md and Line 81 only checks existence. On persistent runners, a previous file can be reused if report generation fails, leading to stale findings being posted.

Proposed fix
-        npx --yes -p "github:protoLabsAI/release-tools#${{ inputs.release-tools-ref }}" review-code report --output /tmp/code-review.md 2>&1 | tee /tmp/report.log || true
-        if [ -f /tmp/code-review.md ]; then
-          echo "report_path=/tmp/code-review.md" >> "$GITHUB_OUTPUT"
+        report_file="${RUNNER_TEMP}/code-review-${GITHUB_RUN_ID}-${GITHUB_RUN_ATTEMPT}.md"
+        rm -f "$report_file"
+        npx --yes -p "github:protoLabsAI/release-tools#${{ inputs.release-tools-ref }}" review-code report --output "$report_file" 2>&1 | tee /tmp/report.log || true
+        if [ -s "$report_file" ]; then
+          echo "report_path=$report_file" >> "$GITHUB_OUTPUT"
         fi
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
npx --yes -p "github:protoLabsAI/release-tools#${{ inputs.release-tools-ref }}" review-code report --output /tmp/code-review.md 2>&1 | tee /tmp/report.log || true
if [ -f /tmp/code-review.md ]; then
echo "report_path=/tmp/code-review.md" >> "$GITHUB_OUTPUT"
fi
report_file="${RUNNER_TEMP}/code-review-${GITHUB_RUN_ID}-${GITHUB_RUN_ATTEMPT}.md"
rm -f "$report_file"
npx --yes -p "github:protoLabsAI/release-tools#${{ inputs.release-tools-ref }}" review-code report --output "$report_file" 2>&1 | tee /tmp/report.log || true
if [ -s "$report_file" ]; then
echo "report_path=$report_file" >> "$GITHUB_OUTPUT"
fi
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/actions/code-review/action.yml around lines 80 - 83, Replace the
fixed /tmp/code-review.md with a unique per-run temp file and update the
subsequent existence check and GITHUB_OUTPUT write to use that variable;
specifically change the npx review-code command invocation (the line calling
"npx ... review-code report --output /tmp/code-review.md") to write to a temp
file created at runtime (e.g., via mktemp or using RUNNER_TEMP/GITHUB_RUN_ID)
and then change the if check and the echo that sets report_path to reference
that same temp variable instead of the hard-coded path, optionally also ensure
the script verifies the file is non-empty before exporting report_path to avoid
using stale/empty files.

Comment on lines +56 to +58
const comments = JSON.parse(gh(['pr', 'view', prNumber, '--json', 'comments']));
const match = (comments.comments ?? []).find((c) => c.body?.includes(MARKER));
if (match) existingId = match.id;
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

❓ Verification inconclusive

🏁 Script executed:

#!/bin/bash
# Verify whether comment IDs from `gh pr view --json comments` are REST-patchable IDs.
set -euo pipefail

PR_NUMBER="${PR_NUMBER:?set PR_NUMBER}"
# Shows the shape of IDs returned by pr view
gh pr view "$PR_NUMBER" --json comments --jq '.comments[] | {id, url}' | head -n 5

# Shows numeric IDs from REST issue comments for the same PR
gh api "repos/${GITHUB_REPOSITORY}/issues/${PR_NUMBER}/comments" --jq '.[] | {id, html_url}' | head -n 5

Repository: protoLabsAI/release-tools

Length of output: 113


Confirm PATCH /issues/comments/{comment_id} uses REST numeric IDs; existingId currently comes from gh pr view --json comments.

post-findings.mjs sets existingId = match.id from gh pr view <prNumber> --json comments, then PATCHes /repos/${owner}/${repo}/issues/comments/${existingId}. The {comment_id} path for that REST endpoint must be the REST comment ID; ensure the id returned by gh pr view --json comments is numeric/REST-compatible, or the script may not update the existing comment.

Proposed fix (lookup via REST comments so IDs are patchable)
-let existingId = null;
+let existingId = null;
 try {
-  const comments = JSON.parse(gh(['pr', 'view', prNumber, '--json', 'comments']));
-  const match = (comments.comments ?? []).find((c) => c.body?.includes(MARKER));
+  const comments = JSON.parse(
+    gh([
+      'api',
+      `repos/${process.env.GITHUB_REPOSITORY}/issues/${prNumber}/comments`,
+      '--paginate',
+    ]),
+  );
+  const match = (comments ?? []).find((c) => c.body?.includes(MARKER));
   if (match) existingId = match.id;
 } catch (err) {
   console.error(`Failed to list comments (continuing as new): ${err.message}`);
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/actions/code-review/post-findings.mjs around lines 56 - 58, The
current code assigns existingId = match.id from gh(['pr','view', prNumber,
'--json', 'comments']) but then PATCHes
/repos/${owner}/${repo}/issues/comments/${existingId}, which requires the REST
numeric comment ID; update the lookup so existingId is the REST comment id:
replace the gh pr view comments fetch with a call to the REST API (e.g., gh api
repos/:owner/:repo/issues/:issue_number/comments) and find the comment whose
body includes MARKER, then set existingId to that comment's id before calling
the PATCH endpoint; ensure the variables referenced are existingId, match (or
the found comment), and the PATCH URL
/repos/${owner}/${repo}/issues/comments/${existingId}.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant