Перейти к содержанию

Как добавить новую Go-зависимость

Пошаговый рецепт vetting'а и подключения нового модуля. Convention — ../conventions/dependencies.md. Эта страница — конкретная последовательность команд + checklist для PR.

Содержание

Когда вообще стоит добавлять

Новая зависимость — это чужой код в supply chain сервиса. Прежде чем делать go get:

  1. stdlib уже умеет? net/http, log/slog, encoding/json, errors, context закрывают большую часть задач.
  2. Уже есть в стеке? См. §1.
  3. Можно написать самому за час? Для простых вещей (URL-builder, проверка email) проще 30 строк своих, чем модуль на 5 МБ.

Если «нет, нет, нет» — дальше по шагам.

1. Есть ли уже в стеке?

Проверь текущие зависимости:

go list -m all | head -40
grep -E "^\s+github" go.mod

Сравни с таблицей «Популярные choices» в ../conventions/dependencies.md. Если нужный функционал уже закрыт — используй его, не тащи дубликат (две 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.md). Модуль с 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.md.

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
- Альтернативы рассмотрены: <что ещё смотрел, почему не подошло>

Ревьюер смотрит:

  1. Раздел «Зачем» — действительно нужно.
  2. Раздел «Альтернативы» — автор реально смотрел, или первая попавшаяся.
  3. Лицензия и vetting проведены.
  4. Размер 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 (v1v2) — отдельная задача со своей процедурой. См. ../conventions/dependencies.md.

Массовый ежемесячный 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).

Связанные разделы