Tarkibga o'tish

Tashqi kutubxonalar va dependency management

Go bilan birga fmt, strings, net/http kabi standart paketlar o‘rnatiladi. Lekin real loyihada standart kutubxonada yo‘q imkoniyat ham kerak bo‘lishi mumkin. Bunday paytda boshqa modul taqdim etgan tashqi paketdan foydalanamiz.

Tashqi kod loyihaga bog‘liqlik (dependency) bo‘lib kiradi. Dependency management — shu kodning versiyasini tanlash, yuklash, yangilash va buildlarda bir xil qolishini boshqarish jarayoni.

Paket, modul va kutubxona

Bu atamalar bir-biriga yaqin, ammo vazifasi boshqa:

  • paket (package) — odatda bitta katalogdagi, bir xil package nomiga ega Go fayllari;
  • modul (module)go.mod fayli bilan belgilangan va bir yoki bir nechta paketni o‘z ichiga oladigan versiyalanadigan birlik;
  • kutubxona (library) — qayta ishlatish uchun yozilgan kodning umumiy nomi. Go buyruqlari asosan paket va modul atamalarini ishlatadi.

Masalan, github.com/google/uuid bir vaqtning o‘zida modul yo‘li va uning ildiz paketining import yo‘lidir. Kattaroq modulda esa example.com/project/client kabi bir nechta paket bo‘lishi mumkin.

Go paketlari bitta markaziy bazada saqlanmaydi. Ularning hujjatlarini pkg.go.dev orqali qidirish mumkin, manba kodi esa modul yo‘li ko‘rsatgan Git repozitoriyda joylashadi.

Tashqi paket nima uchun kerak?

Tashqi paket tayyor va sinovdan o‘tgan yechimdan qayta foydalanish imkonini beradi. Masalan, UUID yaratish, ma’lumotlar bazasi drayveri yoki ma’lum bir protokol bilan ishlash uchun paket tanlash mumkin.

Har bir dependency bilan birga mas’uliyat ham keladi:

  • API yangi versiyada o‘zgarishi mumkin;
  • dependency o‘z navbatida boshqa modullarga bog‘liq bo‘lishi mumkin;
  • litsenziya loyiha talablariga mos kelmasligi mumkin;
  • xato yoki xavfsizlik muammosi sizning dasturingizga ham ta’sir qiladi.

Shuning uchun avval masalani standart kutubxona hal qila olishini tekshiring. Tashqi paket kerak bo‘lsa, uning hujjati, litsenziyasi, yangilanish holati va API barqarorligini ko‘rib chiqing.

Loyihani tayyorlash

Tashqi dependency modul ichida boshqariladi. Yangi katalog yaratib, modulni boshlaymiz:

mkdir uuid-demo
cd uuid-demo
go mod init example.com/uuid-demo

example.com/uuid-demo — modul yo‘li. Faqat lokal mashq uchun bu nom ishlaydi. Ochiq repozitoriyga joylanadigan modulda odatda kodning haqiqiy manzili, masalan, github.com/foydalanuvchi/uuid-demo yoziladi.

Buyruq go.mod faylini yaratadi:

go.mod
module example.com/uuid-demo

go 1.xx

go direktivasidagi qiymat o‘rnatilgan toolchain va loyiha sozlamasiga bog‘liq. Uni darsdagi qiymatga majburan almashtirish shart emas. Modullar keyingi darsda batafsil ko‘rib chiqiladi.

Birinchi tashqi paket

UUID yaratish uchun github.com/google/uuid paketidan foydalanamiz. main.go faylini yarating:

main.go
package main

import (
    "fmt"

    "github.com/google/uuid"
)

func main() {
    id := uuid.New()
    fmt.Println("UUID:", id)
}

So‘ng dependencylarni loyiha fayllari bilan moslang va dasturni ishga tushiring:

go mod tidy
go run .

Natijadagi UUID har ishga tushirishda o‘zgaradi:

UUID: 550e8400-e29b-41d4-a716-446655440000

import paketning import yo‘lini bildiradi. uuid.New() yangi qiymat yaratadi. go mod tidy importlarni tahlil qilib, kerakli modulni go.modga qo‘shadi va kodni modul keshiga yuklaydi. Natijadagi aniq UUID tasodifiy bo‘lgani uchun namunadagi qiymat bilan bir xil chiqishi kutilmaydi.

