Как добавить новую Go-зависимость
Пошаговый рецепт vetting’а и подключения нового модуля. Convention —
../conventions/dependencies. Эта
страница — конкретная последовательность команд + checklist для PR.
Содержание
- Когда вообще стоит добавлять
- 1. Есть ли уже в стеке?
- 2. Vetting репозитория
- 3. Лицензия
- 4. Добавить в go.mod
- 5. govulncheck
- 6. SBOM-проверка
- 7. Локально прогнать тесты
- 8. PR description
- 9. Update: как апгрейдить
- Чеклист для PR-автора
- Связанные разделы
Когда вообще стоит добавлять
Новая зависимость — это чужой код в supply chain сервиса. Прежде чем
делать go get:
- stdlib уже умеет?
net/http,log/slog,encoding/json,errors,contextзакрывают большую часть задач. - Уже есть в стеке? См. §1.
- Можно написать самому за час? Для простых вещей (URL-builder, проверка email) проще 30 строк своих, чем модуль на 5 МБ.
Если «нет, нет, нет» — дальше по шагам.
1. Есть ли уже в стеке?
Проверь текущие зависимости:
go list -m all | head -40
grep -E "^\s+github" go.modСравни с таблицей «Популярные choices» в
../conventions/dependencies.
Если нужный функционал уже закрыт — используй его, не тащи дубликат
(две ORM, два JSON-парсера, два HTTP-роутера в одном сервисе —
антипаттерн).
2. Vetting репозитория
Открой репозиторий модуля (github/gitlab) и ответь на вопросы.
Активность
- Последний commit в main. За 6 месяцев — норма. > 1 года — красный флаг.
- Releases. Есть tagged releases? Или только main-branch’ный код?
- Issues. Бэклог растёт или обрабатывается? Долгие unanswered issues — знак.
Популярность
- Stars > 500. Либо это общеизвестный модуль (
golang.org/x/*, частьprometheus/*/cncf-*). - Сколько других проектов используют. На GitHub — кнопка «Used by». На pkg.go.dev — «Imported by».
Авторство
- Организация или одиночка. Одиночный автор — не сразу «нет», но нужен дополнительный осмотр кода.
- Denylist. Если был случай supply-chain-атаки от этого автора (event-stream / coa / ua-parser-js и подобные прецеденты в экосистеме) — не тянем.
Код-ревью модуля
Бегло прочти:
init()функций быть не должно (или только с очевидным содержимым).init()с сетевыми вызовами / shell-вызовами — стоп.exec.Command("sh", ...),os/execна ровном месте — тоже стоп.- Обфусцированные блоки, base64-кодированные строки на десятки килобайт — однозначный индикатор подозрительного кода.
net/http.Postна неизвестные URL — особенно в constructor’ах.
Это не глубокий аудит, это 10-минутный просмотр на очевидные флаги.
Go-версия
cat go.mod | head -5 # в репозитории модуляgo X.Y должна быть совместима с нашей (1.22+, см.
../conventions/dependencies).
Модуль с go 1.20 — работает; модуль, требующий go 1.23beta —
проблема.
3. Лицензия
Проверь LICENSE в корне репозитория.
| Лицензия | Наш статус |
|---|---|
| MIT, Apache-2.0, BSD-2/3-Clause, ISC, MPL-2.0 | принимаем |
| Unlicense, CC0 | принимаем (public domain) |
| GPL-2.0, GPL-3.0, LGPL, AGPL | нельзя — copyleft, вирусится на наш код |
Любая Non-Commercial | нельзя |
Нет файла LICENSE | нельзя — нет права использовать |
| Кастомная лицензия | обсудить с lead |
Почему это важно: AGPL зависимость в prod-сервисе теоретически потребует открыть весь наш код под AGPL.
Для точной классификации — опираемся на
SPDX license list и пакет
licensecheck:
go install github.com/google/go-licenses@latest
go-licenses report ./... 2>/dev/null | grep -i "<module>"Утилита покажет детектированную лицензию всей цепочки transitive- зависимостей.
4. Добавить в go.mod
Последовательность:
# в корне сервис-репо
go get github.com/<org>/<module>@<version>
go mod tidy- Всегда с явной версией (
@v1.2.3или@latest). Без суффикса Go берёт latest, но не зафиксирует —go mod tidyможет «сдрейфить». - Не редактируй
go.modруками. Только черезgo get/go mod tidy/go mod download. indirectсекция управляется автоматически — вручную туда не пиши.
Проверь размер обновления:
git diff go.mod go.sum | head -50Если один go get притащил 20+ новых модулей — это большая цепочка
transitive-зависимостей, повод подумать, нет ли более узкого
альтернативного модуля.
5. govulncheck
go install golang.org/x/vuln/cmd/govulncheck@latest
govulncheck ./...Ожидаем:
Your code is affected by 0 vulnerabilities.Если findings есть:
- В новом модуле — не тянем его. Ищем альтернативу, или апгрейдим до патч-версии без CVE.
- В transitive-зависимости —
go getконкретной старшей версии transitive-пакета в свойgo.modкак direct require (Go выберет max(required)). LOW severity— допустимо с документированием в PR description: почему не критично, когда апгрейд.
govulncheck не идеален: он проверяет только используемые
символы. Для полного покрытия — trivy fs --scanners vuln ..
6. SBOM-проверка
Software Bill of Materials — список всех компонентов в образе:
# для образа сервиса
syft . -o spdx-json > sbom.json
# посмотреть добавленное
jq '.packages[] | select(.name | test("<module-name>")) | {name, versionInfo, licenseConcluded}' sbom.jsonПрикладывается к tagged-релизу (make release). Если отсутствует в CI
— добавь: syft бесплатный, занимает 10 секунд.
7. Локально прогнать тесты
Полный цикл:
go build ./... # компиляция
go vet ./... # базовые проверки
golangci-lint run # lint
go test -race ./... # unit + race detector
go test -tags=integration ./... # integration (testcontainers)Если integration падает — смотри
../troubleshooting/test-hangs.
8. PR description
Шаблон commit-message:
feat(deps): добавил github.com/<org>/<module> v1.2.3
Что это:
<2-3 строки описания>
Зачем:
<3-4 строки: какую задачу решает, почему не подходит текущий стек>
Vetting:
- Лицензия: <MIT / Apache-2.0 / ...>
- Активность: <X stars, commits N months ago, releases last YY>
- Transitive: <список, если > 3 новых>
- govulncheck: pass
- Альтернативы рассмотрены: <что ещё смотрел, почему не подошло>Ревьюер смотрит:
- Раздел «Зачем» — действительно нужно.
- Раздел «Альтернативы» — автор реально смотрел, или первая попавшаяся.
- Лицензия и vetting проведены.
- Размер diff’а в
go.sum— соизмерим с ожидаемым.
Ссылка на convention в PR description:
см. conventions/dependencies.md#vetting-checklist.
9. Update: как апгрейдить
Патч / minor:
go get -u github.com/<module>
go mod tidy
go test ./...
govulncheck ./...Major (v1 → v2) — отдельная задача со своей процедурой. См.
../conventions/dependencies.
Массовый ежемесячный sweep:
go list -u -m all | grep -v indirectСписок того, что имеет обновления. Один PR — один модуль (кроме
однотипных пачек типа watermill-*).
Чеклист для PR-автора
- Альтернативы в stdlib и текущем стеке рассмотрены и отклонены с обоснованием.
- Лицензия совместима (MIT / Apache-2.0 / BSD / MPL / ISC), не GPL/AGPL/LGPL.
- Последний коммит в main-модуле — не старше 6 месяцев (или обоснование).
-
govulncheck ./...— clean. -
go mod tidy— no-op (изменения только от нашегоgo get). -
go.sumзакоммичен вместе сgo.mod. - Integration-тесты проходят.
- PR description заполнен по шаблону §8.
- Размер transitive-обновления адекватный (не 20+ новых модулей
из-за одного
go get).
Связанные разделы
../conventions/dependencies— принципы, популярные choices, чего не тянуть, CI-проверки.../conventions/security— supply chain в общей security-модели.../conventions/commits-and-prs— формат commit-message для dependency-PR.../troubleshooting/test-hangs— если integration-тесты повисли после добавления зависимости.