Показаны сообщения с ярлыком модули в Golang. Показать все сообщения
Показаны сообщения с ярлыком модули в Golang. Показать все сообщения

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

Нумерация версий модуля

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

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

В этом посте описывается, что означают номера версий модулей.

Смотрите также:

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

Если вы разрабатываете модули для использования другими, вы применяете номер версии при публикации модуля, отмечая (присваивая тег) модуль в его репозитории.

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

Далее описано, как части номера версии обозначают стабильность и обратную совместимость модуля.

    • Модуль в разработке
    • Автоматический номер псевдоверсии, v0.x.x
    • Сигнализирует о том, что модуль все еще находится в разработке и нестабилен. Этот релиз не дает никаких гарантий обратной совместимости или стабильности.
    • Основная (major) версия
    • v1.x.x
    • Сообщает об изменениях общедоступного API, несовместимых с предыдущими версиями. Этот релиз не дает никаких гарантий, что он будет обратно совместим с предыдущими основными версиями.
    • Минорная (minor) версия
    • vx.4.x
    • Сообщает об изменениях общедоступного API с обратной совместимостью. Этот релиз гарантирует обратную совместимость и стабильность.
    • Версия (patch) исправлений
    • vx.x.1
    • Сообщает об изменениях, которые не влияют на общедоступный API модуля или его зависимости. Этот релиз гарантирует обратную совместимость и стабильность.
    • Предварительная версия
    • vx.x.x-beta.2
    • Сигнализирует о том, что это предварительная веха, например, альфа или бета. Этот релиз не дает никаких гарантий стабильности.

В разработке

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

Номер версии может принимать одну из следующих форм:

  • Номер псевдоверсии
    v0.0.0-20170915032832-14c0d48ead0c
  • Номер v0
    v0.x.x

Номер псевдоверсии

Если модуль не был помечен (не имеет тега) в своем репозитории, инструменты Go сгенерируют номер псевдоверсии для использования в файле go.mod кода, который вызывает функции в модуле.

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

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

Номер псевдоверсии состоит из трех частей, разделенных тире, как показано в следующей форме:

Синтаксис

baseVersionPrefix-timestamp-revisionIdentifier

Части

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

Номер v0

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

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

Предварительная версия

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

Пример

vx.x.x-beta.2

Разработчик модуля может использовать идентификатор предварительной версии с любой комбинацией major.minor.patch, добавив дефис и идентификатор предварительной версии.

Минорная версия

Сообщает об обратно совместимых изменениях в общедоступном API модуля. Этот релиз гарантирует обратную совместимость и стабильность.

Пример

vx.4.x

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

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

Версия патча

Сообщает об изменениях, которые не влияют на общедоступный API модуля или его зависимости. Этот релиз гарантирует обратную совместимость и стабильность.

Пример

vx.x.1

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

Основная версия

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

Пример

v1.x.x

Номер версии v1 или выше означает, что модуль стабилен для использования (за исключением его предварительных версий).

Обратите внимание: поскольку версия 0 не дает никаких гарантий стабильности или обратной совместимости, разработчик, обновляющий модуль с v0 до v1, несет ответственность за адаптацию к изменениям, нарушающим обратную совместимость.

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

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

module example.com/mymodule/v2 v2.0.0

Обновление основной версии делает этот модуль новым с историей, отдельной от предыдущей версии модуля.


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


пятница, 23 апреля 2021 г.

Публикация модуля в Golang

Если вы хотите сделать модуль доступным для других разработчиков, вы публикуете его, чтобы он стал видимым для инструментов Go. После публикации модуля разработчики, импортирующие его пакеты, смогут разрешить зависимость от модуля, выполнив такие команды, как go get.

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

Шаги публикации

Чтобы опубликовать модуль, выполните следующие действия.

1. Откройте командную строку и перейдите в корневой каталог вашего модуля в локальном репозитории.

2. Запустите go mod tidy, которая удалит все зависимости, которые модуль мог накопить, которые больше не нужны.

$ go mod tidy

3. Запустите go test ./... последний раз, чтобы убедиться, что все работает.

Это запускает модульные (юнит) тесты, которые вы написали с помощью Go фреймворка testing.

$ go test ./...
ok      example.com/mymodule       0.015s

4. Отметьте проект новым номером версии с помощью команды git tag.

В качестве номера версии используйте номер, который сигнализирует пользователям о характере изменений в этом выпуске.

$ git commit -m "mymodule: changes for v0.1.0"
$ git tag v0.1.0

5. Отправьте новый тег в исходный репозиторий.

$ git push origin v0.1.0

6. Сделайте модуль доступным, выполнив команду go list, чтобы запросить Go обновить его индекс модулей с информацией о модуле, который вы публикуете.

Перед командой укажите инструкцию для установки переменной среды GOPROXY на прокси-сервер Go. Это гарантирует, что ваш запрос достигнет прокси.

$ GOPROXY=proxy.golang.org go list -m example.com/mymodule@v0.1.0

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

$ go get example.com/mymodule@v0.1.0


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


четверг, 22 апреля 2021 г.

Разработка обновления основной версии

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

Примечание. Каждый тип релиза - основной, минорный, патч (релиз исправлений) или предварительный релиз - имеет разное значение для пользователей модуля. Эти пользователи полагаются на эти различия, чтобы понять уровень риска, который релиз представляет для их собственного кода. Другими словами, при подготовке релиза убедитесь, что номер его версии точно отражает характер изменений с момента предыдущего релиза.

Рекомендации по обновлению основной версии

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

  • Сообщите своим пользователям, что выпуск новой основной версии означает для вас поддержку предыдущих основных версий.
  • Предыдущие версии устарели? Поддерживаются как раньше? Будете ли вы поддерживать предыдущие версии, в том числе с исправлениями ошибок?
  • Будьте готовы взять на себя обслуживание двух версий: старой и новой. Например, если вы исправляете ошибки в одной, вы часто будете переносить эти исправления в другой.
  • Помните, что новая основная версия - это новый модуль с точки зрения управления зависимостями. Вашим пользователям необходимо будет выполнить обновление, чтобы использовать новый модуль после выпуска, а не просто выполнить обновление.
    Это потому, что в новой основной версии путь к модулю отличается от пути к предыдущей основной версии. Например, для модуля, чей путь к модулю - example.com/mymodule, версия v2 будет иметь путь к модулю example.com/mymodule/v2.
  • При разработке новой основной версии вы также должны обновить пути импорта везде, где код импортирует пакеты из нового модуля. Пользователи вашего модуля также должны обновить свои пути импорта, если они хотят перейти на новую основную версию.

Разветвление для основного релиза

Самый простой подход к работе с исходным кодом при подготовке к разработке новой основной версии - это разветвление репозитория на последней версии предыдущей основной версии.

Например, в командной строке вы можете перейти в корневой каталог вашего модуля, а затем создать там новую ветку v2.

$ cd mymodule
$ git checkout -b v2
Switched to a new branch "v2"

Схема, показывающая репозиторий, ответвленный от master к v2:

После разветвления исходного кода вам необходимо внести следующие изменения в исходные файлы для вашей новой версии:

  • В файле go.mod новой версии добавьте номер новой основной версии к пути к модулю, как в следующем примере:
    • Существующая версия: example.com/mymodule
    • Новая версия: example.com/mymodule/v2
  • В коде Go обновите каждый импортированный путь к пакету, в который вы импортируете пакет из модуля, добавив номер основной версии к части пути к модулю.
    • Старый оператор импорта: import "example.com/mymodule/package1"
    • Новый оператор импорта: import "example.com/mymodule/v2/package1"

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


среда, 21 апреля 2021 г.

Управление исходным кодом модуля

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

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

Go поддерживает следующие репозитории для публикации модулей: Git, Subversion, Mercurial, Bazaar и Fossil.

Как инструменты Go находят ваш опубликованный модуль

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

Когда разработчик использует команду go get для получения исходного кода для пакетов, импортируемых его кодом, она выполняет следующие действия:

  1. Из операторов импорта в исходном коде Go команда go get определяет путь к модулю в пути к пакету.
  2. Используя URL-адрес, полученный из пути к модулю, команда находит источник модуля на прокси-сервере модуля или непосредственно в его репозитории.
  3. Находит источник для версии модуля для загрузки, сопоставляя номер версии модуля с тегом репозитория, чтобы обнаружить код в репозитории. Если номер версии для использования еще не известен, переходит по ссылке и находит последнюю версию релиза.
  4. Получает исходный код модуля и загружает его в локальный кеш модуля разработчика.

Организация кода в репозитории

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

На следующей диаграмме показана исходная иерархия для простого модуля с двумя пакетами.

Ваш модуль должен включать следующие файлы:

  • LICENSE
    Лицензия для модуля.
  • go.mod
    Описывает модуль, включая его путь к модулю (фактически, его имя) и его зависимости.
    Путь к модулю будет указан в директиве модуля, например:

    module example.com/mymodule
    

    Хотя вы можете редактировать этот файл, большая часть его поддерживается командами go.
  • go.sum
    Содержит криптографические хэши, которые представляют зависимости модуля. Инструменты Go используют эти хэши для аутентификации загруженных модулей, пытаясь подтвердить подлинность загруженного модуля. Если это подтверждение не удалось, Go отобразит ошибку безопасности.
    Файл будет пустым или будет отсутствовать, если нет зависимостей. Вы не должны редактировать этот файл, кроме как с помощью команды go mod tidy, которая удаляет ненужные записи.
  • Каталоги пакетов и исходные файлы .go.
    Каталоги и файлы .go, которые составляют пакеты и исходные коды Go в модуле.

Из командной строки вы можете создать пустой репозиторий, добавить файлы, которые будут частью вашего первоначального коммита, и выполнить коммит с сообщением. Вот пример использования git:

$ git init
$ git add --all
$ git commit -m "mycode: initial commit"
$ git push

Выбор объема репозитория

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

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

Поддержка одного модуля на репозиторий

Вы можете поддерживать репозиторий, в котором есть исходный код одного модуля. В этой модели вы помещаете файл go.mod в корень репозитория с подкаталогами пакетов, содержащими исходный код Go.

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

Диаграмма, показывающая исходный код отдельного модуля в его репозитории

Поддержка нескольких модулей в одном репозитории

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

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

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

Например, для модуля example.com/mymodules/module1 ниже у вас будет следующее для версии v1.2.3:

  • Путь к модулю: example.com/mymodules/module1
  • Тег версии: module1/v1.2.3
  • Путь к пакету, импортированный пользователем: example.com/mymodules/module1/package1
  • Путь к модулю, указанный в директиве require пользователя: example.com/mymodules/module1 module1/v1.2.3

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


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

Выпуск модуля и рабочий процесс управления версиями

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

Обзор разработки модулей смотрите в посте Разработка и публикация модулей.

Если вы просто хотите использовать внешние пакеты в своем коде, обязательно ознакомьтесь с постом Управление зависимостями.

С каждой новой версией вы сигнализируете об изменениях в вашем модуле его номером версии.

Общие шаги рабочего процесса

Следующая последовательность иллюстрирует шаги рабочего процесса выпуска и управления версиями для примера нового модуля.

  • Начните модуль и организуйте его исходный код, чтобы разработчикам было проще его использовать, а вам - поддерживать.
    Если вы новичок в разработке модулей, ознакомьтесь с Созданием модуля Go.
  • Настройте для написания кода локального клиента, который вызывает функции в неопубликованном модуле.
    Перед публикацией модуля он недоступен для обычного рабочего процесса управления зависимостями с использованием таких команд, как go get. Хороший способ протестировать код вашего модуля на этом этапе - это попробовать его, пока он находится в каталоге, локальном для вашего вызывающего кода.
  • Когда код модуля будет готов для других разработчиков, начните публиковать предварительные версии v0, такие как альфа- и бета-версии.
  • Выпустите версию v0, стабильность которой не гарантируется, но пользователи могут попробовать ее.
  • После публикации вашей версии v0 вы можете (и следует) продолжать выпускать ее новые версии.
    Эти новые версии могут включать исправления ошибок (релизы патчей), дополнения к общедоступному API модуля (минорные релизы) и даже критические изменения. Поскольку релиз v0 не дает никаких гарантий стабильности или обратной совместимости, вы можете вносить критические изменения в его версии.
  • Когда вы готовите стабильную версию, готовую к релизу, вы публикуете предварительные релизы как альфа- и бета-версии.
  • Выпустите v1 как первый стабильный релиз.
    Это первый релиз, в котором говорится о стабильности модуля.
  • В версии v1 продолжайте исправлять ошибки и, при необходимости, вносить дополнения в общедоступный API модуля.
  • Если этого невозможно избежать, опубликуйте ломающие изменения в новой основной версии.

Обновление основной версии - например, с v1.x.x до v2.x.x - может сильно помешать пользователям вашего модуля. Это должно быть последнее средство.

Кодирование неопубликованного модуля

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

Вы можете ссылаться на модуль локально из файла go.mod клиентского модуля, используя директиву replace в файле go.mod клиентского модуля.

Публикация предварительных версий

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

К номерам предварительной версии добавляется идентификатор предварительной версии.

Вот два примера:

  • v0.2.1-beta.1
  • v1.2.3-alpha

Делая предварительную версию доступной, имейте в виду, что разработчики, использующие предварительную версию, должны будут явно указать ее по версии с помощью команды go get. Это потому, что по умолчанию команда go отдает предпочтение релизным версиям перед предварительными версиями при поиске запрашиваемого модуля. Поэтому разработчики должны получить предварительную версию, явно указав ее, как в следующем примере:

go get example.com/theirmodule@v1.2.3-alpha

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

Публикация первой (нестабильной) версии

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

