суббота, 30 января 2021 г.

Модули в Golang: преобразование пакета в модуль

Когда команда go загружает пакет, используя путь к пакету, ей необходимо определить, какой модуль предоставляет пакет.

Команда go запускается с поиска в списке сборки модулей, пути которых являются префиксами пути к пакету. Например, если пакет example.com/a/b импортирован, а модуль example.com/a находится в списке сборки, команда go проверит, содержит ли example.com/a пакет в каталоге b. По крайней мере, один файл с расширением .go должен присутствовать в каталоге, чтобы он считался пакетом. Ограничения сборки для этой цели не применяются. Если только один модуль в списке сборки предоставляет пакет, этот модуль используется. Если пакет предоставляют два или более модуля, выдается сообщение об ошибке. Если ни один модуль не предоставляет пакет, команда go попытается найти новый модуль (если не используются флаги -mod=readonly или -mod=vendor, и в этом случае выдается сообщение об ошибке).

Когда команда go ищет новый модуль для пути к пакету, она проверяет переменную среды GOPROXY, которая представляет собой список URL-адресов прокси, разделенных запятыми, или ключевых слов direct или off. URL-адрес прокси-сервера указывает, что команда go должна связаться с прокси-сервером модуля с использованием протокола GOPROXY. direct указывает, что команда go должна взаимодействовать с системой контроля версий. off означает, что не следует пытаться установить связь. Переменные среды GOPRIVATE и GONOPROXY также можно использовать для управления этим поведением.

Для каждой записи в списке GOPROXY команда go запрашивает последнюю версию каждого пути к модулю, который может предоставить пакет (то есть каждого префикса пути к пакету). Для каждого успешно запрошенного пути к модулю команда go загрузит модуль последней версии и проверит, содержит ли модуль запрошенный пакет. Если один или несколько модулей содержат запрошенный пакет, используется модуль с самым длинным путем. Если один или несколько модулей найдены, но ни один из них не содержит запрошенный пакет, выдается сообщение об ошибке. Если модули не найдены, команда go пробует следующую запись в списке GOPROXY. Если записей не осталось, выдается сообщение об ошибке.

Например, предположим, что команда go ищет модуль, который предоставляет пакет golang.org/x/net/html, а для GOPROXY установлено значение https://corp.example.com,https://proxy.golang.org. Команда go может делать следующие запросы:

  • На https://corp.example.com/ (параллельно):
    • Запросить последнюю версию golang.org/x/net/html
    • Запросить последнюю версию golang.org/x/net
    • Запросить последнюю версию golang.org/x
    • Запрос последней версии golang.org
  • На https://proxy.golang.org/, если все запросы к https://corp.example.com/ завершились неудачно с 404 или 410:
    • Запросить последнюю версию golang.org/x/net/html
    • Запросить последнюю версию golang.org/x/net
    • Запросить последнюю версию golang.org/x
    • Запрос последней версии golang.org

После того, как подходящий модуль будет найден, команда go добавит новое требование с путем и версией нового модуля в файл go.mod основного модуля (main module). Это гарантирует, что при загрузке того же пакета в будущем тот же модуль будет использоваться с той же версией. Если разрешенный пакет не импортируется пакетом в основном модуле, новое требование будет иметь // indirect комментарий.


Читайте также:


пятница, 29 января 2021 г.

Модули в Golang: суффиксы основной версии

Начиная с основной версии 2, пути к модулям должны иметь суффикс основной версии, например /v2, который соответствует основной версии. Например, если у модуля есть путь example.com/mod в версии v1.0.0, он должен иметь путь example.com/mod/v2 в версии v2.0.0.

Суффиксы основной версии реализуют правило совместимости импорта:

Если старый и новый пакет имеют одинаковый путь импорта, новый пакет должен быть обратно совместим со старым пакетом.

По определению, пакеты в новой основной версии модуля не имеют обратной совместимости с соответствующими пакетами в предыдущей основной версии. Следовательно, начиная с версии 2, пакетам требуются новые пути импорта. Это достигается путем добавления суффикса основной версии к пути к модулю. Поскольку путь к модулю является префиксом пути импорта для каждого пакета в модуле, добавление суффикса основной версии к пути к модулю обеспечивает отдельный путь импорта для каждой несовместимой версии.