Xuddi shu dependency aniq buyruq bilan ham qo‘shilishi mumkin:

go get github.com/google/uuid@latest

go get modul versiyasini tanlaydi yoki o‘zgartiradi. Paketni faqat kompyuterga “o‘rnatib qo‘yish” deb tushunish noto‘g‘ri: tanlangan versiya joriy modulning go.mod faylida qayd etiladi. Importlardan keyin go mod tidy ishlatish esa kundalik ish oqimida qulayroq.

Info

CLI dasturini o‘rnatish boshqa amal. Buning uchun go install example.com/cmd/tool@version ishlatiladi. go install joriy loyihaning go.mod fayliga dependency qo‘shmaydi.

Bevosita va bilvosita dependency

Sizning kodingiz to‘g‘ridan-to‘g‘ri import qilgan modul bevosita dependency hisoblanadi. U ishlatadigan boshqa modul esa bilvosita dependency (indirect dependency) bo‘lishi mumkin.

Ularni ko‘rish uchun:

go list -m all

Bilvosita dependencylarni qo‘lda o‘chirib tashlash kerak emas. go mod tidy paket importlari va modul grafigiga qarab kerakli yozuvlarni boshqaradi.

Versiyani tanlash va yangilash

Aniq versiya takrorlanuvchi build uchun muhim:

go get github.com/google/[email protected]

Bu yerda versiya faqat buyruq shaklini ko‘rsatish uchun berilgan. Amaliy loyihada kerakli versiyani paketning relizlari va o‘zgarishlar ro‘yxatidan tanlang.

Asosiy buyruqlar:

go list -m -u all
go get example.com/[email protected]
go get example.com/modul@latest
go mod tidy

go list -m -u all mavjud yangilanishlarni ko‘rsatadi. @latest eng yangi mavjud mos versiyani tanlashni so‘raydi, lekin yangilanish API mosligini kafolatlamaydi. Dependency yangilangach testlar va buildni qayta ishlating.

Semantic Versioning bo‘yicha v1.4.0dan v1.5.0ga o‘tish odatda mos bo‘lishi kutiladi. v2 esa katta, mos kelmaydigan o‘zgarish bo‘lishi mumkin. Go modullarida v2 va undan keyingi asosiy versiya ko‘pincha import yo‘liga ham qo‘shiladi:

import "example.com/lib/v2"

go.mod va go.sum

go.mod modul yo‘li, Go direktivasi va tanlangan dependency versiyalarini saqlaydi. go.sum esa yuklangan modul fayllari uchun kriptografik nazorat summalarini saqlaydi.

go.sum:

  • dependency kodi xavfsiz yoki xatosiz ekanini isbotlamaydi;
  • barcha yozuvlar bevosita import qilinayotganini anglatmaydi;
  • oldin olingan modul kontenti keyin o‘zgarmaganini tekshirishga yordam beradi.

go.mod va go.sum fayllarini odatda Git’ga commit qilish kerak. Modul keshi esa lokal keshdir; uni repozitoriyga qo‘shilmaydi.

Keng tarqalgan xatolar

Modul yaratmasdan go get ishlatish

Dependency loyiha kontekstida boshqarilishi uchun avval go mod init bilan modul yarating. Mavjud loyihada esa yuqori kataloglardan birida go.mod borligini tekshiring:

go env GOMOD

Import yo‘li o‘rniga repozitoriy sahifasini yozish

Import yo‘lini paket hujjatidan oling. Modul repozitori ichidagi har bir katalog import qilinadigan paket bo‘lavermaydi.

go.sumni o‘chirib muammoni yashirish

Checksum xatosi bo‘lsa, faylni ko‘r-ko‘rona o‘chirmang. Modul manbasi, proxy sozlamasi va versiya o‘zgarmaganini tekshiring. Xato dependency kontenti kutilmaganda o‘zgarganini ko‘rsatishi mumkin.

Dependency yangilanishini tekshirmaslik

Yangi versiya kompilyatsiyadan o‘tsa ham xatti-harakat o‘zgargan bo‘lishi mumkin. Changelogni o‘qing, testlarni va go vet ./... kabi loyiha tekshiruvlarini bajaring.

Xulosa