Нестабильные релизы - это те, номера версий которых находятся в диапазоне v0.x.x. Версия v0 не дает никаких гарантий стабильности или обратной совместимости. Но это дает вам возможность получить обратную связь и усовершенствовать свой API, прежде чем брать на себя обязательства по стабильности с v1 и более поздними версиями.

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

Вот пример номера версии:

v0.1.3

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

Публикация первой стабильной версии

Ваш первый стабильный релиз будет иметь номер версии v1.x.x. Первый стабильный релиз следует за предварительным релизом и релизом v0, благодаря которым вы получили отзывы, исправили ошибки и стабилизировали модуль для пользователей.

С выпуском v1 вы берете на себя следующие обязательства перед разработчиками, использующими ваш модуль:

  • Они могут перейти на последующие второстепенные (минорные) релизы и релизы исправлений (патч) основной версии, не нарушая собственный код.
  • Вы не будете вносить дальнейшие изменения в общедоступный API модуля, включая его функции и сигнатуры методов, которые нарушают обратную совместимость.
  • Вы не будете удалять экспортированные типы, так как это нарушит обратную совместимость.
  • Будущие изменения в вашем API (например, добавление нового поля в структуру) будут обратно совместимы и будут включены в новый минорный релиз.
  • Исправления ошибок (например, исправления безопасности) будут включены в релиз исправлений или как часть минорного релиза.

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

Вот пример номера стабильной версии:

v1.0.0

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

Публикация исправлений ошибок

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

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

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

Релиз исправлений увеличивает часть исправления номера версии модуля.

В следующем примере v1.0.1 - это релиз исправлений.

Старая версия: v1.0.0

Новая версия: v1.0.1

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

Публикация не-ломающих изменений API

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

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

Минорный релиз увеличивает минорную часть номера версии модуля.

В следующем примере v1.1.0 является минорным релизом.

Старая версия: v1.0.1

Новая версия: v1.1.0

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

Публикация ломающих изменений API

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

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

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

Если для публикации других типов версий требуется по существу пометить код модуля номером версии, публикация обновления основной версии требует дополнительных шагов.

  • Перед началом разработки новой основной версии создайте в своем репозитории место для исходного кода новой версии.
    Один из способов сделать это - создать новую ветку в вашем репозитории, специально предназначенную для новой основной версии и ее последующих минорных версий и версий исправлений.
  • В файле модуля go.mod измените путь к модулю, чтобы добавить новый основной номер версии, как в следующем примере:

    example.com/mymodule/v2
    

    Учитывая, что путь к модулю является идентификатором модуля, это изменение фактически создает новый модуль. Он также изменяет путь к пакету, гарантируя, что разработчики не будут непреднамеренно импортировать версию, нарушающую их код. Вместо этого те, кто хочет обновить, будут явно заменять вхождения старого пути новым.
  • В своем коде измените все пути к пакетам, в которые вы импортируете пакеты в обновляемом модуле, включая пакеты в модуле, который вы обновляете. Вам нужно сделать это, потому что вы изменили путь к модулю.
  • Как и в случае с любым новым выпуском, перед публикацией официального выпуска вам следует публиковать предварительные версии, чтобы получать отзывы и отчеты об ошибках.
  • Опубликуйте новую основную версию, пометив код модуля в своем репозитории, увеличив номер основной версии в теге - например, с v1.5.2 до v2.0.0.

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


четверг, 15 апреля 2021 г.

Разработка и публикация модулей в Golang

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

Для поддержки разработки, публикации и использования модулей вы используете:

  • Рабочий процесс, с помощью которого вы разрабатываете и публикуете модули, со временем обновляя их новыми версиями.
  • Приемы проектирования, которые помогают пользователям модуля понять его и стабильно обновлять до новых версий.
  • Децентрализованная система для публикации модулей и получения их кода. Вы делаете свой модуль доступным для использования другими разработчиками из вашего собственного репозитория и публикуете с номером версии.
  • Система поиска пакетов и браузер документации (pkg.go.dev), в котором разработчики могут найти ваш модуль.
  • Соглашение о нумерации версий модуля, чтобы сообщить разработчикам, использующим ваш модуль, ожидания стабильности и обратной совместимости.
  • Инструменты Go, которые упрощают другим разработчикам управление зависимостями, включая получение исходного кода вашего модуля, обновление и т. д.

Смотрите также

Рабочий процесс разработки и публикации модулей

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

  1. Разработайте и запрограммируйте пакеты, которые будет включать модуль.
  2. Зафиксируйте код в своем репозитории, используя соглашения, которые гарантируют, что он будет доступен другим через инструменты Go.
  3. Опубликуйте модуль, чтобы разработчики могли его обнаружить.
  4. Со временем обновите модуль, добавив в него версии, использующие соглашение о нумерации версий, которое сигнализирует о стабильности каждой версии и обратной совместимости.

Дизайн и развитие

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

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

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

Децентрализованная публикация

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

После импорта вашего пакета в свой код разработчики используют инструменты Go (включая команду go get), чтобы загрузить код вашего модуля для компиляции. Для поддержки этой модели вы следуете соглашениям, которые позволяют инструментам Go (от имени другого разработчика) извлекать исходный код вашего модуля из вашего репозитория. Например, инструменты Go используют указанный вами путь к модулю вместе с номером версии модуля, который вы используете для маркировки модуля для выпуска, чтобы найти и загрузить модуль для его пользователей.

Обнаружение пакетов

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

Чтобы начать использовать модуль, разработчик импортирует пакеты из модуля, затем запускает команду go get, чтобы загрузить исходный код для компиляции.

Дополнительные сведения о том, как разработчики находят и используют модули в посте Управление зависимостями.

Управление версиями

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


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


воскресенье, 11 апреля 2021 г.

Создание модуля в Golang: скомпилируйте и установите приложение

В этом посте вы изучите пару новых команд go. Хотя команда go run является полезным ярлыком для компиляции и запуска программы, когда вы часто вносите изменения, она не создает двоичный исполняемый файл.

В этом разделе представлены две дополнительные команды для сборки кода:

  • Команда go build компилирует пакеты вместе с их зависимостями, но не устанавливает результаты.
  • Команда go install компилирует и устанавливает пакеты.

Примечание. Этот раздел является частью руководства, состоящего из нескольких частей, который начинается с создания модуля Go.

1. Из командной строки в каталоге hello запустите команду go build, чтобы скомпилировать код в исполняемый файл.

$ go build

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

Обратите внимание, что ваш результат может отличаться в зависимости от того, изменили ли вы код greetings.go после его тестирования.

В Linux или Mac:

$ ./hello
map[Darrin:Great to see you, Darrin! Gladys:Hail, Gladys! Well met! Samantha:Hail, Samantha! Well met!]

В Windows:

$ hello.exe
map[Darrin:Great to see you, Darrin! Gladys:Hail, Gladys! Well met! Samantha:Hail, Samantha! Well met!]

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

Далее вы установите исполняемый файл, чтобы его можно было запустить, не указывая путь к нему.

3. Найдите путь установки Go, по которому команда go установит текущий пакет.

Вы можете узнать путь установки, выполнив команду go list, как в следующем примере:

$ go list -f '{{.Target}}'

Например, в выводе команды может быть указано /home/gopher/bin/hello, что означает, что двоичные файлы устанавливаются в /home/gopher/bin. Этот установочный каталог понадобится вам на следующем шаге.

4. Добавьте установочный каталог Go в путь к системной оболочке.

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

В Linux или Mac выполните следующую команду (действует только в текущей сессии пользователя, для постоянного изменения следует использовать файл .bashrc):

$ export PATH=$PATH:/путь/к/вашему/установочному/каталогу

В Windows выполните следующую команду:

$ set PATH=%PATH%;C:\путь\к\вашему\установочному\каталогу

В качестве альтернативы, если у вас уже есть каталог $HOME/bin в пути к оболочке и вы хотите установить туда свои программы Go, вы можете изменить цель установки, установив переменную GOBIN с помощью команды go env:

$ go env -w GOBIN=/путь/к/вашему/bin

или же

$ go env -w GOBIN=C:\путь\к\вашему\bin

5. После обновления пути к оболочке (shell path) запустите команду go install, чтобы скомпилировать и установить пакет.

$ go install

6. Запустите приложение, просто набрав его имя. Чтобы сделать это интересным, откройте новую командную строку и запустите исполняемый файл hello с именем в другом каталоге.

$ hello
map[Darrin:Hail, Darrin! Well met! Gladys:Great to see you, Gladys! Samantha:Hail, Samantha! Well met!]

На этом руководство по созданию модуля Go завершено.


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


Создание модуля в Golang: добавить тест

Теперь, когда вы привели свой код в стабильное состояние, добавьте тест. Тестирование кода во время разработки может выявить ошибки, которые обнаруживаются при внесении вами изменений. В этом посте вы добавите тест для функции Hello.

Примечание. Этот раздел является частью руководства, состоящего из нескольких частей, которое начинается с создания модуля Go.

Встроенная поддержка модульного тестирования Go упрощает тестирование на ходу. В частности, используя соглашения об именах, Go пакет testing и команду go test, вы можете быстро писать и выполнять тесты.

1. В каталоге greetings создайте файл с именем greetings_test.go.

Завершение имени файла _test.go сообщает команде go test, что этот файл содержит тестовые функции.

2. В greetings_test.go вставьте следующий код и сохраните файл.

package greetings

import (
    "testing"
    "regexp"
)

// TestHelloName вызывает greetings.Hello с именем, проверка
// для допустимого возвращаемого значения.
func TestHelloName(t *testing.T) {
    name := "Gladys"
    want := regexp.MustCompile(`\b`+name+`\b`)
    msg, err := Hello("Gladys")
    if !want.MatchString(msg) || err != nil {
        t.Fatalf(`Hello("Gladys") = %q, %v, want match for %#q, nil`, msg, err, want)
    }
}

// TestHelloEmpty вызывает greetings.Hello с пустой строкой,
// проверка на наличие ошибки.
func TestHelloEmpty(t *testing.T) {
    msg, err := Hello("")
    if msg != "" || err == nil {
        t.Fatalf(`Hello("") = %q, %v, want "", error`, msg, err)
    }
}

В этом коде вы:

  • Реализуете тестовые функции в том же пакете, что и тестируемый код.
  • Создаете две тестовые функции для проверки функции greetings.Hello. Имена тестовых функций имеют вид TestName, где Name что-то говорит о конкретном тесте. Кроме того, тестовые функции принимают в качестве параметра указатель на тип testing.T пакета testing. Вы используете методы этого параметра для создания отчетов и ведения журнала из вашего теста.
  • Реализуете два теста:
    TestHelloName вызывает функцию Hello, передавая значение имени, с которым функция должна иметь возможность возвращать допустимое ответное сообщение. Если вызов возвращает сообщение об ошибке или неожиданное ответное сообщение (которое не включает имя, которое вы передали), вы используете метод Fatalf параметра t, чтобы вывести сообщение на консоль и завершить выполнение.
    TestHelloEmpty вызывает функцию Hello с пустой строкой. Этот тест разработан, чтобы подтвердить, что ваша обработка ошибок работает. Если вызов возвращает непустую строку или нет ошибки, вы используете метод Fatalf параметра t для вывода сообщения на консоль и завершения выполнения.

3. В командной строке в каталоге greetings запустите команду go test, чтобы выполнить тест.

Команда go test выполняет тестовые функции (имена которых начинаются с Test) в тестовых файлах (имена которых заканчиваются на _test.go). Вы можете добавить флаг -v, чтобы получить подробный вывод, в котором перечислены все тесты и их результаты.

Тесты должны пройти.

$ go test
PASS
ok      example.com/greetings   0.364s

$ go test -v
=== RUN   TestHelloName
--- PASS: TestHelloName (0.00s)
=== RUN   TestHelloEmpty
--- PASS: TestHelloEmpty (0.00s)
PASS
ok      example.com/greetings   0.372s

4. Сломайте функцию greetings.Hello, чтобы просмотреть неудачный тест.

Тестовая функция TestHelloName проверяет возвращаемое значение для имени, которое вы указали в качестве параметра функции Hello. Чтобы просмотреть неудачный результат теста, измените функцию greetings.Hello так, чтобы она больше не включала имя.

В greetings/greetings.go вставьте следующий код вместо функции Hello. Обратите внимание, что выделенные строки изменяют значение, возвращаемое функцией, как если бы аргумент имени был случайно удален.

// Hello возвращает приветствие для указанного человека.
func Hello(name string) (string, error) {
    // Если имя не было указано, возвращаем ошибку с сообщением.
    if name == "" {
        return name, errors.New("empty name")
    }
    // Создаем сообщение в произвольном формате.
    // message := fmt.Sprintf(randomFormat(), name)
    message := fmt.Sprint(randomFormat())
    return message, nil
}

5 В командной строке в каталоге приветствия запустите go test, чтобы выполнить тест.

На этот раз запустите go test без флага -v. Вывод будет включать результаты только тех тестов, которые не прошли проверку, что может быть полезно, когда у вас много тестов. Тест TestHelloName должен завершиться ошибкой - TestHelloEmpty все еще проходит.

$ go test
--- FAIL: TestHelloName (0.00s)
    greetings_test.go:15: Hello("Gladys") = "Hail, %v! Well met!", <nil>, want match for `\bGladys\b`, nil
FAIL
exit status 1
FAIL    example.com/greetings   0.182s

В следующем посте вы увидите, как скомпилировать и установить код для его локального запуска.


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


суббота, 10 апреля 2021 г.

Создание модуля в Golang: ответные приветствия для нескольких человек

В последних изменениях, которые вы внесете в код своего модуля, вы добавите поддержку получения приветствия для нескольких человек в одном запросе. Другими словами, вы обрабатываете ввод с несколькими значениями, а затем объединяете значения в этом вводе с выводом с несколькими значениями. Для этого вам нужно передать набор имен функции, которая может возвращать приветствие для каждого из них.

