7dab35de41
PR Ready Gate / clear-labels (pull_request) Successful in 2s
CI / test (pull_request) Successful in 14s
CI / review (anthropic--claude-4.6-sonnet, sonnet, SONNET_REVIEW_TOKEN) (pull_request) Successful in 35s
CI / review (gpt-5, gpt, GPT_REVIEW_TOKEN) (pull_request) Successful in 1m30s
CI / review (gpt-5, security, SECURITY_REVIEW.md, SECURITY_REVIEW_TOKEN) (pull_request) Successful in 2m0s
Add native SAP AI Core provider that handles OAuth token management and deployment discovery automatically. This eliminates the need for the external LLM proxy when running in SAP environments. Changes: - Add AICoreClient with OAuth token caching and deployment URL discovery - Support both Anthropic and OpenAI models via AI Core deployments - Update CI to use native AI Core provider - Update action inputs to accept AI Core credentials - Update README with AI Core configuration examples Model names must match AI Core deployment names (e.g. anthropic--claude-4.6-sonnet, gpt-5).
201 lines
7.0 KiB
YAML
201 lines
7.0 KiB
YAML
# This composite action is designed for Gitea Actions runners.
|
|
# Gitea Actions supports GitHub Actions syntax including $GITHUB_OUTPUT,
|
|
# actions/cache, and actions/checkout.
|
|
# Requirements: python3, sha256sum, curl (all present on ubuntu-* runners).
|
|
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 (not required for aicore provider)'
|
|
required: false
|
|
default: ''
|
|
llm-api-key:
|
|
description: 'LLM API key (not required for aicore provider)'
|
|
required: false
|
|
default: ''
|
|
llm-model:
|
|
description: 'LLM model name'
|
|
required: true
|
|
llm-provider:
|
|
description: 'LLM API provider: openai, anthropic, or aicore (default openai)'
|
|
required: false
|
|
default: 'openai'
|
|
aicore-client-id:
|
|
description: 'SAP AI Core client ID (required for aicore provider)'
|
|
required: false
|
|
default: ''
|
|
aicore-client-secret:
|
|
description: 'SAP AI Core client secret (required for aicore provider)'
|
|
required: false
|
|
default: ''
|
|
aicore-auth-url:
|
|
description: 'SAP AI Core authentication URL (required for aicore provider)'
|
|
required: false
|
|
default: ''
|
|
aicore-api-url:
|
|
description: 'SAP AI Core API URL (required for aicore provider)'
|
|
required: false
|
|
default: ''
|
|
aicore-resource-group:
|
|
description: 'SAP AI Core resource group (default: default)'
|
|
required: false
|
|
default: 'default'
|
|
conventions-file:
|
|
description: 'Path to conventions file in the repo (e.g. CLAUDE.md)'
|
|
required: false
|
|
default: ''
|
|
patterns-repo:
|
|
description: 'Comma-separated repos with language patterns (e.g. rodin/elixir-patterns,rodin/phoenix-conventions)'
|
|
required: false
|
|
default: ''
|
|
patterns-files:
|
|
description: 'Comma-separated file paths or directories to fetch from patterns repos'
|
|
required: false
|
|
default: 'README.md'
|
|
temperature:
|
|
description: 'LLM temperature (0 = server default)'
|
|
required: false
|
|
default: '0'
|
|
timeout:
|
|
description: 'LLM request timeout in seconds (default 300)'
|
|
required: false
|
|
default: '300'
|
|
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'
|
|
update-existing:
|
|
description: 'Delete previous review from same bot after posting new one. Accepts: true/1/yes or false/0/no (default true)'
|
|
required: false
|
|
default: 'true'
|
|
system-prompt-file:
|
|
description: 'Local file with additional system prompt instructions (e.g. security review focus)'
|
|
required: false
|
|
default: ''
|
|
persona:
|
|
description: 'Built-in persona name (security, architect, docs)'
|
|
required: false
|
|
default: ''
|
|
persona-file:
|
|
description: 'Path to custom persona JSON file'
|
|
required: false
|
|
default: ''
|
|
|
|
runs:
|
|
using: 'composite'
|
|
steps:
|
|
- name: Determine version
|
|
id: version
|
|
shell: bash
|
|
run: |
|
|
GITEA_URL="${{ inputs.gitea-url || github.server_url }}"
|
|
REPO="${{ inputs.repo || 'rodin/review-bot' }}"
|
|
if [ "${{ inputs.version }}" = "latest" ]; then
|
|
VERSION=$(curl -sSf "${GITEA_URL}/api/v1/repos/${REPO}/releases?limit=1" \
|
|
| python3 -c "import sys, json; releases = json.load(sys.stdin); print(releases[0]['tag_name'] if releases else '')")
|
|
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: ${{ runner.temp }}/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 }}"
|
|
REPO="${{ inputs.repo || 'rodin/review-bot' }}"
|
|
VERSION="${{ steps.version.outputs.version }}"
|
|
BINARY="review-bot-linux-amd64"
|
|
|
|
curl -sSfL "${GITEA_URL}/${REPO}/releases/download/${VERSION}/${BINARY}" \
|
|
-o "${{ runner.temp }}/review-bot"
|
|
curl -sSfL "${GITEA_URL}/${REPO}/releases/download/${VERSION}/checksums.txt" \
|
|
-o "${{ runner.temp }}/checksums.txt"
|
|
|
|
# Verify SHA-256 checksum
|
|
cd "${{ runner.temp }}"
|
|
EXPECTED=$(grep "${BINARY}" checksums.txt | awk '{print $1}')
|
|
ACTUAL=$(sha256sum review-bot | awk '{print $1}')
|
|
|
|
if [ -z "$EXPECTED" ]; then
|
|
echo "Error: no checksum found for ${BINARY}" >&2
|
|
exit 1
|
|
fi
|
|
if [ "$EXPECTED" != "$ACTUAL" ]; then
|
|
echo "Error: checksum mismatch!" >&2
|
|
echo " Expected: $EXPECTED" >&2
|
|
echo " Actual: $ACTUAL" >&2
|
|
exit 1
|
|
fi
|
|
|
|
chmod +x "${{ runner.temp }}/review-bot"
|
|
echo "Installed review-bot ${VERSION} (checksum verified)"
|
|
|
|
- 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 }}
|
|
PATTERNS_REPO: ${{ inputs.patterns-repo }}
|
|
PATTERNS_FILES: ${{ inputs.patterns-files }}
|
|
LLM_TEMPERATURE: ${{ inputs.temperature }}
|
|
LLM_TIMEOUT: ${{ inputs.timeout }}
|
|
LLM_PROVIDER: ${{ inputs.llm-provider }}
|
|
UPDATE_EXISTING: ${{ inputs.update-existing }}
|
|
SYSTEM_PROMPT_FILE: ${{ inputs.system-prompt-file }}
|
|
PERSONA: ${{ inputs.persona }}
|
|
PERSONA_FILE: ${{ inputs.persona-file }}
|
|
AICORE_CLIENT_ID: ${{ inputs.aicore-client-id }}
|
|
AICORE_CLIENT_SECRET: ${{ inputs.aicore-client-secret }}
|
|
AICORE_AUTH_URL: ${{ inputs.aicore-auth-url }}
|
|
AICORE_API_URL: ${{ inputs.aicore-api-url }}
|
|
AICORE_RESOURCE_GROUP: ${{ inputs.aicore-resource-group }}
|
|
run: |
|
|
ARGS=""
|
|
if [ "${{ inputs.dry-run }}" = "true" ]; then
|
|
ARGS="--dry-run"
|
|
fi
|
|
${{ runner.temp }}/review-bot $ARGS
|