Обращение к GitHub API без библиотеки github-api#82
Conversation
Переводит GitHubReleaseClient на java.net.http.HttpClient и встроенный минимальный JSON-парсер (RFC 8259) вместо org.kohsuke:github-api. Из рантайм-замкнутости уходят github-api, jackson-databind/core/annotations (~4 МБ) — остаются только slf4j-api, commons-compress, semver4j и jspecify, что упрощает встраивание в OSGi (EDT) и убирает конфликт версий Jackson. Поведение сохранено: стабильный канал берётся из releases/latest (404 = нет подходящих релизов), pre-release — первый не-draft из списка releases с дочитыванием страниц. Ассеты читаются из того же ответа, так что вместо двух обращений к API выполняется одно. Добавлен конструктор с внешним HttpClient (настройка прокси, тесты). Fixes #81 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WwLcL3YofsPmKBJpcDoVor
WalkthroughИз удалённых библиотек GitHub API и Commons Compress загрузчик переведён на ChangesПоток загрузки релиза
Estimated code review effort: 4 (Complex) | ~45 minutes Sequence Diagram(s)sequenceDiagram
participant Downloader
participant GitHubReleaseClient
participant HttpClient
participant GitHubREST
Downloader->>GitHubReleaseClient: запрашивает последний релиз
GitHubReleaseClient->>HttpClient: отправляет REST-запрос
HttpClient->>GitHubREST: получает данные релиза
GitHubREST-->>HttpClient: возвращает JSON и URL ассетов
HttpClient-->>GitHubReleaseClient: передаёт ответ
GitHubReleaseClient-->>Downloader: возвращает релиз и ассеты
Possibly related PRs
Suggested reviewers: Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Убирает зависимость org.apache.commons:commons-compress и притянутые ею commons-codec/commons-io/commons-lang3 (~2.6 МБ). Распаковка архива сервера переведена на java.util.zip.ZipFile. java.util.zip не переносит unix-права из архива, поэтому бит исполнения на POSIX-системах выставляется явно на файле, который и будет запущен (binaryPath) — для native-image раскладки это единственный исполняемый файл бандла. Добавлен тест на это (пропускается на Windows). Итоговая рантайм-замкнутость downloader'а: slf4j-api, semver4j, jspecify. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WwLcL3YofsPmKBJpcDoVor
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (4)
src/test/java/com/github/_1c_syntax/utils/downloader/JsonTest.java (2)
86-90: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueИмпортировать
java.util.Arraysвместо полного имени в выражении.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/test/java/com/github/_1c_syntax/utils/downloader/JsonTest.java` around lines 86 - 90, In JsonTest.parsesNestedArrays, import java.util.Arrays at the file level and replace the fully qualified java.util.Arrays.asList reference with Arrays.asList, leaving the test behavior unchanged.
94-113: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick winДобавить кейсы со знаком в
\u-escape.Список malformed не содержит
"\u+041"/"\u-041"— именно они сейчас проходят разбор (см.Json.readUnicodeEscape).💚 Дополнить набор
"\"bad unicode \\u12GX\"", + "\"signed unicode \\u+041\"", + "\"signed unicode \\u-041\"", "\"truncated unicode \\u12",🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/test/java/com/github/_1c_syntax/utils/downloader/JsonTest.java` around lines 94 - 113, Добавьте в список malformed samples в тесте JsonTest кейсы со знаком в Unicode escape, включая строки с "\u+041" и "\u-041". Убедитесь, что эти некорректные JSON-строки проверяются как ошибки разбора через существующий тестовый поток.src/main/java/com/github/_1c_syntax/utils/downloader/GitHubReleaseClient.java (1)
167-172: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winСообщение об ошибке теряет тело ответа GitHub.
Для 403 непонятно, это rate limit, SAML-ограничение или неверный токен — GitHub кладёт причину в
messageтела. Стоит добавить усечённое тело в сообщение исключения (токен в теле не возвращается, так что утечки секретов нет).♻️ Добавить фрагмент тела в диагностику
private static String body(HttpResponse<String> response) throws IOException { if (response.statusCode() != 200) { + var details = response.body(); + if (details != null && details.length() > 500) { + details = details.substring(0, 500); + } throw new IOException( - "GitHub API request " + response.request().uri() + " failed: HTTP " + response.statusCode()); + "GitHub API request " + response.request().uri() + " failed: HTTP " + response.statusCode() + + " " + details); } return response.body(); }🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/main/java/com/github/_1c_syntax/utils/downloader/GitHubReleaseClient.java` around lines 167 - 172, Обновите метод body(HttpResponse<String> response), чтобы при ненулевом HTTP-статусе добавлять в IOException усечённый фрагмент response.body() с причиной ошибки GitHub. Сохраните текущий URL и код статуса в сообщении, ограничьте длину тела для диагностики и не изменяйте поведение успешных ответов.src/main/java/com/github/_1c_syntax/utils/downloader/Json.java (1)
179-204: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueПарсер чисел мягче RFC 8259, а вложенный
tryстоит вынести.
isNumberCharразрешает ведущие нули (01→1L) и не проверяет структуру мантиссы/экспоненты, тогда как javadoc класса обещает «весь синтаксис RFC 8259». Для ответов GitHub это безопасно, но формулировку в javadoc лучше смягчить до «надмножество RFC 8259». Заодно — подсказка Sonar по строке 190: вложенныйtryпросится в отдельный метод.♻️ Вынести разбор целого литерала
- try { - if (literal.indexOf('.') < 0 && literal.indexOf('e') < 0 && literal.indexOf('E') < 0) { - try { - return Long.parseLong(literal); - } catch (NumberFormatException outOfLongRange) { - return Double.parseDouble(literal); - } - } - return Double.parseDouble(literal); - } catch (NumberFormatException e) { - throw error("Invalid number '" + literal + "'"); - } + try { + if (literal.indexOf('.') < 0 && literal.indexOf('e') < 0 && literal.indexOf('E') < 0) { + return parseIntegral(literal); + } + return Double.parseDouble(literal); + } catch (NumberFormatException e) { + throw error("Invalid number '" + literal + "'"); + } + } + + private static Number parseIntegral(String literal) { + try { + return Long.parseLong(literal); + } catch (NumberFormatException outOfLongRange) { + return Double.parseDouble(literal); + }🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/main/java/com/github/_1c_syntax/utils/downloader/Json.java` around lines 179 - 204, Обновите javadoc класса Json, заменив обещание полного синтаксиса RFC 8259 на формулировку о поддержке его надмножества, поскольку readNumber и isNumberChar допускают более мягкий формат. Вынесите вложенный try для разбора целого литерала из readNumber в отдельный именованный метод, сохранив текущую логику Long.parseLong с переходом на Double.parseDouble при выходе за диапазон.Source: Linters/SAST tools
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In
`@src/main/java/com/github/_1c_syntax/utils/downloader/GitHubReleaseClient.java`:
- Around line 116-128: Limit pagination in latestNonDraftRelease to a finite
maximum page count, stopping and returning null once that bound is reached while
preserving the existing draft filtering and empty-page behavior. Reuse an
existing page-limit constant if available; otherwise add a clearly named
constant near the other release settings.
In `@src/main/java/com/github/_1c_syntax/utils/downloader/Json.java`:
- Around line 165-177: Update readUnicodeEscape to validate that all four
characters in hex are hexadecimal digits before parsing, rejecting '+' and '-'
(and any other non-hex character) with the existing IOException path. Preserve
the exact four-character boundary check and only advance pos after successful
validation and conversion.
---
Nitpick comments:
In
`@src/main/java/com/github/_1c_syntax/utils/downloader/GitHubReleaseClient.java`:
- Around line 167-172: Обновите метод body(HttpResponse<String> response), чтобы
при ненулевом HTTP-статусе добавлять в IOException усечённый фрагмент
response.body() с причиной ошибки GitHub. Сохраните текущий URL и код статуса в
сообщении, ограничьте длину тела для диагностики и не изменяйте поведение
успешных ответов.
In `@src/main/java/com/github/_1c_syntax/utils/downloader/Json.java`:
- Around line 179-204: Обновите javadoc класса Json, заменив обещание полного
синтаксиса RFC 8259 на формулировку о поддержке его надмножества, поскольку
readNumber и isNumberChar допускают более мягкий формат. Вынесите вложенный try
для разбора целого литерала из readNumber в отдельный именованный метод,
сохранив текущую логику Long.parseLong с переходом на Double.parseDouble при
выходе за диапазон.
In `@src/test/java/com/github/_1c_syntax/utils/downloader/JsonTest.java`:
- Around line 86-90: In JsonTest.parsesNestedArrays, import java.util.Arrays at
the file level and replace the fully qualified java.util.Arrays.asList reference
with Arrays.asList, leaving the test behavior unchanged.
- Around line 94-113: Добавьте в список malformed samples в тесте JsonTest кейсы
со знаком в Unicode escape, включая строки с "\u+041" и "\u-041". Убедитесь, что
эти некорректные JSON-строки проверяются как ошибки разбора через существующий
тестовый поток.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro Plus
Run ID: 08edf374-1af8-4cd7-a671-0e90a8c5c65e
📒 Files selected for processing (7)
build.gradle.ktssrc/main/java/com/github/_1c_syntax/utils/downloader/BslLanguageServerDownloader.javasrc/main/java/com/github/_1c_syntax/utils/downloader/GitHubReleaseClient.javasrc/main/java/com/github/_1c_syntax/utils/downloader/Json.javasrc/test/java/com/github/_1c_syntax/utils/downloader/BslLanguageServerDownloaderTest.javasrc/test/java/com/github/_1c_syntax/utils/downloader/GitHubReleaseClientTest.javasrc/test/java/com/github/_1c_syntax/utils/downloader/JsonTest.java
💤 Files with no reviewable changes (1)
- build.gradle.kts
- Json.readUnicodeEscape: разбор ровно четырёх hex-цифр через Character.digit вместо Integer.parseInt(hex, 16), который принимал знак (+/-) — escape со знаком теперь корректно отвергается как некорректный JSON. - Json.readNumber: вложенный try вынесен в parseIntegral (замечание Sonar); javadoc смягчён — парсер чуть мягче RFC 8259 к числовым литералам. - GitHubReleaseClient: пагинация pre-release ограничена MAX_RELEASES_PAGES, чтобы зеркало/прокси, бесконечно отдающее draft-страницу, не гоняло опрос до rate limit; текст ошибки HTTP дополнен усечённым телом ответа GitHub. - Тесты на границу пагинации, тело в ошибке и escape со знаком. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WwLcL3YofsPmKBJpcDoVor
Для такого узкого клиента (один репозиторий, валидный ответ GitHub) полноценный JSON-парсер избыточен. Оба канала запрашиваются так, чтобы в ответе был ровно один релиз: - stable — releases/latest (как раньше); - pre-release — releases?per_page=1&page=N: каждая страница = один релиз, draft'ы пропускаются переходом к следующей странице (граница MAX_RELEASES_PAGES). Версия и ссылки на ассеты извлекаются регэкспом по самим download-ссылкам https://github.com/<repo>/releases/download/<tag>/<file>: версия берётся из тега в пути, имя ассета — из последнего сегмента. Поэтому произвольное содержимое поля body релиза (markdown со скобками/кавычками) на результат не влияет — раньше именно ради корректного скоупинга ассетов и был нужен структурный парсер. Удалены Json.java и JsonTest.java. Добавлен тест на «отравленные» tag_name/body. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WwLcL3YofsPmKBJpcDoVor
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In
`@src/main/java/com/github/_1c_syntax/utils/downloader/GitHubReleaseClient.java`:
- Around line 70-77: Замените regex-извлечение в логике GitHubReleaseClient на
структурный разбор RFC 8259 JSON: извлекайте только поле draft и
browser_download_url из объектов assets[*], игнорируя ссылки в body и других
полях. Удалите или перестаньте использовать ASSET_URL и связанные с ним
совпадения; некорректный JSON должен обрабатываться как ошибка, а не приниматься
по найденной ссылке. Добавьте регрессионный тест для ссылки того же репозитория
в body перед настоящими ассетами.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro Plus
Run ID: 25b1b4ba-9182-42ab-8083-2373f2f11130
📒 Files selected for processing (2)
src/main/java/com/github/_1c_syntax/utils/downloader/GitHubReleaseClient.javasrc/test/java/com/github/_1c_syntax/utils/downloader/GitHubReleaseClientTest.java
Ревью справедливо отметило: голая ссылка на старый ассет того же репозитория, упомянутая в релиз-ноутах (body), попадала бы в ASSET_URL и задавала неверную версию. Теперь совпадение привязано к ключу "browser_download_url", который в корректном ответе GitHub есть только у объектов assets[*], — текст body уже не совпадает. Регрессионный тест дополнен голой ссылкой на старый ассет в body. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WwLcL3YofsPmKBJpcDoVor
По просьбе — переход на gson: лёгкий (~0.28 МБ, без транзитивов), OSGi-совместимый JSON-парсер вместо самодельного разбора регэкспом. Возвращён естественный поток: - pre-release снова берёт первый не-draft из списка releases (per_page=30) со строгим скоупингом ассетов к выбранному релизу, а не постранично по одному; - версия берётся из tag_name, флаг draft и ассеты — из своих полей структурно, поэтому содержимое body на результат не влияет (нулевой остаточный риск); - некорректный JSON — IOException, как ошибка обмена. Транзитивный error_prone_annotations (только compile-time) исключён, чтобы не попадал в рантайм-замкнутость: jspecify + slf4j-api + gson + semver4j. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WwLcL3YofsPmKBJpcDoVor
| } | ||
|
|
||
| @Test | ||
| @DisabledOnOs(OS.WINDOWS) |
There was a problem hiding this comment.
Тест проверяет, что makeLauncherExecutable выставляет бит исполнения, а это поведение POSIX-only:
makeLauncherExecutableработает только когда файловая система поддерживаетposix(константаPOSIXв загрузчике). На Windows такого view нет — метод там no-op, выставлять/проверять нечего.Files.isExecutable()на Windows не отражает POSIX-бит: он смотрит на ACL/расширение и для лаунчера (bsl-language-server.exe) вернётtrueв любом случае. То есть на Windows ассертisTrue()прошёл бы тривиально, ничего фактически не проверяя.
Поэтому тест ограничен POSIX-системами (Linux/macOS проходят), а не отключён везде. Если предпочтительнее — могу заменить на assumeTrue(POSIX) внутри теста, но @DisabledOnOs(WINDOWS) тут ровно тот же смысл и стандартнее.
Generated by Claude Code
Инвариант «возвращённый бинарь исполняемый» держится и на Windows (.exe исполняется по расширению), поэтому нет причин отключать тест на Windows. Убран @DisabledOnOs(WINDOWS), тест переименован в OS-нейтральный. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WwLcL3YofsPmKBJpcDoVor
|