Суффиксы основной версии не допускаются в основных версиях v0 или v1. Нет необходимости изменять путь к модулю между v0 и v1, поскольку версии v0 нестабильны и не имеют гарантии совместимости. Кроме того, для большинства модулей v1 имеет обратную совместимость с последней версией v0; версия v1 действует как обязательство по совместимости, а не как указание на несовместимые изменения по сравнению с v0.

Как особый случай, пути модулей, начинающиеся с gopkg.in/, всегда должны иметь суффикс основной версии, даже в v0 и v1. Суффикс должен начинаться с точки, а не с косой черты (например, gopkg.in/yaml.v2).

Суффиксы основной версии позволяют сосуществовать нескольким основным версиям модуля в одной сборке. Это может быть необходимо из-за проблемы с diamond зависимостью. Обычно, если модуль требуется в двух разных версиях из-за транзитивных зависимостей, будет использоваться более высокая версия. Однако, если две версии несовместимы, ни одна из версий не удовлетворит всех клиентов. Поскольку несовместимые версии должны иметь разные основные номера версий, они также должны иметь разные пути к модулям из-за суффиксов основной версии. Это разрешает конфликт: модули с разными суффиксами обрабатываются как отдельные модули, а их пакеты - даже пакеты в одном подкаталоге относительно их корней модулей - различны.

Многие проекты Go выпускали версии v2 или выше без использования суффикса основной версии перед переходом на модули (возможно, до того, как модули были даже введены). Эти версии помечены тегом сборки +incompatible (например, v2.0.0+incompatible).


Читайте также:


понедельник, 25 января 2021 г.

Модули в Golang: псевдоверсии

Псевдоверсия - это специально отформатированная предварительная версия, которая кодирует информацию о конкретной ревизии в репозитории управления версиями. Например, v0.0.0-20191109021931-daa7c04131f5 - это псевдоверсия.

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

Каждая псевдоверсия состоит из трех частей:

  • Префикс базовой версии (vX.0.0 или vX.Y.Z-0), который является производным от тега семантической версии, предшествующего ревизии, или vX.0.0, если такой тег отсутствует.
  • Отметка времени (ггггммддччммсс), которая представляет собой время создания ревизии в формате UTC. В Git это время коммита, а не время автора.
  • Идентификатор ревизии (abcdefabcdef), который представляет собой 12-символьный префикс хэша коммита, или в Subversion, номер ревизии, дополненный нулями.

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

  • vX.0.0-yyyymmddhhmmss-abcdefabcdef используется, когда нет известной базовой версии. Как и все версии, основная версия X должна соответствовать суффиксу основной версии модуля.
  • vX.Y.Z-pre.0.yyyymmddhhmmss-abcdefabcdef используется, когда базовая версия является предварительной версией, например vX.Y.Z-pre.
  • vX.Y.(Z+1)-0.yyyymmddhhmmss-abcdefabcdef используется, когда базовая версия является версией релиза, например vX.Y.Z. Например, если базовая версия - v1.2.3, псевдо-версия может быть v1.2.4-0.20191109021931-daa7c04131f5.

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

Эти формы придают псевдоверсиям два полезных свойства:

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

Команда go выполняет несколько проверок, чтобы убедиться, что авторы модуля имеют контроль над тем, как псевдоверсии сравниваются с другими версиями, и что псевдоверсии относятся к ревизиям, которые фактически являются частью истории коммитов модуля.

  • Если указана базовая версия, должен быть соответствующий тег семантической версии, являющийся предком ревизии, описанной псевдоверсией. Это не позволяет разработчикам обойти выбор минимальной версии с помощью псевдоверсии, которая сравнивает более высокую, чем все версии с тегами, например v1.999.999-99999999999999-daa7c04131f5.
  • Отметка времени должна соответствовать отметке времени ревизии. Это не позволяет злоумышленникам переполнять прокси-серверы модулей неограниченным количеством идентичных псевдоверсий. Это также не позволяет потребителям модулей изменять относительный порядок версий.
  • Ревизия должна быть предком одной из ветвей или тегов репозитория модулей. Это не позволяет злоумышленникам ссылаться на неутвержденные изменения или пулл-запросы (pull requests).

