Зарегистрируйтесь, чтобы продолжить обучение

Публикация модулей Go: Настройка окружения

Go-environment

В Go используется нестандартная система публикации модулей. В отличие от большинства других популярных языков, в Go не нужно самостоятельно загружать код в централизованные репозитории. Каждый Go-модуль публикуется только в репозиториях исходного кода, например, на GitHub или GitLab.

В этом уроке мы рассмотрим, как работает публикация модулей в Go.

Как работает загрузка через прокси-сервер

Команда go не загружает пакеты напрямую из хранилищ исходного кода, а опрашивает прокси-сервер.

Этот сервер хранит все публичные Go-модули во всех версиях. Если модуль или запрошенная версия отсутствуют на сервере, то прокси попытается сохранить их у себя, прежде чем отдать пользователю. Такая система защищает пользователей от потери доступа к пакету, если автор изменит нужный тег, перезапишет его или удалит пакет целиком.

Еще пользователя оберегает база данных контрольных сумм, которая хранит записи из файлов go.sum. Представьте, что вы загружаете модуль командой go. В этот момент команда считывает хэш загружаемого модуля и сверяет его с данными из базы контрольных сумм. Если они не совпадают, то установка завершается с ошибкой.

Как найти запрашиваемый пакет в прокси

Проследим установку пакета на примере популярной библиотеки lo. Возьмем базовый пример:

package hxlt

import (
    "github.com/samber/lo"
)

func isEven(x int, _ int) bool {
    return x%2 == 0
}

func Even(vals []int) []int {
    return lo.Filter[int](vals, isEven)
}

Вызов команды go get github.com/samber/lo@latest проверит переменную окружения GOPROXY. Допустим, она содержит список прокси-серверов:

https://corp.example.com,https://proxy.golang.org

Тогда команда go выполнит следующие шаги:

  • Запросит последнюю версию github.com/samber/lo от сервера https://corp.example.com/
  • Запросит последнюю версию github.com/samber от сервера https://corp.example.com/
  • Запросит последнюю версию github.com от сервера https://corp.example.com/

Представим, что все запросы к https://corp.example.com/ выдали ошибку 404 или 410. Тогда команда перейдет к следующей записи:

  • Запросит последнюю версию github.com/samber/lo от сервера https://proxy.golang.org/
  • Запросит последнюю версию github.com/samber от сервера https://proxy.golang.org/
  • Запросит последнюю версию github.com от сервера https://proxy.golang.org/

Если в результате найдется один модуль с именем github.com/samber/lo и пакетом lo, то команда go обновит файлы go.mod и go.sum и скачает сам пакет.

Как найти последнюю версию пакета

Продолжим пример с запросом go get github.com/samber/lo@latest. Здесь можно использовать атрибут @latest, и тогда go определит последнюю версию запрашиваемого пакета.

Рассмотрим, как это работает. Допустим в системе контроля версий проекта есть несколько веток и тегов:

  • Ветка main
  • Ветка v2
  • Тег v1.0.0
  • Тег 1101-1663105068

В этом случае последней версией будет считаться тег v1.0.0, потому что Go отдает приоритет тегам перед ветками. Обратите внимание, что используются только те теги, которые начинаются с буквы v и следуют семантическому версионированию.

Если в проекте нет тегов, команда создает псевдо-версию, используя последний коммит.

Как публиковать модуль

Попробуем опубликовать собственный модуль. Этот процесс состоит из нескольких шагов.

Создаем go-модуль:

go mod init github.com/hexlet-components/hxlt

go: creating new go.mod: module github.com/hexlet-components/hxlt

Добавляем код в модуль, созданный выше:

package hxlt

import (
    "github.com/samber/lo"
)

func isEven(x int, _ int) bool {
    return x%2 == 0
}

func Even(vals []int) []int {
    return lo.Filter[int](vals, isEven)
}

Запускаем go mod tidy, чтобы загрузить недостающие пакеты и удалить лишние:

go mod tidy

go: finding module for package github.com/samber/lo
go: found github.com/samber/lo in github.com/samber/lo v1.28.0

Дальше нужно отформатировать код с помощью встроенного инструмента go fmt. Не забывайте запускать форматирование перед публикацией пакета — так вы убедитесь, что вывод команды не содержит ошибок.

После этого проверяем код линтером. Для Go написано множество линтеров, поэтому появились утилиты-аггрегаторы, запускающие несколько линтеров разом. Мы рекомендуем пользоваться golangci-lint.

