Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 27 additions & 0 deletions .github/workflows/ai-review-self.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# AI Review — self-caller for this (.github) repository.
#
# Exercises the new one-shot ai-review reusable (Claude, CODE mode) on this
# repo's own PRs. Intended to replace claude-review.yml (claude-code-action)
# once validated.

name: AI Review (self)

on:
pull_request:
types: [opened, reopened, ready_for_review] # no synchronize: avoid re-reviewing on every push (cost). Use @claude for an on-demand re-review.

concurrency:
group: ai-review-${{ github.event.pull_request.number }}
cancel-in-progress: true

jobs:
review:
uses: ./.github/workflows/ai-review.yml
permissions:
contents: read
pull-requests: write
secrets:
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
with:
model_code: claude-sonnet-4-6
review_mode: CODE
111 changes: 97 additions & 14 deletions .github/workflows/ai-review.yml
Original file line number Diff line number Diff line change
@@ -1,25 +1,108 @@
# AI Reviewer (Gemini) — caller for this (.github) repository itself.
# Reusable AI Review Workflow
#
# Other repositories call the reusable workflow via
# uses: smkwlab/.github/.github/workflows/ai-reviewer.yml@v1
# but for self-review we reference the reusable by local path so that each PR
# is reviewed with its own version of the workflow (no need to wait for a tag).
# One-shot PR review via the smkwlab/ai-academic-paper-reviewer action (a single
# LLM call, not an agent loop). Provider- and mode-pluggable:
# - MODEL_CODE chooses the provider: claude-* -> Anthropic, otherwise Google.
# - REVIEW_MODE: CODE (inline, bugs/logic) or ACADEMIC (paper review).
# - single_comment: post one summary comment instead of inline (recommended
# for ACADEMIC, whose feedback spans the whole document and would otherwise
# hit GitHub's diff-line constraint for inline comments).
#
# Requires the GEMINI_API_KEY secret. When it is unset (e.g. fork PRs), the
# reusable workflow skips the review gracefully.
# This replaces the claude-code-action based claude-code-review / claude-paper-
# review, which were slow/expensive (agent loop). claude-mention stays on
# claude-code-action (interactive work genuinely needs the agent).
#
# Secrets: pass the key for the chosen provider (the other may be omitted).
# - anthropic_api_key: for claude-* models
# - gemini_api_key: for gemini-* models

name: AI Reviewer
name: AI Review (Reusable)

on:
pull_request:
types: [opened, synchronize, reopened, ready_for_review]
workflow_call:
inputs:
model_code:
description: "Model: claude-* (Anthropic) or gemini-* (Google), e.g. claude-sonnet-4-6"
type: string
required: false
default: "claude-sonnet-4-6"
review_mode:
description: "CODE (inline; bugs/logic) or ACADEMIC (paper review)"
type: string
required: false
default: "CODE"
single_comment:
description: "Post one summary comment instead of inline (recommended for ACADEMIC)"
type: boolean
required: false
default: false
language:
description: "Review language (e.g. Japanese)"
type: string
required: false
default: "Japanese"
exclude_paths:
description: "Comma-separated path globs to exclude (e.g. *.bib,*.sty,*.cls)"
type: string
required: false
default: ""
timeout_minutes:
description: "Job timeout (minutes)"
type: number
required: false
default: 10
secrets:
gemini_api_key:
required: false
anthropic_api_key:
required: false

jobs:
ai-review:
# Skip drafts; review once the PR is ready.
review:
runs-on: ubuntu-latest
timeout-minutes: ${{ inputs.timeout_minutes }}
# In a workflow_call, github.event is INHERITED from the caller's triggering
# event, so for a pull_request caller this draft guard works as expected
# (and is null -> skip for non-PR events).
if: github.event.pull_request.draft == false

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🚨 [HIGH] [HIGH] if: github.event.pull_request.draft == falseworkflow_call トリガーでは機能しません。workflow_call イベントには github.event.pull_request コンテキストが存在しないため、この条件は常に false と評価され、ジョブが常にスキップされます。

ドラフトスキップのロジックは caller 側(ai-review-self.yml 等)で行うか、あるいは workflow_call の場合は条件を省略する必要があります。例えば以下のように修正できます:

if: ${{ github.event_name != 'workflow_call' || github.event.pull_request.draft == false }}