Псевдоверсии никогда не нужно набирать вручную. Многие команды принимают хэш коммита или имя ветки и автоматически переводят его в псевдоверсию (или версию с тегами, если она доступна). Например:

go get -d example.com/mod@master
go list -m -json example.com/mod@abcd1234


Читайте также:


среда, 20 января 2021 г.

Модули в Golang: версии

Версия определяет неизменяемый снимок модуля, который может быть либо релизом, либо предварительным релизом (pre-release). Каждая версия начинается с буквы v, за которой следует семантическая версия.

Семантическая версия состоит из трех неотрицательных целых чисел (основная (major), дополнительная (minor) и патч-версия (patch) слева направо), разделенных точками. За версией патча может следовать необязательная строка предварительного выпуска, начинающаяся с дефиса. За строкой предварительного выпуска или патч-версии может следовать строка метаданных сборки, начинающаяся с плюса. Например, допустимые версии v0.0.0, v1.12.134, v8.0.5-pre и v2.0.9+meta.

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

  • Основная версия должна быть увеличена, а дополнительная версия и патч-версия должны быть установлены на ноль после внесения обратно несовместимых изменений в общедоступный интерфейс модуля или задокументированные функции, например, после удаления пакета.
  • Дополнительная версия должна быть увеличена, а версия исправления (патч-версия) должна быть установлена на ноль после изменения обратной совместимости, например, после добавления новой функции.
  • Версия патча должна быть увеличена после изменения, которое не влияет на общедоступный интерфейс модуля, например исправления ошибки или оптимизации.
  • Суффикс предварительного выпуска указывает, что версия является предварительным выпуском. Предварительные версии сортируются перед соответствующими версиями выпуска. Например, v1.2.3-pre предшествует v1.2.3.
  • Суффикс метаданных сборки игнорируется для сравнения версий. Теги с метаданными сборки игнорируются в репозиториях контроля версий, но метаданные сборки сохраняются в версиях, указанных в файлах go.mod. Суффикс +incompatible означает версию, выпущенную до перехода на модули версии 2 или более поздней.

Версия считается нестабильной, если ее основная версия - 0 или у нее есть pre-release суффикс. На нестабильные версии не распространяются требования совместимости. Например, версия v0.2.0 может быть несовместима с версией v0.1.0, а версия v1.5.0-beta может быть несовместима с версией v1.5.0.

Go может обращаться к модулям в системах контроля версий с помощью тегов, веток или ревизий, которые не соответствуют этим соглашениям. Однако в основном модуле команда go автоматически преобразует имена ревизий, которые не соответствуют этому стандарту, в канонические версии. Команда go также удалит суффиксы метаданных сборки (за исключением +incompatible) как часть этого процесса. Это может привести к созданию псевдоверсии, предварительной версии, которая кодирует идентификатор ревизии (например, хеш Git коммита) и временную метку из системы контроля версий. Например, команда go get -d golang.org/x/net@daa7c041 преобразует хеш коммита daa7c041 в псевдо-версию v0.0.0-20191109021931-daa7c04131f5. Канонические версии требуются вне основного модуля, и команда go сообщит об ошибке, если неканоническая версия, такая как master, появится в файле go.mod.


Читайте также:


суббота, 16 января 2021 г.

Модули в Golang: пути к модулям

Путь к модулю - это каноническое имя модуля, объявленное с помощью директивы module в файле go.mod модуля. Путь к модулю - это префикс для путей к пакетам внутри модуля.

Путь к модулю должен описывать как то, что модуль делает, так и где его найти. Обычно путь к модулю состоит из корневого пути репозитория, каталога внутри репозитория (обычно пустого) и суффикса основной версии (только для основной версии 2 или выше).

  • Корневой путь репозитория - это часть пути к модулю, соответствующая корневому каталогу репозитория системы контроля версий, в котором разрабатывается модуль. Большинство модулей определены в корневом каталоге своего репозитория, поэтому обычно это весь путь. Например, golang.org/x/net - это корневой путь репозитория для одноименного модуля.
  • Если модуль не определен в корневом каталоге репозитория, подкаталог модуля - это часть пути к модулю, которая называет каталог, не включая суффикс основной версии. Это также служит префиксом для тегов семантической версии. Например, модуль golang.org/x/tools/gopls находится в подкаталоге gopls репозитория с корневым путем golang.org/x/tools, поэтому в нем есть подкаталог модулей gopls.
  • Если модуль выпущен в основной версии 2 или выше, путь к модулю должен заканчиваться суффиксом основной версии, например /v2. Это может быть или не быть частью имени подкаталога. Например, модуль с путем golang.org/x/repo/sub/v2 может находиться в подкаталоге /sub или /sub/v2 репозитория golang.org/x/repo.

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