Tashqi paket tayyor koddan foydalanishga yordam beradi, lekin uning versiyasi va xavfi ham loyiha tarkibiga kiradi. Paketni hujjati va litsenziyasiga qarab tanlang, dependencylarni go.mod va go.sum orqali qayd eting, yangilanishni esa test bilan tekshiring. Keyingi darslarda Go dokumentatsiyasi va test yozishni ko‘ramiz.

Misollar

Quyidagi misollarda github.com/google/uuid modulining v1.6.0 versiyasi ishlatiladi. Har bir misolni alohida bo‘sh katalogda bajaring.

1. Dependency versiyasini aniq belgilash

Bu misolda tashqi modul aniq versiya bilan qo‘shilib, yangi UUID yaratiladi.

package main

import (
    "fmt"

    "github.com/google/uuid"
)

func main() {
    fmt.Println("UUID:", uuid.NewString())
}
go mod init example.com/pinned-uuid
go get github.com/google/[email protected]
go run .

@v1.6.0 dependency versiyasini aniq tanlaydi. uuid.NewString() har ishga tushganda yangi qiymat qaytaradi, shu sabab natijadagi UUID oldindan ma’lum emas. Aniq versiya turli muhitlarda bir xil dependency tanlanishiga yordam beradi.

2. Importdan keyin go mod tidy ishlatish

Bu misolda kod avval yoziladi, keyin go mod tidy yetishmayotgan dependency yozuvini qo‘shadi.

package main

import (
    "fmt"

    "github.com/google/uuid"
)

func main() {
    id := uuid.New()
    fmt.Println(id.String())
}
go mod init example.com/tidy-uuid
go get github.com/google/[email protected]
go mod tidy
go run .

Aniq versiya go get bilan tanlanadi. go mod tidy esa importlar asosida go.mod va go.sumni yakuniy holatga keltiradi. uuid.New() qiymat qaytaradi, String() metodi uni matn ko‘rinishiga o‘giradi.

3. Tanlangan modul versiyasini ko‘rish

Bu misolda UUID matndan o‘qiladi va build ro‘yxatidagi aniq versiya tekshiriladi.

package main

import (
    "fmt"

    "github.com/google/uuid"
)

func main() {
    id, err := uuid.Parse("550e8400-e29b-41d4-a716-446655440000")
    if err != nil {
        fmt.Println("Xato:", err)
        return
    }

    fmt.Println(id)
}
go mod init example.com/list-version
go get github.com/google/[email protected]
go list -m github.com/google/uuid
go run .

go list -m github.com/google/uuid modul grafigida tanlangan yo‘l va versiyani ko‘rsatadi. Bu misolda natija v1.6.0 bo‘lishi kerak. uuid.Parse() noto‘g‘ri matn uchun error qaytargani sabab xato avval tekshiriladi.

4. Mavjud versiyalar ro‘yxatini olish

Bu misolda dependency versiyalari ko‘riladi, dastur esa UUID formatini tekshiradi.

package main

import (
    "fmt"

    "github.com/google/uuid"
)

func main() {
    value := "550e8400-e29b-41d4-a716-446655440000"
    if err := uuid.Validate(value); err != nil {
        fmt.Println("Noto‘g‘ri UUID:", err)
        return
    }

    fmt.Println("UUID formati to‘g‘ri")
}
go mod init example.com/uuid-versions
go get github.com/google/[email protected]
go list -m -versions github.com/google/uuid
go run .

go list -m -versions modul uchun topilgan versiyalarni ko‘rsatadi, lekin loyihani avtomatik yangilamaydi. Kerakli versiya reliz yozuvlari va API mosligi tekshirilgandan keyin tanlanadi. Validate() to‘g‘ri format uchun nil qaytaradi.

5. Dependency nima sababdan kerakligini tekshirish

Bu misolda UUID map kaliti sifatida ishlatiladi va go mod why dependencyga olib kelgan import yo‘lini ko‘rsatadi.

package main

import (
    "fmt"

    "github.com/google/uuid"
)

func main() {
    id := uuid.MustParse("550e8400-e29b-41d4-a716-446655440000")
    users := map[uuid.UUID]string{
        id: "Ali",
    }

    fmt.Println(users[id])
}
go mod init example.com/why-uuid
go get github.com/google/[email protected]
go mod why -m github.com/google/uuid
go run .

go mod why -m asosiy moduldan dependencygacha bo‘lgan paketlar yo‘lini chiqaradi. uuid.UUID solishtiriladigan tur bo‘lgani uchun map kaliti sifatida ishlatiladi. MustParse() faqat kod ichida oldindan ma’lum va o‘zgarmas to‘g‘ri qiymat uchun tanlangan.

