Проверьте GitHub-репозиторий до его внедрения
Анализ GitHub-репозиториев через git clone: release notes, changelog, структура, code review, сравнение версий, статистика контрибьюторов.
Как агент работает
Глубина разбора задаётся целью: оценка библиотеки перед зависимостью требует 12–24 месяцев истории плюс issue и релизы, приёмка работы подрядчика — периода договора, поиск причины регрессии — истории до последней рабочей версии, а «что это вообще такое» обходится одним коммитом. Клон по умолчанию приходит с глубиной 1: счётчик коммитов даёт единицу, тегов ноль, а в авторах ровно один человек.
То, чего в истории нет, берётся из публичного REST GitHub: состояние archived, license.spdx_id, размер репозитория, дата последнего пуша, tag_name и published_at последнего релиза, закрытые issue со временем закрытия и метками. Поле open_issues_count включает открытые pull request и завышает число багов, а лимит без токена — 60 запросов в час и 10 в минуту к поиску, отсюда 5–8 на репозиторий.
Техника чтения истории отсекает тихо ложные числа. Подсчёт объёма идёт без merge-коммитов, лента релизов — по первому родителю, авторы склеиваются по e-mail до подсчёта, иначе один человек с тремя адресами удваивает bus factor. Churn считается с исключением lock-файлов, миграций и сгенерированного: один файл схемы легко даёт плюс-минус сорок пять тысяч строк и забивает метрику.
Вердикт о живости формулируется числами: archived значит закрытый проект, коммит свежее 90 дней — активный, тишина больше 12 месяцев — заброшенный, а лаг между релизом и последним коммитом свыше 180 дней означает, что фиксы живут только в основной ветке. Bus factor — минимальное число авторов, дающих половину коммитов за год; лицензии MIT, BSD и Apache-2.0 идут в закрытый продукт, AGPL — нет.
Границы разбора названы прямо: по числу строк качество кода не оценивается, строки не переводятся в часы и рубли, расхождение заявленного с найденным — вопрос подрядчику, а не обвинение. Категоризация по Conventional Commits ниже 60% применимости недостоверна и так и пишется в отчёте, а bisect на обрезанной истории упрётся в границу и назовёт виновником самый старый коммит.
1. Цель определяет глубину
| Цель | История | GitHub API | Что решается |
|---|---|---|---|
| Оценка библиотеки перед зависимостью | 12–24 мес | issues, releases | Брать / не брать / форкнуть |
| Приёмка работы подрядчика | период договора | нет | Подписывать ли акт |
| Что изменилось между версиями | диапазон тегов | releases | Обновляться ли |
| Причина регрессии | до рабочей версии | нет | Какой коммит виноват |
| Release notes | период отчёта | PR-заголовки | Текст для пользователей |
| Структура и технологии | 1 коммит | нет | Что это вообще такое |
Только последняя строка обходится клоном по умолчанию, остальным нужен добор истории.
2. Клонирование: инструмент, а не shell
2.1. Клон приходит обрезанным, и это меняет цифры
git_clone клонирует с --depth=1. Проверено на живом репозитории: после клона git rev-list --count HEAD даёт 1, git tag — 0 тегов, git shortlog -sn HEAD — одного автора. Статистика на таком клоне выйдет уверенной и полностью ложной, поэтому первой командой после клона:
true — истории нет, добирай. Три рецепта, все проверены:
Первый — вся история, годится до ~200 МБ (размер смотри в поле size из API заранее). Второй быстрее, но --depth=N считает коммиты по каждой ветви слияния отдельно: --depth=300 дал на реальном репозитории 642 коммита, количество из глубины не выводится. Третий — единственный способ взять календарный период, и он требует явного refspec (origin main): без имени ветки fetch обновит только remote-ref, а HEAD останется на одном коммите. --tags добирай всегда — без тегов нет ни сравнения версий, ни лага релизов.
2.2. Правки и PR
Нужно что-то предложить по итогам — ветка через session_branch у git_clone, правки edit_file, коммит через панель ревью, затем open_pull_request: он сам определит GitHub или GitLab и вернёт номер со ссылкой. Пушить из sandbox_bash бессмысленно, токена в shell нет.
3. Что не видно в git и берётся из API
Про issue, обсуждения, отзывчивость мейнтейнеров и релизы история не знает ничего. Это берётся web_fetch по публичному REST GitHub, база https://api.github.com (проверено 2026-07-28):
/repos/{owner}/{repo}—pushed_at,archived,license.spdx_id,open_issues_count,forks_count,subscribers_count,size,default_branch;/repos/{owner}/{repo}/releases/latest—tag_name,published_at;/repos/{owner}/{repo}/issues?state=closed&sort=updated&per_page=100—created_at,closed_at,comments, метки;/search/issues?q=repo:{owner}/{repo}+is:issue+is:open—total_count(сis:pr— открытые PR).
Две ловушки. open_issues_count включает открытые pull request: у psf/requests на 2026-07-28 поле показывало 233 при 146 issue и 87 PR, то есть цитата «233 открытых бага» завышает на 60% — считай раздельно через search. И лимит без токена: 60 запросов в час на IP, 10 в минуту к search (проверено там же), поэтому 5–8 запросов на репозиторий, без сплошной пагинации.
4. Техника чтения истории
Merge-коммиты
--no-merges для подсчёта работы, --first-parent для ленты релизов: при PR-модели первый родитель — ровно последовательность влитых PR с номерами в заголовках.
git shortlog в неинтерактивной оболочке читает stdin и молча возвращает пустоту — команда не падает, просто ноль строк, и в отчёт уходит «контрибьюторов не найдено». Всегда указывай ref явно:
Поиск по содержимому
-S'строка' — коммиты, где изменилось число вхождений; -G'regex' — где строка попала в diff. Инструмент вопроса «когда это появилось или исчезло»: git log -S'DEFAULT_TIMEOUT' --format='%h %ad %an %s' --date=short --no-merges.
Переименования
--follow -- path/to/file тянет историю через переименования, но принимает один путь и не дружит с --name-only. Без него история обрывается на переименовании, и «файл создан 3 месяца назад» — ложь.
Один автор ≠ одна строка
На реальном репозитории shortlog показал Lev Kokovkin и Kokovkin Lev с одним e-mail, а один человек — три github-noreply адреса. Сначала git log --format='%an <%ae>' | sort -u и склейка по почте, потом счёт: иначе bus factor завышен вдвое.
Объём правок
--numstat даёт строки по файлам. Топ churn за 90 дней на реальном репозитории возглавили schemas.generated.json (+45 481 −45 481) и generated.ts: генерация, миграции и лок-файлы забивают любую метрику строк. Исключай pathspec'ом: -- . ':(exclude)*.lock' ':(exclude)*generated*' ':(exclude)*/migrations/*'.
5. Живой проект или заброшенный
Признаки по убыванию надёжности:
archived: trueв API — проект закрыт автором, дальше только форк.- Последний коммит в основную ветку: до 90 дней — активен, 90–365 — дремлет, больше 12 месяцев без коммитов и ответов в issue — заброшен.
- Лаг релиза — дней между
published_atпоследнего релиза и последним коммитом. Уpsf/requestsна 2026-07-28: релиз v2.34.2 от 2026-05-14 при коммите 2026-07-27, лаг 74 дня — норма для зрелой библиотеки. Больше 180 дней при живых коммитах значит, что фиксы есть только в основной ветке и ставить придётся по git-ссылке, которую внутренние прокси-репозитории часто не пропускают. - Авторов хотя бы с одним коммитом за 12 месяцев: один — проект держится на человеке, двое-трое — обычная небольшая библиотека, десять и больше — есть сообщество.
- Ритм по помесячной гистограмме: ровная линия — живая разработка, всплеск год назад и тишина — выложенный и брошенный проект.
Вердикт формулируй числами: «за 12 месяцев 1 234 коммита от 8 авторов, последний 2026-07-27, релиз отстаёт на 74 дня».
6. Отзывчивость мейнтейнеров и характер issue
Скорость закрытия сама по себе ничего не значит: бот, закрывающий всё «as stale», даёт отличную медиану на мёртвом проекте. По последним 100 закрытым issue считай медиану closed_at − created_at; долю с comments == 0 (больше 30% — вопросы не читают); долю с меткой stale/wontfix (выше доли закрытых по фиксу — это уборка, а не поддержка); медиану времени до первого ответа мейнтейнера — её и почувствует тот, кто придёт со своим багом.
Вердикт: «медиана закрытия 12 дней, но 41 из 100 закрыты без ответа и 18 помечены stale — на ваш баг, скорее всего, не ответят».
7. Bus factor
Bus factor = минимальное число авторов, дающих 50% коммитов за 12 месяцев (после склейки дублей из раздела 4).
Ответ — номер первой строки, где накопленная доля перевалила за 50%. Проверено: первый автор 73,4% при восьми авторах всего, то есть bus factor = 1 — обычная картина, и показывать надо именно её. Порог решения: bus factor 1 у библиотеки в критичном пути (платежи, обмен с 1С, выгрузка на маркетплейс) — закладывай стоимость форка; у утилиты форматирования дат — приемлемо.
8. Лицензия
Бери license.spdx_id из API и сверяй с файлом LICENSE в клоне: детектор ошибается на правленых текстах, а NOASSERTION значит «не распознано», то есть юридически все права у автора.
- MIT, BSD, Apache-2.0 — можно в закрытый продукт; Apache-2.0 даёт патентную лицензию и требует сохранять NOTICE.
- GPL-2.0/3.0 — производное распространяется на тех же условиях: для SaaS, который никому не передают, чаще терпимо, для коробки, десктопа и мобильного приложения — нет.
- AGPL-3.0 — обязательства срабатывают и при доступе по сети: для SaaS это означает открыть код.
- Нет файла лицензии — не «свободно», а запрет: использование без письменного разрешения автора неправомерно.
- Смена лицензии —
git log --oneline --follow -- LICENSE. Переезд с MIT на BSL оставляет старые версии под старой лицензией, иногда это единственный законный путь.
9. Безопасность, видимая из репозитория
Секреты в истории
Удалённый из рабочего дерева секрет остаётся в объектах навсегда, а git grep смотрит только текущий срез и его не видит. Ищи в двух плоскостях:
Первая даёт файлы, когда-либо добавленные в репозиторий; отсеки .env.example и публичные сертификаты — на реальном репозитории ими оказались пять попаданий из семи. Найденный ключ скомпрометирован независимо от того, удалён ли он: отозвать и перевыпустить, а не «вычистить историю».
Устаревшие зависимости
Объявленные версии из package.json, requirements.txt, pyproject.toml сравни с актуальными через web_fetch (https://pypi.org/pypi/{name}/json, https://registry.npmjs.org/{name}). Отчитывайся отставанием, а не числом пакетов: «7 из 34 отстают больше чем на 2 мажорные версии» — это оценка стоимости обновления.
Гигиена процесса
.github/workflows с прогоном тестов, SECURITY.md, dependabot.yml. Отсутствие CI при двух десятках контрибьюторов — сигнал, что в основной ветке регулярно бывает сломанное состояние.
10. Категоризация коммитов и её предел
Классифицируй по Conventional Commits с падением на ключевые слова текста, включая русские.
Сначала измерь применимость — долю сообщений под ^[a-z]+(\([^)]*\))?!?: . Ниже 60% — категоризация по префиксам недостоверна, и это пишут в отчёте, а не заменяют процентами. На проверенном репозитории конвенцию соблюдали 330 из 372 сообщений (89%), но 49% коммитов всё равно ушли в «Прочее»: feat: носили релизные коммиты, склеившие десятки изменений.
Правило: проценты по категориям — вспомогательная цифра, а не вывод. Вывод делается чтением заголовков. Больше трети в «Прочем» — так и пиши: категоризация здесь не работает, разбор сделан вручную по N коммитам.
11. Сравнение версий и поиск регрессии
Версии и то, что между ними:
Для обновления отвечай по порядку: изменился ли публичный API (!: и BREAKING CHANGE в теле коммита, диффы файлов экспорта), появились ли обязательные настройки (конфиги, .env.example), выросли ли минимальные требования (python_requires, engines, go). Остальное — детали реализации.
Регрессия: сузь диапазон тегами («работало на 1.4.0, сломалось на 1.5.0»), затем -S по имени функции или тексту ошибки, затем --follow по файлу. git bisect применим только при воспроизводимой проверке и неурезанной истории: на shallow-клоне он упрётся в границу и назовёт виновником самый старый доступный коммит.
12. Приёмка работы подрядчика
Цифры пойдут в разговор о деньгах, поэтому порядок жёсткий: границы (период договора и ветки, git branch -a --sort=-committerdate) → авторы подрядчика со склеенными по разделу 4 дублями → объём без сгенерированного (разница кратная: сгенерированный клиент API даёт 60 тысяч строк одним коммитом) → состав работы по разделу 10 плюс чтение заголовков → процесс: распределение по датам (равномерно или всё за два дня до сдачи), часы (--date=format:'%H'), появились ли тесты, зелёный ли CI.
Чего нельзя: превращать строки в часы и рубли — рефакторинг, сокративший 2 000 строк, может стоить дороже добавленных 10 000. С почасовой ставкой договора историю сопоставляй как проверку правдоподобия: «276 коммитов за 3 месяца, распределение равномерное» подтверждает регулярную работу, но не считает сумму. Расхождение заявленного и наблюдаемого — вопрос подрядчику, а не обвинение.
13. Шаблоны отчётов
Release notes
только то, что заметит пользователь: заголовок с периодом, абзац «коротко», «Новые возможности» (что теперь может пользователь), «Исправления» (что перестало ломаться и у кого проявлялось), «Важно при обновлении» (ломающее изменение и что с ним делать), в конце строка «Внутренние изменения: N коммитов» и счётчики. Каждый пункт — с 7-символьным хешем.
Приёмка — таблица «заявлено / найдено в истории / вывод» по пунктам ТЗ, ниже объём без сгенерированного, даты, замечания. Регрессия — подозреваемый коммит, диапазон, чем подтверждено, обратимо ли откатом.
14. Как переводить коммиты
Читатель отчёта — чаще владелец бизнеса, решающий, платить ли и обновляться ли, а не разработчик.
15. Режимы отказа
- Обрезанный клон (
--is-shallow-repository= true, 1 коммит, 0 тегов),shortlogбезHEAD(пустой вывод при непустомgit log), авторы, не склеенные по e-mail, churn по сгенерированным файлам,open_issues_countкак число багов — все пять дают тихо неверные числа. - Выводы о качестве кода по числу строк — из истории видно поведение команды, а не качество кода.
- Ответ по README — README это намерение автора, история — реальность, и расхождение само по себе находка: «README обещает поддержку версии X, в коде она удалена в {hash} восемь месяцев назад».
Похожие навыки
Попробуйте этот навык
Зарегистрируйтесь и используйте навык «GitHub Repo Analyzer» бесплатно.