Примечание. Этот раздел является частью руководства, состоящего из нескольких частей, которое начинается с создания модуля Go.

Но есть загвоздка. Изменение параметра функции Hello с одного имени на набор имен приведет к изменению сигнатуры функции. Если вы уже опубликовали модуль example.com/greetings, а пользователи уже написали код, вызывающий Hello, это изменение нарушит их программы.

В этой ситуации лучше написать новую функцию с другим именем. Новая функция примет несколько параметров. Это сохраняет старую функцию для обратной совместимости.

1. В greetings/greetings.go измените свой код так, чтобы он выглядел следующим образом.

package greetings

import (
    "errors"
    "fmt"
    "math/rand"
    "time"
)

// Hello возвращает приветствие для указанного человека.
func Hello(name string) (string, error) {
    // Если имя не было указано, возвращаем ошибку с сообщением.
    if name == "" {
        return name, errors.New("empty name")
    }
    // Создаем сообщение в произвольном формате.
    message := fmt.Sprintf(randomFormat(), name)
    return message, nil
}

// Hellos возвращает карту, 
// которая связывает каждого из названных людей
// с приветственным сообщением.
func Hellos(names []string) (map[string]string, error) {
    // Карта для связывания имен с сообщениями.
    messages := make(map[string]string)
    // Перебираем полученный срез имен, вызываем
    // функция Hello для получения сообщения для каждого имени.
    for _, name := range names {
        message, err := Hello(name)
        if err != nil {
            return nil, err
        }
        // В карте свяжите полученное сообщение с
        // именем.
        messages[name] = message
    }
    return messages, nil
}

// Init устанавливает начальные значения для переменных, 
// используемых в функции.
func init() {
    rand.Seed(time.Now().UnixNano())
}

// randomFormat возвращает одно 
// из набора приветственных сообщений. 
// Возвращаемое сообщение выбирается случайным образом.
func randomFormat() string {
    // Срез форматов сообщений.
    formats := []string{
        "Hi, %v. Welcome!",
        "Great to see you, %v!",
        "Hail, %v! Well met!",
    }

    // Возвращаем один из случайно выбранных форматов сообщений.
    return formats[rand.Intn(len(formats))]
}

В этом коде вы:

  • Добавляете функцию Hellos, параметр которой представляет собой срез имен, а не одно имя. Кроме того, вы меняете один из возвращаемых им типов со строки на карту, чтобы вы могли возвращать имена, сопоставленные с приветственными сообщениями.
  • Пусть новая функция Hellos вызовет существующую функцию Hello. Это помогает уменьшить дублирование, оставив при этом обе функции на своих местах.
  • Создаете карту сообщений, чтобы связать каждое из полученных имен (как ключ) со сгенерированным сообщением (как значение). В Go вы инициализируете карту со следующим синтаксисом: make(map[key-type]value-type). У вас есть функция Hellos, возвращающая эту карту вызывающей стороне.
  • Пройдете в цикле по именам, полученным вашей функцией, проверяя, что каждое из них имеет непустое значение, затем свяжете с каждым сообщение. В этом цикле for range возвращает два значения: индекс текущего элемента в цикле и копию значения элемента. Вам не нужен индекс, поэтому вы используете пустой идентификатор Go (подчеркивание), чтобы игнорировать его.

2. В коде вызова hello/hello.go передайте срез имен, а затем распечатайте содержимое карты имен/сообщений, которую вы получите.

В hello.go измените свой код так, чтобы он выглядел следующим образом.

package main

import (
    "fmt"
    "log"

    "example.com/greetings"
)

func main() {
    // Устанавливаем свойства предопределенного Logger,
    // включая префикс записи журнала и флаг отключения печати
    // времени, исходного файла и номера строки.
    log.SetPrefix("greetings: ")
    log.SetFlags(0)

    // Срез имен.
    names := []string{"Gladys", "Samantha", "Darrin"}

    // Запрашиваем приветственные сообщения для имен.
    messages, err := greetings.Hellos(names)
    if err != nil {
        log.Fatal(err)
    }
    // Если ошибок не было, распечатываем возвращенную карту
    // сообщения на консоль.
    fmt.Println(messages)
}

С этими изменениями вы:

  • Создаете переменную имен как тип среза, содержащий три имени.
  • Передаете переменную names в качестве аргумента функции Hellos.

3. В командной строке перейдите в каталог, содержащий hello/hello.go, затем используйте команду go run, чтобы убедиться, что код работает.

Вывод должен быть строковым представлением карты, связывающей имена с сообщениями, примерно следующего вида:

$ go run .
map[Darrin:Hail, Darrin! Well met! Gladys:Hi, Gladys. Welcome! Samantha:Hail, Samantha! Well met!]

В этом посте представлены карты для представления пар имя/значение. Он также представил идею сохранения обратной совместимости путем реализации новой функции для новых или измененных функций в модуле.

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


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


Создание модуля в Golang: возврат случайного приветствия

В этом посте вы измените свой код так, чтобы вместо того, чтобы каждый раз возвращать одно приветствие, он возвращал одно из нескольких предопределенных приветственных сообщений.

Примечание. Этот раздел является частью руководства, состоящего из нескольких частей, которое начинается с создания модуля Go.

Для этого вы воспользуетесь срезом Go. Срез похож на массив, за исключением того, что его размер изменяется динамически при добавлении и удалении элементов. Срез - один из самых полезных типов Go.

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

1. В greetings/greetings.go измените свой код так, чтобы он выглядел следующим образом.

package greetings

import (
    "errors"
    "fmt"
    "math/rand"
    "time"
)

// Hello возвращает приветствие для указанного человека.
func Hello(name string) (string, error) {
    // Если имя не было указано, возвращаем ошибку с сообщением.
    if name == "" {
        return name, errors.New("empty name")
    }
    // Создаем сообщение в произвольном формате.
    message := fmt.Sprintf(randomFormat(), name)
    return message, nil
}

// init устанавливает начальные значения для переменных, 
// используемых в функции.
func init() {
    rand.Seed(time.Now().UnixNano())
}

// randomFormat возвращает 
// одно из набора приветственных сообщений. 
// Возвращаемое сообщение выбирается случайным образом.
func randomFormat() string {
    // Срез форматов сообщений.
    formats := []string{
        "Hi, %v. Welcome!",
        "Great to see you, %v!",
        "Hail, %v! Well met!",
    }

    // Возвращаем случайно выбранный формат сообщения,
    // указав случайный индекс для среза форматов.
    return formats[rand.Intn(len(formats))]
}

В этом коде вы:

  • Добавите функцию randomFormat, которая возвращает произвольно выбранный формат для приветственного сообщения. Обратите внимание, что randomFormat начинается со строчной буквы, что делает ее доступной только для кода в собственном пакете (другими словами, она не экспортируется).
  • В randomFormat объявите срез форматов с тремя форматами сообщений. При объявлении среза вы опускаете его размер в скобках, например: []string. Это сообщает Go, что размер массива, лежащего в основе среза, можно динамически изменять.
  • Используете пакет math/rand, чтобы сгенерировать случайное число для выбора элемента из среза.
  • Добавите функцию init для заполнения пакета rand текущим временем. Go автоматически выполняет функции init при запуске программы после инициализации глобальных переменных.
  • В Hello вызовете функцию randomFormat, чтобы получить формат возвращаемого сообщения, а затем используете формат и значение имени вместе для создания сообщения.
  • Вернете сообщение (или ошибку), как и раньше.

2. В hello/hello.go измените свой код так, чтобы он выглядел следующим образом.

Вы просто добавляете имя Gladys (или другое имя, если хотите) в качестве аргумента при вызове функции Hello в hello.go.

package main

import (
    "fmt"
    "log"

    "example.com/greetings"
)

func main() {
    // Устанавливаем свойства предопределенного Logger, включая
    // префикс записи журнала и флаг отключения печати
    // время, исходный файл и номер строки.
    log.SetPrefix("greetings: ")
    log.SetFlags(0)

    // Запрос приветственного сообщения.
    message, err := greetings.Hello("Gladys")
    // Если вернулась ошибка, выводим ее в консоль и
    // выходим из программы.
    if err != nil {
        log.Fatal(err)
    }

    // Если ошибок не было, распечатываем возвращенное сообщение
    // в консоль.
    fmt.Println(message)
}

3. В командной строке в каталоге hello запустите hello.go, чтобы убедиться, что код работает. Запустите его несколько раз, заметив, что приветствие изменится.

$ go run .
Great to see you, Gladys!

$ go run .
Hi, Gladys. Welcome!

$ go run .
Hail, Gladys! Well met!

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


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


четверг, 8 апреля 2021 г.

Создание модуля в Golang: возврат и обработка ошибок

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

Примечание. Этот раздел является частью руководства, состоящего из нескольких частей, который начинается с создания модуля Go.

В greetings/greetings.go добавьте код, выделенный ниже.

Нет смысла отправлять приветствие в ответ, если вы не знаете, кого приветствовать. Вернуть вызывающему абоненту сообщение об ошибке, если имя пусто. Скопируйте следующий код в greetings.go и сохраните файл.

package greetings

import (
    "errors"
    "fmt"
)

// Hello возвращает приветствие для указанного человека.
func Hello(name string) (string, error) {
    // Если имя не было указано, возвращаем ошибку с сообщением.
    if name == "" {
        return "", errors.New("empty name")
    }

    // Если имя было получено, возвращаем значение,
    // включающее имя в приветственном сообщении.
    message := fmt.Sprintf("Hi, %v. Welcome!", name)
    return message, nil
}

В этом коде вы:

  • Измените функцию так, чтобы она возвращала два значения: строку и ошибку. Ваш абонент проверит второе значение, чтобы увидеть, произошла ли ошибка. (Любая функция Go может возвращать несколько значений.)
  • Импортируете пакет errors стандартной библиотеки Go, чтобы вы могли использовать его errors.New функцию.
  • Добавляете оператор if, чтобы проверить недействительный запрос (пустую строку, где должно быть имя) и вернуть ошибку, если запрос недействителен. Функция errors.New возвращает ошибку с вашим сообщением внутри.
  • Добавляете nil (что означает отсутствие ошибки) в качестве второго значения в успешном возврате. Таким образом, вызывающий может увидеть, что функция выполнена успешно.

В файле hello/hello.go обработайте ошибку, возвращаемую функцией Hello, вместе со значением, не связанным с ошибкой. Вставьте следующий код в hello.go.

package main

import (
    "fmt"
    "log"

    "example.com/greetings"
)

func main() {
    // Устанавливаем свойства предопределенного Logger, включая
    // префикс записи журнала и флаг отключения печати
    // время, исходный файл и номер строки.
    log.SetPrefix("greetings: ")
    log.SetFlags(0)

    // Запрос приветственного сообщения.
    message, err := greetings.Hello("")
    // Если вернулась ошибка, выводим ее в консоль и
    // выходим из программы.
    if err != nil {
        log.Fatal(err)
    }

    // Если ошибок не было, распечатываем возвращенное сообщение
    // в консоль.
    fmt.Println(message)
}

В этом коде вы:

  • Настроете пакет log для печати имени команды ("greetings: ") в начале сообщений журнала без отметки времени или информации об исходном файле.
  • Присвоете переменным оба возвращаемых значения Hello, включая ошибку.
  • Измените аргумент Hello с имени Gladys на пустую строку, чтобы вы могли опробовать свой код обработки ошибок.
  • Найдете значение ошибки, отличное от нуля. В этом случае нет смысла продолжать.
  • Используете функции из пакета log стандартной библиотеки для вывода информации об ошибках. Если вы получили сообщение об ошибке, вы используете функцию Fatal пакета log, чтобы распечатать ошибку и остановить программу.

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

Теперь, когда вы передаете пустое имя, вы получите сообщение об ошибке.

$ go run .
greetings: empty name
exit status 1

Это обычная обработка ошибок в Go: возвращать ошибку как значение, чтобы вызывающий мог ее проверить.

Далее, в следующем посте, вы будете использовать срез Go, чтобы вернуть случайно выбранное приветствие.


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


среда, 7 апреля 2021 г.

Создание модуля в Golang: вызов своего кода из другого модуля

В предыдущем посте вы создали модуль greetings. В этом посте вы напишете код для вызова функции Hello в только что написанном модуле. Вы напишете код, который можно выполнить как приложение, и который вызывает код в модуле greetings.

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

1. Создайте каталог hello для исходного кода модуля Go. Здесь вы напишете свой код, который будет вызывать greetings.

После создания этого каталога у вас должны быть каталоги hello и greetings на одном уровне иерархии, например:

<home>/
 |-- greetings/
 |-- hello/

Например, если ваша командная строка находится в каталоге greetings, вы можете использовать следующие команды:

cd ..
mkdir hello
cd hello

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

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

Для целей этого руководства используйте example.com/hello в качестве пути к модулю.

$ go mod init example.com/hello
go: creating new go.mod: module example.com/hello

3. В текстовом редакторе в каталоге hello создайте файл для записи кода и назовите его hello.go.

4. Напишите код для вызова функции Hello, а затем распечатайте возвращаемое значение функции.

Для этого вставьте следующий код в hello.go.

package main

import (
    "fmt"

    "example.com/greetings"
)

func main() {
    // Получаем приветственное сообщение и распечатываем его.
    message := greetings.Hello("Gladys")
    fmt.Println(message)
}

В этом коде вы:

  • Объявляете main пакет. В Go код, выполняемый как приложение, должен находиться в main пакете.
  • Импортируете два пакета: example.com/greetings и пакет fmt. Это дает вашему коду доступ к функциям в этих пакетах. Импорт example.com/greetings (пакет, содержащийся в модуле, который вы создали ранее) дает вам доступ к функции Hello. Вы также импортируете fmt с функциями для обработки ввода и вывода текста (например, вывода текста на консоль).
  • Получаете приветствие, вызвав функцию Hello пакета greetings.

5. Отредактируйте модуль example.com/hello, чтобы использовать локальный модуль example.com/greetings.

Для производственного использования вы опубликуете модуль example.com/greetings из его репозитория (с путем к модулю, который отражает его опубликованное местоположение), где инструменты Go могут найти его для загрузки. На данный момент, поскольку вы еще не опубликовали модуль, вам необходимо адаптировать модуль example.com/hello, чтобы он мог найти код example.com/greetings в вашей локальной файловой системе.

Для этого используйте команду go mod edit, чтобы отредактировать модуль example.com/hello, чтобы перенаправить инструменты Go с его пути к модулю (где модуля нет) в локальный каталог (где он находится).

В командной строке в каталоге hello выполните следующую команду:

$ go mod edit -replace=example.com/greetings=../greetings

Команда указывает, что example.com/greetings следует заменить на ../greetings с целью определения зависимости. После запуска команды файл go.mod в каталоге hello должен содержать директиву replace:

module example.com/hello

go 1.16

replace example.com/greetings => ../greetings

Из командной строки в каталоге hello запустите команду go mod tidy, чтобы синхронизировать зависимости модуля example.com/hello, добавив те, которые требуются кодом, но еще не отслеживаются в модуле.

$ go mod tidy
go: found example.com/greetings in example.com/greetings v0.0.0-00010101000000-000000000000

После завершения команды файл go.mod модуля example.com/hello должен выглядеть следующим образом:

module example.com/hello

go 1.16

replace example.com/greetings => ../greetings

require example.com/greetings v0.0.0-00010101000000-000000000000

Команда нашла локальный код в каталоге greetings, а затем добавила директиву require, чтобы указать, что example.com/hello требует example.com/greetings. Вы создали эту зависимость, когда импортировали пакет greetings в hello.go.

Номер, следующий за путем к модулю, является номером псевдоверсии - сгенерированным числом, используемым вместо семантического номера версии (которого у модуля еще нет).

Для ссылки на опубликованный модуль в файле go.mod обычно опускается директива replace и используется директива require с помеченным номером версии в конце.

require example.com/greetings v1.1.0

6. В командной строке в каталоге hello запустите свой код, чтобы убедиться, что он работает.

$ go run .
Hi, Gladys. Welcome!

Вы написали два функционирующих модуля.

В следующем посте вы добавите обработку ошибок.


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


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

Создание модуля в Golang

Это первая часть учебного пособия, которое знакомит с некоторыми фундаментальными особенностями языка Go. Если вы только начинаете работать с Go, обязательно ознакомьтесь с Быстрое начало с Go, в котором представлены команда go, модули Go и очень простой код Go.

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

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

  • Создание модуля - напишите небольшой модуль с функциями, которые вы можете вызывать из другого модуля.
  • Вызовите свой код из другого модуля - импортируйте и используйте свой новый модуль.
  • Возврат и обработка ошибки - добавьте простую обработку ошибок.
  • Возврат случайного приветствия - обработка данных в срезах (массивы Go с динамическим размером).
  • Возврат приветствия для нескольких людей - храните пары ключ/значение в карте.
  • Добавить тест - используйте встроенные функции модульного тестирования Go для тестирования вашего кода.
  • Скомпилируйте и установите приложение - скомпилируйте и установите код локально.

Предпосылки

  • Некоторый опыт программирования. Код здесь довольно простой, но он помогает кое-что узнать о функциях, циклах и массивах.
  • Инструмент для редактирования вашего кода. Любой текстовый редактор, который у вас есть, будет работать нормально. Большинство текстовых редакторов хорошо поддерживают Go. Наиболее популярными являются VSCode (бесплатно), GoLand (платно) и Vim (бесплатно).
  • Командный терминал. Go хорошо работает с любым терминалом в Linux и Mac, а также в PowerShell или cmd в Windows.

Запустите модуль, который могут использовать другие

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

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

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

1. Откройте командную строку и перейдите в домашний каталог.

В Linux или Mac:

cd

В Windows:

cd %HOMEPATH%

2. Создайте каталог greetings для исходного кода вашего модуля Go.

Например, из вашего домашнего каталога используйте следующие команды:

mkdir greetings
cd greetings

3. Запустите свой модуль с помощью команды go mod init.

Запустите команду go mod init, указав ей путь к вашему модулю - здесь используйте example.com/greetings. Если вы публикуете модуль, это должен быть путь, по которому ваш модуль может быть загружен инструментами Go. Это будет репозиторий вашего кода.

$ go mod init example.com/greetings
go: creating new go.mod: module example.com/greetings

Команда go mod init создает файл go.mod для отслеживания зависимостей вашего кода. Пока что файл включает только имя вашего модуля и версию Go, которую поддерживает ваш код. Но по мере добавления зависимостей в файле go.mod будут перечислены версии, от которых зависит ваш код. Это обеспечивает воспроизводимость сборок и дает вам прямой контроль над тем, какие версии модулей использовать.

4. В текстовом редакторе создайте файл для написания кода и назовите его greetings.go.

5. Вставьте следующий код в файл greetings.go и сохраните файл.

package greetings

import "fmt"

// Hello возвращает приветствие для указанного человека.
func Hello(name string) string {
    // Возвращаем приветствие, включающее имя в сообщение.
    message := fmt.Sprintf("Hi, %v. Welcome!", name)
    return message
}

Это первый код для вашего модуля. Он возвращает приветствие любому вызывающему абоненту, который его запрашивает. На следующем шаге вы напишете код, вызывающий эту функцию.

В этом коде вы:

  • Объявляете пакет greetings для сбора связанных функций.
  • Реализуйте функцию Hello, чтобы вернуть приветствие.
    Эта функция принимает параметр name, тип которого - строка. Функция также возвращает строку. В Go функция, имя которой начинается с заглавной буквы, может быть вызвана функцией из другого пакета. Это известно в Go как экспортируемое имя.
  • Объявляете переменную message для хранения вашего приветствия.
    В Go оператор := - это ярлык для объявления и инициализации переменной в одной строке (Go использует значение справа для определения типа переменной). Пройдя долгий путь, вы могли бы написать это как:

    var message string
    message = fmt.Sprintf("Hi, %v. Welcome!", name)
    

  • Используете функцию Sprintf пакета fmt для создания приветственного сообщения. Первый аргумент - это строка формата, и Sprintf заменяет значение параметра name на команду формата %v. Вставка значения параметра name завершает текст приветствия.
  • Возвращаете отформатированный текст приветствия вызывающему абоненту.

На следующем шаге вы вызовете эту функцию из другого модуля.


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


пятница, 26 марта 2021 г.

Новые изменения модулей в Go 1.16

В Go 1.16 много новых функций, особенно для модулей. В примечаниях к релизу кратко описаны эти изменения, но давайте подробно рассмотрим некоторые из них.

Модули включены по умолчанию

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

