JavaRush /Курси /Go SELF /go mod tidy і go get — керування залежностями та версіями...

go mod tidy і go get — керування залежностями та версіями

Go SELF
Рівень 35 , Лекція 1
Відкрита

1. Навіщо взагалі дві команди: go get і go mod tidy

Коли проєкт ще маленький, здається, що залежності «якось самі з’являються»: імпортували пакет — і все зібралося. Але щойно ви починаєте використовувати зовнішні бібліотеки, тобто ті, що не входять до стандартної бібліотеки, у проєкту з’являється ще одне важливе завдання: зафіксувати, які саме версії зовнішнього коду ви використовуєте, щоб збірка була відтворюваною. І ось тут Go поводиться дисципліновано: він не просто завантажує щось з інтернету, а акуратно записує ваш вибір у файли проєкту.

У цій лекції розберемо дві команди, які найчастіше викликають або повагу, або легку паніку — зазвичай одночасно: go get і go mod tidy. Одразу домовимося: ми не заглиблюємося в replace, vendor та інші «швейцарські ножі» модульної системи. Наша мета — навчитися керувати версіями залежностей і підтримувати порядок у go.mod/go.sum.

Модель у голові: що взагалі вважається «залежністю»

Якщо говорити просто, залежність з’являється тоді, коли ваш код імпортує пакет, який не входить до стандартної бібліотеки. Стандартна бібліотека — це fmt, strings, time, net/http і друзі. Усе, що виглядає як github.com/... або golang.org/x/..., — уже зовнішнє.

Далі починається важлива частина. Go думає не «пакетами», а модулями. Один модуль може містити багато пакетів. Ви імпортуєте пакет, але фіксуєте в go.mod модуль і його версію. Тому іноді ви імпортуєте golang.org/x/net/html, а в go.mod фіксується модуль golang.org/x/net цілком.

І ще один ключовий момент: залежності бувають прямі (direct) і транзитивні (transitive). Прямі — це те, що імпортуєте ви. Транзитивні — те, що імпортують ваші залежності. Це як знайомство через знайомих: ви запросили одну людину, а вона прийшла з друзями — інколи корисними, інколи галасливими. Але таке вже життя.

2. Що робить go get: «обрати версію і записати вибір»

go get — це команда про вибір версій модулів. Не про «скачати бібліотеку», не про «встановити все на комп’ютер», а саме про те, щоб сказати: «Проєкту, ось цю залежність бери в такій версії». Після цього Go оновлює go.mod і майже завжди go.sum.

Є корисна деталь: сучасні go-команди часто не намагаються виправити проблему автоматично, а натомість показують помилку й прямим текстом підказують команду, яку варто виконати. Наприклад, під час збірки ви можете побачити повідомлення на кшталт: «no required module provides package ...; to add it: go get ...». Це нормальний сценарій: Go не мовчки чаклує, а каже, що саме не так і який наступний крок очікується.

Базовий сценарій: «додати залежність за імпортом»

Уявімо, що в нашому навчальному застосунку todoapp ми хочемо додати невелику фічу: нормалізувати заголовки задач не лише через strings.TrimSpace, а й, скажімо, підготуватися до складнішої обробки тексту. Для прикладу візьмемо зовнішній пакет golang.org/x/net/html — він часто трапляється в реальному житті й добре підходить як «навчальна» залежність.

Припустімо, ви додали імпорт у код, а потім запускаєте збірку:

go build ./...

Якщо модуль не додано, Go може видати помилку й підказку «to add it: go get ...». Такий стиль повідомлень характерний для режиму з підтримкою модулів і спеціально так задуманий, щоб ви не гадали, яку команду вводити.

Тоді ви виконуєте:

go get golang.org/x/net/html

Тепер Go робить кілька важливих кроків: обирає версію модуля golang.org/x/net, записує її в go.mod і додає контрольні суми в go.sum.

Керування версіями: @version, @latest, відкат

Дуже корисно сприймати go get як «перемикач версій».

Ви можете явно вказати версію:

go get golang.org/x/net@v0.20.0

Ви можете попросити найсвіжішу версію, яка підходить відповідно до правил вибору версій:

go get golang.org/x/net@latest

І так, ви можете відкотитися на старішу:

go get golang.org/x/net@v0.19.0

Важливо звикнути до думки: go get — це не «встанови бібліотеку», а «зміни вимоги проєкту до версій». Тому після нього майже завжди є зміни в go.mod/go.sum. І це нормально.

4. Що робить go mod tidy: «навести лад і синхронізувати факти»

Якщо go get — це команда про «вибір версій», то go mod tidy — це команда про «давайте приведемо декларацію залежностей у відповідність до реальності».

На практиці є дві сторони: ваш код і ваші тести. go mod tidy дивиться на імпорти у вихідних файлах і у файлах _test.go та робить дві основні речі: додає те, чого бракує для збірки й тестів, і видаляє те, що більше не використовується. При цьому оновлюються і go.mod, і go.sum.

Уявіть, що go.mod — це список покупок, а проєкт — це ваша кухня. go mod tidy — це коли ви не просто докупили молока (go get), а ще й викинули зі списку покупок «манго», яке ви додали в пориві ентузіазму два тижні тому й так і не купили.

5. Корисні нюанси

Чому в go.mod з’являється // indirect

Новачки часто сприймають // indirect як щось на кшталт «це залежність другого сорту». Насправді це просто підказка: залежність не імпортується вашим кодом напряму, але потрібна для збірки через інші модулі.

go mod tidy якраз любить розставляти ці позначки й прибирати їх, коли ситуація змінюється. Пам’ятайте: не потрібно вручну воювати з // indirect, видаляти їх «для краси» або дописувати «щоб було правильно». Правильність визначає інструмент, а ваше завдання — розуміти сенс і тримати проєкт у стані, що успішно збирається.

Міні-таблиця: хто за що відповідає

Після теорії корисно чітко розкласти, хто за що відповідає в різних ситуаціях. Таблиця невелика, але часто економить 20 хвилин роздратування.

Ситуація в проєкті Що ви хочете зробити Команда, яка зазвичай доречна Що зміниться
Додали новий імпорт зовнішнього пакета, збірка не проходить Зафіксувати модуль і версію
go get <module-or-package>
go.mod, go.sum
Хочете оновити залежність до конкретної версії Обрати версію
go get <module>@<version>
go.mod, go.sum
Видалили імпорт/код, але залежності залишилися зайвим хвостом Прибрати зайве
go mod tidy
go.mod, go.sum
Підозрюєте, що go.mod і код «роз’їхалися» Синхронізувати факти
go mod tidy
go.mod, go.sum

Фраза, яку варто запам’ятати: go get — про версії, go mod tidy — про порядок.

6. Приклад на todoapp

Уявімо структуру проєкту (спрощено):

todoapp/
  go.mod
  go.sum
  cmd/todoapp/main.go
  internal/todo/normalize.go

Додамо функцію нормалізації без зовнішніх залежностей

Почнемо з простого й знайомого. Файл internal/todo/normalize.go:

package todo

import "strings"

// NormalizeTitle приводить заголовок до охайного вигляду.
func NormalizeTitle(s string) string {
	return strings.TrimSpace(s)
}

А тепер використаємо це в cmd/todoapp/main.go:

package main

import (
	"fmt"

	"example.com/todoapp/internal/todo"
)

func main() {
	fmt.Println(todo.NormalizeTitle("   купити молоко   ")) // купити молоко
}

Поки все тримається на стандартній бібліотеці, залежності не додаються.

Додамо зовнішній імпорт для демонстрації механіки

Тепер додамо штучну, але правдоподібну причину використати зовнішній пакет. Наприклад, ми хочемо в майбутньому парсити HTML — нехай це буде лише навчальний приклад. Змінимо normalize.go, додавши посилання на тип із golang.org/x/net/html:

package todo

import (
	"strings"

	"golang.org/x/net/html"
)

// NormalizeTitle приводить заголовок до охайного вигляду.
func NormalizeTitle(s string) string {
	_ = html.Node{} // просто щоб імпорт був «живим»
	return strings.TrimSpace(s)
}

Найімовірніше, після цього go build ./... почне скаржитися, що потрібний модуль не додано, і запропонує виконати go get. Такий формат підказки — очікувана поведінка інструментів Go.

Виправлення — виконати:

go get golang.org/x/net/html

Після цього відкрийте go.mod: там з’явиться require для golang.org/x/net .... Також з’являться або оновляться записи в go.sum.

Прибираємо зовнішній імпорт і чистимо хвости

Припустімо, ви передумали, видалили html.Node{} і сам імпорт. Після цього проєкт знову збирається, але залежність може залишитися в go.mod, бо ви колись її додали.

Ось тут настає час go mod tidy:

go mod tidy

Ця команда синхронізує залежності з фактичними імпортами. Вона прибере з go.mod те, що більше не потрібно, і підчистить go.sum. Це очікувано: обидві команди керують цими файлами за призначенням.

7. Як читати go.mod і не боятися go.sum

Коли ви працюєте в команді або просто цінуєте охайність, ви бачитимете зміни в go.mod і go.sum. Це нормальна частина життя Go-проєкту.

go.mod — зрозумілий і відносно людяний. Там можна побачити module, go, список require. Його можна читати, а іноді й редагувати вручну, але обережно й з розумінням наслідків.

go.sum — це не «другий go.mod». Він зберігає контрольні суми модулів і слугує для перевірки цілісності та відтворюваності. До нього краще ставитися як до продукту роботи інструментів. На практиці майже завжди правильний шлях такий: ви змінюєте код і версії командами, а go.sum оновлюється автоматично. Ручне редагування go.sum зазвичай закінчується тим, що ви або ламаєте збірку, або за хвилину все одно знову запускаєте go mod tidy, і інструмент повертає все назад.

8. Схема життєвого циклу залежності

Іноді корисно побачити процес як «конвеєр», а не як набір випадкових команд.

flowchart TD
    A[Додали імпорт зовнішнього пакета в код] --> B[go build ./... або go test ./...]
    B -->|помилка: немає модуля| C[go get 
 
  ]
    C --> D[оновилися go.mod/go.sum]
    D --> E[код знову збирається]
    E --> F[видалили імпорт/код]
    F --> G[go mod tidy]
    G --> H[go.mod/go.sum синхронізовані]

 

Тут важливий сам ритм: go get зазвичай з’являється, коли ви додаєте залежності або змінюєте версії, а go mod tidy — коли ви хочете, щоб файли залежностей знову відповідали поточному стану коду.

9. Типові помилки

Помилка №1: сприймати go get як «просто скачати бібліотеку один раз».
Таке мислення походить зі світу, де ви вручну встановлюєте пакети в систему. У Go це працює інакше: go get фіксує версії залежностей саме для поточного модуля й змінює go.mod/go.sum. Тому «я просто хотів завантажити» перетворюється на зміни в репозиторії — і це не баг, а сенс команди.

Помилка №2: запускати go mod tidy, не розуміючи, що він враховує ще й тести.
Іноді студент видаляє імпорт з основного коду, запускає go mod tidy, а залежність чомусь залишається. Часто причина в тому, що цей імпорт живе в _test.go. Це не «Go впирається», це ви забули, що тести — теж частина проєкту, і для їхньої збірки потрібні залежності.

Помилка №3: правити go.sum руками, щоб «зійшлося».
go.sum — не місце для ручної творчості. Якщо ви бачите, що він змінився, це зазвичай наслідок зміни залежностей або їхніх версій. Правильна дія — зрозуміти причину: що додали, оновили або видалили, а не «підкручувати файл», бо інструмент усе одно перевірить цілісність і може знову перегенерувати записи.

Помилка №4: плутати шлях імпорту пакета і шлях модуля під час go get.
Іноді пишуть go get з одним шляхом, а імпортують інший, і з’являється відчуття «магії». Правило просте: ви можете вказувати go get і на конкретний пакет (.../html), і на корінь модуля (.../net). Go сам розбереться, який модуль потрібен. Але важливо, щоб імпорт у коді був коректним і відповідав реальному розташуванню пакета.

Помилка №5: лякатися змін у go.mod після go get або go mod tidy.
Інструменти Go очікувано оновлюють файли залежностей, бо їхнє головне завдання — керувати залежностями проєкту, і go get, і go mod tidy саме для цього й призначені. Якщо ви щоразу намагаєтеся «відкотити назад, бо страшно», ви позбавляєте проєкт головної переваги: відтворюваності.

Коментарі
ЩОБ ПОДИВИТИСЯ ВСІ КОМЕНТАРІ АБО ЗАЛИШИТИ КОМЕНТАР,
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