Введение в go mod

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

Введение

Системы управления зависимостями есть в почти каждом языке программирования, в некоторых даже два. Например в php это getcomposer.org, в js это npmjs.com. Но разработчики golang оставили этот момент на сообщество, и в итоге получили несколько решений например dep и vgo, разнообразие это конечно хорошо, но не в ключевых моментах. Разработчикам библиотеки не понятно какие лучше использовать, поскольку не известно кто будет использовать(какая у нах система контроля). С выходом golang 1.11 добавили команду `go mod`, к этому моменту большинство разработчиков уже использовало какую-то из систем, а некоторые даже сохраняли зависимость в своем репозитории. Основные моменты которые нам дает `go mod` это повторяемость сборки как следствие не нужна папка vendor, и так как официальная система контроля версий то очень большая вероятность что библиотеки которые вам нужны уже ее используют. Если вам нужна полная документация то тут ее не будет можете прочитать из первоисточника.

Инициализация

Заходите в папку проекта и запускаете команду go mod init учитывайте только, что он берет название проекта от $GOPATH/src поэтому если по каким-то причинам ваш проект лежит к примеру в $GOPATH/src/github.com/myname/myasesomeproject то необходимо в файле `go.mod` поправить module ... на имя проекта из репозитория, иначе будут проблемы установкой вашего проекта как зависимости. Одна из полезных команд go mod tidy которая позволяет ваш go.mod держать в чистоте. После предыдущих двух команд нужно сделать только go mod download и и можно использовать go run или go build, все просто.

Установка зависимостей

В проекте необходимо выполнить go get package@latest, при этом в go.mod и go.sum добавляется новый пакет, и следующий build будет использовать ту же версию пакета которая прописана в go.mod. Если что-то пошло не так то используйте команду go get -v package@latest что покажет в каком моменте что-то пошло не так.

Замена зависимостей 

Бывают моменты когда необходимо заменить пакет на такой же, к примеру в исходном пакете найден баг или он стал недоступен по старому url. Для этого существует директива replace в файле go.mod например

replace bou.ke/monkey v1.0.1 => github.com/bouk/monkey v1.0.1

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

  1. fork пакета в свой репозиторий и исправление бага
  2. добавление директивы replace в go.mod 
  3. создание Pull request в исходный репозиторий, поскольку как только вы поставили replace все остальные исправления не будут вам приходить.

Если к примеру вы знаете где находится зависимость но там не поддерживается функциональность go get ... можно указать путь на git к примеру если по каким-то причинам GitLab перестал правильно работать с go mod можно добавить replace

replace (
	gitlab.com/4devs/pass => gitlab.com/4devs/pass.git v0.0.2
	gitlab.com/4devs/pass/password => gitlab.com/4devs/pass.git/password v0.0.0-20190819053721-35c7ec7a72b1
)

В данном примере показано не только путь к пакету но и ко вложенному пакету. К сожалению до сих пор Gitlab плохо работает со вложенными группами если они не public, и вам прийдется ставить такие `replace` во все вложенные пакеты для gitlab. Так же replace работает с локальной структурой папок, например можно использовать такой подход

module github.com/go-4devs/httpclient/json
replace (
	github.com/go-4devs/httpclient/dc => ../dc
	github.com/go-4devs/httpclient/decoder => ../decoder
)
require (
	github.com/go-4devs/httpclient/dc v0.0.0-20190814063109-82955e154764
	github.com/go-4devs/httpclient/decoder v0.0.0-20190814063109-82955e154764
)

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

Стоит также учитывать что `replace` работает только в рамках текущего пакета где прописан, например в предыдущем примере github.com/go-4devs/httpclient/decoder, будет все равно скачиваться теми кто использует github.com/go-4devs/httpclient/json.

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

Для указания версии используется SemVer, ставим git тэг формата v0.1.1, обязательно начинать с v... Версия v0.1.1 используется как имя пакета в импорте. Начиная с v2.0.0 к имени пакета добавляться путь .../v2 что позволяет использовать обе версии в одном приложении, или по крайней мере переходить постепенно. Если у вас один репозиторий и много пакетов, то на каждый пакет можно поставить версию просто добавив тэг формата path/to/pkg/v0.0.1. Например для github.com/go-4devs/httpclient/dc будет dc/v0.0.1.

Выводы

Управление зависимостями пришло и в golang, и сделано удобно, что позволяет не только повторять сборки, но и полностью отказаться от папки `vendor`. Также есть удобный механизм исправления багов в зависимостях как и можно полностью их заменить. Добавляются новые возможность такие как GOPROXY, GONOSUMDB и тп.

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

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

Разворачивание golang приложения с помощью gitlab-ci и docker swarm

У меня есть несколько своих проектов, которые соответственно приходится самому поддерживать разворачивать и тп, к примеру данный блог и другие простые проекты. Когда я использовал symfony и php, развернуть можно просто с помощью capifony, даже не зная Ruby. В связи с тем что я решил перейти на golang то данные инструменты не совсем подходят. Почему не использовать к примеру в проектах тот же php, чаще быстрее разрабатывать на том что используешь в работе. Для начала решил попробовать обычный способ с помощью docker swarm и gitlab ci, есть конечно решения с heroku, но я пока решил использовать vps например vscale. K8s для маленьких проектов возможно будет излишним.

Объединение строк в golang

Одной из частых операций может оказаться объединения(concatenation) строк, есть много библиотек для решения задач, мы рассмотрим несколько самых распространенных примеров.