Переводит
GitHubReleaseClientсorg.kohsuke:github-apiнаjava.net.http.HttpClient+ gson, а распаковку архива сервера — сcommons-compressнаjava.util.zip. У загрузчика больше нет ни клиентских библиотек GitHub, ни Jackson, ни commons-*.Closes #81
Closes #80
Что изменилось
GitHubReleaseClient— переписан наjava.net.http.HttpClient, ответ разбирается через gson:GET /releases/latest(404 трактуется как «нет подходящих релизов»);GET /releases?per_page=30: первый не-draft из списка, ассеты строго скоупятся к выбранному релизу; границаMAX_RELEASES_PAGESзащищает от бесконечного опроса;tag_name, флагdraftи ассеты — из своих полей структурно, поэтому содержимоеbody(релиз-ноуты) на результат не влияет; некорректный JSON —IOException;HttpClient+ request/response 30 с на каждом запросе (.timeout(...)) — путь проверки релиза больше не может зависнуть на «немом»api.github.com;Accept: application/vnd.github+json,X-GitHub-Api-Version,User-Agent,Authorization: Bearerпри непустом токене; текст HTTP-ошибки включает усечённое тело ответа для диагностики;GitHubReleaseClient(token, httpClient)— для настройки прокси и тестов.org.apache.commons.compressнаjava.util.zip.ZipFile. Так какjava.util.zipне переносит unix-права из архива, бит исполнения на POSIX выставляется явно на запускаемом файле (binaryPath) — для native-image раскладки это единственный исполняемый файл бандла.build.gradle.kts— удаленыorg.kohsuke:github-api:1.330иorg.apache.commons:commons-compress:1.28.0; добавленcom.google.code.gson:gson:2.13.1(транзитивный compile-timeerror_prone_annotationsисключён из рантайма).Эффект для рантайм-замкнутости
Из classpath уходят
github-api+jackson-databind/core/annotations(~4 МБ) иcommons-compress+commons-codec/io/lang3(~2.6 МБ). Добавляется только gson (OSGi-совместим, без транзитивов). Итоговый граф:Итоговый размер замкнутости для использования загрузчика — 5 jar, ≈ 422 КиБ:
Против исходных ~5.6 МБ / 12 jar — примерно 13× меньше. Конфликт версий Jackson (2.20 ↔ 2.12 из target-платформы EDT) исчезает — встраивание в OSGi-бандл (1C:EDT) становится лёгким.
Тесты
GitHubReleaseClientTest(оба канала, скоупинг ассетов к выбранному релизу, пагинация по draft'ам и её граница, заголовки/авторизация, 403/404, некорректный JSON, релиз без тега/без ассетов, устойчивость к «отравленному»body, восстановление interrupt-флага) и тест на исполняемость лаунчера (на всех ОС). Прежние тестыBslLanguageServerDownloaderпроходят без изменений.🤖 Generated with Claude Code
https://claude.ai/code/session_01WwLcL3YofsPmKBJpcDoVor