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
7 changes: 6 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,9 @@ FILE_PATTERNS: "*.ts,*.tsx"
FILE_PATTERNS: "src/**/*.go"

# Multiple patterns
FILE_PATTERNS: "*.py,*.js,!*.test.js"
FILE_PATTERNS: "*.py,*.js"

# Note: currently only positive matching is supported (no leading '!').
```

## 🚀 Usage
Expand All @@ -134,13 +136,16 @@ or
docker run --rm \
-v $(pwd):/workspace \
-e GITEA_TOKEN="your-token" \
-e GITEA_SERVER_URL="https://your-gitea.example.com" \
-e DEEPSEEK_API_KEY="your-key" \
-e PR_NUMBER=123 \
-e REPO_OWNER="your-org" \
-e REPO_NAME="your-repo" \
ghcr.io/ccsert/opencode-review:latest
```

`REPO_NAME` can be either `repo` or `owner/repo` (entrypoint will normalize it).

### Local Testing (Source)

```bash
Expand Down
7 changes: 6 additions & 1 deletion README_zh.md
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,9 @@ FILE_PATTERNS: "*.ts,*.tsx"
FILE_PATTERNS: "src/**/*.go"

# 多种模式
FILE_PATTERNS: "*.py,*.js,!*.test.js"
FILE_PATTERNS: "*.py,*.js"

# 说明:目前仅支持正向匹配(不支持以 '!' 开头的排除模式)
```

