feat: add composite action for clean distribution
- .gitea/actions/review/action.yml: composite action with caching
Consumers just use:
uses: https://gitea.weiker.me/rodin/review-bot/.gitea/actions/review@v0.1.0
No Go toolchain needed, binary cached by version tag.
- Remove install.sh (replaced by composite action)
- CI workflow: use matrix strategy to parallelize reviews
- Self-review still builds from source (pre-release)
This commit is contained in:
@@ -0,0 +1,103 @@
|
|||||||
|
name: 'AI Code Review'
|
||||||
|
description: 'Run AI-powered code review on a pull request using review-bot'
|
||||||
|
|
||||||
|
inputs:
|
||||||
|
gitea-url:
|
||||||
|
description: 'Gitea instance URL (defaults to server_url)'
|
||||||
|
required: false
|
||||||
|
default: ''
|
||||||
|
repo:
|
||||||
|
description: 'Repository (owner/name, defaults to current)'
|
||||||
|
required: false
|
||||||
|
default: ''
|
||||||
|
pr-number:
|
||||||
|
description: 'Pull request number (defaults to current PR)'
|
||||||
|
required: false
|
||||||
|
default: ''
|
||||||
|
reviewer-token:
|
||||||
|
description: 'Gitea token for posting the review'
|
||||||
|
required: true
|
||||||
|
reviewer-name:
|
||||||
|
description: 'Display name for the reviewer'
|
||||||
|
required: false
|
||||||
|
default: ''
|
||||||
|
llm-base-url:
|
||||||
|
description: 'OpenAI-compatible LLM API base URL'
|
||||||
|
required: true
|
||||||
|
llm-api-key:
|
||||||
|
description: 'LLM API key'
|
||||||
|
required: true
|
||||||
|
llm-model:
|
||||||
|
description: 'LLM model name'
|
||||||
|
required: true
|
||||||
|
conventions-file:
|
||||||
|
description: 'Path to conventions file in the repo (e.g. CLAUDE.md)'
|
||||||
|
required: false
|
||||||
|
default: ''
|
||||||
|
temperature:
|
||||||
|
description: 'LLM temperature (0 = server default)'
|
||||||
|
required: false
|
||||||
|
default: '0'
|
||||||
|
version:
|
||||||
|
description: 'review-bot version to install (e.g. v0.1.0, defaults to latest)'
|
||||||
|
required: false
|
||||||
|
default: 'latest'
|
||||||
|
dry-run:
|
||||||
|
description: 'Print review to stdout instead of posting'
|
||||||
|
required: false
|
||||||
|
default: 'false'
|
||||||
|
|
||||||
|
runs:
|
||||||
|
using: 'composite'
|
||||||
|
steps:
|
||||||
|
- name: Determine version
|
||||||
|
id: version
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
GITEA_URL="${{ inputs.gitea-url || github.server_url }}"
|
||||||
|
if [ "${{ inputs.version }}" = "latest" ]; then
|
||||||
|
VERSION=$(curl -sSf "${GITEA_URL}/api/v1/repos/rodin/review-bot/releases?limit=1" | sed -n 's/.*"tag_name":"\([^"]*\)".*/\1/p' | head -1)
|
||||||
|
if [ -z "$VERSION" ]; then
|
||||||
|
echo "Failed to determine latest version" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
VERSION="${{ inputs.version }}"
|
||||||
|
fi
|
||||||
|
echo "version=${VERSION}" >> "$GITHUB_OUTPUT"
|
||||||
|
|
||||||
|
- name: Cache review-bot binary
|
||||||
|
id: cache
|
||||||
|
uses: actions/cache@v4
|
||||||
|
with:
|
||||||
|
path: /usr/local/bin/review-bot
|
||||||
|
key: review-bot-linux-amd64-${{ steps.version.outputs.version }}
|
||||||
|
|
||||||
|
- name: Install review-bot
|
||||||
|
if: steps.cache.outputs.cache-hit != 'true'
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
GITEA_URL="${{ inputs.gitea-url || github.server_url }}"
|
||||||
|
VERSION="${{ steps.version.outputs.version }}"
|
||||||
|
curl -sSfL "${GITEA_URL}/rodin/review-bot/releases/download/${VERSION}/review-bot-linux-amd64" -o /usr/local/bin/review-bot
|
||||||
|
chmod +x /usr/local/bin/review-bot
|
||||||
|
|
||||||
|
- name: Run review
|
||||||
|
shell: bash
|
||||||
|
env:
|
||||||
|
GITEA_URL: ${{ inputs.gitea-url || github.server_url }}
|
||||||
|
GITEA_REPO: ${{ inputs.repo || github.repository }}
|
||||||
|
PR_NUMBER: ${{ inputs.pr-number || github.event.pull_request.number }}
|
||||||
|
REVIEWER_TOKEN: ${{ inputs.reviewer-token }}
|
||||||
|
REVIEWER_NAME: ${{ inputs.reviewer-name }}
|
||||||
|
LLM_BASE_URL: ${{ inputs.llm-base-url }}
|
||||||
|
LLM_API_KEY: ${{ inputs.llm-api-key }}
|
||||||
|
LLM_MODEL: ${{ inputs.llm-model }}
|
||||||
|
CONVENTIONS_FILE: ${{ inputs.conventions-file }}
|
||||||
|
LLM_TEMPERATURE: ${{ inputs.temperature }}
|
||||||
|
run: |
|
||||||
|
ARGS=""
|
||||||
|
if [ "${{ inputs.dry-run }}" = "true" ]; then
|
||||||
|
ARGS="--dry-run"
|
||||||
|
fi
|
||||||
|
review-bot $ARGS
|
||||||
+13
-14
@@ -18,35 +18,34 @@ jobs:
|
|||||||
- run: go vet ./...
|
- run: go vet ./...
|
||||||
- run: go build -o review-bot ./cmd/review-bot
|
- run: go build -o review-bot ./cmd/review-bot
|
||||||
|
|
||||||
|
# Self-review: builds from source since we're pre-release
|
||||||
review:
|
review:
|
||||||
runs-on: ubuntu-24.04
|
runs-on: ubuntu-24.04
|
||||||
if: github.event_name == 'pull_request'
|
if: github.event_name == 'pull_request'
|
||||||
needs: test
|
needs: test
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
include:
|
||||||
|
- name: sonnet
|
||||||
|
token_secret: SONNET_REVIEW_TOKEN
|
||||||
|
model: gpt-5
|
||||||
|
- name: gpt
|
||||||
|
token_secret: GPT_REVIEW_TOKEN
|
||||||
|
model: gpt-5-mini
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
- uses: actions/setup-go@v5
|
- uses: actions/setup-go@v5
|
||||||
with:
|
with:
|
||||||
go-version: '1.26'
|
go-version: '1.26'
|
||||||
- run: go build -o review-bot ./cmd/review-bot
|
- run: go build -o review-bot ./cmd/review-bot
|
||||||
- name: Run Sonnet Review
|
- name: Run ${{ matrix.name }} review
|
||||||
env:
|
env:
|
||||||
GITEA_URL: ${{ github.server_url }}
|
GITEA_URL: ${{ github.server_url }}
|
||||||
GITEA_REPO: ${{ github.repository }}
|
GITEA_REPO: ${{ github.repository }}
|
||||||
PR_NUMBER: ${{ github.event.pull_request.number }}
|
PR_NUMBER: ${{ github.event.pull_request.number }}
|
||||||
REVIEWER_TOKEN: ${{ secrets.SONNET_REVIEW_TOKEN }}
|
REVIEWER_TOKEN: ${{ secrets[matrix.token_secret] }}
|
||||||
LLM_BASE_URL: ${{ secrets.LLM_BASE_URL }}
|
LLM_BASE_URL: ${{ secrets.LLM_BASE_URL }}
|
||||||
LLM_API_KEY: ${{ secrets.LLM_API_KEY }}
|
LLM_API_KEY: ${{ secrets.LLM_API_KEY }}
|
||||||
LLM_MODEL: "gpt-5"
|
LLM_MODEL: ${{ matrix.model }}
|
||||||
CONVENTIONS_FILE: "CONVENTIONS.md"
|
|
||||||
run: ./review-bot
|
|
||||||
- name: Run GPT Review
|
|
||||||
env:
|
|
||||||
GITEA_URL: ${{ github.server_url }}
|
|
||||||
GITEA_REPO: ${{ github.repository }}
|
|
||||||
PR_NUMBER: ${{ github.event.pull_request.number }}
|
|
||||||
REVIEWER_TOKEN: ${{ secrets.GPT_REVIEW_TOKEN }}
|
|
||||||
LLM_BASE_URL: ${{ secrets.LLM_BASE_URL }}
|
|
||||||
LLM_API_KEY: ${{ secrets.LLM_API_KEY }}
|
|
||||||
LLM_MODEL: "gpt-5-mini"
|
|
||||||
CONVENTIONS_FILE: "CONVENTIONS.md"
|
CONVENTIONS_FILE: "CONVENTIONS.md"
|
||||||
run: ./review-bot
|
run: ./review-bot
|
||||||
|
|||||||
-75
@@ -1,75 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
# Install review-bot from latest Gitea release
|
|
||||||
# Usage: curl -sSfL https://gitea.weiker.me/rodin/review-bot/raw/branch/main/install.sh | bash
|
|
||||||
|
|
||||||
set -euo pipefail
|
|
||||||
|
|
||||||
GITEA_URL="${GITEA_URL:-https://gitea.weiker.me}"
|
|
||||||
REPO="rodin/review-bot"
|
|
||||||
INSTALL_DIR="${INSTALL_DIR:-}"
|
|
||||||
|
|
||||||
# Determine install directory with fallback
|
|
||||||
if [ -z "$INSTALL_DIR" ]; then
|
|
||||||
if [ -w /usr/local/bin ]; then
|
|
||||||
INSTALL_DIR="/usr/local/bin"
|
|
||||||
else
|
|
||||||
INSTALL_DIR="${HOME}/.local/bin"
|
|
||||||
mkdir -p "$INSTALL_DIR"
|
|
||||||
echo "Note: Installing to $INSTALL_DIR (add to PATH if needed)"
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
OS=$(uname -s | tr '[:upper:]' '[:lower:]')
|
|
||||||
ARCH=$(uname -m)
|
|
||||||
case "$ARCH" in
|
|
||||||
x86_64) ARCH="amd64" ;;
|
|
||||||
aarch64|arm64) ARCH="arm64" ;;
|
|
||||||
*) echo "Unsupported architecture: $ARCH" >&2; exit 1 ;;
|
|
||||||
esac
|
|
||||||
|
|
||||||
BINARY="review-bot-${OS}-${ARCH}"
|
|
||||||
|
|
||||||
# Get latest release tag (POSIX-safe parsing without jq)
|
|
||||||
LATEST=$(curl -sSf "${GITEA_URL}/api/v1/repos/${REPO}/releases?limit=1" | sed -n 's/.*"tag_name":"\([^"]*\)".*/\1/p' | head -1)
|
|
||||||
|
|
||||||
if [ -z "$LATEST" ]; then
|
|
||||||
echo "Failed to determine latest release" >&2
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo "Installing review-bot ${LATEST} (${OS}/${ARCH})..."
|
|
||||||
|
|
||||||
DOWNLOAD_URL="${GITEA_URL}/${REPO}/releases/download/${LATEST}/${BINARY}"
|
|
||||||
CHECKSUM_URL="${GITEA_URL}/${REPO}/releases/download/${LATEST}/checksums.txt"
|
|
||||||
|
|
||||||
# Download binary and checksums
|
|
||||||
TMPDIR=$(mktemp -d)
|
|
||||||
trap 'rm -rf "$TMPDIR"' EXIT
|
|
||||||
|
|
||||||
curl -sSfL -o "${TMPDIR}/${BINARY}" "$DOWNLOAD_URL"
|
|
||||||
curl -sSfL -o "${TMPDIR}/checksums.txt" "$CHECKSUM_URL"
|
|
||||||
|
|
||||||
# Verify checksum
|
|
||||||
cd "$TMPDIR"
|
|
||||||
EXPECTED=$(grep "${BINARY}" checksums.txt | awk '{print $1}')
|
|
||||||
ACTUAL=$(sha256sum "${BINARY}" | awk '{print $1}')
|
|
||||||
|
|
||||||
if [ -z "$EXPECTED" ]; then
|
|
||||||
echo "Error: no checksum found for ${BINARY} in checksums.txt" >&2
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ "$EXPECTED" != "$ACTUAL" ]; then
|
|
||||||
echo "Error: checksum mismatch!" >&2
|
|
||||||
echo " Expected: $EXPECTED" >&2
|
|
||||||
echo " Actual: $ACTUAL" >&2
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo "Checksum verified ✓"
|
|
||||||
|
|
||||||
# Install
|
|
||||||
cp "${TMPDIR}/${BINARY}" "${INSTALL_DIR}/review-bot"
|
|
||||||
chmod +x "${INSTALL_DIR}/review-bot"
|
|
||||||
|
|
||||||
echo "Installed review-bot ${LATEST} to ${INSTALL_DIR}/review-bot"
|
|
||||||
Reference in New Issue
Block a user