ただし workflow_call では pull_request イベント情報が引き継がれないため、caller 側で types: [opened, reopened, ready_for_review]synchronize を除く)のようにフィルタリングするのが最も確実です。

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

[warning] workflow_call reusable でのドラフトチェックに副作用があります。

github.event.pull_request は呼び出し元のイベントコンテキストを継承しますが、呼び出し元が pull_request 以外のイベント(例: workflow_dispatch)でトリガーされた場合、github.event.pull_requestnull になります。GitHub Actions の式評価では null.draft''(空文字列)となり、'' == falsefalse と評価されるため、ジョブがサイレントにスキップされます。

このチェックは本来呼び出し元の責務であり、reusable 側に置くと汎用性を損ないます。このチェックを呼び出し元(ai-review-self.yml)に移動し、reusable からは削除することを検討してください。

uses: ./.github/workflows/ai-reviewer.yml
permissions:
contents: read
pull-requests: write
secrets: inherit
steps:
# Validate review_mode and gracefully skip when the provider's key is
# absent (e.g. fork PRs receive no secrets).
- name: Check inputs and API key
id: check
env:
MODEL_CODE: ${{ inputs.model_code }}
REVIEW_MODE: ${{ inputs.review_mode }}
GEMINI_API_KEY: ${{ secrets.gemini_api_key }}
ANTHROPIC_API_KEY: ${{ secrets.anthropic_api_key }}
run: |
case "$REVIEW_MODE" in
CODE|ACADEMIC) ;;
*) echo "::error::invalid review_mode '$REVIEW_MODE' (CODE|ACADEMIC)"; exit 1 ;;
esac
case "$MODEL_CODE" in
*claude*) key="$ANTHROPIC_API_KEY"; name="anthropic_api_key" ;;
*) key="$GEMINI_API_KEY"; name="gemini_api_key" ;;
esac
if [ -z "$key" ]; then
echo "::notice::$name is not configured (or unavailable on fork PRs). Skipping AI review."
echo "skip=true" >> "$GITHUB_OUTPUT"
else
echo "skip=false" >> "$GITHUB_OUTPUT"
fi

- name: AI Review
if: steps.check.outputs.skip != 'true'
uses: smkwlab/ai-academic-paper-reviewer@v1.5

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ [MEDIUM] [MEDIUM] smkwlab/ai-academic-paper-reviewer@v1.5 のようにバージョンをタグで固定しています。タグは mutable(上書き可能)であるため、セキュリティおよび再現性の観点から、コミット SHA で固定することが推奨されます(例: smkwlab/ai-academic-paper-reviewer@<full-sha>)。特に外部アクションを利用する場合、タグが悪意ある変更で上書きされるサプライチェーン攻撃のリスクがあります。

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

[warning] ミュータブルタグ @v1.5 へのピン留めはサプライチェーンリスクがあります。

タグは任意のコミットに移動可能なため、タグが書き換えられた場合、悪意のあるコードが ANTHROPIC_API_KEY / GEMINI_API_KEY の両方にアクセスできる状態になります。コミット SHA へのピン留めを推奨します。

Suggested change
uses: smkwlab/ai-academic-paper-reviewer@v1.5
uses: smkwlab/ai-academic-paper-reviewer@<commit-sha> # v1.5

同組織のリポジトリであり直接的な攻撃面は限られますが、CI の再現性と防御的なベストプラクティスの観点から SHA ピン留めを推奨します。

with:
GEMINI_API_KEY: ${{ secrets.gemini_api_key }}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

ℹ️ [LOW] [LOW] GEMINI_API_KEYANTHROPIC_API_KEY の両方を常にアクションに渡しています。使用しないプロバイダのキーも渡されるため、不要なシークレットの露出が生じます。steps.check で判定済みのプロバイダに対応するキーのみを渡す設計にすることで、最小権限の原則に沿った実装になります。

ANTHROPIC_API_KEY: ${{ secrets.anthropic_api_key }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
MODEL_CODE: ${{ inputs.model_code }}
REVIEW_MODE: ${{ inputs.review_mode }}
LANGUAGE: ${{ inputs.language }}
EXCLUDE_PATHS: ${{ inputs.exclude_paths }}
USE_SINGLE_COMMENT_REVIEW: ${{ inputs.single_comment }}
Loading