@alibaba-group/open-code-review 1.3.14 → 1.3.19
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.ja-JP.md +1 -1
- package/README.ko-KR.md +1 -1
- package/README.md +1 -1
- package/README.ru-RU.md +1 -1
- package/README.zh-CN.md +1 -1
- package/bin/ocr.js +10 -2
- package/package.json +16 -1
- package/scripts/install.js +8 -2
- package/scripts/platform.js +66 -0
- package/scripts/update.js +6 -0
- package/.claude-plugin/marketplace.json +0 -16
- package/CONTRIBUTING.ja-JP.md +0 -218
- package/CONTRIBUTING.ko-KR.md +0 -223
- package/CONTRIBUTING.md +0 -224
- package/CONTRIBUTING.ru-RU.md +0 -224
- package/CONTRIBUTING.zh-CN.md +0 -218
- package/examples/README.md +0 -10
- package/examples/github_actions/README.md +0 -223
- package/examples/github_actions/ocr-review.yml +0 -357
- package/examples/gitlab_ci/.gitlab-ci.yml +0 -244
- package/examples/gitlab_ci/README.md +0 -269
- package/plugins/open-code-review/.claude-plugin/plugin.json +0 -6
- package/plugins/open-code-review/.codex-plugin/plugin.json +0 -34
- package/plugins/open-code-review/CODEX.ko-KR.md +0 -108
- package/plugins/open-code-review/commands/review.md +0 -35
- package/plugins/open-code-review/skills/open-code-review/SKILL.md +0 -236
- package/scripts/github-actions/post-review-comments.test.js +0 -171
- package/skills/open-code-review/SKILL.md +0 -231
|
@@ -1,244 +0,0 @@
|
|
|
1
|
-
# OpenCodeReview - GitLab CI Merge Request Auto-Review Demo
|
|
2
|
-
#
|
|
3
|
-
# This pipeline automatically reviews Merge Requests using OpenCodeReview
|
|
4
|
-
# and posts review comments (discussions) directly on the MR diff.
|
|
5
|
-
# Supports both same-repo and forked MR scenarios.
|
|
6
|
-
#
|
|
7
|
-
# Required CI/CD Variables (Settings → CI/CD → Variables):
|
|
8
|
-
# OCR_LLM_URL - LLM API endpoint (e.g., https://api.openai.com/v1/chat/completions)
|
|
9
|
-
# OCR_LLM_AUTH_TOKEN - Authentication token for the LLM API (mark as "Masked")
|
|
10
|
-
#
|
|
11
|
-
# Optional CI/CD Variables:
|
|
12
|
-
# OCR_LLM_MODEL - Model name (default: gpt-4o)
|
|
13
|
-
# GITLAB_API_TOKEN - GitLab Personal/Project Access Token with "api" scope
|
|
14
|
-
# (falls back to CI_JOB_TOKEN if not set)
|
|
15
|
-
#
|
|
16
|
-
# Fork MR Support:
|
|
17
|
-
# The script uses CI_COMMIT_SHA as the diff target to correctly resolve the
|
|
18
|
-
# source commit from forked repos. For some GitLab versions, you may need to enable:
|
|
19
|
-
# Project Settings → CI/CD → General pipelines →
|
|
20
|
-
# "Run pipelines in the parent project for merge requests from forked projects"
|
|
21
|
-
|
|
22
|
-
stages:
|
|
23
|
-
- review
|
|
24
|
-
|
|
25
|
-
code-review:
|
|
26
|
-
stage: review
|
|
27
|
-
interruptible: true
|
|
28
|
-
resource_group: mr-review-$CI_MERGE_REQUEST_IID
|
|
29
|
-
image: node:20
|
|
30
|
-
only:
|
|
31
|
-
- merge_requests
|
|
32
|
-
variables:
|
|
33
|
-
GIT_DEPTH: 0 # Full history needed for merge-base diff
|
|
34
|
-
script:
|
|
35
|
-
# Install OpenCodeReview
|
|
36
|
-
- npm install -g @alibaba-group/open-code-review
|
|
37
|
-
|
|
38
|
-
# Configure OCR
|
|
39
|
-
- mkdir -p ~/.open-code-review
|
|
40
|
-
# Gitlab CI/CD does not support configuring variables with value length less than 8, so you can't set use_anthropic as a CI variable
|
|
41
|
-
- |
|
|
42
|
-
ocr config set llm.url $OCR_LLM_URL
|
|
43
|
-
ocr config set llm.auth_token $OCR_LLM_AUTH_TOKEN
|
|
44
|
-
ocr config set llm.model $OCR_LLM_MODEL
|
|
45
|
-
ocr config set llm.use_anthropic false
|
|
46
|
-
ocr config set llm.extra_body '{"thinking": {"type": "disabled"}}'
|
|
47
|
-
|
|
48
|
-
# Run OCR review (use CI_COMMIT_SHA for --to to support both same-repo and forked MRs)
|
|
49
|
-
- |
|
|
50
|
-
echo "Reviewing MR: ${CI_MERGE_REQUEST_SOURCE_BRANCH_NAME} against ${CI_MERGE_REQUEST_TARGET_BRANCH_NAME}"
|
|
51
|
-
ocr review \
|
|
52
|
-
--from "origin/${CI_MERGE_REQUEST_TARGET_BRANCH_NAME}" \
|
|
53
|
-
--to "${CI_COMMIT_SHA}" \
|
|
54
|
-
--format json \
|
|
55
|
-
--audience agent \
|
|
56
|
-
> /tmp/ocr-result.json 2>/tmp/ocr-stderr.log || true
|
|
57
|
-
echo "OCR review completed."
|
|
58
|
-
cat /tmp/ocr-result.json
|
|
59
|
-
|
|
60
|
-
# Post review comments to MR
|
|
61
|
-
- |
|
|
62
|
-
python3 << 'PYTHON_SCRIPT'
|
|
63
|
-
import json
|
|
64
|
-
import os
|
|
65
|
-
import sys
|
|
66
|
-
import urllib.request
|
|
67
|
-
import urllib.error
|
|
68
|
-
|
|
69
|
-
GITLAB_URL = os.environ.get("CI_SERVER_URL", "https://gitlab.com")
|
|
70
|
-
PROJECT_ID = os.environ["CI_PROJECT_ID"]
|
|
71
|
-
MR_IID = os.environ["CI_MERGE_REQUEST_IID"]
|
|
72
|
-
# Fall back to CI_JOB_TOKEN for fork MR pipelines where GITLAB_API_TOKEN is unavailable
|
|
73
|
-
API_TOKEN = os.environ.get("GITLAB_API_TOKEN") or os.environ.get("CI_JOB_TOKEN", "")
|
|
74
|
-
SOURCE_BRANCH = os.environ["CI_MERGE_REQUEST_SOURCE_BRANCH_NAME"]
|
|
75
|
-
TARGET_BRANCH = os.environ["CI_MERGE_REQUEST_TARGET_BRANCH_NAME"]
|
|
76
|
-
COMMIT_SHA = os.environ["CI_COMMIT_SHA"]
|
|
77
|
-
|
|
78
|
-
if not API_TOKEN:
|
|
79
|
-
print("ERROR: No API token available (GITLAB_API_TOKEN or CI_JOB_TOKEN). Cannot post comments.", file=sys.stderr)
|
|
80
|
-
sys.exit(1)
|
|
81
|
-
|
|
82
|
-
API_BASE = f"{GITLAB_URL}/api/v4/projects/{PROJECT_ID}/merge_requests/{MR_IID}"
|
|
83
|
-
|
|
84
|
-
# Determine auth header: PRIVATE-TOKEN for personal/project tokens, JOB-TOKEN for CI_JOB_TOKEN
|
|
85
|
-
USE_JOB_TOKEN = not os.environ.get("GITLAB_API_TOKEN")
|
|
86
|
-
AUTH_HEADER = "JOB-TOKEN" if USE_JOB_TOKEN else "PRIVATE-TOKEN"
|
|
87
|
-
|
|
88
|
-
def api_request(endpoint, data=None, method="POST"):
|
|
89
|
-
"""Make a GitLab API request."""
|
|
90
|
-
url = f"{API_BASE}{endpoint}"
|
|
91
|
-
headers = {
|
|
92
|
-
AUTH_HEADER: API_TOKEN,
|
|
93
|
-
"Content-Type": "application/json"
|
|
94
|
-
}
|
|
95
|
-
body = json.dumps(data).encode("utf-8") if data else None
|
|
96
|
-
req = urllib.request.Request(url, data=body, headers=headers, method=method)
|
|
97
|
-
try:
|
|
98
|
-
with urllib.request.urlopen(req) as resp:
|
|
99
|
-
return json.loads(resp.read().decode("utf-8"))
|
|
100
|
-
except urllib.error.HTTPError as e:
|
|
101
|
-
print(f"API error {e.code}: {e.read().decode('utf-8')}", file=sys.stderr)
|
|
102
|
-
return None
|
|
103
|
-
|
|
104
|
-
def post_note(body):
|
|
105
|
-
"""Post a general note/comment on the MR."""
|
|
106
|
-
return api_request("/notes", {"body": body})
|
|
107
|
-
|
|
108
|
-
def post_discussion(path, line, body, base_sha, start_sha, head_sha):
|
|
109
|
-
"""Post an inline discussion on a specific file/line in the MR diff."""
|
|
110
|
-
position = {
|
|
111
|
-
"position_type": "text",
|
|
112
|
-
"new_path": path,
|
|
113
|
-
"old_path": path,
|
|
114
|
-
"new_line": line,
|
|
115
|
-
"base_sha": base_sha,
|
|
116
|
-
"start_sha": start_sha,
|
|
117
|
-
"head_sha": head_sha,
|
|
118
|
-
}
|
|
119
|
-
data = {
|
|
120
|
-
"body": body,
|
|
121
|
-
"position": position
|
|
122
|
-
}
|
|
123
|
-
return api_request("/discussions", data)
|
|
124
|
-
|
|
125
|
-
def format_comment(comment):
|
|
126
|
-
"""Format a single review comment as markdown."""
|
|
127
|
-
body = comment.get("content", "")
|
|
128
|
-
|
|
129
|
-
existing = comment.get("existing_code", "")
|
|
130
|
-
suggestion = comment.get("suggestion_code", "")
|
|
131
|
-
if suggestion and existing:
|
|
132
|
-
body += "\n\n**Suggestion:**\n"
|
|
133
|
-
body += f"```suggestion:-0+0\n{suggestion}\n```"
|
|
134
|
-
|
|
135
|
-
return body
|
|
136
|
-
|
|
137
|
-
def format_comment_fallback(comment):
|
|
138
|
-
"""Format a comment for fallback (non-inline) display."""
|
|
139
|
-
path = comment.get("path", "unknown")
|
|
140
|
-
start_line = comment.get("start_line", 0)
|
|
141
|
-
end_line = comment.get("end_line", 0)
|
|
142
|
-
content = comment.get("content", "")
|
|
143
|
-
|
|
144
|
-
md = f"### 📄 `{path}`"
|
|
145
|
-
if start_line and end_line:
|
|
146
|
-
md += f" (L{start_line}-L{end_line})"
|
|
147
|
-
md += f"\n\n{content}"
|
|
148
|
-
|
|
149
|
-
existing = comment.get("existing_code", "")
|
|
150
|
-
suggestion = comment.get("suggestion_code", "")
|
|
151
|
-
if suggestion and existing:
|
|
152
|
-
md += "\n\n<details><summary>💡 Suggested Change</summary>\n\n"
|
|
153
|
-
md += f"**Before:**\n```\n{existing}\n```\n\n"
|
|
154
|
-
md += f"**After:**\n```\n{suggestion}\n```\n\n"
|
|
155
|
-
md += "</details>"
|
|
156
|
-
|
|
157
|
-
return md
|
|
158
|
-
|
|
159
|
-
# --- Main ---
|
|
160
|
-
|
|
161
|
-
# Read OCR result (skip first line which is summary, not JSON)
|
|
162
|
-
try:
|
|
163
|
-
with open("/tmp/ocr-result.json", "r") as f:
|
|
164
|
-
result = json.load(f)
|
|
165
|
-
except (FileNotFoundError, json.JSONDecodeError) as e:
|
|
166
|
-
print(f"Failed to parse OCR output: {e}", file=sys.stderr)
|
|
167
|
-
stderr_content = ""
|
|
168
|
-
try:
|
|
169
|
-
with open("/tmp/ocr-stderr.log", "r") as f:
|
|
170
|
-
stderr_content = f.read().strip()
|
|
171
|
-
except FileNotFoundError:
|
|
172
|
-
pass
|
|
173
|
-
if stderr_content:
|
|
174
|
-
post_note(f"⚠️ **OpenCodeReview** encountered an error:\n```\n{stderr_content}\n```")
|
|
175
|
-
sys.exit(0)
|
|
176
|
-
|
|
177
|
-
comments = result.get("comments", [])
|
|
178
|
-
warnings = result.get("warnings", [])
|
|
179
|
-
|
|
180
|
-
# No comments - post summary
|
|
181
|
-
if not comments:
|
|
182
|
-
message = result.get("message", "No comments generated. Looks good to me.")
|
|
183
|
-
post_note(f"✅ **OpenCodeReview**: {message}")
|
|
184
|
-
print("No review comments to post.")
|
|
185
|
-
sys.exit(0)
|
|
186
|
-
|
|
187
|
-
# Get MR diff metadata for position calculation
|
|
188
|
-
diff_refs = None
|
|
189
|
-
try:
|
|
190
|
-
versions_url = f"{API_BASE}/versions"
|
|
191
|
-
req = urllib.request.Request(versions_url, headers={AUTH_HEADER: API_TOKEN})
|
|
192
|
-
with urllib.request.urlopen(req) as resp:
|
|
193
|
-
versions = json.loads(resp.read().decode("utf-8"))
|
|
194
|
-
if versions:
|
|
195
|
-
latest = versions[0]
|
|
196
|
-
diff_refs = {
|
|
197
|
-
"base_sha": latest.get("base_commit_sha", ""),
|
|
198
|
-
"start_sha": latest.get("start_commit_sha", ""),
|
|
199
|
-
"head_sha": latest.get("head_commit_sha", ""),
|
|
200
|
-
}
|
|
201
|
-
except Exception as e:
|
|
202
|
-
print(f"Warning: Could not fetch MR versions: {e}", file=sys.stderr)
|
|
203
|
-
|
|
204
|
-
# Post inline discussions for each comment
|
|
205
|
-
success_count = 0
|
|
206
|
-
failed_comments = []
|
|
207
|
-
|
|
208
|
-
for comment in comments:
|
|
209
|
-
path = comment.get("path", "")
|
|
210
|
-
end_line = comment.get("end_line", 0)
|
|
211
|
-
start_line = comment.get("start_line", end_line)
|
|
212
|
-
body = format_comment(comment)
|
|
213
|
-
|
|
214
|
-
if not path or not end_line or not diff_refs:
|
|
215
|
-
failed_comments.append(comment)
|
|
216
|
-
continue
|
|
217
|
-
|
|
218
|
-
result_resp = post_discussion(path, end_line, body, **diff_refs)
|
|
219
|
-
if result_resp:
|
|
220
|
-
success_count += 1
|
|
221
|
-
else:
|
|
222
|
-
failed_comments.append(comment)
|
|
223
|
-
|
|
224
|
-
print(f"Successfully posted {success_count}/{len(comments)} inline comments.")
|
|
225
|
-
|
|
226
|
-
# Post fallback for any failed inline comments
|
|
227
|
-
if failed_comments:
|
|
228
|
-
fallback_body = f"🔍 **OpenCodeReview** found issues that could not be posted inline:\n\n---\n\n"
|
|
229
|
-
for comment in failed_comments:
|
|
230
|
-
fallback_body += format_comment_fallback(comment) + "\n\n---\n\n"
|
|
231
|
-
post_note(fallback_body)
|
|
232
|
-
|
|
233
|
-
# Post summary last
|
|
234
|
-
total_count = len(comments)
|
|
235
|
-
failed_count = len(failed_comments)
|
|
236
|
-
summary = f"🔍 **OpenCodeReview** found **{total_count}** issue(s) in this MR."
|
|
237
|
-
if total_count > 0:
|
|
238
|
-
summary += f"\n- ✅ {success_count} posted as inline comment(s)"
|
|
239
|
-
summary += f"\n- 📝 {failed_count} posted as summary (missing line info)"
|
|
240
|
-
if warnings:
|
|
241
|
-
summary += f"\n\n⚠️ {len(warnings)} warning(s) occurred during review."
|
|
242
|
-
post_note(summary)
|
|
243
|
-
|
|
244
|
-
PYTHON_SCRIPT
|
|
@@ -1,269 +0,0 @@
|
|
|
1
|
-
# OpenCodeReview - GitLab CI Demo
|
|
2
|
-
|
|
3
|
-
This demo shows how to integrate OpenCodeReview into your GitLab CI/CD pipeline to automatically review Merge Requests and post review comments as inline discussions.
|
|
4
|
-
|
|
5
|
-
## How It Works
|
|
6
|
-
|
|
7
|
-
```
|
|
8
|
-
MR Created/Updated → GitLab Pipeline Triggered → OCR Reviews Diff → Discussions Posted on MR
|
|
9
|
-
```
|
|
10
|
-
|
|
11
|
-
1. When a Merge Request is opened or updated, the pipeline triggers
|
|
12
|
-
2. It installs OCR via npm in a `node:20` Docker image
|
|
13
|
-
3. Runs `ocr review --from origin/<target> --to <commit_sha> --format json --audience agent` to analyze the diff (uses commit SHA to support fork MRs)
|
|
14
|
-
4. Parses the JSON output and posts inline discussions on the MR using GitLab's Discussions API
|
|
15
|
-
|
|
16
|
-
## Setup
|
|
17
|
-
|
|
18
|
-
### 1. Copy the pipeline file
|
|
19
|
-
|
|
20
|
-
Copy `.gitlab-ci.yml` to your repository root (or include it via `include:`):
|
|
21
|
-
|
|
22
|
-
```bash
|
|
23
|
-
cp .gitlab-ci.yml /path/to/your/repo/.gitlab-ci.yml
|
|
24
|
-
```
|
|
25
|
-
|
|
26
|
-
Or use GitLab's `include` feature in your existing `.gitlab-ci.yml`:
|
|
27
|
-
|
|
28
|
-
```yaml
|
|
29
|
-
include:
|
|
30
|
-
- local: 'ci_demo/gitlab_ci/.gitlab-ci.yml'
|
|
31
|
-
```
|
|
32
|
-
|
|
33
|
-
### 2. Configure CI/CD Variables
|
|
34
|
-
|
|
35
|
-
Go to your project's **Settings → CI/CD → Variables** and add:
|
|
36
|
-
|
|
37
|
-
| Variable | Required | Masked | Description |
|
|
38
|
-
|----------|----------|--------|-------------|
|
|
39
|
-
| `OCR_LLM_URL` | Yes | No | LLM API endpoint URL (e.g., `https://api.openai.com/v1/chat/completions`) |
|
|
40
|
-
| `OCR_LLM_AUTH_TOKEN` | Yes | Yes | API authentication token |
|
|
41
|
-
| `OCR_LLM_MODEL` | No | No | Model name (defaults to `gpt-4o`) |
|
|
42
|
-
| `GITLAB_API_TOKEN` | No | Yes | GitLab access token with `api` scope (falls back to `CI_JOB_TOKEN` if not set) |
|
|
43
|
-
|
|
44
|
-
> **Note:** GitLab CI/CD does not support variables with values shorter than 8 characters, so `use_anthropic` cannot be set as a CI variable. The pipeline sets it to `false` by default. If you need to use Anthropic Claude models, you'll need to modify the `.gitlab-ci.yml` script directly.
|
|
45
|
-
>
|
|
46
|
-
> The pipeline also configures `llm.extra_body` to disable thinking mode for compatibility with various LLM providers.
|
|
47
|
-
|
|
48
|
-
### 3. Create a GitLab Access Token
|
|
49
|
-
|
|
50
|
-
You need a token with `api` scope to post discussions on MRs. Options:
|
|
51
|
-
|
|
52
|
-
- **Project Access Token** (recommended): Settings → Access Tokens → Create with `api` scope
|
|
53
|
-
- **Personal Access Token**: User Settings → Access Tokens → Create with `api` scope
|
|
54
|
-
- **Group Access Token**: For organization-wide usage
|
|
55
|
-
|
|
56
|
-
> **Note:** The built-in `CI_JOB_TOKEN` has limited API scope and may not support all discussion features (e.g., creating new threads on older GitLab versions). If `GITLAB_API_TOKEN` is not set, the pipeline falls back to `CI_JOB_TOKEN` automatically — but for best results, a dedicated token with `api` scope is recommended.
|
|
57
|
-
>
|
|
58
|
-
> **Tip:** For Project Access Tokens and Group Access Tokens, the token name determines the bot name shown in MR discussions. For example, naming your token `OpenCodeReview Bot` will make review comments appear as posted by `OpenCodeReview Bot`.
|
|
59
|
-
|
|
60
|
-
## Example Output
|
|
61
|
-
|
|
62
|
-
When an MR is reviewed, comments appear as:
|
|
63
|
-
|
|
64
|
-
- **Inline discussions**: Directly on the changed lines in the MR diff view
|
|
65
|
-
- **Summary note**: A final note summarizing the total number of issues found
|
|
66
|
-
- **Fallback notes**: If inline posting fails for specific comments, they appear as regular MR notes with file/line references
|
|
67
|
-
|
|
68
|
-
### Inline Discussion Example
|
|
69
|
-
|
|
70
|
-
Comments are posted using GitLab's Discussion API with position data, so they appear directly next to the relevant code in the "Changes" tab.
|
|
71
|
-
|
|
72
|
-
## Supported LLM Providers
|
|
73
|
-
|
|
74
|
-
OCR supports both OpenAI and Anthropic API formats:
|
|
75
|
-
|
|
76
|
-
- **OpenAI-compatible APIs** (default):
|
|
77
|
-
- OpenAI (GPT-4o, GPT-4, etc.)
|
|
78
|
-
- Azure OpenAI
|
|
79
|
-
- Self-hosted models (vLLM, Ollama, etc.)
|
|
80
|
-
- **Anthropic APIs** (modify `.gitlab-ci.yml` to set `use_anthropic: true`):
|
|
81
|
-
- Anthropic Claude models
|
|
82
|
-
|
|
83
|
-
## Customization
|
|
84
|
-
|
|
85
|
-
### Use a specific OCR version
|
|
86
|
-
|
|
87
|
-
```yaml
|
|
88
|
-
script:
|
|
89
|
-
- npm install -g @alibaba-group/open-code-review@1.0.0
|
|
90
|
-
```
|
|
91
|
-
|
|
92
|
-
### Add custom review rules
|
|
93
|
-
|
|
94
|
-
Use the `--rule` flag to pass a custom rules JSON file:
|
|
95
|
-
|
|
96
|
-
```yaml
|
|
97
|
-
script:
|
|
98
|
-
- ocr review --rule ./my-rules.json --from origin/$CI_MERGE_REQUEST_TARGET_BRANCH_NAME --to $CI_COMMIT_SHA
|
|
99
|
-
```
|
|
100
|
-
|
|
101
|
-
### Limit concurrency
|
|
102
|
-
|
|
103
|
-
Adjust the `--concurrency` flag for large MRs to control the number of concurrent LLM requests:
|
|
104
|
-
|
|
105
|
-
```yaml
|
|
106
|
-
script:
|
|
107
|
-
- ocr review --concurrency 5 --from origin/$CI_MERGE_REQUEST_TARGET_BRANCH_NAME --to $CI_COMMIT_SHA
|
|
108
|
-
```
|
|
109
|
-
|
|
110
|
-
### Provide background context
|
|
111
|
-
|
|
112
|
-
Use the `--background` flag to pass additional context that helps OCR better understand the purpose of the changes:
|
|
113
|
-
|
|
114
|
-
```yaml
|
|
115
|
-
script:
|
|
116
|
-
- ocr review --background "$CI_MERGE_REQUEST_TITLE" --from origin/$CI_MERGE_REQUEST_TARGET_BRANCH_NAME --to $CI_COMMIT_SHA
|
|
117
|
-
```
|
|
118
|
-
|
|
119
|
-
This is particularly useful when your MR titles follow semantic conventions (e.g., `feat(auth): add OAuth2 support`) that clearly summarize what the MR implements. The background information helps OCR provide more relevant and context-aware review comments.
|
|
120
|
-
|
|
121
|
-
### Change the trigger events
|
|
122
|
-
|
|
123
|
-
By default, the pipeline uses `only: [merge_requests]`, which triggers on **all** MR events (creation, updates, reopen). GitLab CI does not natively support fine-grained control to trigger **only on MR creation**.
|
|
124
|
-
|
|
125
|
-
To avoid re-reviewing on every push to an existing MR (and wasting LLM API tokens), you can check for existing OCR reviews **before** running `ocr review`. Use a wrapper script that skips the review step if OCR comments already exist:
|
|
126
|
-
|
|
127
|
-
```yaml
|
|
128
|
-
script:
|
|
129
|
-
# Install OpenCodeReview
|
|
130
|
-
- npm install -g @alibaba-group/open-code-review
|
|
131
|
-
|
|
132
|
-
# Configure OCR
|
|
133
|
-
- mkdir -p ~/.open-code-review
|
|
134
|
-
- |
|
|
135
|
-
ocr config set llm.url $OCR_LLM_URL
|
|
136
|
-
ocr config set llm.auth_token $OCR_LLM_AUTH_TOKEN
|
|
137
|
-
ocr config set llm.model $OCR_LLM_MODEL
|
|
138
|
-
ocr config set llm.use_anthropic false
|
|
139
|
-
ocr config set llm.extra_body '{"thinking": {"type": "disabled"}}'
|
|
140
|
-
|
|
141
|
-
# Check for existing OCR reviews and run review only if not found
|
|
142
|
-
- |
|
|
143
|
-
python3 << 'WRAPPER_SCRIPT'
|
|
144
|
-
import json
|
|
145
|
-
import os
|
|
146
|
-
import subprocess
|
|
147
|
-
import sys
|
|
148
|
-
import urllib.request
|
|
149
|
-
|
|
150
|
-
GITLAB_URL = os.environ.get("CI_SERVER_URL", "https://gitlab.com")
|
|
151
|
-
PROJECT_ID = os.environ["CI_PROJECT_ID"]
|
|
152
|
-
MR_IID = os.environ["CI_MERGE_REQUEST_IID"]
|
|
153
|
-
API_TOKEN = os.environ["GITLAB_API_TOKEN"]
|
|
154
|
-
SOURCE_BRANCH = os.environ["CI_MERGE_REQUEST_SOURCE_BRANCH_NAME"]
|
|
155
|
-
TARGET_BRANCH = os.environ["CI_MERGE_REQUEST_TARGET_BRANCH_NAME"]
|
|
156
|
-
|
|
157
|
-
# Check for existing OCR reviews
|
|
158
|
-
url = f"{GITLAB_URL}/api/v4/projects/{PROJECT_ID}/merge_requests/{MR_IID}/notes?per_page=100"
|
|
159
|
-
req = urllib.request.Request(url, headers={"PRIVATE-TOKEN": API_TOKEN})
|
|
160
|
-
with urllib.request.urlopen(req) as resp:
|
|
161
|
-
notes = json.loads(resp.read().decode("utf-8"))
|
|
162
|
-
|
|
163
|
-
for note in notes:
|
|
164
|
-
if "OpenCodeReview" in note.get("body", ""):
|
|
165
|
-
print("⏭️ OCR has already reviewed this MR. Skipping to save tokens.")
|
|
166
|
-
print("Delete previous OCR comments to re-trigger review.")
|
|
167
|
-
sys.exit(0)
|
|
168
|
-
|
|
169
|
-
# No existing review found - run OCR
|
|
170
|
-
print("🔍 No existing OCR review found. Running review...")
|
|
171
|
-
COMMIT_SHA = os.environ["CI_COMMIT_SHA"]
|
|
172
|
-
result = subprocess.run([
|
|
173
|
-
"ocr", "review",
|
|
174
|
-
"--from", f"origin/{TARGET_BRANCH}",
|
|
175
|
-
"--to", COMMIT_SHA,
|
|
176
|
-
"--format", "json",
|
|
177
|
-
"--audience", "agent"
|
|
178
|
-
], capture_output=True, text=True)
|
|
179
|
-
|
|
180
|
-
# Save output for the posting script
|
|
181
|
-
with open("/tmp/ocr-result.json", "w") as f:
|
|
182
|
-
f.write(result.stdout)
|
|
183
|
-
with open("/tmp/ocr-stderr.log", "w") as f:
|
|
184
|
-
f.write(result.stderr)
|
|
185
|
-
|
|
186
|
-
print("OCR review completed.")
|
|
187
|
-
WRAPPER_SCRIPT
|
|
188
|
-
|
|
189
|
-
# Post review comments to MR
|
|
190
|
-
- |
|
|
191
|
-
python3 << 'PYTHON_SCRIPT'
|
|
192
|
-
...existing post script...
|
|
193
|
-
PYTHON_SCRIPT
|
|
194
|
-
```
|
|
195
|
-
|
|
196
|
-
The key logic: the Python wrapper checks for existing OCR comments before running `ocr review`. If found, it exits early with `sys.exit(0)` before consuming any LLM tokens. To re-trigger a review, users can manually delete the previous OCR comments.
|
|
197
|
-
|
|
198
|
-
### Self-hosted GitLab
|
|
199
|
-
|
|
200
|
-
The script automatically uses `CI_SERVER_URL` to determine the GitLab API base URL, so it works with self-hosted GitLab instances out of the box.
|
|
201
|
-
|
|
202
|
-
### Use a Service Account as Review Bot
|
|
203
|
-
|
|
204
|
-
By default, review comments are posted using the user who owns the access token configured in `GITLAB_API_TOKEN`. You can create a dedicated service account bot to post reviews with a custom identity, making it easier to distinguish automated reviews from human comments.
|
|
205
|
-
|
|
206
|
-
For more details about GitLab service accounts, see the [GitLab Service Accounts documentation](https://docs.gitlab.com/ee/user/profile/service_accounts.html).
|
|
207
|
-
|
|
208
|
-
#### Step 1: Create a Service Account
|
|
209
|
-
|
|
210
|
-
Create a service account in your project:
|
|
211
|
-
|
|
212
|
-
1. Go to your **Project → Settings → Service Accounts**
|
|
213
|
-
2. Click **New service account**
|
|
214
|
-
3. Fill in the following:
|
|
215
|
-
- **Name**: e.g., `OpenCodeReview Bot` (this will be the bot name shown in MR discussions)
|
|
216
|
-
- **Username**: Will be auto-generated based on the name
|
|
217
|
-
4. Click **Create service account**
|
|
218
|
-
|
|
219
|
-
#### Step 2: Invite the Service Account to Your Project
|
|
220
|
-
|
|
221
|
-
After the service account is created, invite it to your project with appropriate permissions:
|
|
222
|
-
|
|
223
|
-
1. Go to your **Project → Settings → Members**
|
|
224
|
-
2. Click **Invite member**
|
|
225
|
-
3. Search for the service account by name (e.g., `OpenCodeReview Bot`)
|
|
226
|
-
4. Select the service account and assign a role (`Developer` or `Maintainer` required for posting discussions)
|
|
227
|
-
5. Click **Invite**
|
|
228
|
-
|
|
229
|
-
#### Step 3: Create an Access Token
|
|
230
|
-
|
|
231
|
-
Generate an access token for the service account:
|
|
232
|
-
|
|
233
|
-
1. Go to your **Project → Settings → Service Accounts**
|
|
234
|
-
2. Click on the service account to view its details
|
|
235
|
-
3. Click **Add new token**
|
|
236
|
-
4. Configure the token:
|
|
237
|
-
- **Name**: e.g., `ocr-review-token`
|
|
238
|
-
- **Expiration**: As needed
|
|
239
|
-
- **Scope**: Select `api` (required for Discussions API)
|
|
240
|
-
5. Click **Create token** and copy the token value
|
|
241
|
-
|
|
242
|
-
#### Step 4: Update CI/CD Variables
|
|
243
|
-
|
|
244
|
-
Update the `GITLAB_API_TOKEN` variable in your project's CI/CD settings:
|
|
245
|
-
|
|
246
|
-
Go to **Settings → CI/CD → Variables** and update `GITLAB_API_TOKEN` with the service account's token.
|
|
247
|
-
|
|
248
|
-
Now review comments will be posted with your service account identity (e.g., `OpenCodeReview Bot`), providing a clear and professional appearance for automated code reviews.
|
|
249
|
-
|
|
250
|
-
## Troubleshooting
|
|
251
|
-
|
|
252
|
-
### Common Issues
|
|
253
|
-
|
|
254
|
-
1. **"API error 403"**: The `GITLAB_API_TOKEN` lacks `api` scope or doesn't have access to the project
|
|
255
|
-
2. **"Failed to parse OCR output"**: Check that `OCR_LLM_URL` and `OCR_LLM_AUTH_TOKEN` variables are correctly set
|
|
256
|
-
3. **"Cannot find merge-base"**: Ensure `GIT_DEPTH: 0` is set (full clone)
|
|
257
|
-
4. **Inline comments on wrong lines**: GitLab requires exact SHA matching; the script fetches MR version metadata to get correct diff refs
|
|
258
|
-
|
|
259
|
-
### Debugging
|
|
260
|
-
|
|
261
|
-
Add verbose output to the review step:
|
|
262
|
-
|
|
263
|
-
```yaml
|
|
264
|
-
script:
|
|
265
|
-
- cat /tmp/ocr-result.json
|
|
266
|
-
- cat /tmp/ocr-stderr.log
|
|
267
|
-
```
|
|
268
|
-
|
|
269
|
-
|
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "open-code-review",
|
|
3
|
-
"commands": "./commands",
|
|
4
|
-
"description": "Perform AI code review on Git diffs — supports workspace changes, branch ranges, and single commits with concurrent per-file analysis, codebase search, and deep context-aware review.",
|
|
5
|
-
"version": "1.0.0"
|
|
6
|
-
}
|
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "open-code-review",
|
|
3
|
-
"version": "1.0.0",
|
|
4
|
-
"description": "Use Alibaba Open Code Review from local Codex.",
|
|
5
|
-
"author": {
|
|
6
|
-
"name": "Alibaba"
|
|
7
|
-
},
|
|
8
|
-
"homepage": "https://github.com/alibaba/open-code-review",
|
|
9
|
-
"repository": "https://github.com/alibaba/open-code-review",
|
|
10
|
-
"license": "Apache-2.0",
|
|
11
|
-
"keywords": [
|
|
12
|
-
"code-review",
|
|
13
|
-
"codex",
|
|
14
|
-
"ocr",
|
|
15
|
-
"open-code-review"
|
|
16
|
-
],
|
|
17
|
-
"skills": "./skills/",
|
|
18
|
-
"interface": {
|
|
19
|
-
"displayName": "Open Code Review",
|
|
20
|
-
"shortDescription": "Run structured code reviews from local Codex.",
|
|
21
|
-
"longDescription": "Adds a Codex skill that invokes the local Open Code Review CLI to review Git workspace changes, commits, and branch comparisons.",
|
|
22
|
-
"developerName": "Alibaba",
|
|
23
|
-
"category": "Developer Tools",
|
|
24
|
-
"capabilities": [
|
|
25
|
-
"Read"
|
|
26
|
-
],
|
|
27
|
-
"websiteURL": "https://github.com/alibaba/open-code-review",
|
|
28
|
-
"defaultPrompt": [
|
|
29
|
-
"Use Open Code Review to review my current changes.",
|
|
30
|
-
"Use Open Code Review to review this branch against main.",
|
|
31
|
-
"Use Open Code Review to review and fix high-confidence issues."
|
|
32
|
-
]
|
|
33
|
-
}
|
|
34
|
-
}
|
|
@@ -1,108 +0,0 @@
|
|
|
1
|
-
# Open Code Review Codex 플러그인 사용법
|
|
2
|
-
|
|
3
|
-
이 문서는 로컬 Codex에서 Alibaba Open Code Review를 사용하는 방법을 설명합니다.
|
|
4
|
-
|
|
5
|
-
## 개요
|
|
6
|
-
|
|
7
|
-
이 플러그인은 Open Code Review를 Codex 내부 LLM backend로 바꾸지 않습니다. Codex에서 로컬 `ocr` CLI를 호출할 수 있도록 skill을 제공하는 통합입니다.
|
|
8
|
-
|
|
9
|
-
```text
|
|
10
|
-
Codex
|
|
11
|
-
└─ Open Code Review plugin
|
|
12
|
-
└─ ocr review --audience agent
|
|
13
|
-
```
|
|
14
|
-
|
|
15
|
-
## 사전 준비
|
|
16
|
-
|
|
17
|
-
`ocr` CLI가 설치되어 있어야 합니다.
|
|
18
|
-
|
|
19
|
-
```bash
|
|
20
|
-
npm install -g @alibaba-group/open-code-review
|
|
21
|
-
```
|
|
22
|
-
|
|
23
|
-
설치 확인:
|
|
24
|
-
|
|
25
|
-
```bash
|
|
26
|
-
command -v ocr
|
|
27
|
-
ocr version
|
|
28
|
-
```
|
|
29
|
-
|
|
30
|
-
OCR 자체의 LLM 설정도 필요합니다.
|
|
31
|
-
|
|
32
|
-
```bash
|
|
33
|
-
ocr llm test
|
|
34
|
-
```
|
|
35
|
-
|
|
36
|
-
이 명령이 실패하면 Codex 플러그인 설치와 별개로 OCR의 LLM 설정을 먼저 완료해야 합니다.
|
|
37
|
-
|
|
38
|
-
## Codex에서 설치
|
|
39
|
-
|
|
40
|
-
Codex에서 이 repo를 marketplace로 추가합니다.
|
|
41
|
-
|
|
42
|
-
```bash
|
|
43
|
-
codex plugin marketplace add alibaba/open-code-review
|
|
44
|
-
codex
|
|
45
|
-
```
|
|
46
|
-
|
|
47
|
-
Codex 안에서 `/plugins`를 열고 `Open Code Review`를 설치 및 활성화합니다.
|
|
48
|
-
|
|
49
|
-
로컬 checkout 또는 fork에서 테스트할 때는 다음을 사용할 수 있습니다.
|
|
50
|
-
|
|
51
|
-
```bash
|
|
52
|
-
codex plugin marketplace add .
|
|
53
|
-
codex
|
|
54
|
-
```
|
|
55
|
-
|
|
56
|
-
## 사용 예시
|
|
57
|
-
|
|
58
|
-
새 Codex thread에서 다음처럼 요청합니다.
|
|
59
|
-
|
|
60
|
-
```text
|
|
61
|
-
@Open Code Review review my current changes
|
|
62
|
-
```
|
|
63
|
-
|
|
64
|
-
브랜치 비교:
|
|
65
|
-
|
|
66
|
-
```text
|
|
67
|
-
@Open Code Review review this branch against main
|
|
68
|
-
```
|
|
69
|
-
|
|
70
|
-
검토 후 안전한 항목만 수정:
|
|
71
|
-
|
|
72
|
-
```text
|
|
73
|
-
@Open Code Review review and fix high-confidence issues
|
|
74
|
-
```
|
|
75
|
-
|
|
76
|
-
## 내부적으로 실행되는 명령
|
|
77
|
-
|
|
78
|
-
현재 workspace 변경사항 검토:
|
|
79
|
-
|
|
80
|
-
```bash
|
|
81
|
-
ocr review --audience agent
|
|
82
|
-
```
|
|
83
|
-
|
|
84
|
-
특정 commit 검토:
|
|
85
|
-
|
|
86
|
-
```bash
|
|
87
|
-
ocr review --audience agent --commit <sha>
|
|
88
|
-
```
|
|
89
|
-
|
|
90
|
-
브랜치 비교:
|
|
91
|
-
|
|
92
|
-
```bash
|
|
93
|
-
ocr review --audience agent --from <base-ref> --to <head-ref>
|
|
94
|
-
```
|
|
95
|
-
|
|
96
|
-
미리보기:
|
|
97
|
-
|
|
98
|
-
```bash
|
|
99
|
-
ocr review --preview
|
|
100
|
-
```
|
|
101
|
-
|
|
102
|
-
## 주의사항
|
|
103
|
-
|
|
104
|
-
- 이 플러그인은 OpenAI Responses API endpoint를 설정하지 않습니다.
|
|
105
|
-
- 이 플러그인은 `OPENAI_API_KEY`나 `gpt-5.1-codex-max` 설정을 요구하지 않습니다.
|
|
106
|
-
- OCR 자체는 별도의 LLM 설정이 필요합니다.
|
|
107
|
-
- 파일 수정은 사용자가 명시적으로 요청한 경우에만 수행합니다.
|
|
108
|
-
- commit 생성은 사용자가 명시적으로 요청한 경우에만 수행합니다.
|