@alibaba-group/open-code-review 1.3.0 → 1.3.4
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/CONTRIBUTING.ja-JP.md +1 -1
- package/CONTRIBUTING.ko-KR.md +1 -1
- package/CONTRIBUTING.md +4 -2
- package/CONTRIBUTING.ru-RU.md +224 -0
- package/CONTRIBUTING.zh-CN.md +1 -1
- package/README.ja-JP.md +1 -1
- package/README.ko-KR.md +1 -1
- package/README.md +1 -1
- package/README.ru-RU.md +476 -0
- package/README.zh-CN.md +1 -1
- package/examples/github_actions/ocr-review.yml +55 -29
- package/package.json +3 -2
- package/scripts/github-actions/post-review-comments.test.js +171 -0
package/CONTRIBUTING.ja-JP.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
OpenCodeReviewへのコントリビューションに興味を持っていただきありがとうございます!タイポの修正、バグ報告、新機能の実装など、あらゆる貢献が重要です。
|
|
4
4
|
|
|
5
|
-
[English Version](CONTRIBUTING.md) | [简体中文版](CONTRIBUTING.zh-CN.md)
|
|
5
|
+
[English Version](CONTRIBUTING.md) | [简体中文版](CONTRIBUTING.zh-CN.md) | [한국어](CONTRIBUTING.ko-KR.md) | [Русский](CONTRIBUTING.ru-RU.md)
|
|
6
6
|
|
|
7
7
|
## 行動規範
|
|
8
8
|
|
package/CONTRIBUTING.ko-KR.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
OpenCodeReview에 기여해 주셔서 감사합니다. 오타 수정, bug report, 새 기능 구현 등 모든 기여는 프로젝트에 도움이 됩니다.
|
|
4
4
|
|
|
5
|
-
[English](CONTRIBUTING.md) | [简体中文版](CONTRIBUTING.zh-CN.md) | [日本語版](CONTRIBUTING.ja-JP.md) | 한국어
|
|
5
|
+
[English](CONTRIBUTING.md) | [简体中文版](CONTRIBUTING.zh-CN.md) | [日本語版](CONTRIBUTING.ja-JP.md) | 한국어 | [Русский](CONTRIBUTING.ru-RU.md)
|
|
6
6
|
|
|
7
7
|
## Code of Conduct
|
|
8
8
|
|
package/CONTRIBUTING.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
Thank you for your interest in contributing to OpenCodeReview! Every contribution matters — whether it's fixing a typo, reporting a bug, or implementing a new feature.
|
|
4
4
|
|
|
5
|
-
[简体中文版](CONTRIBUTING.zh-CN.md) | [日本語版](CONTRIBUTING.ja-JP.md) | [한국어](CONTRIBUTING.ko-KR.md)
|
|
5
|
+
[简体中文版](CONTRIBUTING.zh-CN.md) | [日本語版](CONTRIBUTING.ja-JP.md) | [한국어](CONTRIBUTING.ko-KR.md) | [Русский](CONTRIBUTING.ru-RU.md)
|
|
6
6
|
|
|
7
7
|
## Code of Conduct
|
|
8
8
|
|
|
@@ -135,7 +135,7 @@ Documentation is a crucial part of OpenCodeReview. We welcome improvements to RE
|
|
|
135
135
|
- Clarifying confusing explanations or adding missing context
|
|
136
136
|
- Adding usage examples for commands or configuration options
|
|
137
137
|
- Updating outdated content (e.g., after a feature change)
|
|
138
|
-
- Translating or improving localized documentation (`README.zh-CN.md`, `README.ja-JP.md`, `README.ko-KR.md`, `CONTRIBUTING.zh-CN.md`, `CONTRIBUTING.ja-JP.md`, `CONTRIBUTING.ko-KR.md`)
|
|
138
|
+
- Translating or improving localized documentation (`README.zh-CN.md`, `README.ja-JP.md`, `README.ko-KR.md`, `README.ru-RU.md`, `CONTRIBUTING.zh-CN.md`, `CONTRIBUTING.ja-JP.md`, `CONTRIBUTING.ko-KR.md`, `CONTRIBUTING.ru-RU.md`)
|
|
139
139
|
|
|
140
140
|
### Documentation Workflow
|
|
141
141
|
|
|
@@ -151,10 +151,12 @@ Documentation is a crucial part of OpenCodeReview. We welcome improvements to RE
|
|
|
151
151
|
| `README.zh-CN.md` | Chinese translation |
|
|
152
152
|
| `README.ja-JP.md` | Japanese translation |
|
|
153
153
|
| `README.ko-KR.md` | Korean translation |
|
|
154
|
+
| `README.ru-RU.md` | Russian translation |
|
|
154
155
|
| `CONTRIBUTING.md` | Contribution guide (English) |
|
|
155
156
|
| `CONTRIBUTING.zh-CN.md` | Contribution guide (Chinese) |
|
|
156
157
|
| `CONTRIBUTING.ja-JP.md` | Contribution guide (Japanese) |
|
|
157
158
|
| `CONTRIBUTING.ko-KR.md` | Contribution guide (Korean) |
|
|
159
|
+
| `CONTRIBUTING.ru-RU.md` | Contribution guide (Russian) |
|
|
158
160
|
|
|
159
161
|
## Submitting Changes
|
|
160
162
|
|
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
# Участие в разработке OpenCodeReview
|
|
2
|
+
|
|
3
|
+
Спасибо за интерес к развитию OpenCodeReview! Важен любой вклад — будь то исправленная опечатка, сообщение о баге или новая функциональность.
|
|
4
|
+
|
|
5
|
+
[English](CONTRIBUTING.md) | [简体中文版](CONTRIBUTING.zh-CN.md) | [日本語版](CONTRIBUTING.ja-JP.md) | [한국어](CONTRIBUTING.ko-KR.md) | Русский
|
|
6
|
+
|
|
7
|
+
## Кодекс поведения
|
|
8
|
+
|
|
9
|
+
Участвуя в этом проекте, вы соглашаетесь поддерживать уважительную и инклюзивную атмосферу. Пожалуйста, будьте доброжелательны и конструктивны в любом взаимодействии.
|
|
10
|
+
|
|
11
|
+
## Как можно помочь
|
|
12
|
+
|
|
13
|
+
Помимо написания кода, есть много способов внести вклад:
|
|
14
|
+
|
|
15
|
+
- **Сообщайте о багах** — нашли поломку? Заведите issue с шагами воспроизведения.
|
|
16
|
+
- **Предлагайте улучшения** — есть идея? Начните обсуждение в [GitHub Discussions](https://github.com/alibaba/open-code-review/discussions/categories/ideas) или заведите issue [Feature Request](https://github.com/alibaba/open-code-review/issues/new?template=feature_request.yml).
|
|
17
|
+
- **Улучшайте документацию** — исправляйте опечатки, проясняйте формулировки, добавляйте примеры. Чтобы сообщить о проблеме, можно также завести [Documentation Issue](https://github.com/alibaba/open-code-review/issues/new?template=docs_report.yml).
|
|
18
|
+
- **Ревьюйте pull request'ы** — помогайте нам проверять код других контрибьюторов.
|
|
19
|
+
- **Пишите код** — исправляйте баги, добавляйте функциональность, улучшайте производительность.
|
|
20
|
+
|
|
21
|
+
## С чего начать
|
|
22
|
+
|
|
23
|
+
### Требования
|
|
24
|
+
|
|
25
|
+
- [Go 1.25+](https://go.dev/dl/)
|
|
26
|
+
- [Git](https://git-scm.com/)
|
|
27
|
+
- [Make](https://www.gnu.org/software/make/)
|
|
28
|
+
|
|
29
|
+
### Настройка
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
# 1. Сделайте форк репозитория на GitHub
|
|
33
|
+
|
|
34
|
+
# 2. Склонируйте свой форк
|
|
35
|
+
git clone https://github.com/<your-username>/open-code-review.git
|
|
36
|
+
cd open-code-review
|
|
37
|
+
|
|
38
|
+
# 3. Добавьте remote upstream (для синхронизации с основным репозиторием)
|
|
39
|
+
git remote add upstream https://github.com/alibaba/open-code-review.git
|
|
40
|
+
|
|
41
|
+
# 4. Соберите проект
|
|
42
|
+
make build
|
|
43
|
+
|
|
44
|
+
# 5. Запустите тесты
|
|
45
|
+
make test
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
Если всё прошло успешно — вы готовы контрибьютить.
|
|
49
|
+
|
|
50
|
+
> **Примечание:** remote `upstream` для контрибьюторов доступен только на чтение — он используется, чтобы подтягивать свежие изменения из основного репозитория. Пушить напрямую в upstream нельзя. Все изменения отправляются в ваш форк (`origin`) и подаются через Pull Request.
|
|
51
|
+
|
|
52
|
+
## Процесс разработки
|
|
53
|
+
|
|
54
|
+
### Ветки
|
|
55
|
+
|
|
56
|
+
Создайте feature-ветку от `main`:
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
git checkout main
|
|
60
|
+
git pull upstream main
|
|
61
|
+
git checkout -b feat/your-feature-name
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
Используйте префиксы, обозначающие тип изменения:
|
|
65
|
+
|
|
66
|
+
| Префикс | Назначение |
|
|
67
|
+
| ----------- | --------------------------------------- |
|
|
68
|
+
| `feat/` | Новая функциональность |
|
|
69
|
+
| `fix/` | Исправление бага |
|
|
70
|
+
| `docs/` | Только документация |
|
|
71
|
+
| `refactor/` | Рефакторинг (без изменения поведения) |
|
|
72
|
+
| `test/` | Добавление или обновление тестов |
|
|
73
|
+
| `chore/` | Сборка, CI или инструментарий |
|
|
74
|
+
|
|
75
|
+
### Сообщения коммитов
|
|
76
|
+
|
|
77
|
+
Следуйте формату [Conventional Commits](https://www.conventionalcommits.org/):
|
|
78
|
+
|
|
79
|
+
```
|
|
80
|
+
<type>(<scope>): <краткое описание>
|
|
81
|
+
|
|
82
|
+
[необязательное тело]
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
Примеры:
|
|
86
|
+
|
|
87
|
+
```
|
|
88
|
+
feat(agent): add support for custom tool definitions
|
|
89
|
+
fix(llm): handle timeout errors in Anthropic API calls
|
|
90
|
+
docs(README): update configuration examples
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
### Качество кода
|
|
94
|
+
|
|
95
|
+
Перед отправкой изменений убедитесь, что они проходят все проверки:
|
|
96
|
+
|
|
97
|
+
```bash
|
|
98
|
+
# Форматирование и линт (стандартный инструментарий Go)
|
|
99
|
+
go fmt ./...
|
|
100
|
+
go vet ./...
|
|
101
|
+
|
|
102
|
+
# Тесты с детектором гонок
|
|
103
|
+
make test
|
|
104
|
+
|
|
105
|
+
# Успешная сборка
|
|
106
|
+
make build
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
### Структура проекта
|
|
110
|
+
|
|
111
|
+
```
|
|
112
|
+
├── cmd/opencodereview/ # Точка входа CLI
|
|
113
|
+
├── internal/
|
|
114
|
+
│ ├── agent/ # Логика ревью-агента
|
|
115
|
+
│ ├── config/ # Управление конфигурацией
|
|
116
|
+
│ ├── diff/ # Разбор git-диффов
|
|
117
|
+
│ ├── llm/ # Клиент LLM API (Anthropic и OpenAI)
|
|
118
|
+
│ ├── model/ # Модели данных
|
|
119
|
+
│ ├── session/ # Управление сессиями ревью
|
|
120
|
+
│ ├── tool/ # Встроенные инструменты (file_read, code_search и др.)
|
|
121
|
+
│ ├── telemetry/ # Интеграция с OpenTelemetry
|
|
122
|
+
│ └── viewer/ # WebUI-просмотрщик сессий
|
|
123
|
+
├── pages/ # Фронтенд WebUI
|
|
124
|
+
├── scripts/ # Скрипты сборки и установки
|
|
125
|
+
└── bin/ # NPM-обёртка
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
## Вклад в документацию
|
|
129
|
+
|
|
130
|
+
Документация — важнейшая часть OpenCodeReview. Мы приветствуем улучшения README-файлов, комментариев в коде, примеров конфигурации и любых текстов, обращённых к пользователю.
|
|
131
|
+
|
|
132
|
+
### Что считается вкладом в документацию
|
|
133
|
+
|
|
134
|
+
- Исправление опечаток, грамматических ошибок и битых ссылок
|
|
135
|
+
- Прояснение запутанных объяснений и добавление недостающего контекста
|
|
136
|
+
- Добавление примеров использования команд и параметров конфигурации
|
|
137
|
+
- Обновление устаревшего содержимого (например, после изменения функциональности)
|
|
138
|
+
- Перевод и улучшение локализованной документации (`README.zh-CN.md`, `README.ja-JP.md`, `README.ko-KR.md`, `README.ru-RU.md`, `CONTRIBUTING.zh-CN.md`, `CONTRIBUTING.ja-JP.md`, `CONTRIBUTING.ko-KR.md`, `CONTRIBUTING.ru-RU.md`)
|
|
139
|
+
|
|
140
|
+
### Процесс работы с документацией
|
|
141
|
+
|
|
142
|
+
1. Если вы заметили проблему, но не планируете исправлять её сами, заведите [Documentation Issue](https://github.com/alibaba/open-code-review/issues/new?template=docs_report.yml).
|
|
143
|
+
2. Если хотите исправить сами — сделайте форк, внесите изменения и подайте PR с префиксом ветки `docs/` (например, `docs/fix-config-example`).
|
|
144
|
+
3. PR, затрагивающие только документацию, не требуют изменений в тестах, но, пожалуйста, проверяйте точность всех приводимых команд и фрагментов кода.
|
|
145
|
+
|
|
146
|
+
### Файлы документации
|
|
147
|
+
|
|
148
|
+
| Файл | Назначение |
|
|
149
|
+
| ----------------------- | ------------------------------------------- |
|
|
150
|
+
| `README.md` | Основная документация проекта (английский) |
|
|
151
|
+
| `README.zh-CN.md` | Китайский перевод |
|
|
152
|
+
| `README.ja-JP.md` | Японский перевод |
|
|
153
|
+
| `README.ko-KR.md` | Корейский перевод |
|
|
154
|
+
| `README.ru-RU.md` | Русский перевод |
|
|
155
|
+
| `CONTRIBUTING.md` | Руководство контрибьютора (английский) |
|
|
156
|
+
| `CONTRIBUTING.zh-CN.md` | Руководство контрибьютора (китайский) |
|
|
157
|
+
| `CONTRIBUTING.ja-JP.md` | Руководство контрибьютора (японский) |
|
|
158
|
+
| `CONTRIBUTING.ko-KR.md` | Руководство контрибьютора (корейский) |
|
|
159
|
+
| `CONTRIBUTING.ru-RU.md` | Руководство контрибьютора (русский) |
|
|
160
|
+
|
|
161
|
+
## Отправка изменений
|
|
162
|
+
|
|
163
|
+
### Заведение issue
|
|
164
|
+
|
|
165
|
+
Прежде чем браться за существенное изменение, пожалуйста, сначала заведите issue и обсудите подход. Это предотвращает дублирование работы и гарантирует, что ваш вклад согласуется с направлением развития проекта.
|
|
166
|
+
|
|
167
|
+
Сообщая о баге, укажите:
|
|
168
|
+
|
|
169
|
+
1. Версию OpenCodeReview (`ocr version`)
|
|
170
|
+
2. ОС и архитектуру
|
|
171
|
+
3. Шаги воспроизведения
|
|
172
|
+
4. Ожидаемое и фактическое поведение
|
|
173
|
+
5. Релевантные логи или сообщения об ошибках
|
|
174
|
+
|
|
175
|
+
### Процесс Pull Request
|
|
176
|
+
|
|
177
|
+
1. **Держите PR сфокусированным** — одно логическое изменение на PR. Несколько независимых изменений лучше подать отдельными PR.
|
|
178
|
+
2. **Пишите тесты** — добавляйте или обновляйте тесты при любых изменениях поведения.
|
|
179
|
+
3. **Обновляйте документацию** — если изменение затрагивает видимое пользователю поведение, обновите соответствующую документацию.
|
|
180
|
+
4. **Подпишите CLA** — прежде чем PR может быть принят, все контрибьюторы должны подписать Contributor License Agreement (см. ниже).
|
|
181
|
+
5. **Заполните шаблон PR** — опишите, что делает ваше изменение и зачем оно нужно.
|
|
182
|
+
|
|
183
|
+
### Формат заголовка PR
|
|
184
|
+
|
|
185
|
+
Используйте тот же формат Conventional Commits, что и для сообщений коммитов:
|
|
186
|
+
|
|
187
|
+
```
|
|
188
|
+
feat(agent): add support for custom tool definitions
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
### Процесс ревью
|
|
192
|
+
|
|
193
|
+
- Мейнтейнер посмотрит ваш PR — обычно в течение нескольких рабочих дней.
|
|
194
|
+
- Мы можем попросить внести изменения — это нормальная совместная работа, а не противостояние.
|
|
195
|
+
- После одобрения мейнтейнер смёржит ваш PR.
|
|
196
|
+
|
|
197
|
+
## Лицензионное соглашение контрибьютора (CLA)
|
|
198
|
+
|
|
199
|
+
Прежде чем мы сможем принять ваш вклад, необходимо подписать Alibaba Open Source Contributor License Agreement. Это гарантирует, что проект может распространяться на условиях своей лицензии.
|
|
200
|
+
|
|
201
|
+
Когда вы откроете свой первый PR, CLA-бот оставит комментарий с инструкциями. Просто перейдите по ссылке и подпишите соглашение электронно — это занимает минуту.
|
|
202
|
+
|
|
203
|
+
## Новичкам
|
|
204
|
+
|
|
205
|
+
Впервые в проекте? Ищите issues с метками:
|
|
206
|
+
|
|
207
|
+
- [`good first issue`](https://github.com/alibaba/open-code-review/labels/good%20first%20issue) — небольшие, хорошо очерченные задачи, идеальные для старта.
|
|
208
|
+
- [`help wanted`](https://github.com/alibaba/open-code-review/labels/help%20wanted) — задачи, где мы будем рады помощи сообщества.
|
|
209
|
+
|
|
210
|
+
С чего удобно начать:
|
|
211
|
+
|
|
212
|
+
- Улучшение сообщений об ошибках и вывода CLI
|
|
213
|
+
- Написание тестов для непокрытых участков кода
|
|
214
|
+
- Улучшение документации
|
|
215
|
+
|
|
216
|
+
## Сообщество
|
|
217
|
+
|
|
218
|
+
- **Сообщения о багах** — [GitHub Issues](https://github.com/alibaba/open-code-review/issues)
|
|
219
|
+
- **Предложения функциональности** — [GitHub Discussions (Ideas)](https://github.com/alibaba/open-code-review/discussions/categories/ideas) или issue [Feature Request](https://github.com/alibaba/open-code-review/issues/new?template=feature_request.yml)
|
|
220
|
+
- **Вопросы и помощь** — если у вас есть вопросы об использовании OpenCodeReview, задавайте их в [GitHub Discussions](https://github.com/alibaba/open-code-review/discussions)
|
|
221
|
+
|
|
222
|
+
## Лицензия
|
|
223
|
+
|
|
224
|
+
Внося вклад в OpenCodeReview, вы соглашаетесь с тем, что ваш вклад будет лицензирован на условиях [Apache License 2.0](LICENSE).
|
package/CONTRIBUTING.zh-CN.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
感谢你对 OpenCodeReview 的关注!无论是修复拼写错误、报告 Bug,还是实现新功能,每一份贡献都很有价值。
|
|
4
4
|
|
|
5
|
-
[English version](CONTRIBUTING.md) | [日本語版](CONTRIBUTING.ja-JP.md)
|
|
5
|
+
[English version](CONTRIBUTING.md) | [日本語版](CONTRIBUTING.ja-JP.md) | [한국어](CONTRIBUTING.ko-KR.md) | [Русский](CONTRIBUTING.ru-RU.md)
|
|
6
6
|
|
|
7
7
|
## 行为准则
|
|
8
8
|
|
package/README.ja-JP.md
CHANGED
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
<a href="https://github.com/alibaba/open-code-review/blob/main/LICENSE"><img alt="License" src="https://img.shields.io/github/license/alibaba/open-code-review?style=flat-square" /></a>
|
|
12
12
|
</p>
|
|
13
13
|
<p align="center">
|
|
14
|
-
<a href="README.md">English</a> | <a href="README.zh-CN.md">简体中文</a> | 日本語 | <a href="README.ko-KR.md">한국어</a>
|
|
14
|
+
<a href="README.md">English</a> | <a href="README.zh-CN.md">简体中文</a> | 日本語 | <a href="README.ko-KR.md">한국어</a> | <a href="README.ru-RU.md">Русский</a>
|
|
15
15
|
</p>
|
|
16
16
|
|
|
17
17
|
---
|
package/README.ko-KR.md
CHANGED
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
<a href="https://github.com/alibaba/open-code-review/blob/main/LICENSE"><img alt="License" src="https://img.shields.io/github/license/alibaba/open-code-review?style=flat-square" /></a>
|
|
12
12
|
</p>
|
|
13
13
|
<p align="center">
|
|
14
|
-
<a href="README.md">English</a> | <a href="README.zh-CN.md">简体中文</a> | <a href="README.ja-JP.md">日本語</a> | 한국어
|
|
14
|
+
<a href="README.md">English</a> | <a href="README.zh-CN.md">简体中文</a> | <a href="README.ja-JP.md">日本語</a> | 한국어 | <a href="README.ru-RU.md">Русский</a>
|
|
15
15
|
</p>
|
|
16
16
|
|
|
17
17
|
---
|
package/README.md
CHANGED
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
<a href="https://github.com/alibaba/open-code-review/blob/main/LICENSE"><img alt="License" src="https://img.shields.io/github/license/alibaba/open-code-review?style=flat-square" /></a>
|
|
12
12
|
</p>
|
|
13
13
|
<p align="center">
|
|
14
|
-
English | <a href="README.zh-CN.md">简体中文</a> | <a href="README.ja-JP.md">日本語</a> | <a href="README.ko-KR.md">한국어</a>
|
|
14
|
+
English | <a href="README.zh-CN.md">简体中文</a> | <a href="README.ja-JP.md">日本語</a> | <a href="README.ko-KR.md">한국어</a> | <a href="README.ru-RU.md">Русский</a>
|
|
15
15
|
</p>
|
|
16
16
|
|
|
17
17
|
---
|
package/README.ru-RU.md
ADDED
|
@@ -0,0 +1,476 @@
|
|
|
1
|
+
<p align="center">
|
|
2
|
+
<a href="https://alibaba.github.io/open-code-review/">
|
|
3
|
+
<img src="imgs/logo.svg" alt="OpenCodeReview logo" width="240" height="240">
|
|
4
|
+
</a>
|
|
5
|
+
</p>
|
|
6
|
+
<p align="center">AI-агент код-ревью с открытым исходным кодом.</p>
|
|
7
|
+
<p align="center">
|
|
8
|
+
<a href="https://www.npmjs.com/package/@alibaba-group/open-code-review"><img alt="npm" src="https://img.shields.io/npm/v/@alibaba-group/open-code-review?style=flat-square" /></a>
|
|
9
|
+
<a href="https://github.com/alibaba/open-code-review/actions/workflows/release.yml"><img alt="Build status" src="https://img.shields.io/github/actions/workflow/status/alibaba/open-code-review/release.yml?style=flat-square" /></a>
|
|
10
|
+
<a href="https://goreportcard.com/report/github.com/alibaba/open-code-review"><img alt="Go Report Card" src="https://goreportcard.com/badge/github.com/alibaba/open-code-review?style=flat-square" /></a>
|
|
11
|
+
<a href="https://github.com/alibaba/open-code-review/blob/main/LICENSE"><img alt="License" src="https://img.shields.io/github/license/alibaba/open-code-review?style=flat-square" /></a>
|
|
12
|
+
</p>
|
|
13
|
+
<p align="center">
|
|
14
|
+
<a href="README.md">English</a> | <a href="README.zh-CN.md">简体中文</a> | <a href="README.ja-JP.md">日本語</a> | <a href="README.ko-KR.md">한국어</a> | Русский
|
|
15
|
+
</p>
|
|
16
|
+
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
## Что такое Open Code Review?
|
|
20
|
+
|
|
21
|
+
Open Code Review — это CLI-инструмент для код-ревью на основе ИИ. Он появился как внутренний официальный ИИ-ассистент код-ревью Alibaba Group: за последние два года им воспользовались десятки тысяч разработчиков, и он выявил миллионы дефектов в коде. После тщательной проверки в огромных масштабах мы превратили его в open-source-проект для сообщества. Чтобы начать работу, достаточно настроить эндпоинт модели.
|
|
22
|
+
|
|
23
|
+
Инструмент читает git-диффы, отправляет изменённые файлы настраиваемой LLM через агента с поддержкой вызова инструментов (tool use) и генерирует структурированные ревью-комментарии с точностью до строки. Агент может читать полное содержимое файлов, искать по кодовой базе, заглядывать в другие изменённые файлы за контекстом и выполнять глубокое ревью — а не только давать поверхностные замечания по диффу.
|
|
24
|
+
|
|
25
|
+

|
|
26
|
+
|
|
27
|
+
## Почему Open Code Review?
|
|
28
|
+
|
|
29
|
+
### Проблема агентов общего назначения
|
|
30
|
+
|
|
31
|
+
Если вы использовали для код-ревью агентов общего назначения, например Claude Code со Skills, вы наверняка сталкивались с этими болевыми точками:
|
|
32
|
+
|
|
33
|
+
- **Неполное покрытие** — на крупных ченджсетах агенты склонны «срезать углы»: выборочно проверяют часть файлов и пропускают остальные.
|
|
34
|
+
- **Дрейф позиций** — найденные проблемы часто не совпадают с реальным местом в коде: номера строк и ссылки на файлы «уезжают» от цели.
|
|
35
|
+
- **Нестабильное качество** — Skills, управляемые естественным языком, трудно отлаживать, и качество ревью заметно колеблется при небольших изменениях промпта.
|
|
36
|
+
|
|
37
|
+
Первопричина: чисто языковая архитектура не накладывает жёстких ограничений на процесс ревью.
|
|
38
|
+
|
|
39
|
+
### Ключевая идея: детерминированная инженерия × агент
|
|
40
|
+
|
|
41
|
+
Ключевая философия Open Code Review — сочетать детерминированную инженерию и агента так, чтобы каждый занимался тем, что у него получается лучше всего.
|
|
42
|
+
|
|
43
|
+
**Детерминированная инженерия — жёсткие гарантии**
|
|
44
|
+
|
|
45
|
+
Для тех шагов ревью, где *нельзя ошибаться*, корректность гарантирует инженерная логика, а не языковая модель:
|
|
46
|
+
|
|
47
|
+
- **Точный отбор файлов** — точно определяет, какие файлы нуждаются в ревью, а какие следует отфильтровать, гарантируя, что ни одно важное изменение не будет упущено.
|
|
48
|
+
- **Умный бандлинг файлов** — группирует связанные файлы в одну единицу ревью (например, `message_en.properties` и `message_zh.properties` объединяются вместе). Каждый бандл выполняется как суб-агент с изолированным контекстом — стратегия «разделяй и властвуй», которая сохраняет стабильность на очень больших ченджсетах и естественным образом поддерживает конкурентное ревью.
|
|
49
|
+
- **Тонкий матчинг правил** — сопоставляет правила ревью с характеристиками каждого файла, удерживая внимание модели сфокусированным и устраняя информационный шум у самого источника. По сравнению с чисто языковым управлением правилами матчинг правил на основе шаблонизатора стабильнее и предсказуемее.
|
|
50
|
+
- **Внешние модули позиционирования и рефлексии** — независимые модули позиционирования комментариев и рефлексии над комментариями системно повышают точность как расположения, так и содержания замечаний ИИ.
|
|
51
|
+
|
|
52
|
+
**Агент — динамические решения**
|
|
53
|
+
|
|
54
|
+
Сильные стороны агента сосредоточены там, где они важнее всего, — в динамических решениях и динамическом доборе контекста:
|
|
55
|
+
|
|
56
|
+
- **Промпты, заточенные под сценарий** — шаблоны промптов, глубоко оптимизированные под код-ревью: выше качество при меньшем расходе токенов.
|
|
57
|
+
- **Набор инструментов, заточенный под сценарий** — выведен из глубокого анализа трейсов вызовов инструментов на больших продакшен-данных, включая распределение частоты вызовов, долю повторных вызовов каждого инструмента и влияние новых инструментов на всю цепочку вызовов. В результате получился специализированный набор инструментов, который для код-ревью стабильнее и предсказуемее, чем универсальный агентский тулкит.
|
|
58
|
+
|
|
59
|
+
## Как использовать
|
|
60
|
+
|
|
61
|
+
### CLI
|
|
62
|
+
|
|
63
|
+
#### Установка
|
|
64
|
+
|
|
65
|
+
**Через NPM (рекомендуется)**
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
npm install -g @alibaba-group/open-code-review
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
После установки команда `ocr` доступна глобально.
|
|
72
|
+
|
|
73
|
+
**Из GitHub Release**
|
|
74
|
+
|
|
75
|
+
Скачайте свежий бинарный файл со страницы [GitHub Releases](https://github.com/alibaba/open-code-review/releases):
|
|
76
|
+
|
|
77
|
+
```bash
|
|
78
|
+
# macOS (Apple Silicon)
|
|
79
|
+
curl -Lo ocr https://github.com/alibaba/open-code-review/releases/latest/download/opencodereview-darwin-arm64
|
|
80
|
+
chmod +x ocr && sudo mv ocr /usr/local/bin/ocr
|
|
81
|
+
|
|
82
|
+
# macOS (Intel)
|
|
83
|
+
curl -Lo ocr https://github.com/alibaba/open-code-review/releases/latest/download/opencodereview-darwin-amd64
|
|
84
|
+
chmod +x ocr && sudo mv ocr /usr/local/bin/ocr
|
|
85
|
+
|
|
86
|
+
# Linux (x86_64)
|
|
87
|
+
curl -Lo ocr https://github.com/alibaba/open-code-review/releases/latest/download/opencodereview-linux-amd64
|
|
88
|
+
chmod +x ocr && sudo mv ocr /usr/local/bin/ocr
|
|
89
|
+
|
|
90
|
+
# Linux (ARM64)
|
|
91
|
+
curl -Lo ocr https://github.com/alibaba/open-code-review/releases/latest/download/opencodereview-linux-arm64
|
|
92
|
+
chmod +x ocr && sudo mv ocr /usr/local/bin/ocr
|
|
93
|
+
|
|
94
|
+
# Windows (x86_64) — переместите ocr.exe в каталог из вашего PATH
|
|
95
|
+
curl -Lo ocr.exe https://github.com/alibaba/open-code-review/releases/latest/download/opencodereview-windows-amd64.exe
|
|
96
|
+
|
|
97
|
+
# Windows (ARM64) — переместите ocr.exe в каталог из вашего PATH
|
|
98
|
+
curl -Lo ocr.exe https://github.com/alibaba/open-code-review/releases/latest/download/opencodereview-windows-arm64.exe
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
**Из исходников**
|
|
102
|
+
|
|
103
|
+
```bash
|
|
104
|
+
git clone https://github.com/alibaba/open-code-review.git
|
|
105
|
+
cd open-code-review
|
|
106
|
+
make build
|
|
107
|
+
sudo cp dist/opencodereview /usr/local/bin/ocr
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
#### Быстрый старт
|
|
111
|
+
|
|
112
|
+
**1. Настройте LLM**
|
|
113
|
+
|
|
114
|
+
**Перед запуском ревью необходимо настроить LLM.**
|
|
115
|
+
|
|
116
|
+
```bash
|
|
117
|
+
# Вариант A: настройка через CLI
|
|
118
|
+
ocr config set llm.url https://api.anthropic.com/v1/messages
|
|
119
|
+
ocr config set llm.auth_token your-api-key-here
|
|
120
|
+
ocr config set llm.model claude-opus-4-6
|
|
121
|
+
ocr config set llm.use_anthropic true
|
|
122
|
+
|
|
123
|
+
# Вариант B: переменные окружения (наивысший приоритет)
|
|
124
|
+
export OCR_LLM_URL=https://api.anthropic.com/v1/messages
|
|
125
|
+
export OCR_LLM_TOKEN=your-api-key-here
|
|
126
|
+
export OCR_LLM_MODEL=claude-opus-4-6
|
|
127
|
+
export OCR_USE_ANTHROPIC=true
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
Конфигурация хранится в `~/.opencodereview/config.json`.
|
|
131
|
+
|
|
132
|
+
**`auth_header` (необязательно):** определяет, в каком HTTP-заголовке передаётся API-ключ при работе с Anthropic. Если не задан, по умолчанию используется `authorization` (Bearer-токен). Если у вас стандартный API-ключ вида `sk-ant-*`, необходимо установить значение `x-api-key`:
|
|
133
|
+
|
|
134
|
+
```bash
|
|
135
|
+
ocr config set llm.auth_header x-api-key
|
|
136
|
+
# или
|
|
137
|
+
export OCR_LLM_AUTH_HEADER=x-api-key
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
Поддерживаемые значения: `x-api-key`, `authorization` (алиас: `bearer`). Прочие значения отклоняются с ошибкой.
|
|
141
|
+
|
|
142
|
+
Инструмент также совместим с переменными окружения Claude Code (`ANTHROPIC_BASE_URL`, `ANTHROPIC_AUTH_TOKEN`, `ANTHROPIC_MODEL`) и разбирает `~/.zshrc` / `~/.bashrc` в поисках соответствующих export'ов.
|
|
143
|
+
|
|
144
|
+
> **Примечание для пользователей CC-Switch**: если вы используете [CC-Switch](https://github.com/farion1231/cc-switch) с включённым [routing service](https://www.ccswitch.io/en/docs?section=proxy&item=service), можно указать в `llm.url` адрес прокси CC-Switch без дополнительной настройки:
|
|
145
|
+
> - для провайдера **Claude**: установите `llm.url` в `http://127.0.0.1:15721`
|
|
146
|
+
> - для провайдера **Codex**: установите `llm.url` в `http://127.0.0.1:15721/v1`
|
|
147
|
+
> - `llm.model` задайте в соответствии с настройками вашего провайдера
|
|
148
|
+
> - `llm.auth_token` может быть любым
|
|
149
|
+
> - настройки `extra_body` продолжают действовать
|
|
150
|
+
|
|
151
|
+
**2. Проверьте подключение**
|
|
152
|
+
|
|
153
|
+
```bash
|
|
154
|
+
ocr llm test
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
**3. Запустите ревью**
|
|
158
|
+
|
|
159
|
+
```bash
|
|
160
|
+
cd your-project
|
|
161
|
+
|
|
162
|
+
# Режим рабочей копии — ревью всех staged, unstaged и untracked изменений
|
|
163
|
+
ocr review
|
|
164
|
+
|
|
165
|
+
# Диапазон веток — сравнение двух ref'ов
|
|
166
|
+
ocr review --from main --to feature-branch
|
|
167
|
+
|
|
168
|
+
# Один коммит
|
|
169
|
+
ocr review --commit abc123
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
### Интеграция с кодинг-агентами
|
|
173
|
+
|
|
174
|
+
OCR легко встраивается в ИИ-агентов для разработки в виде slash-команды, позволяя выполнять код-ревью прямо в рабочем процессе агента.
|
|
175
|
+
|
|
176
|
+
#### Вариант 1: установка как Skill
|
|
177
|
+
|
|
178
|
+
Установите скилл OCR в свой проект через `npx`:
|
|
179
|
+
|
|
180
|
+
```bash
|
|
181
|
+
npx skills add alibaba/open-code-review --skill open-code-review
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
Это установит скилл `open-code-review` из [реестра скиллов](skills/open-code-review/SKILL.md), который объясняет вашему кодинг-агенту, как вызывать `ocr` для код-ревью, классифицировать найденные проблемы по приоритету и при необходимости применять исправления.
|
|
185
|
+
|
|
186
|
+
#### Вариант 2: установка как плагин Claude Code
|
|
187
|
+
|
|
188
|
+
Для [Claude Code](https://docs.anthropic.com/en/docs/claude-code) установите плагин с командой, выполнив в Claude Code:
|
|
189
|
+
|
|
190
|
+
```bash
|
|
191
|
+
/plugin marketplace add alibaba/open-code-review
|
|
192
|
+
/plugin install open-code-review@open-code-review
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
Это зарегистрирует slash-команду `/open-code-review:review`, которая запускает OCR и автоматически фильтрует и исправляет найденные проблемы.
|
|
196
|
+
|
|
197
|
+
#### Вариант 3: установка как плагин Codex
|
|
198
|
+
|
|
199
|
+
Для локального Codex установите плагин Open Code Review из этого репозитория:
|
|
200
|
+
|
|
201
|
+
```bash
|
|
202
|
+
codex plugin marketplace add alibaba/open-code-review
|
|
203
|
+
codex
|
|
204
|
+
/plugins
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
Для локального чекаута или форка:
|
|
208
|
+
|
|
209
|
+
```bash
|
|
210
|
+
codex plugin marketplace add .
|
|
211
|
+
codex
|
|
212
|
+
/plugins
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
Установите и включите `Open Code Review`, затем начните новый тред Codex и вызывайте плагин явно:
|
|
216
|
+
|
|
217
|
+
```text
|
|
218
|
+
@Open Code Review review my current changes
|
|
219
|
+
@Open Code Review review this branch against main
|
|
220
|
+
@Open Code Review review and fix high-confidence issues
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
Это зарегистрирует Codex-скилл, запускающий локальный CLI OCR:
|
|
224
|
+
|
|
225
|
+
```bash
|
|
226
|
+
ocr review --audience agent
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
Эта интеграция не меняет внутренний LLM-бэкенд OCR и не требует настройки эндпоинта OpenAI Responses API для Codex. Самому OCR по-прежнему нужен установленный и настроенный CLI `ocr`, как описано в разделе про настройку CLI.
|
|
230
|
+
|
|
231
|
+
Руководство на корейском: [`plugins/open-code-review/CODEX.ko-KR.md`](plugins/open-code-review/CODEX.ko-KR.md)
|
|
232
|
+
|
|
233
|
+
#### Вариант 4: просто скопировать файл команды
|
|
234
|
+
|
|
235
|
+
Для быстрой настройки без пакетных менеджеров достаточно скопировать файл команды, чтобы использовать slash-команду `/open-code-review` в Claude Code.
|
|
236
|
+
|
|
237
|
+
**На уровне проекта** (общий для команды через git):
|
|
238
|
+
|
|
239
|
+
```bash
|
|
240
|
+
mkdir -p .claude/commands
|
|
241
|
+
curl -o .claude/commands/open-code-review.md \
|
|
242
|
+
https://raw.githubusercontent.com/alibaba/open-code-review/main/plugins/open-code-review/commands/review.md
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
**На уровне пользователя** (личное глобальное использование во всех проектах):
|
|
246
|
+
|
|
247
|
+
```bash
|
|
248
|
+
mkdir -p ~/.claude/commands
|
|
249
|
+
curl -o ~/.claude/commands/open-code-review.md \
|
|
250
|
+
https://raw.githubusercontent.com/alibaba/open-code-review/main/plugins/open-code-review/commands/review.md
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
> **Требование**: для всех способов интеграции необходим установленный CLI `ocr` и настроенная LLM. См. разделы [Установка](#установка) и [Настройте LLM](#быстрый-старт) выше.
|
|
254
|
+
|
|
255
|
+
### Интеграция с CI/CD
|
|
256
|
+
|
|
257
|
+
OCR можно встроить в CI/CD-пайплайны для автоматического код-ревью Merge Request'ов / Pull Request'ов.
|
|
258
|
+
|
|
259
|
+
Базовая команда для интеграции с CI:
|
|
260
|
+
|
|
261
|
+
```bash
|
|
262
|
+
ocr review \
|
|
263
|
+
--from "origin/main" \
|
|
264
|
+
--to "<commit_sha>" \
|
|
265
|
+
--format json
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
Флаг `--from` принимает в качестве базы ref ветки (например, `origin/main`) или SHA коммита, а `--to` — SHA коммита или ref ветки в качестве head. В CI-окружениях для `--to` рекомендуется использовать SHA коммита: это корректно обрабатывает PR/MR из форков, у которых исходная ветка отсутствует в remote `origin`.
|
|
269
|
+
|
|
270
|
+
Флаг `--format json` выводит машиночитаемый результат, удобный для разбора в CI-скриптах.
|
|
271
|
+
|
|
272
|
+
Примеры интеграции — в каталоге [`examples/`](./examples/):
|
|
273
|
+
|
|
274
|
+
- [`github_actions/`](./examples/github_actions/) — пример интеграции с GitHub Actions
|
|
275
|
+
- [`gitlab_ci/`](./examples/gitlab_ci/) — пример интеграции с GitLab CI
|
|
276
|
+
|
|
277
|
+
## Команды
|
|
278
|
+
|
|
279
|
+
| Команда | Алиас | Описание |
|
|
280
|
+
|---------|-------|----------|
|
|
281
|
+
| `ocr review` | `ocr r` | Запустить код-ревью |
|
|
282
|
+
| `ocr rules check <file>` | — | Показать, какое правило ревью применяется к пути файла |
|
|
283
|
+
| `ocr config set <key> <value>` | — | Установить значения конфигурации |
|
|
284
|
+
| `ocr llm test` | — | Проверить подключение к LLM |
|
|
285
|
+
| `ocr viewer` | `ocr v` | Запустить WebUI-просмотрщик сессий на `localhost:5483` |
|
|
286
|
+
| `ocr version` | — | Показать информацию о версии |
|
|
287
|
+
|
|
288
|
+
### Флаги `ocr review`
|
|
289
|
+
|
|
290
|
+
| Флаг | Короткая форма | По умолчанию | Описание |
|
|
291
|
+
|------|----------------|--------------|----------|
|
|
292
|
+
| `--repo` | — | текущий каталог | Корень git-репозитория |
|
|
293
|
+
| `--from` | — | — | Исходный ref (например, `main`) |
|
|
294
|
+
| `--to` | — | — | Целевой ref (например, `feature-branch`) |
|
|
295
|
+
| `--commit` | `-c` | — | Один коммит для ревью |
|
|
296
|
+
| `--preview` | `-p` | `false` | Показать, какие файлы попадут в ревью, без запуска LLM |
|
|
297
|
+
| `--format` | `-f` | `text` | Формат вывода: `text` или `json` |
|
|
298
|
+
| `--concurrency` | — | `8` | Максимум одновременных ревью файлов |
|
|
299
|
+
| `--timeout` | — | `10` | Таймаут конкурентной задачи в минутах |
|
|
300
|
+
| `--audience` | — | `human` | `human` (показывать прогресс) или `agent` (только сводка) |
|
|
301
|
+
| `--background` | `-b` | — | Необязательный контекст требований/бизнес-логики для ревью; при `--commit` автоматически заполняется из сообщения коммита |
|
|
302
|
+
| `--rule` | — | — | Путь к пользовательским JSON-правилам ревью |
|
|
303
|
+
| `--max-tools` | — | встроенное | Максимум раундов вызова инструментов на файл; действует, только если больше значения шаблона по умолчанию |
|
|
304
|
+
| `--max-git-procs` | — | встроенное | Максимум одновременных git-подпроцессов |
|
|
305
|
+
| `--tools` | — | — | Путь к пользовательскому JSON-конфигу инструментов |
|
|
306
|
+
|
|
307
|
+
## Примеры
|
|
308
|
+
|
|
309
|
+
```bash
|
|
310
|
+
# Показать, какие файлы попадут в ревью (без вызовов LLM)
|
|
311
|
+
ocr review --preview
|
|
312
|
+
ocr review -c abc123 -p
|
|
313
|
+
|
|
314
|
+
# Ревью изменений рабочей копии с настройками по умолчанию
|
|
315
|
+
ocr review
|
|
316
|
+
|
|
317
|
+
# Ревью диффа веток с заданной конкурентностью
|
|
318
|
+
ocr review --from main --to my-feature --concurrency 4
|
|
319
|
+
|
|
320
|
+
# Ревью конкретного коммита с подробным JSON-выводом
|
|
321
|
+
ocr review --commit abc123 --format json --audience agent
|
|
322
|
+
|
|
323
|
+
# Передать контекст требований для более прицельного ревью
|
|
324
|
+
ocr review --background "Добавляем rate limiting в API логина"
|
|
325
|
+
|
|
326
|
+
# Использовать собственные правила ревью
|
|
327
|
+
ocr review --rule /path/to/my-rules.json
|
|
328
|
+
|
|
329
|
+
# Посмотреть, какое правило применяется к файлу
|
|
330
|
+
ocr rules check src/main/java/com/example/Foo.java
|
|
331
|
+
ocr rules check --rule custom.json src/main/resources/mapper/UserMapper.xml
|
|
332
|
+
|
|
333
|
+
# Открыть историю сессий ревью в браузере
|
|
334
|
+
ocr viewer
|
|
335
|
+
ocr viewer --addr :3000
|
|
336
|
+
```
|
|
337
|
+
|
|
338
|
+
### Безопасность viewer'а
|
|
339
|
+
|
|
340
|
+
Viewer отдаёт содержимое сессионных JSONL-файлов (сообщения запросов к LLM и ответы) по HTTP. На каждый запрос применяется allowlist по заголовку Host: loopback-имена (`localhost`, `127.0.0.0/8`, `::1`) и конкретный хост привязки разрешены всегда. Wildcard-привязки (`--addr :3000`, `--addr 0.0.0.0:3000`) и прочие не-loopback имена хостов нужно добавлять через переменную окружения `OCR_VIEWER_ALLOWED_HOSTS` (через запятую):
|
|
341
|
+
|
|
342
|
+
```bash
|
|
343
|
+
OCR_VIEWER_ALLOWED_HOSTS=review.internal,ocr.lan ocr viewer --addr :3000
|
|
344
|
+
```
|
|
345
|
+
|
|
346
|
+
Это блокирует атаки DNS rebinding на локальный viewer.
|
|
347
|
+
|
|
348
|
+
## Правила ревью
|
|
349
|
+
|
|
350
|
+
OCR разрешает правила ревью по цепочке приоритетов из четырёх уровней. На каждом уровне действует принцип «первое совпадение побеждает»: если путь файла совпал с паттерном, используется это правило; иначе поиск продолжается на следующем уровне.
|
|
351
|
+
|
|
352
|
+
| Приоритет | Источник | Путь | Описание |
|
|
353
|
+
|-----------|----------|------|----------|
|
|
354
|
+
| 1 (высший) | Флаг `--rule` | Путь, указанный пользователем | Явное переопределение из CLI |
|
|
355
|
+
| 2 | Конфиг проекта | `<repoDir>/.opencodereview/rule.json` | Правила уровня проекта, можно коммитить в git |
|
|
356
|
+
| 3 | Глобальный конфиг | `~/.opencodereview/rule.json` | Личные настройки пользователя |
|
|
357
|
+
| 4 (низший) | Системные по умолчанию | Встроенный `system_rules.json` | Встроенные правила для распространённых языков и типов файлов |
|
|
358
|
+
|
|
359
|
+
### Формат файла правил
|
|
360
|
+
|
|
361
|
+
Уровни 1–3 используют один и тот же JSON-формат:
|
|
362
|
+
|
|
363
|
+
```json
|
|
364
|
+
{
|
|
365
|
+
"rules": [
|
|
366
|
+
{
|
|
367
|
+
"path": "force-api/**/*.java",
|
|
368
|
+
"rule": "Все новые методы должны проверять обязательные параметры на null"
|
|
369
|
+
},
|
|
370
|
+
{
|
|
371
|
+
"path": "**/*mapper*.xml",
|
|
372
|
+
"rule": "Проверять SQL на риски инъекций, ошибки в параметрах и незакрытые теги"
|
|
373
|
+
}
|
|
374
|
+
]
|
|
375
|
+
}
|
|
376
|
+
```
|
|
377
|
+
|
|
378
|
+
- `path` поддерживает рекурсивное сопоставление `**` и расширение фигурных скобок `{java,kt}`.
|
|
379
|
+
- Внутри каждого уровня правила проверяются в порядке объявления — побеждает первое совпадение.
|
|
380
|
+
- Если файл правил не существует, он молча пропускается.
|
|
381
|
+
|
|
382
|
+
### Фильтрация путей
|
|
383
|
+
|
|
384
|
+
Файлы правил также поддерживают поля `include` и `exclude`, управляющие тем, какие файлы попадают в область ревью:
|
|
385
|
+
|
|
386
|
+
```json
|
|
387
|
+
{
|
|
388
|
+
"rules": [
|
|
389
|
+
{"path": "**/*.java", "rule": "Проверять null-безопасность"}
|
|
390
|
+
],
|
|
391
|
+
"include": ["src/main/**/*.java", "lib/**/*.kt"],
|
|
392
|
+
"exclude": ["**/generated/**", "vendor/**"]
|
|
393
|
+
}
|
|
394
|
+
```
|
|
395
|
+
|
|
396
|
+
**Приоритет решений фильтра (от высшего к низшему):**
|
|
397
|
+
|
|
398
|
+
| Шаг | Условие | Результат |
|
|
399
|
+
|-----|---------|-----------|
|
|
400
|
+
| 1 | Файл бинарный | Исключён |
|
|
401
|
+
| 2 | Путь совпадает с пользовательским паттерном `exclude` | Исключён |
|
|
402
|
+
| 3 | Расширение файла не входит в список поддерживаемых | Исключён |
|
|
403
|
+
| 4 | `include` настроен и путь совпадает | **В ревью** (шаг 5 пропускается) |
|
|
404
|
+
| 5 | Путь совпадает со встроенным паттерном исключения по умолчанию (тестовые файлы и т. п.) | Исключён |
|
|
405
|
+
| 6 | Ничего из перечисленного | В ревью |
|
|
406
|
+
|
|
407
|
+
**Как это работает:**
|
|
408
|
+
|
|
409
|
+
- `include` и `exclude` следуют той же цепочке приоритетов, что и правила ревью (`--rule` > конфиг проекта > глобальный конфиг). Действует **целиком самый приоритетный уровень, на котором include/exclude настроены** — паттерны разных уровней не объединяются.
|
|
410
|
+
- `exclude` всегда сильнее `include` — файл, совпавший с обоими, исключается.
|
|
411
|
+
- `include` работает как **обход встроенных паттернов исключения по умолчанию** (например, тестовых файлов), а не как эксклюзивный allowlist: файлы, не совпавшие ни с одним паттерном `include`, всё равно обычным образом проходят проверки фильтра по умолчанию.
|
|
412
|
+
- Синтаксис паттернов: поддерживаются рекурсивное сопоставление `**`, односегментное `*` и расширение фигурных скобок `{a,b}`. Сопоставление регистронезависимое.
|
|
413
|
+
|
|
414
|
+
**Встроенные паттерны исключения по умолчанию** (отфильтровывают тестовые файлы и т. п. — можно переопределить через `include`):
|
|
415
|
+
|
|
416
|
+
```
|
|
417
|
+
**/*_test.go, **/*Test.java, **/*Tests.java, **/*_test.rs,
|
|
418
|
+
**/*.test.{js,jsx,ts,tsx}, **/*.spec.{js,jsx,ts,tsx}, **/__tests__/**,
|
|
419
|
+
**/src/test/java/**/*.java, **/src/test/**/*.kt,
|
|
420
|
+
**/test/**/*_test.py, **/tests/**/*_test.py, **/*_test.py,
|
|
421
|
+
**/*_spec.rb, **/spec/**/*_spec.rb, **/oh_modules/**
|
|
422
|
+
```
|
|
423
|
+
|
|
424
|
+
## Справочник по конфигурации
|
|
425
|
+
|
|
426
|
+
Файл конфигурации: `~/.opencodereview/config.json`
|
|
427
|
+
|
|
428
|
+
| Ключ | Тип | Пример |
|
|
429
|
+
|------|-----|--------|
|
|
430
|
+
| `llm.url` | string | `https://api.openai.com/v1/chat/completions` |
|
|
431
|
+
| `llm.auth_token` | string | `sk-xxxxxxx` |
|
|
432
|
+
| `llm.auth_header` | string | Только для Anthropic: `x-api-key` \| `authorization` |
|
|
433
|
+
| `llm.model` | string | `claude-opus-4-6` |
|
|
434
|
+
| `llm.use_anthropic` | boolean | `true` \| `false` |
|
|
435
|
+
| `language` | string | `English` \| `Chinese` (по умолчанию: Chinese) |
|
|
436
|
+
| `telemetry.enabled` | boolean | `true` \| `false` |
|
|
437
|
+
| `telemetry.exporter` | string | `console` \| `otlp` |
|
|
438
|
+
| `telemetry.otlp_endpoint` | string | Адрес OTLP-коллектора |
|
|
439
|
+
| `telemetry.content_logging` | boolean | Включать промпты в телеметрию |
|
|
440
|
+
|
|
441
|
+
Переменные окружения имеют приоритет над файлом конфигурации.
|
|
442
|
+
|
|
443
|
+
### Переменные окружения
|
|
444
|
+
|
|
445
|
+
| Переменная | Назначение |
|
|
446
|
+
|------------|------------|
|
|
447
|
+
| `OCR_LLM_URL` | URL эндпоинта LLM API |
|
|
448
|
+
| `OCR_LLM_TOKEN` | API-ключ / токен авторизации |
|
|
449
|
+
| `OCR_LLM_AUTH_HEADER` | Заголовок авторизации Anthropic (`x-api-key` или `authorization`) |
|
|
450
|
+
| `OCR_LLM_MODEL` | Имя модели |
|
|
451
|
+
| `OCR_USE_ANTHROPIC` | `true` = Anthropic, `false` = OpenAI |
|
|
452
|
+
|
|
453
|
+
|
|
454
|
+
## Телеметрия
|
|
455
|
+
|
|
456
|
+
Интеграция с OpenTelemetry для наблюдаемости (спаны, метрики). По умолчанию выключена.
|
|
457
|
+
|
|
458
|
+
```bash
|
|
459
|
+
ocr config set telemetry.enabled true
|
|
460
|
+
ocr config set telemetry.exporter otlp
|
|
461
|
+
ocr config set telemetry.otlp_endpoint localhost:4317
|
|
462
|
+
```
|
|
463
|
+
|
|
464
|
+
Установите `telemetry.content_logging`, чтобы включать промпты и ответы LLM в экспортируемые данные.
|
|
465
|
+
|
|
466
|
+
## Участие в разработке
|
|
467
|
+
|
|
468
|
+
В [CONTRIBUTING.ru-RU.md](CONTRIBUTING.ru-RU.md) описаны настройка окружения разработки, рекомендации по коду и порядок отправки pull request'ов.
|
|
469
|
+
|
|
470
|
+
## История звёзд
|
|
471
|
+
|
|
472
|
+
[](https://star-history.com/#alibaba/open-code-review&Date)
|
|
473
|
+
|
|
474
|
+
## Лицензия
|
|
475
|
+
|
|
476
|
+
[Apache-2.0](LICENSE) — Copyright 2026 Alibaba
|
package/README.zh-CN.md
CHANGED
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
<a href="https://github.com/alibaba/open-code-review/blob/main/LICENSE"><img alt="License" src="https://img.shields.io/github/license/alibaba/open-code-review?style=flat-square" /></a>
|
|
12
12
|
</p>
|
|
13
13
|
<p align="center">
|
|
14
|
-
<a href="README.md">English</a> | 简体中文 | <a href="README.ja-JP.md">日本語</a> | <a href="README.ko-KR.md">한국어</a>
|
|
14
|
+
<a href="README.md">English</a> | 简体中文 | <a href="README.ja-JP.md">日本語</a> | <a href="README.ko-KR.md">한국어</a> | <a href="README.ru-RU.md">Русский</a>
|
|
15
15
|
</p>
|
|
16
16
|
|
|
17
17
|
---
|
|
@@ -136,7 +136,7 @@ jobs:
|
|
|
136
136
|
owner: context.repo.owner,
|
|
137
137
|
repo: context.repo.repo,
|
|
138
138
|
issue_number: context.issue.number,
|
|
139
|
-
body: `⚠️ **OpenCodeReview** encountered an error:\n
|
|
139
|
+
body: `⚠️ **OpenCodeReview** encountered an error:\n${fencedBlock(stderr)}`
|
|
140
140
|
});
|
|
141
141
|
}
|
|
142
142
|
return;
|
|
@@ -208,27 +208,17 @@ jobs:
|
|
|
208
208
|
reviewComment.side = 'RIGHT';
|
|
209
209
|
}
|
|
210
210
|
|
|
211
|
-
reviewComments.push(reviewComment);
|
|
211
|
+
reviewComments.push({ comment, reviewComment });
|
|
212
212
|
}
|
|
213
213
|
|
|
214
214
|
// Submit as a single PR review with all comments
|
|
215
215
|
const totalCount = comments.length;
|
|
216
216
|
const inlineCount = reviewComments.length;
|
|
217
217
|
const summaryCount = commentsWithoutLine.length;
|
|
218
|
-
let summaryBody =
|
|
219
|
-
if (totalCount > 0) {
|
|
220
|
-
summaryBody += `\n- ✅ ${inlineCount} posted as inline comment(s)`;
|
|
221
|
-
summaryBody += `\n- 📝 ${summaryCount} posted as summary (missing line info)`;
|
|
222
|
-
}
|
|
223
|
-
if (warnings.length > 0) {
|
|
224
|
-
summaryBody += `\n\n⚠️ ${warnings.length} warning(s) occurred during review.`;
|
|
225
|
-
}
|
|
218
|
+
let summaryBody = buildSummaryBody(totalCount, inlineCount, summaryCount, warnings);
|
|
226
219
|
|
|
227
220
|
// Add comments without line info to summary body
|
|
228
|
-
|
|
229
|
-
summaryBody += '\n\n---\n\n';
|
|
230
|
-
summaryBody += formatCommentMarkdown(comment);
|
|
231
|
-
}
|
|
221
|
+
summaryBody += formatSummaryComments(commentsWithoutLine);
|
|
232
222
|
|
|
233
223
|
// Statistics tracking
|
|
234
224
|
let successCount = 0;
|
|
@@ -243,7 +233,7 @@ jobs:
|
|
|
243
233
|
commit_id: commitSha,
|
|
244
234
|
body: summaryBody,
|
|
245
235
|
event: 'COMMENT',
|
|
246
|
-
comments: reviewComments
|
|
236
|
+
comments: reviewComments.map(({ reviewComment }) => reviewComment)
|
|
247
237
|
});
|
|
248
238
|
successCount = reviewComments.length;
|
|
249
239
|
console.log(`Successfully posted review with ${successCount} inline comments (${commentsWithoutLine.length} in summary)`);
|
|
@@ -252,7 +242,7 @@ jobs:
|
|
|
252
242
|
console.log('Falling back to posting comments individually...');
|
|
253
243
|
|
|
254
244
|
// Fallback: post comments one by one
|
|
255
|
-
for (const reviewComment of reviewComments) {
|
|
245
|
+
for (const { comment, reviewComment } of reviewComments) {
|
|
256
246
|
try {
|
|
257
247
|
await github.rest.pulls.createReview({
|
|
258
248
|
owner: context.repo.owner,
|
|
@@ -267,26 +257,27 @@ jobs:
|
|
|
267
257
|
console.log(`Successfully posted comment for ${reviewComment.path}`);
|
|
268
258
|
} catch (innerE) {
|
|
269
259
|
failedCount++;
|
|
270
|
-
failedComments.push({ comment
|
|
260
|
+
failedComments.push({ comment, error: innerE.message });
|
|
271
261
|
console.log(`Failed to post comment for ${reviewComment.path}: ${innerE.message}`);
|
|
272
262
|
}
|
|
273
263
|
}
|
|
274
264
|
|
|
275
265
|
// Post summary comment with statistics
|
|
276
|
-
let finalBody =
|
|
266
|
+
let finalBody = buildSummaryBody(totalCount, successCount, commentsWithoutLine.length + failedComments.length, warnings);
|
|
267
|
+
finalBody += formatSummaryComments(commentsWithoutLine);
|
|
277
268
|
finalBody += `\n\n---\n\n📊 **Posting Statistics:**`;
|
|
278
269
|
finalBody += `\n- ✅ Successfully posted: ${successCount} comment(s)`;
|
|
279
270
|
if (failedCount > 0) {
|
|
280
271
|
finalBody += `\n- ❌ Failed to post: ${failedCount} comment(s)`;
|
|
281
272
|
}
|
|
282
273
|
|
|
283
|
-
// Add failed comments
|
|
274
|
+
// Add failed comments as summary content so review feedback is not lost.
|
|
284
275
|
if (failedComments.length > 0) {
|
|
285
|
-
finalBody += '\n\n
|
|
276
|
+
finalBody += '\n\n---\n\n### ⚠️ Inline comments shown in summary';
|
|
286
277
|
for (const { comment, error } of failedComments) {
|
|
287
|
-
finalBody +=
|
|
278
|
+
finalBody += '\n\n---\n\n';
|
|
279
|
+
finalBody += formatCommentMarkdown(comment, error);
|
|
288
280
|
}
|
|
289
|
-
finalBody += '\n</details>';
|
|
290
281
|
}
|
|
291
282
|
|
|
292
283
|
await github.rest.issues.createComment({
|
|
@@ -303,29 +294,64 @@ jobs:
|
|
|
303
294
|
// Add code suggestion if available
|
|
304
295
|
if (comment.suggestion_code && comment.existing_code) {
|
|
305
296
|
body += '\n\n**Suggestion:**\n';
|
|
306
|
-
body += '
|
|
307
|
-
body += comment.suggestion_code;
|
|
308
|
-
if (!comment.suggestion_code.endsWith('\n')) body += '\n';
|
|
309
|
-
body += '```';
|
|
297
|
+
body += fencedBlock(comment.suggestion_code, 'suggestion');
|
|
310
298
|
}
|
|
311
299
|
|
|
312
300
|
return body;
|
|
313
301
|
}
|
|
314
302
|
|
|
315
|
-
function formatCommentMarkdown(comment) {
|
|
303
|
+
function formatCommentMarkdown(comment, error) {
|
|
316
304
|
let md = `### 📄 \`${comment.path}\``;
|
|
317
305
|
if (comment.start_line && comment.end_line) {
|
|
318
306
|
md += ` (L${comment.start_line}-L${comment.end_line})`;
|
|
319
307
|
}
|
|
320
308
|
md += '\n\n';
|
|
309
|
+
if (error) {
|
|
310
|
+
md += `⚠️ GitHub could not post this as an inline comment: ${error}\n\n`;
|
|
311
|
+
}
|
|
321
312
|
md += comment.content || '';
|
|
322
313
|
|
|
323
314
|
if (comment.suggestion_code && comment.existing_code) {
|
|
324
315
|
md += '\n\n<details><summary>💡 Suggested Change</summary>\n\n';
|
|
325
|
-
md += '**Before:**\n
|
|
326
|
-
md += '**After:**\n
|
|
316
|
+
md += '**Before:**\n' + fencedBlock(comment.existing_code) + '\n\n';
|
|
317
|
+
md += '**After:**\n' + fencedBlock(comment.suggestion_code) + '\n\n';
|
|
327
318
|
md += '</details>';
|
|
328
319
|
}
|
|
329
320
|
|
|
330
321
|
return md;
|
|
331
322
|
}
|
|
323
|
+
|
|
324
|
+
function buildSummaryBody(totalCount, inlineCount, summaryCount, warnings) {
|
|
325
|
+
let body = `🔍 **OpenCodeReview** found **${totalCount}** issue(s) in this PR.`;
|
|
326
|
+
if (totalCount > 0) {
|
|
327
|
+
body += `\n- ✅ ${inlineCount} posted as inline comment(s)`;
|
|
328
|
+
body += `\n- 📝 ${summaryCount} posted as summary`;
|
|
329
|
+
}
|
|
330
|
+
if (warnings.length > 0) {
|
|
331
|
+
body += `\n\n⚠️ ${warnings.length} warning(s) occurred during review.`;
|
|
332
|
+
}
|
|
333
|
+
return body;
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
function formatSummaryComments(summaryComments) {
|
|
337
|
+
let body = '';
|
|
338
|
+
for (const { comment } of summaryComments) {
|
|
339
|
+
body += '\n\n---\n\n';
|
|
340
|
+
body += formatCommentMarkdown(comment);
|
|
341
|
+
}
|
|
342
|
+
return body;
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
function fencedBlock(content, language = '') {
|
|
346
|
+
const text = String(content || '');
|
|
347
|
+
const fence = safeFence(text);
|
|
348
|
+
let block = fence + language + '\n' + text;
|
|
349
|
+
if (!text.endsWith('\n')) block += '\n';
|
|
350
|
+
return block + fence;
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
function safeFence(content) {
|
|
354
|
+
const matches = String(content || '').match(/`+/g) || [];
|
|
355
|
+
const maxTicks = matches.reduce((max, ticks) => Math.max(max, ticks.length), 0);
|
|
356
|
+
return '`'.repeat(Math.max(3, maxTicks + 1));
|
|
357
|
+
}
|
package/package.json
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@alibaba-group/open-code-review",
|
|
3
|
-
"version": "1.3.
|
|
3
|
+
"version": "1.3.4",
|
|
4
4
|
"description": "OpenCodeReview CLI — AI-powered code review tool",
|
|
5
5
|
"bin": {
|
|
6
6
|
"ocr": "bin/ocr.js"
|
|
7
7
|
},
|
|
8
8
|
"scripts": {
|
|
9
|
-
"postinstall": "node scripts/install.js"
|
|
9
|
+
"postinstall": "node scripts/install.js",
|
|
10
|
+
"test:github-actions": "node scripts/github-actions/post-review-comments.test.js"
|
|
10
11
|
},
|
|
11
12
|
"repository": {
|
|
12
13
|
"type": "git",
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
|
|
4
|
+
const assert = require("assert");
|
|
5
|
+
const fs = require("fs");
|
|
6
|
+
const path = require("path");
|
|
7
|
+
const vm = require("vm");
|
|
8
|
+
|
|
9
|
+
const repoRoot = path.join(__dirname, "..", "..");
|
|
10
|
+
const workflowFiles = [
|
|
11
|
+
".github/workflows/ocr-review.yml",
|
|
12
|
+
"examples/github_actions/ocr-review.yml",
|
|
13
|
+
];
|
|
14
|
+
|
|
15
|
+
function extractPostReviewScript(workflowPath) {
|
|
16
|
+
const text = fs.readFileSync(path.join(repoRoot, workflowPath), "utf8");
|
|
17
|
+
const lines = text.split("\n");
|
|
18
|
+
|
|
19
|
+
for (let i = 0; i < lines.length; i++) {
|
|
20
|
+
const line = lines[i];
|
|
21
|
+
const marker = line.match(/^(\s*)script:\s*\|\s*$/);
|
|
22
|
+
if (!marker) continue;
|
|
23
|
+
|
|
24
|
+
const blockIndent = marker[1].length + 2;
|
|
25
|
+
const block = [];
|
|
26
|
+
for (let j = i + 1; j < lines.length; j++) {
|
|
27
|
+
const current = lines[j];
|
|
28
|
+
if (current.trim() === "") {
|
|
29
|
+
block.push("");
|
|
30
|
+
continue;
|
|
31
|
+
}
|
|
32
|
+
const indent = current.match(/^ */)[0].length;
|
|
33
|
+
if (indent < blockIndent) break;
|
|
34
|
+
block.push(current.slice(blockIndent));
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const script = block.join("\n");
|
|
38
|
+
if (script.includes("/tmp/ocr-result.json")) {
|
|
39
|
+
return script;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
throw new Error(`post review script not found in ${workflowPath}`);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function mockFs(resultText, stderrText) {
|
|
47
|
+
return {
|
|
48
|
+
readFileSync(file) {
|
|
49
|
+
if (file === "/tmp/ocr-result.json") return resultText;
|
|
50
|
+
if (file === "/tmp/ocr-stderr.log") return stderrText;
|
|
51
|
+
throw new Error(`unexpected read: ${file}`);
|
|
52
|
+
},
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function mockGithub(options) {
|
|
57
|
+
const createReviewCalls = [];
|
|
58
|
+
const issueComments = [];
|
|
59
|
+
|
|
60
|
+
return {
|
|
61
|
+
createReviewCalls,
|
|
62
|
+
issueComments,
|
|
63
|
+
rest: {
|
|
64
|
+
pulls: {
|
|
65
|
+
get: async () => ({ data: { head: { sha: "head-sha" } } }),
|
|
66
|
+
createReview: async (params) => {
|
|
67
|
+
createReviewCalls.push(params);
|
|
68
|
+
if (createReviewCalls.length === 1 && options.bulkError) {
|
|
69
|
+
throw new Error(options.bulkError);
|
|
70
|
+
}
|
|
71
|
+
if (createReviewCalls.length > 1 && options.individualError) {
|
|
72
|
+
throw new Error(options.individualError);
|
|
73
|
+
}
|
|
74
|
+
return { data: {} };
|
|
75
|
+
},
|
|
76
|
+
},
|
|
77
|
+
issues: {
|
|
78
|
+
createComment: async (params) => {
|
|
79
|
+
issueComments.push(params);
|
|
80
|
+
return { data: {} };
|
|
81
|
+
},
|
|
82
|
+
},
|
|
83
|
+
},
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
async function runPostReviewScript(workflowPath, options) {
|
|
88
|
+
const script = extractPostReviewScript(workflowPath);
|
|
89
|
+
const github = mockGithub(options);
|
|
90
|
+
const context = {
|
|
91
|
+
repo: { owner: "owner", repo: "repo" },
|
|
92
|
+
issue: { number: 123 },
|
|
93
|
+
eventName: "pull_request_target",
|
|
94
|
+
payload: { pull_request: { head: { sha: "head-sha" } } },
|
|
95
|
+
};
|
|
96
|
+
const sandbox = {
|
|
97
|
+
github,
|
|
98
|
+
context,
|
|
99
|
+
console: { log() {} },
|
|
100
|
+
require(name) {
|
|
101
|
+
if (name === "fs") return options.fs;
|
|
102
|
+
throw new Error(`unexpected require: ${name}`);
|
|
103
|
+
},
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
await vm.runInNewContext(`(async () => {\n${script}\n})()`, sandbox, {
|
|
107
|
+
timeout: 1000,
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
return github;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
async function testFailedInlineCommentsAreSummarized(workflowPath) {
|
|
114
|
+
const result = {
|
|
115
|
+
comments: [
|
|
116
|
+
{
|
|
117
|
+
path: "docs/no-line.md",
|
|
118
|
+
content:
|
|
119
|
+
"No-line content with a fenced block:\n\n```js\nconsole.log('still visible');\n```",
|
|
120
|
+
existing_code: "",
|
|
121
|
+
suggestion_code: "",
|
|
122
|
+
start_line: 0,
|
|
123
|
+
end_line: 0,
|
|
124
|
+
},
|
|
125
|
+
{
|
|
126
|
+
path: "src/app.js",
|
|
127
|
+
content: "Failed inline content must remain visible in the PR summary.",
|
|
128
|
+
existing_code: "oldCall();",
|
|
129
|
+
suggestion_code: "newCall();",
|
|
130
|
+
start_line: 10,
|
|
131
|
+
end_line: 10,
|
|
132
|
+
},
|
|
133
|
+
],
|
|
134
|
+
warnings: [],
|
|
135
|
+
};
|
|
136
|
+
|
|
137
|
+
const github = await runPostReviewScript(workflowPath, {
|
|
138
|
+
fs: mockFs(JSON.stringify(result), ""),
|
|
139
|
+
bulkError: 'Unprocessable Entity: "Line could not be resolved"',
|
|
140
|
+
individualError: 'Unprocessable Entity: "Line could not be resolved"',
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
assert.strictEqual(github.createReviewCalls.length, 2);
|
|
144
|
+
assert.strictEqual(github.issueComments.length, 1);
|
|
145
|
+
const body = github.issueComments[0].body;
|
|
146
|
+
assert.match(body, /No-line content with a fenced block/);
|
|
147
|
+
assert.match(body, /Failed inline content must remain visible/);
|
|
148
|
+
assert.match(body, /Line could not be resolved/);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
async function testErrorCommentUsesSafeFence(workflowPath) {
|
|
152
|
+
const github = await runPostReviewScript(workflowPath, {
|
|
153
|
+
fs: mockFs("not json", "stderr includes a fence\n```js\nbroken();\n```"),
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
assert.strictEqual(github.issueComments.length, 1);
|
|
157
|
+
const body = github.issueComments[0].body;
|
|
158
|
+
assert.match(body, /\n````\nstderr includes a fence/);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
async function main() {
|
|
162
|
+
for (const workflowPath of workflowFiles) {
|
|
163
|
+
await testFailedInlineCommentsAreSummarized(workflowPath);
|
|
164
|
+
await testErrorCommentUsesSafeFence(workflowPath);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
main().catch((err) => {
|
|
169
|
+
console.error(err);
|
|
170
|
+
process.exit(1);
|
|
171
|
+
});
|