Остался последний шаг — добавляем новый тег в репозиторий:

git commit -am "feat: changes for v0.0.1"
git tag v0.0.1
git push origin v0.0.1

Модуль готов к скачиванию! Чтобы пользователи смогли найти его до загрузки, можно запустить команду go list — она добавит модуль в индекс go.pkg.

Как добавлять обратно несовместимые изменения

В работе над проектом разработчики могут переименовывать функции, изменять ее сигнатуры, удалять устаревший код. Подобные действия могут вносить ломающие изменения в API. Это значит, что пользователям придется вносить изменения в собственную кодовую базу.

Go-разработчики серьезно относятся к подобным изменениям в API. Если вам нужно внести ломающие изменения, не получится ограничиться добавлением тега с увеличенной мажорной версией. Рассмотрим подробнее, как устроено внесение таких изменений.

Возьмем модуль из примера выше. Его структура выглядит так:

.
├── go.mod
├── go.sum
└── hxlt.go

При этом файл go.mod выглядит так:

module github.com/hexlet-components/hxlt

go 1.19

require github.com/samber/lo v1.28.0

require golang.org/x/exp v0.0.0-20220303212507-bbda1eaf7a17 // indirect

Чтобы создать версию 2.0.0 этого модуля, нужно обновить go.mod, чтобы он содержал в названии новую мажорную версию пакета. Сохраним текущий код в ветке v1:

git branch v1

Обновим go.mod файл, добавив ему в название v2:

go mod edit -module github.com/hexlet-components/hxlt/v2

Если бы в проекте было несколько пакетов, то для папки с v2 пришлось бы обновить все импорты, чтобы они включали суффикс v2. Добавим тег и опубликуем:

git tag v2.0.0
git push origin v2.0.0

Выводы

  • Чтобы опубликовать Go-модуль, нужно внести его в хранилище исходного кода: то есть добавить сам код, файлы go.mod и go.sum в систему контроля версий
  • Имя модуля при этом должно содержать путь до модуля в этом хранилище
  • Модули по умолчанию загружаются не из хранилищ кода, а из центрального репозитория пакетов
  • Чтобы точнее указать требования к версии пакета, можно использовать version query. Отсутствующий version query имеет дефолтное значение latest
  • Перед публикацией не забывайте форматировать код и проверять его линтером
  • В работе над публичными проектами на Go добавляйте новые версии, а не изменяйте уже существующие

Самостоятельная работа

  1. Опубликуйте ваш модуль на Github, указав версию v1.0.0 (команду go list выполнять не нужно)

  2. Внесите изменения в ваш код, например так, чтобы программа выводила "Hexlet for Brave!"

  3. Обновите версию модуля в go.mod. Опубликуйте новую версию на Github, указав новый тег


Дополнительные материалы

  1. Установка golangci-lint
  2. Go modules Wiki

Аватары экспертов Хекслета

Остались вопросы? Задайте их в разделе «Обсуждение»

Вам ответят команда поддержки Хекслета или другие студенты

Для полного доступа к курсу нужен базовый план

Базовый план откроет полный доступ ко всем курсам, упражнениям и урокам Хекслета, проектам и пожизненный доступ к теории пройденных уроков. Подписку можно отменить в любой момент.

Получить доступ
1000
упражнений
2000+
часов теории
3200
тестов

Открыть доступ

Курсы программирования для новичков и опытных разработчиков. Начните обучение бесплатно

  • 130 курсов, 2000+ часов теории
  • 1000 практических заданий в браузере
  • 360 000 студентов
Отправляя форму, вы принимаете «Соглашение об обработке персональных данных» и условия «Оферты», а также соглашаетесь с «Условиями использования»

Наши выпускники работают в компаниях:

Логотип компании Альфа Банк
Логотип компании Aviasales
Логотип компании Yandex
Логотип компании Tinkoff

Используйте Хекслет по-максимуму!

  • Задавайте вопросы по уроку
  • Проверяйте знания в квизах
  • Проходите практику прямо в браузере
  • Отслеживайте свой прогресс

Зарегистрируйтесь или войдите в свой аккаунт

Отправляя форму, вы принимаете «Соглашение об обработке персональных данных» и условия «Оферты», а также соглашаетесь с «Условиями использования»
Изображение Тото

Задавайте вопросы, если хотите обсудить теорию или упражнения. Команда поддержки Хекслета и опытные участники сообщества помогут найти ответы и решить задачу