AI-powered code review by Single Origin. Reviews pull request files and posts inline comments. Supports reply threading for follow-up conversations.
Add this workflow to your repository at .github/workflows/review.yml:
name: Code Review
on:
pull_request:
types: [opened, synchronize, reopened]
pull_request_review_comment:
types: [created]
concurrency:
group: >-
${{ github.event_name == 'pull_request'
&& format('review-{0}', github.event.pull_request.number)
|| format('reply-{0}-{1}', github.event.pull_request.number,
github.event.comment.id) }}
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
jobs:
review:
runs-on: ubuntu-latest
permissions:
pull-requests: write
contents: read
steps:
- uses: single-origin/code-review-action@v1
with:
backend-url: ${{ secrets.SO_BACKEND_URL }}
api-key: ${{ secrets.SO_API_KEY }}Add secrets in Github UI. Navigate to YourRepo → Settings → Secrets and Variables → Actions.
Set SO_BACKEND_URL to https://{yourcompanyname}-api.singleorigin.tech
You can create an API key with these steps:
- Login to the Single Origin UI, click on the user icon on the top right of the page, navigate to the Account page
- Choose "Access Tokens"
- Create an Access Token. This will give you an {id} and {secret}. Save these in a secure place.
- To derive the string for SO_API_KEY, base64 encode the id and secret with the
command
echo -n '{id}:{secret}' | base64. Make sure to pass the-noption to avoid a newline in the string.
| Input | Required | Default | Description |
|---|---|---|---|
backend-url |
Yes | Backend API base URL | |
api-key |
Yes | API key (id:secret, base64-encoded) |
|
github-token |
No | github.token |
GitHub token for API calls |
timeout-seconds |
No | 300 |
Backend request timeout in seconds |
file-filter |
No | **/*.sql |
Glob inclusion filter for file paths (only matching files are reviewed) |
upload-artifact |
No | false |
Upload raw result JSON as artifact for debugging |
- Determines review scope:
opened/reopened(or asynchronizewith no prior review) → full: all files changed between the base branch and the PR head.synchronizewith a prior review → incremental: only files changed since the last review, via the compare API (GET /compare/{lastReviewedSha}...{head}). Falls back to a full review if the branch was force-pushed/rebased (compare status is notahead).
- Filters out binary, deleted, and generated files
- Fetches full file content for each reviewable file
- Sends files to backend (
POST /api/v1/code-review/review) with the scope - Posts a review on the PR (inline comments, or "No new issues found" when there are none), skipping any comment on a line the bot has already commented on
The last-reviewed commit is read back from GitHub itself: every review the action posts is stamped with its head commit, so a review is always posted (even with no comments) to leave that marker for the next incremental run. No server-side state is needed.
- Detects when a user replies to a bot-generated comment
- Fetches the full conversation thread and file content
- Sends context to backend (
POST /api/v1/code-review/reply) - Posts a threaded reply
Only files matching the file-filter glob pattern are reviewed (default:
**/*.sql). Examples:
**/*.sql— all SQL files**/*.{sql,py}— SQL and Python filessrc/**/*.sql— SQL files undersrc/**/{db,migrations}/**/*.sql— SQL files indb/ormigrations/directories
Set to empty string to match nothing.
Additionally, these are always skipped:
- Binary files
- Deleted files
- Generated/vendor files (
node_modules/,vendor/,dist/,*.min.js, lock files,*.pb.go, etc.)
- Uses
pull_requesttrigger only (safe from fork injection attacks) - API key is masked in logs via
core.setSecret() GITHUB_TOKENscoped to minimum permissions (pull-requests: write,contents: read)
- PR reviews: cancel-in-progress (rapid pushes cancel stale reviews)
- Reply threads: independent runs per comment
Fork PRs do not receive reviews. The pull_request trigger provides a read-only
GITHUB_TOKEN and no access to repo secrets for fork PRs.
npm install
npm run test # jest unit tests
npm run package # rollup bundle to dist/
npm run lint # eslint
npm run format:write # prettier --write
npm run format:check # prettier --check
npm run all # format + lint + test + packageThe dist/index.js file is committed. After making changes, run
npm run package and commit the updated dist/.
Test against your local backend using
@github/local-action:
- Copy
.env.exampleto.envand fill in your values - Run:
npm run local-action