Читайте также:


среда, 13 января 2021 г.

Модули в Golang: модули, пакеты и версии

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

Модуль идентифицируется по пути к модулю, который объявлен в файле go.mod вместе с информацией о зависимостях модуля. Корневой каталог модуля - это каталог, содержащий файл go.mod. Главный модуль (main module) - это модуль, содержащий каталог, в котором вызывается команда go.

Каждый пакет в модуле представляет собой набор исходных файлов в одном каталоге, которые скомпилированы вместе. Путь к пакету - это путь к модулю, соединенный с подкаталогом, содержащим пакет (относительно корня модуля). Например, модуль "golang.org/x/net" содержит пакет в каталоге "html". Путь к этому пакету - "golang.org/x/net/html".


Читайте также:


вторник, 5 января 2021 г.

Разбор затруднений при установке Go: переменные окружения

При возникновении проблем с установкой Go для начала проверьте следующее:

  • GOROOT

    Это должно быть установлено только в том случае, если вы использовали двоичный дистрибутив, и он не установлен в расположение по умолчанию.

  • GOPATH

    Это должен быть каталог, в котором вы хотите разместить исходный код (и сторонние пакеты).

    Это также может быть установлено в виде списка абсолютных путей, разделенных : (или ; в Windows).

    Обратите внимание, что ~/some/path не является абсолютным и, вероятно, не будет работать так, как вы ожидаете (вместо этого попробуйте $HOME/some/path).

    GOPATH не должен быть установлен в или содержать GOROOT

  • GOBIN

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

  • GOOS, GOARCH, GOHOSTOS, GOHOSTARCH

    В обычных случаях устанавливать их не требуется.

В linux и darwin (Mac OS) убедитесь, что любая из указанных выше переменных действительно экспортируется. Когда вы запускаете env | grep GO, они должны быть перечислены. Вы также можете проверить свою среду с помощью команды go env. В bash это делается с помощью команды export GOPATH (если она уже установлена) или export GOPATH=/путь/к/gopath (аналогично для других переменных), обычно в вашем .bashrc или .bash_profile.

GOROOT vs GOPATH

Пакеты под GOROOT хранят свои исходные файлы в

$GOROOT/src/pkg/import/path/*.go

Обратите внимание, что это src/pkg; в GOPATH исходные файлы хранятся в

$GOPATH/src/import/path/*.go

Из-за этого несоответствия обычно не рекомендуется устанавливать для GOPATH значение GOROOT или содержать его; в его каталогах будет производиться автоматический поиск импортируемых файлов независимо от настройки GOPATH.


Читайте также:


воскресенье, 3 января 2021 г.

Go style guides: форматировать строки вне Printf

Если вы объявляете строки формата для функций в стиле Printf вне строкового литерала, сделайте их константными значениями.

Это помогает провести статический анализ строки формата.

Менее удачный вариант:

msg := "unexpected values %v, %v\n"
fmt.Printf(msg, 1, 2)

Более удачный вариант:

const msg = "unexpected values %v, %v\n"
fmt.Printf(msg, 1, 2)

Именование функций в стиле Printf

Когда вы объявляете функцию в стиле Printf, убедитесь, что go vet может ее обнаружить и проверить строку формата.

Это означает, что по возможности следует использовать предопределенные имена функций в стиле Printf. go vet проверит их по умолчанию. Смотрите Printf для получения дополнительной информации.

Если использование предопределенных имен невозможно, завершите выбранное имя буквой f: Wrapf, а не Wrap. go vet можно попросить проверить конкретные имена в стиле Printf, но они должны оканчиваться на f.

$ go vet -printfuncs=wrapf,statusf


Читайте также: