07 / 73

Bog'liqliklarni boshqarish

Goning bog'liqliklarga yondashuvi — tilning soddalikka bo'lgan ishtiyoqi shunchaki o'sha g'oyaning yengilroq nusxasini emas, chindan boshqacha narsani vujudga keltirgan sohalardan biri. Bu yerda boshqa ekotizimdan olingan lock-fayl formati ham, til ustiga o'rnatadigan alohida paket menejeri ham, yodlash kerak bo'lgan versiya oralig'i sintaksisi ham yo'q. Ikkita fayl — go.mod va go.sum — hamda go mod ning kichik ichki buyruqlar to'plami butun muammoni qoplaydi. Ushbu bobda ular qanday ishlashi va ulardan qanday qilib yaxshi foydalanish ko'rib chiqiladi.

go.mod: modulingizga nima kerak

go.mod modul ildizida turadi va uning o'ziga xosligini, minimal Go versiyasini hamda bog'liqliklarini e'lon qiladi:

matn
module github.com/username/myproject

go 1.22

require (
	github.com/gorilla/mux v1.8.1
	github.com/lib/pq v1.10.9
)

require (
	golang.org/x/crypto v0.14.0 // indirect
)

module — boshqa kod sizning kodingizga yetib kelish uchun ishlatadigan import yo'li, kelishuvga ko'ra u joylashgan repozitoriy manzili. go 1.22 modulga kerak bo'lgan minimal til versiyasini yozib qo'yadi; bu kompilyator qaysi til imkoniyatlari va standart kutubxona xatti-harakatlarini mavjud deb hisoblashiga, hamda go vet o'zining stdversion tahlilchisi orqali nimaga qarshi tekshirishiga ta'sir qiladi (bu statik tahlil bobida ko'rib chiqiladi), shunda siz loyihangizda ko'rsatilgan Go versiyasida hali mavjud bo'lmagan yangi standart kutubxona funksiyalarini tasodifan chaqirib qo'ymaysiz. require bloklari bog'liqliklarni sanaydi: kodingiz ochiq import qiladigan to'g'ridan-to'g'ri bog'liqliklar, va tranzitiv bo'lgan bilvosita bog'liqliklar — ya'ni bog'liqliklaringizning bog'liqliklari. Ular siz hali to'g'ridan-to'g'ri import qilmagan bo'lsangiz ham build'lar takrorlanuvchan bo'lib qolishi uchun yozib qo'yiladi.

Kamdan-kam ishlatsangiz ham bilish foydali bo'lgan yana ikkita go.mod direktivasi bor: ignore (Go 1.25+) papkani paket patternlariga mos kelishidan butunlay chiqarib tashlaydi — bu go build ./... yoki go vet ./... hech qachon kirishini istamagan generatsiya qilingan kod yoki fixture'lar daraxti uchun foydali — va godebug (Go 1.23+) runtime moslik sozlamasini (GODEBUG qiymatini) har bir dasturchi va har bir deploy to'g'ri muhit o'zgaruvchisini qo'lda eksport qilishiga tayanish o'rniga modul darajasida mahkamlaydi.

Yangi modulni shunday yarating:

bash
go mod init github.com/username/myproject

bu faqat module va go qatorlari bor bo'sh go.mod ni hosil qiladi. Go 1.26 dan boshlab o'sha go qatori siz go mod init ni ishga tushirgan asboblar to'plamidan bitta kichik versiya orqada bo'lgan qiymatni oladi — Go 1.27 ostida ishga tushirsangiz, go 1.27 emas, go 1.26 yoziladi. Bu birinchi kundanoq eng so'nggi asboblar to'plamini talab qilish o'rniga oldingi reliz bilan moslikka ataylab qilingan turtki; aniq yangiroq minimal versiya kerak bo'lsa, uni qo'lda ko'taring. Bundan keyin bog'liqliklar siz kutgan tarzda — ulardan foydalanish orqali qo'shiladi:

example.go
import "github.com/gorilla/mux"

so'ngra:

bash
go mod tidy

go mod tidy go.mod ni kodingiz haqiqatan import qilayotgan narsalar bilan moslashtiradi: yetishmayotganini qo'shadi, endi murojaat qilinmaydiganini olib tashlaydi.

go mod tidy ni ishga tushirish importlar o'zgarganda dasturchining tabiiy odatiga aylanishi kerak. CI/CD tizimlarida esa ko'pincha go mod tidy hech qanday o'zgarish qoldirmasligi tekshiriladi — bu biror dasturchi yangi paket import qilib, modullarni tartibga solishni (tidy) unutib qoldirganini darhol aniqlaydi.

Go 1.23 dan boshlab go mod tidy -diff bu bo'lishi mumkin bo'lgan o'zgarishni fayllaringizga tegmasdan yagona diff ko'rinishida chiqaradi, bu CI'da tidy qilib, so'ng ishchi daraxtni o'zingiz taqqoslashdan ko'ra tozaroq tekshiruv; go 1.27 yoki undan yangi versiyani e'lon qilgan modullar uchun esa go mod tidy endi go.mod dagi takrorlanuvchi require bloklarini avtomatik birlashtiradi (to'g'ridan-to'g'ri va bilvosita talablar orasidagi odatiy ajratishni saqlagan holda), ilgari ularni qo'lda birlashtirish sizga qolar edi. Bog'liqlikni avval importni yozmasdan, ochiq ravishda ham qo'shishingiz mumkin:

bash
go get github.com/gorilla/mux

go.sum: shunchaki hisob-kitob emas, yaxlitlik

go.mod qaysi versiyalarga bog'liqligingizni aytsa, go.sum bugun yuklab olayotgan kodingiz shu modulni yig'gan boshqa hamma odam yuklab olgan kod bilan baytma-bayt bir xil ekanini isbotlovchi kriptografik xeshlarni yozib qo'yadi:

matn
github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNNY/69lYlwv5YjXe8h0qgQ=
github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ=

Har bir yozuv modul versiyasini uning mazmuni xeshi va go.mod faylining alohida xeshi bilan juftlaydi. Go bog'liqlikni olganda, uni ishlatishdan oldin shu xeshlarga — hamda standart holda sum.golang.org dagi ochiq nazorat summalari bazasiga — qarab tekshiradi, ya'ni buzilgan mirror (oyna server) yoki o'zgartirilgan soxta paketlar sezilmasdan ikkilik faylingizga kirib qolmaydi, balki darhol rad etiladi.

go.sum ni doim commit qiling. Bu qayta hosil qilinadigan kesh emas; bu sizning build'laringizni takrorlanuvchan, ta'minot zanjiringizni esa tekshirib bo'ladigan qiladigan narsa.

50-bob buni noto'g'ri qilishning xavfsizlik oqibatlari haqida ko'proq gapiradi.

Semantik versiyalash va minimal versiya tanlash

Go modul versiyalari semantik versiyalashga amal qiladi — vMAJOR.MINOR.PATCH — bunda katta (major) o'zgarish moslikni buzadigan o'zgarishlarni bildiradi, kichik (minor) o'zgarish orqaga moslikni saqlagan holda yangi imkoniyat qo'shadi, patch esa xatti-harakatni boshqa jihatdan o'zgartirmasdan xatolarni tuzatadi. v1 dan oldingi versiyalar (v0.x.y) umuman hech qanday moslik kafolatini bermaydi.

Goda paket menejerlaridagi kabi ^1.2.3 yoki ~1.2.3 kabi murakkab versiya oraliqlari (version ranges) yo'q. Buning o'rniga Go Minimal Versiya Tanlash (Minimal Version Selection — MVS) algoritmidan foydalanadi: u butun bog'liqliklar grafigida ko'rsatilgan aniq versiyalarni tahlil qiladi va har bir paket uchun talab qilingan eng kichik, lekin barcha talablarga javob beradigan versiyani tanlaydi. Masalan, agar sizning modulingiz A v1.2.0 va B v1.1.0 ga bog'liq bo'lsa, A moduli o'z navbatida C v1.0.0 ni, B moduli esa C v1.3.0 ni talab qilsa, loyiha C v1.3.0 bilan yig'iladi. Bu barcha talablarni qanoatlantiruvchi eng minimal versiyadir.

Natija doimo deterministik bo'ladi: butun modul grafigi bo'ylab bir xil require qatorlari to'plami har doim aynan bir xil versiyalarga olib keladi va ertaga kutilmaganda boshqacha versiya tanlanib qolishi xavfi mutlaqo yo'q.

go list -m all build uchun haqiqatan tanlangan versiyalarni ko'rsatadi, go mod why -m <yo'l> esa muayyan modul grafikka umuman nega tushib qolganini tushuntiradi.

Bog'liqliklarni yangilab turish

Nima eskirganini shu buyruq bilan ko'ring:

bash
go list -m -u all

u har bir bog'liqlikning joriy versiyasini va qavs ichida mavjud eng so'nggisini chiqaradi. Yangilash — bu go get, kerakli miqyosda qo'llaniladi:

bash
go get -u ./...                       # hammasini eng so'nggi minor/patch ga yangilash
go get -u github.com/gorilla/mux      # bitta bog'liqlikni yangilash
go get github.com/gorilla/mux@v1.9.0  # aniq versiyaga mahkamlash

Go modullarida katta versiya o'zgarishi import yo'lining o'zini o'zgartiradi — v2 va undan yuqorisi versiya qo'shimchasini qo'shadi — aynan shu sababdan bitta modulning v1 va v2 versiyalari bitta build grafigida to'qnashmasdan yonma-yon yashay oladi:

bash
go get github.com/user/package/v2

Yangilash miqyosi qanday bo'lishidan qat'i nazar, keyingi qadamlar yangilashning o'zidan ko'ra muhimroq: natijaga ishonishdan oldin go mod tidy ni, so'ng to'liq test to'plamini, so'ng to'liq build'ni ishga tushiring.

bash
go get -u ./...
go mod tidy
go test ./...
go build ./...

Bog'liqliklarni birdaniga emas, balki birma-bir (yoki kichik, o'zaro bog'liq guruhlar ko'rinishida) yangilash yuzaga kelishi mumkin bo'lgan regressiyani va xatolik manbasini tezda aniqlashni ancha osonlashtiradi. Ayniqsa birvarakayiga barcha paketlar yangilanganda, chuqur ichki (tranzitiv) bog'liqliklar sababli loyiha ishlamay qolishi xavfi yuqori bo'ladi.