## 🚀 使用方法
Expand All @@ -134,13 +136,16 @@ FILE_PATTERNS: "*.py,*.js,!*.test.js"
docker run --rm \
-v $(pwd):/workspace \
-e GITEA_TOKEN="your-token" \
-e GITEA_SERVER_URL="https://your-gitea.example.com" \
-e DEEPSEEK_API_KEY="your-key" \
-e PR_NUMBER=123 \
-e REPO_OWNER="your-org" \
-e REPO_NAME="your-repo" \
ghcr.io/ccsert/opencode-review:latest
```

`REPO_NAME` 既可以传 `repo`,也可以传 `owner/repo`(entrypoint 会自动规范化)。

### 本地测试(源码)

```bash
Expand Down
69 changes: 69 additions & 0 deletions entrypoint.sh
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,11 @@ validate_env() {
missing+=("GITEA_TOKEN or GITHUB_TOKEN")
fi

# Check for server URL
Copy link

Copilot AI Jan 30, 2026

Choose a reason for hiding this comment

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

After infer_gitea_server_url() is called, the validation at lines 56-58 checks for both GITEA_SERVER_URL and GITHUB_SERVER_URL. However, the inference function always exports to GITEA_SERVER_URL (line 82), so if inference succeeds via GITHUB_SERVER_URL, the validation will pass even though GITHUB_SERVER_URL might not be set.

This is functionally correct but could be clearer. Consider either:

  1. Only checking GITEA_SERVER_URL in validation (since inference always normalizes to that variable)
  2. Adding a comment explaining that inference runs before validation and normalizes to GITEA_SERVER_URL
Suggested change
# Check for server URL
# Check for server URL.
# Note: infer_gitea_server_url() runs before this and normalizes any detected
# GitHub or Gitea server URL into GITEA_SERVER_URL. We still allow either
# GITEA_SERVER_URL or GITHUB_SERVER_URL here for backwards compatibility.

Copilot uses AI. Check for mistakes.
if [ -z "$GITEA_SERVER_URL" ] && [ -z "$GITHUB_SERVER_URL" ]; then
missing+=("GITEA_SERVER_URL or GITHUB_SERVER_URL")
fi

# Check for at least one LLM API key
if [ -z "$DEEPSEEK_API_KEY" ] && [ -z "$ANTHROPIC_API_KEY" ] && [ -z "$OPENAI_API_KEY" ]; then
missing+=("DEEPSEEK_API_KEY, ANTHROPIC_API_KEY, or OPENAI_API_KEY")
Expand All @@ -68,6 +73,63 @@ validate_env() {
log_success "Environment validated"
}

infer_gitea_server_url() {
if [ -n "$GITEA_SERVER_URL" ]; then
return 0
fi

if [ -n "$GITHUB_SERVER_URL" ]; then
export GITEA_SERVER_URL="$GITHUB_SERVER_URL"
return 0
fi

# Best-effort inference from git remote when running locally with -v $(pwd):/workspace
if [ -d "/workspace/.git" ]; then
Comment on lines +86 to +87
Copy link

Copilot AI Jan 30, 2026

Choose a reason for hiding this comment

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

The git remote inference assumes that the remote URL uses the same protocol (https) for API access. This may not always be true - some Gitea instances might use custom ports or paths. For example, a git remote of "https://git.example.com:3000" would be correctly inferred, but some instances might have API at "https://example.com/gitea" while the git remote is "https://git.example.com".

Consider documenting this limitation in the comment, or add a note that users should explicitly set GITEA_SERVER_URL if their setup differs from standard patterns.

Copilot uses AI. Check for mistakes.
local remote
remote=$(git -C /workspace remote get-url origin 2>/dev/null || true)

if [[ "$remote" =~ ^https?:// ]]; then
local host
host=$(echo "$remote" | sed -E 's#^(https?://[^/]+).*#\1#')
if [ -n "$host" ]; then
export GITEA_SERVER_URL="$host"
log_info "Inferred GITEA_SERVER_URL from git remote: $GITEA_SERVER_URL"
return 0
fi
fi

# SSH-style: git@host:owner/repo.git -> assume https://host
if [[ "$remote" =~ ^git@[^:]+: ]]; then
local host
host=$(echo "$remote" | sed -E 's#^git@([^:]+):.*#\1#')
if [ -n "$host" ]; then
export GITEA_SERVER_URL="https://$host"
log_info "Inferred GITEA_SERVER_URL from git remote: $GITEA_SERVER_URL"
return 0
Comment on lines +104 to +108
Copy link

Copilot AI Jan 30, 2026

Choose a reason for hiding this comment

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

The SSH remote parsing regex at line 102 uses ^git@[^:]+: which requires at least one character for the host, but doesn't validate that it's a valid hostname. While this is a best-effort inference and the regex will work for typical cases, it could potentially extract invalid hosts (e.g., containing spaces or special characters).

Since this is best-effort inference, this is acceptable, but consider adding a basic sanity check that the extracted host looks reasonable (e.g., doesn't contain whitespace).

Suggested change
host=$(echo "$remote" | sed -E 's#^git@([^:]+):.*#\1#')
if [ -n "$host" ]; then
export GITEA_SERVER_URL="https://$host"
log_info "Inferred GITEA_SERVER_URL from git remote: $GITEA_SERVER_URL"
return 0
host=$(echo "$remote" | sed -E 's#^git@([A-Za-z0-9._-]+):.*#\1#')
if [ -n "$host" ] && [[ "$host" =~ ^[A-Za-z0-9._-]+$ ]]; then
export GITEA_SERVER_URL="https://$host"
log_info "Inferred GITEA_SERVER_URL from git remote: $GITEA_SERVER_URL"
return 0
else
log_warn "Skipping SSH remote inference due to invalid host: '$host'"

Copilot uses AI. Check for mistakes.
fi
fi
fi
}

normalize_repo_context() {
# Accept REPO_NAME as either "repo" or "owner/repo".
if [ -n "$REPO_NAME" ] && [[ "$REPO_NAME" == */* ]]; then
local owner_part="${REPO_NAME%%/*}"
local repo_part="${REPO_NAME#*/}"

if [ -z "$REPO_OWNER" ]; then
export REPO_OWNER="$owner_part"
export REPO_NAME="$repo_part"
return 0
fi

if [ "$owner_part" = "$REPO_OWNER" ]; then
export REPO_NAME="$repo_part"
return 0
Copy link

Copilot AI Jan 30, 2026

Choose a reason for hiding this comment

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

The normalize_repo_context() function silently fails when REPO_NAME contains "owner/repo" format but the owner_part doesn't match the existing REPO_OWNER. In this case, REPO_NAME remains in "owner/repo" format instead of being normalized to just "repo", which could cause issues in tools expecting only the repository name.

For example, if REPO_NAME="alice/myrepo" and REPO_OWNER="bob", the function returns without normalizing, leaving REPO_NAME as "alice/myrepo". This might break API calls that construct paths like "/repos/{REPO_OWNER}/{REPO_NAME}" resulting in "/repos/bob/alice/myrepo".

Consider logging a warning when there's a mismatch, or explicitly handling this conflict case.

Suggested change
return 0
return 0
else
# Mismatch between owner in REPO_NAME and existing REPO_OWNER.
# Normalize REPO_NAME to just the repo part to avoid paths like /repos/OWNER/owner/repo.
log_warn "REPO_NAME owner '$owner_part' does not match REPO_OWNER '$REPO_OWNER'; using REPO_OWNER with repo '$repo_part'."
export REPO_NAME="$repo_part"
return 0

Copilot uses AI. Check for mistakes.
Comment on lines +116 to +128
Copy link

Copilot AI Jan 30, 2026

Choose a reason for hiding this comment

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

The normalize_repo_context() function doesn't handle the edge case where REPO_NAME contains multiple slashes (e.g., "owner/sub/repo"). The pattern matching */* will match this case, and the parsing logic using %%/* and #*/ will split on the first and last slash respectively, potentially creating unexpected results.

For example, if REPO_NAME="org/team/myrepo", then:

  • owner_part="org" (first part before first slash)
  • repo_part="team/myrepo" (everything after first slash)

This would set REPO_NAME to "team/myrepo" which is still not a valid repository name. Consider adding validation to ensure REPO_NAME contains at most one slash.

Suggested change
if [ -n "$REPO_NAME" ] && [[ "$REPO_NAME" == */* ]]; then
local owner_part="${REPO_NAME%%/*}"
local repo_part="${REPO_NAME#*/}"
if [ -z "$REPO_OWNER" ]; then
export REPO_OWNER="$owner_part"
export REPO_NAME="$repo_part"
return 0
fi
if [ "$owner_part" = "$REPO_OWNER" ]; then
export REPO_NAME="$repo_part"
return 0
if [ -n "$REPO_NAME" ]; then
# Validate that REPO_NAME contains at most one slash.
# Examples of valid values:
# - "myrepo"
# - "myorg/myrepo"
# Invalid example (multiple slashes):
# - "org/team/myrepo"
if [[ "$REPO_NAME" == */*/* ]]; then
log_error "Invalid REPO_NAME '$REPO_NAME'. Expected 'repo' or 'owner/repo' (at most one '/')."
exit 1
fi
if [[ "$REPO_NAME" == */* ]]; then
local owner_part="${REPO_NAME%%/*}"
local repo_part="${REPO_NAME#*/}"
if [ -z "$REPO_OWNER" ]; then
export REPO_OWNER="$owner_part"
export REPO_NAME="$repo_part"
return 0
fi
if [ "$owner_part" = "$REPO_OWNER" ]; then
export REPO_NAME="$repo_part"
return 0
fi

Copilot uses AI. Check for mistakes.
fi
fi
}

# Build the review prompt based on environment
build_prompt() {
local pr_num="${PR_NUMBER:-}"
Expand Down Expand Up @@ -127,13 +189,17 @@ print_config() {
echo " Model: ${MODEL:-deepseek/deepseek-chat}"
echo " Style: ${REVIEW_STYLE:-balanced}"
echo " Language: ${REVIEW_LANGUAGE:-auto}"
echo " Server: ${GITEA_SERVER_URL:-${GITHUB_SERVER_URL:-}}"
echo " Config: $OPENCODE_CONFIG_DIR"
if [ -n "$FILE_PATTERNS" ]; then
echo " Filter: $FILE_PATTERNS"
fi
if [ -n "$PR_NUMBER" ]; then
echo " PR: #$PR_NUMBER"
fi
if [ -n "$REPO_OWNER" ] && [ -n "$REPO_NAME" ]; then
echo " Repo: $REPO_OWNER/$REPO_NAME"
fi
}

# Main entrypoint
Expand All @@ -144,6 +210,8 @@ main() {
case "$command" in
review)
setup_config
infer_gitea_server_url
normalize_repo_context
validate_env
print_config

Expand Down Expand Up @@ -180,6 +248,7 @@ main() {
echo ""
echo "Environment Variables:"
echo " GITEA_TOKEN Gitea API token (required)"
echo " GITEA_SERVER_URL Base URL like https://gitea.example.com (required)"
echo " DEEPSEEK_API_KEY DeepSeek API key"
echo " ANTHROPIC_API_KEY Anthropic API key"
echo " OPENAI_API_KEY OpenAI API key"
Expand Down