Скоро выходит версия Go 1.18, и в массовом сознании она, скорее всего, будет ассоциироваться с Generic-ами. Но помимо них туда попадает еще несколько вкусных фичей. Например, Go Workspaces.
Что даёт Go Workspaces?
Go Workspaces позволяет работать одновременно с несколькими модулями. Теоретически это можно было делать и раньше, но Go Workspaces добавляет немного комфорта.
Ранее при работе с несколькими модулями одновременно их нужно было «связать» через директиву replace
в go.mod
-файле.
Подход с replace-ами в go.mod
имеет ряд существенных недостатков:
каждый модуль живёт своей независимой жизнью и некоторый инструментарий разработки умеет одновременно работать только с одним модулем.
replace
-ы не транзитивны. То есть если вы добавляете вgo.mod
новыйreplace
, то его надо добавить во все использующие его модули. Даже при небольшом количестве модулей это доставляет заметные неудобства.запустить утилиту через
go run
можно только из каталогов тех модулей, которые либо содержат эту утилиту, либо «видят» её через replace (при этом она еще должна быть объявлена какrequire
).
Go Workspaces добавляет немного комфорта:
gopls
предоставляет информацию сразу по всем используемым модулям;общие
replace
-ы можно описать в одном месте, и они видны для всех объявленных модулей;локальные правки с директивой
replace
не нужно прописывать внутриgo.mod
файлов, которые лежат в репозитории;сборка и запуск утилит для всех объявленных модулей может быть из каталога, в котором лежит
go.work
и его дочерних каталогов;добавляется команда
go work sync
, которая позволяет обновить зависимости нескольких модулей до наиболее свежей общей версии.
Как оно работает?
При использовании go.work
-файла Go собирает общий список зависимостей из всех перечисленных в go.mod
-файлов, с учетом replace
-директив. На базе всех перечисленных зависимостей формируется единый граф зависимостей, который и применяется ко всем используемым модулям. Если для одного модуля указано в разных go.mod
несколько конфликтующих replace
-директив — будет ошибка.
К примеру, если один модуль требует зависимость на github.com/davecgh/go-spew версии v1.1.1
, а другой модуль требует зависимость на github.com/davecgh/go-spew версии v1.1.0
, то оба модуля будут исполняться с более старшей версией v1.1.1
.
Это работает для всех модулей одного Go Workspace, даже если между модулями нет зависимости.
Что не даёт Go Workspace?
Если вы хотите использовать несколько go.mod
-файлов в рамках одного (моно)репозитория, то go.work
вам никак в этом не поможет: в рамках одного Go Workspace используются общие зависимости, собранные на основе всех используемых go.mod
-файлов. То есть такой подход ничем принципиально не будет отличаться от использования одного go.mod
-файла, кроме увеличения сложности за счет большего количества используемых файлов.
Собственно ссылка на соседние модули через тэги или идентификатор коммита также остаются за кадром Go Workspace.
Как начать использовать Go Workspaces?
Для использования Go Workspaces добавилась группа команд go work
.
Для начала нужно вызвать команду go work init
в корне рабочего пространства.
Допустим у нас есть рабочее пространство вида:
shared
— с модулем из пакета github.com/bozaro/go/go-work-play/sharedtools
— с модулем из пакета github.com/bozaro/go/go-work-play/tools
Тогда для использования Go Workspaces надо:
-
Создать
go.work
файл:go work init
-
Добавить туда модули:
go work use shared go work use tools
В итоге создастся файл go.work
с содержимым вида:
go 1.18
use (
shared
tools
)
И далее, к примеру, можно вызвать сборку из любого подкаталога внутри рабочего пространства:
go run github.com/bozaro/go/go-work-play/tools/hello
Если в go.work
добавить replace
-ы, то они так же будут иметь эффект на все модули рабочего пространства.
Какие еще есть проблемы?
Важно понимать, что использование Go Workspaces меняет поведение некоторых команд, например:
go run
перестаёт работать для модулей, которые не объявлены вgo.work
;go list
не может менятьgo.mod
файлы:
go list -mod=mod -m -json
go: -mod may only be set to readonly when in workspace mode, but it is set to "mod"
Remove the -mod flag to use the default readonly value,
or set -workfile=off to disable workspace mode.
К этому изменению некоторые утилиты могут быть не готовы.
Подведение итогов
Я для себя сделал следующие выводы.
Использование Go Workspaces сильно упрощает выполнение каких-либо утилит за счет возможности вызова их из любого места рабочего пространства без лишних приседаний.
Можно не добавлять паразитные
replace
директивы вgo.mod
файлы для того, чтобы править одновременно несколько связанных проектов.При использовании
go.work
можно получить ситуацию, запускаемый код будет выполняться не с теми зависимостями, которые указаны вgo.mod
файле, и об этом нужно помнить. К счастью, зависимости можно свести через командуgo work sync
.Проблема с несколькими
go.mod
в рамках одного репозитория с помощью Go Workspaces не решается никак.
Комментарии (9)
newprim
18.02.2022 12:56Я конечно джун на Go, но за 5 месяцев работы ни разу вообще не пришлось даже зайти в go.mod, не то что что-то самому там писать. IDE и go mod tidy вроде справлялись сами неплохо.
В общем, лично мне что-то не хватило понимания, что поменялось и зачем это вообще надо :)
tendium
19.02.2022 22:50+1Чую, это будет одна из самых непонятых фич go. На go пишу лет 5 уже, и — признаюсь — так и не осилил, для чего нужен этот go.work. До этого читал в оригинале, теперь тут. Хотя, может проблема во мне, а не в go.work.
Bozaro Автор
20.02.2022 03:05Я изначально то же ждал от
go.work
совершенно другого поведения: он меня интересовал в контексте работы с несколькимиgo.mod
в рамках монорепозитория.Но он сделан для другого:
решение проблемы с IDE, которые используют
gopls
для интеграции, например VS Code (в этом случае при работе с несколькимиgo.mod
надо открывать по одному экземпляру IDE на каждыйgo.mod
);решение проблемы работы с несколькими связанным
go.mod
в разных репозиториях.
Если для Вас эти сценарии не актуальны, то эта фича действительно выглядит предельно странно.
shortcircuit
Я правильно понимаю, что при запуске через
go run ...
отдельной утилиты с микроскопическимgo.mod
, лежащей в монорепе с гигантскимиgo.mod
, всё равно скачаются все-все-все зависимости всехgo.mod
, которые есть в воркспейсе?Bozaro Автор
Да. Именно так. Микроскопическая утилита запустится с обобщенным
go.mod
.С моей точки зрения, в контексте монорепы Go Workspaces бесполезны, так как по факту мы просто получаем распределённый
go.mod
.shortcircuit
А есть какой-то способ сказать, что "вот этот конкретный
go.mod
не относится к воркспейсу"?Bozaro Автор
В
go.work
явно перечисляются используемыеgo.mod
-ы.Если директория с
go.mod
там не указана, то GoLang будет её игнорировать и запустить из неё без ключа-workfile=off
не получится. Будет ошибка вида:go: no modules were found in the current workspace; see 'go help work'