По-прежнему можно создавать пакеты в режиме GOPATH, отключив переменную среды GO111MODULE. Вы также можете установить GO111MODULE на auto, чтобы включить режим с поддержкой модулей, только если файл go.mod присутствует в текущем или любом родительском каталоге. Раньше это было по умолчанию. Обратите внимание, что вы можете установить GO111MODULE и другие переменные навсегда с помощью go env -w:

go env -w GO111MODULE=auto

Планируется отказ от поддержки режима GOPATH в Go 1.17. Другими словами, Go 1.17 игнорирует GO111MODULE. Если у вас есть проекты, которые не собираются в режиме с поддержкой модулей, сейчас самое время выполнить миграцию. Если есть проблема, мешающая вам выполнить миграцию, рассмотрите возможность заполнения проблемы или отчета об опыте.

Никаких автоматических изменений go.mod и go.sum

Раньше, когда команда go обнаруживала проблему с go.mod или go.sum, например, отсутствующую директиву require или отсутствующую сумму, она пыталась устранить проблему автоматически. Было получено много отзывов о том, что такое поведение было неожиданным, особенно для таких команд, как go list, которые обычно не имеют побочных эффектов. Автоматические исправления не всегда были желательными: если импортированный пакет не был предоставлен каким-либо обязательным модулем, команда go добавляла новую зависимость, возможно, вызывая обновления общих зависимостей. Даже неверный путь импорта приведет к (неудачному) поиску в сети.

В Go 1.16 команды с поддержкой модулей сообщают об ошибке после обнаружения проблемы в go.mod или go.sum, вместо того, чтобы пытаться исправить проблему автоматически. В большинстве случаев сообщение об ошибке рекомендует команду для устранения проблемы.

$ go build
example.go:3:8: no required module provides package golang.org/x/net/html; to add it:
    go get golang.org/x/net/html
$ go get golang.org/x/net/html
$ go build

Как и раньше, команда go может использовать каталог vendor, если он присутствует. Такие команды, как go get and go mod tidy, по-прежнему изменяют go.mod и go.sum, поскольку их основная цель - управлять зависимостями.

Установка исполняемого файла определенной версии

Команда go install теперь может установить исполняемый файл определенной версии, указав суффикс @version.

go install golang.org/x/tools/gopls@v0.6.5

При использовании этого синтаксиса go install устанавливает команду из этой точной версии модуля, игнорируя любые файлы go.mod в текущем каталоге и родительских каталогах. (Без суффикса @version go install продолжает работать как всегда, собирая программу с использованием требований к версии и замен, перечисленных в go.mod текущего модуля.)

Раньше рекомендовалось go get -u program для установки исполняемого файла, но это использование вызвало слишком сильную путаницу со значением go get для добавления или изменения требований к версии модуля в go.mod. И чтобы избежать случайного изменения go.mod, люди начали предлагать более сложные команды, такие как:

cd $HOME; GO111MODULE=on go get program@latest

Теперь мы все можем использовать go install program@latest.

Чтобы устранить двусмысленность в отношении используемых версий, существует несколько ограничений на то, какие директивы могут присутствовать в файле go.mod программы при использовании этого синтаксиса установки. В частности, директивы replace и exclude запрещены, по крайней мере, на данный момент. В долгосрочной перспективе, когда новая программа go install program@version будет работать хорошо для достаточного количества вариантов использования, планируется сделать так, чтобы go get прекратил установку двоичных файлов команд.

Отзыв модуля

Вы когда-нибудь случайно публиковали версию модуля до того, как она была готова? Или вы обнаружили проблему сразу после публикации версии, которую нужно было быстро исправить? Ошибки в опубликованных версиях трудно исправить. Чтобы сборки модулей оставались детерминированными, версия не может быть изменена после публикации. Даже если вы удалите или измените тег версии, proxy.golang.org и другие прокси-серверы, вероятно, уже имеют исходный кешированный файл.

Авторы модулей теперь могут отзывать версии модулей с помощью директивы retract в go.mod. Отозванная версия все еще существует и может быть загружена (поэтому сборки, которые от нее зависят, не сломаются), но команда go не выберет ее автоматически при разрешении версий, таких как @latest. go get and go list -m -u выведет предупреждения о существующем использовании.

Например, предположим, что автор популярной библиотеки example.com/lib выпускает v1.0.5, а затем обнаруживает новую проблему безопасности. Он может добавить в свой файл go.mod директиву, подобную приведенной ниже:

// Remote-triggered crash in package foo. See CVE-2021-01234.
retract v1.0.5

Затем автор может пометить и опубликовать версию v1.0.6, новую высшую версию. После этого пользователи, которые уже зависят от версии 1.0.5, будут уведомлены об отзыве при проверке наличия обновлений или при обновлении зависимого пакета. Сообщение с уведомлением может включать текст из комментария над директивой retract.

$ go list -m -u all
example.com/lib v1.0.5 (retracted)
$ go get .
go: warning: example.com/lib@v1.0.5: retracted by module author:
    Remote-triggered crash in package foo. See CVE-2021-01234.
go: to switch to the latest unretracted version, run:
    go get example.com/lib@latest

Управление инструментами контроля версий с помощью GOVCS

Команда go может загружать исходный код модуля с зеркала, такого как proxy.golang.org, или напрямую из репозитория системы управления версиями, используя git, hg, svn, bzr или fossil. Прямой доступ к управлению версиями важен, особенно для частных модулей, которые недоступны на прокси-серверах, но это также потенциально проблема безопасности: ошибка в инструменте управления версиями может быть использована вредоносным сервером для запуска непредусмотренного кода.

Go 1.16 представляет новую конфигурационную переменную GOVCS, которая позволяет пользователю указывать, каким модулям разрешено использовать определенные инструменты контроля версий. GOVCS принимает разделенный запятыми список правил pattern:vcslist. pattern - это path.Match шаблон соответствующий одному или нескольким ведущим элементам пути к модулю. Специальные шаблоны public и private соответствуют общедоступным и частным модулям (private определяется как модули, соответствующие шаблонам в GOPRIVATE; public - это все остальное). Vcslist - это список разрешенных команд управления версиями, разделенных вертикальной чертой, или ключевое слово all или off.

Например:

GOVCS=github.com:git,evil.com:off,*:git|hg

С этой настройкой модули с путями на github.com можно загружать с помощью git; пути на evil.com нельзя загрузить с помощью какой-либо команды управления версиями, а все другие пути (* соответствует всему) можно загрузить с помощью git или hg.

Если GOVCS не установлен или модуль не соответствует какому-либо шаблону, команда go использует значение по умолчанию: git и hg разрешены для общедоступных модулей, а все инструменты разрешены для частных модулей. Причина, по которой разрешено использование только Git и Mercurial, заключается в том, что эти две системы уделяли наибольшее внимание вопросам работы в качестве клиентов ненадежных серверов. Напротив, Bazaar, Fossil и Subversion в основном использовались в надежных, аутентифицированных средах и не так тщательно изучаются, как поверхности для атак. То есть настройка по умолчанию:

GOVCS=public:git|hg,private:all


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