6. Modul bog‘lanishlari grafigini ko‘rish

Bu misolda go mod graph modul bog‘lanishlarini chiqaradi, dastur esa version 7 UUID yaratadi.

package main

import (
    "fmt"

    "github.com/google/uuid"
)

func main() {
    id, err := uuid.NewV7()
    if err != nil {
        fmt.Println("UUID yaratilmadi:", err)
        return
    }

    fmt.Println(id)
}
go mod init example.com/uuid-graph
go get github.com/google/[email protected]
go mod graph
go run .

go mod graph har bir satrda talab qiluvchi va talab qilinuvchi modulni ko‘rsatadi. Kichik loyihada grafik qisqa bo‘lishi mumkin. uuid.NewV7() qiymat bilan birga error qaytaradi, shu sabab natijani ishlatishdan oldin xato tekshiriladi.

7. Dependencylarni oldindan yuklash va tekshirish

Bu misolda modul kodi keshga yuklanadi, nazorat summalari tekshiriladi va 16 baytdan UUID yaratiladi.

package main

import (
    "fmt"

    "github.com/google/uuid"
)

func main() {
    data := make([]byte, 16)
    id, err := uuid.FromBytes(data)
    if err != nil {
        fmt.Println("Xato:", err)
        return
    }

    fmt.Println(id)
}
go mod init example.com/verify-uuid
go get github.com/google/[email protected]
go mod download
go mod verify
go run .

go mod download kerakli modullarni lokal keshga yuklaydi. go mod verify keshdagi kontent nazorat summalariga mosligini tekshiradi. UUID aynan 16 bayt bo‘lgani uchun slice uzunligi 16; make yaratgan barcha baytlar dastlab 0.

8. Yangilanishni alohida tekshirish

Bu misolda mavjud yangilanishlar ko‘riladi, lekin dependency avtomatik almashtirilmaydi.

package main

import (
    "fmt"

    "github.com/google/uuid"
)

func main() {
    first := uuid.NewString()
    second := uuid.NewString()

    fmt.Println("Bir xilmi:", first == second)
}
go mod init example.com/check-version
go get github.com/google/[email protected]
go list -m -u github.com/google/uuid
go run .

go list -m -u tanlangan versiya bilan birga yangiroq versiya mavjudligini ko‘rsatishi mumkin. Buyruq go.modni o‘zgartirmaydi. Yangilashdan oldin reliz yozuvlari o‘qiladi va loyiha tekshiruvlari bajariladi. Ikki yangi UUID odatda turli bo‘ladi, shu sabab taqqoslash false qaytaradi.

9. Ishlatilmaydigan dependencyni olib tashlash

Bu misoldagi yakuniy kod tashqi paketni import qilmaydi. go mod tidy oldin qo‘shilgan, ammo endi ishlatilmaydigan dependencyni olib tashlaydi.

package main

import "fmt"

func main() {
    fmt.Println("Tashqi dependency kerak emas")
}
go mod init example.com/remove-dependency
go get github.com/google/[email protected]
go mod tidy
go run .

Kodda github.com/google/uuid importi yo‘q. Shu sabab go mod tidy uning require yozuvini go.moddan olib tashlaydi. Dependency qatorini qo‘lda o‘chirish o‘rniga importlar bilan moslashtirish uchun shu buyruq ishlatiladi.

10. Dependencylarni vendor katalogiga yig‘ish

Bu misolda build uchun kerakli tashqi paket kodi loyiha ichidagi vendor katalogiga ko‘chiriladi.

package main

import (
    "fmt"

    "github.com/google/uuid"
)

func main() {
    id := uuid.MustParse("550e8400-e29b-41d4-a716-446655440000")
    fmt.Println("UUID versiyasi:", id.Version())
}
go mod init example.com/vendor-uuid
go get github.com/google/[email protected]
go mod vendor
go run -mod=vendor .

go mod vendor build uchun kerakli dependency paketlarini vendor katalogiga joylaydi. -mod=vendor buyruqqa shu nusxadan foydalanishni aniq bildiradi. vendor katalogini Git’da saqlash loyiha siyosatiga bog‘liq; go.mod va go.sum baribir dependency tanlovining asosiy fayllari bo‘lib qoladi.