Vendoring, almashtirish va lokal ishlab chiqish

go mod vendor har bir bog'liqlik kodini modulingiz ichidagi vendor/ papkasiga nusxalaydi, bu esa build jarayonini tarmoqqa umuman ulanmasdan go build -mod=vendor bilan bajarish imkonini beradi. Bu izolyatsiyalangan (offline) muhitlar, qat'iy korporativ xavfsizlik qoidalari yoki loyihaning barcha tashqi kodlarini bir joyga to'liq muhrlash uchun juda qulay:

bash
go mod vendor
go build -mod=vendor

go mod download modullarni go.mod ga tegmasdan lokal modullar keshiga yuklaydi, go mod verify esa keshlangan modullar hali ham yozib qo'yilgan nazorat summalariga to'liq mos kelishini tekshiradi.

replace direktivasi biror modulni boshqasiga — ko'pincha lokal katalog yo'liga almashtirish uchun xizmat qiladi. Bu kutubxona va asosiy loyihani oraliq versiyalarni repozitoriyga chiqarmasdan, bir vaqtning o'zida birgalikda ishlab chiqish uchun ishlatiladi:

matn
replace github.com/user/mylib => ../mylib

yoki vaqtincha yoxud doimiy ravishda fork qilingan repozitoriyga yo'naltirish mumkin:

matn
replace github.com/user/package => github.com/yourfork/package v1.2.3

Kutubxona va uni chaqiruvchi loyihani yonma-yon parallel tahrirlash uchun ko'p hollarda ish maydonlari (go work, keyingi bobda ko'rib chiqiladi) ancha qulay vosita hisoblanadi, chunki ular go.mod faylini o'zgartirishni talab qilmaydi. replace dan esa ushbu almashtirish Git'ga commit qilinishi hamda butun jamoa yoki CI/CD tizimi uchun ham amal qilishi shart bo'lgan holatlardagina foydalaning.

Tool (asboblar) bog'liqliklarini boshqarish

Ko'plab loyihalar shunchaki import qilingan paketlardan tashqari boshqa vositalarga ham bog'liq bo'ladi — masalan, kod generatori, linter yoki mock generatorlari. Ular ham loyiha kutubxonalari kabi aniq bir versiyaga mahkamlanishi kerak, toki jamoadagi har bir dasturchi va CI/CD tizimi aynan bir xil versiyadan foydalansin. Go 1.24 dan oldin buning idiomatik usuli kichik bir hiyla edi: odatda tools.go deb nomlangan, yakuniy binary faylga tushmasligi uchun //go:build tools cheklovi qo'yilgan fayl yaratilar va uning ichiga kerakli paketlar bo'sh import (_ "...") bilan yozib qo'yilardi:

example.go
//go:build tools

package tools

import (
	_ "golang.org/x/tools/cmd/stringer"
)

Bu usul ishlardi, ammo bu to'laqonli yechim emas, shunchaki chetlab o'tish yo'li edi. Go 1.24 dan boshlab asbob-uskunalar bog'liqligini boshqarish uchun rasmiy mexanizm taqdim etildi: go.mod dagi tool direktivasi. U go get -tool buyrug'i orqali avtomatik ravishda qo'shiladi:

bash
go get -tool golang.org/x/tools/cmd/stringer

bu bog'liqlikni bevosita go.mod ga yozadi:

matn
tool golang.org/x/tools/cmd/stringer

Shundan so'ng go tool stringer ... buyrug'i uni go.mod da ko'rsatilgan aniq versiyada ishga tushiradi — dasturchining PATH tizimida tasodifan o'rnatilgan boshqa versiyalarga bog'liqlik qolmaydi. Argumentsiz go tool buyrug'i esa modulga biriktirilgan barcha vositalar ro'yxatini chiqaradi. Maxsus tool kalit so'zi orqali barcha vositalarni birdan boshqarish mumkin: masalan, go get tool yoki go install tool bitta buyruq bilan kuzatuvdagi barcha yordamchi dasturlarni yangilaydi yoki o'rnatadi. Agar siz eski loyihalarni qo'llab-quvvatlayotgan bo'lsangiz, ulardagi tools.go fayllarini rasmiy tool direktivasiga o'tkazish tavsiya etiladi.

Yopiq (private) modullar va modul proksisi

Standart holatda go get barcha modullarni Google'ning ochiq proksi serveri (proxy.golang.org) orqali yuklab oladi va ularni ochiq nazorat summalari bazasi (sum.golang.org) orqali tekshiradi. Tashkilotning yopiq (private) repozitoriylari uchun Goga qaysi paketlarni ochiq proksi orqali emas, bevosita repozitoriydan olish kerakligini ko'rsatish lozim:

bash
go env -w GOPRIVATE=github.com/yourcompany/*

va Git orqali ularga ulanishni to'g'ri sozlash kerak (odatda HTTPS o'rniga SSH kalitlaridan foydalaniladi):

bash
git config --global url."git@github.com:".insteadOf "https://github.com/"

Go 1.24 da autentifikatsiya uchun yana bir zamonaviy imkoniyat qo'shildi: GOAUTH muhit o'zgaruvchisi. U go buyrug'iga yopiq modullar uchun chiquvchi so'rovlarni qanday autentifikatsiya qilishni aniq ko'rsatish imkonini beradi (masalan, token yoki hisob ma'lumotlarini taqdim etuvchi maxsus buyruqni chaqirish yoki netrc fayliga tayanish orqali) — bu Git sozlamalarini o'zgartirish zaruratini yo'qotadi. Agar hozirgi SSH almashtirish usuli qiyinchilik tug'dirayotgan bo'lsa, GOAUTH qulay yechim bo'ladi.

To'liq izolyatsiyalangan ichki tarmoqlarda proksini ham, nazorat summalari bazasini ham butunlay o'chirib qo'yish mumkin:

bash
go env -w GOPROXY=direct
go env -w GOSUMDB=off

— ammo shuni unutmangki, nazorat summasi tekshiruvini o'chirish ta'minot zanjiri (supply chain) yaxlitligidan voz kechish demakdir, shuning uchun bu qadamni faqat o'ta zarur va asoslangan hollardagina qo'llash kerak.

Yuklab olingan barcha modullar kompyuteringizdagi umumiy keshda — $GOMODCACHE (standart holatda $GOPATH/pkg/mod) ichida saqlanadi. Agar keshni butunlay tozalab, hammasini qayta yuklash kerak bo'lsa, go clean -modcache buyrug'i beriladi.

Muammolar va xatoliklarni bartaraf etish

"Cannot find module providing package" xatosi deyarli har doim quyidagi sabablardan birini bildiradi: import yo'lida xatolik bor, paket hali yuklab olinmagan (go get bilan paketni yuklang) yoki yopiq repozitoriy uchun GOPRIVATE to'g'ri sozlanmagan.

Ikkita turli paket uchinchi bir umumiy bog'liqlikning bir-biriga to'g'ri kelmaydigan versiyalarini talab qilganda yuzaga keladigan versiya to'qnashuvlari ulardan birini yoki ikkalasini mos versiyaga yangilash orqali, yoxud oxirgi chora sifatida replace direktivasi orqali hal qilinadi.

Nazorat summasining mos kelmasligi (go.sum tekshiruvida xatolik chiqishi) — xavfsizlikka signal sifatida qaralishi shart. Avval go mod verify ni ishga tushiring, va faqatgina bu nomuvofiqlik paket xavfsizligining buzilishi emas, balki qonuniy yangilanish ekanligiga to'liq ishonch hosil qilgandagina go.sum ni qayta generatsiya qiling.

Goda bog'liqliklarni boshqarish kundalik ish jarayonining eng kam bosh og'ritadigan qismlaridan biridir va bu aynan maqsad qilingan natijadir: ikkita aniq fayl, ixcham buyruqlar to'plami hamda sodda, deterministik va oldindan bashorat qilsa bo'ladigan algoritm.

Keyingi bo'limda loyihangiz bir nechta o'zaro bog'liq modullardan iborat bo'lganda nima qilish kerakligini — Go ish maydonlari (go.work) bilan bir vaqtda bir nechta modul ustida ishlashni ko'rib chiqamiz.