Беглый взгляд на Go Workspaces в Go 1.18

Скоро выходит версия Go 1.18, и в массовом сознании она, скорее всего, будет ассоциироваться с Generic-ами. Но помимо них туда попадает еще несколько вкусных фичей. Например, Go Workspaces.

6dc633ed21e8a56ac931ce1428ab063b.png

Что даёт 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 в корне рабочего пространства.

Допустим у нас есть рабочее пространство вида:

Тогда для использования Go Workspaces надо:

  1. Создать go.workфайл:

    go work init
  2. Добавить туда модули:

    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 не решается никак.

© Habrahabr.ru