Pengantar Modul Go

Rilis mendatang dari versi 1.11 bahasa pemrograman Go akan membawa dukungan eksperimental untuk modul - sistem manajemen ketergantungan baru untuk Go. (terjemahan catatan: rilis berlangsung )


Baru-baru ini, saya sudah menulis posting kecil tentang ini . Sejak itu, ada sesuatu yang sedikit berubah, dan kami menjadi lebih dekat dengan rilis, jadi bagi saya tampaknya sudah tiba saatnya untuk artikel baru - mari kita tambahkan lebih banyak latihan.


Jadi, inilah yang akan kita lakukan: membuat paket baru dan kemudian membuat beberapa rilis untuk melihat cara kerjanya.


Pembuatan modul


Pertama, buat paket kami. Sebut saja testmod. Detail penting: direktori paket harus ditempatkan di luar $GOPATH , karena, di dalamnya, dukungan modul dinonaktifkan secara default . Modul Go adalah langkah pertama menuju pengabaian total $GOPATH di masa depan.


 $ mkdir testmod $ cd testmod 

Paket kami cukup sederhana:


 package testmod import "fmt" // Hi returns a friendly greeting func Hi(name string) string { return fmt.Sprintf("Hi, %s", name) } 

Paket sudah siap, tetapi belum menjadi modul . Mari kita perbaiki.


 $ go mod init github.com/robteix/testmod go: creating new go.mod: module github.com/robteix/testmod 

Kami memiliki file baru bernama go.mod di direktori paket dengan konten berikut:


 module github.com/robteix/testmod 

Sedikit, tapi itulah yang mengubah paket kami menjadi modul .


Sekarang kita bisa memasukkan kode ini ke dalam repositori:


 $ git init $ git add * $ git commit -am "First commit" $ git push -u origin master 

Sejauh ini, siapa pun yang ingin menggunakan paket kami akan mendaftar, go get :


 $ go get github.com/robteix/testmod 

Dan perintah ini akan membawa kode terbaru dari cabang master . Opsi ini masih berfungsi, tetapi akan lebih baik jika kita tidak melakukannya lagi, karena sekarang "ada cara yang lebih baik." Mengambil kode langsung dari cabang master , sebenarnya, berbahaya, karena kita tidak pernah tahu pasti bahwa pembuat paket tidak membuat perubahan yang akan β€œmerusak” kode kita. Untuk mengatasi masalah ini, modul Go diciptakan.


Penyimpangan kecil tentang modul versi


Modul Go diversiasikan, ditambah ada beberapa kekhususan dari masing-masing versi. Anda harus terbiasa dengan konsep yang mendasari versi semantik .


Selain itu, Go menggunakan tag repositori saat mencari versi, dan beberapa versi berbeda dari yang lain: misalnya, versi 2 dan lebih banyak harus memiliki jalur impor yang berbeda dari versi 0 dan 1 (kita akan membahas ini).


Secara default, Go mengunduh versi terbaru , yang memiliki tag yang tersedia di repositori.
Ini adalah fitur penting, karena dapat digunakan ketika bekerja dengan cabang master .


Bagi kami sekarang, penting bahwa ketika membuat rilis paket kami, kami perlu memberi label dengan versi di repositori.


Ayo lakukan.


Membuat rilis pertama Anda


Paket kami siap dan kami dapat "meluncurkan" itu ke seluruh dunia. Kami melakukan ini menggunakan label berversi. Biarkan nomor versi menjadi 1.0.0:


 $ git tag v1.0.0 $ git push --tags 

Perintah-perintah ini membuat tag di repositori Github saya yang menandai komit saat ini sebagai rilis 1.0.0.


Go tidak bersikeras tentang ini, tetapi merupakan ide bagus untuk membuat cabang baru tambahan ("v1") yang dapat kami kirim tambalan.


 $ git checkout -b v1 $ git push -u origin v1 

Sekarang kita dapat bekerja di cabang master tanpa khawatir bahwa kita dapat merusak rilis kita.


Menggunakan modul kami


Mari kita gunakan modul yang dibuat. Kami akan menulis program sederhana yang mengimpor paket baru kami:


 package main import ( "fmt" "github.com/robteix/testmod" ) func main() { fmt.Println(testmod.Hi("roberto")) } 

Sampai sekarang, Anda akan menjalankan go get github.com/robteix/testmod untuk mengunduh paket, tetapi dengan modul itu menjadi lebih menarik. Pertama, kita perlu mengaktifkan dukungan modul di program baru kita.


 $ go mod init mod 

Seperti yang mungkin Anda harapkan, berdasarkan apa yang Anda baca sebelumnya, file go.mod baru muncul di direktori dengan nama modul di dalamnya:


 module mod 

Situasi menjadi lebih menarik ketika kami mencoba menyusun program kami:


 $ go build go: finding github.com/robteix/testmod v1.0.0 go: downloading github.com/robteix/testmod v1.0.0 

Seperti yang Anda lihat, perintah go secara otomatis menemukan dan mengunduh paket yang diimpor oleh program kami.
Jika kami memeriksa file go.mod kami, kami akan melihat bahwa ada sesuatu yang berubah:


 module mod require github.com/robteix/testmod v1.0.0 

Dan kami mendapat file baru bernama go.sum , yang berisi hash dari paket untuk memeriksa versi dan file yang benar.


 github.com/robteix/testmod v1.0.0 h1:9EdH0EArQ/rkpss9Tj8gUnwx3w5p0jkzJrd5tRAhxnA= github.com/robteix/testmod v1.0.0/go.mod h1:UVhi5McON9ZLc5kl5iN2bTXlL6ylcxE9VInV71RrlO8= 

Membuat rilis rilis perbaikan bug


Sekarang, katakanlah kami menemukan masalah dalam paket kami: tidak ada tanda baca dalam sambutannya!
Sebagian orang akan geram, karena salam ramah kita sudah tidak ramah lagi.
Mari perbaiki ini dan lepaskan versi baru:


 // Hi returns a friendly greeting func Hi(name string) string { - return fmt.Sprintf("Hi, %s", name) + return fmt.Sprintf("Hi, %s!", name) } 

Kami membuat perubahan ini tepat di cabang v1 , karena tidak ada hubungannya dengan apa yang akan kami lakukan selanjutnya di cabang v2 , tetapi dalam kehidupan nyata, mungkin Anda harus membuat perubahan ini di master dan kemudian mendukungnya ke v1 . Bagaimanapun, perbaikannya harus di cabang v1 dan kita harus menandai ini sebagai rilis baru.


 $ git commit -m "Emphasize our friendliness" testmod.go $ git tag v1.0.1 $ git push --tags origin v1 

Pembaruan modul


Secara default, Go tidak memperbarui modul tanpa permintaan. "Dan itu bagus," karena kita semua ingin prediktabilitas dalam bangunan kami. Jika modul Go akan diperbarui secara otomatis setiap kali versi baru dirilis, kami akan kembali ke "zaman kegelapan sebelum-Go1.11". Tapi tidak, kami harus memberi tahu Go untuk memperbarui modul untuk kami.


Dan kami akan melakukannya dengan bantuan teman lama kami - go get :


  • jalankan go get -u untuk menggunakan rilis minor atau patch terakhir (mis. perintah akan memperbarui dari 1.0.0 ke, katakanlah, 1.0.1 atau 1.1.0, jika versi seperti itu tersedia)


  • run go get -u=patch untuk menggunakan versi patch terbaru (mis. paket akan diperbarui ke 1.0.1, tetapi tidak ke 1.1.0)


  • jalankan go get package@version untuk meningkatkan ke versi tertentu (misalnya, github.com/robteix/testmod@v1.0.1 )



Tidak ada cara dalam daftar ini untuk meningkatkan ke versi utama terbaru. Ada alasan bagus untuk ini, karena kita akan segera melihat.


Karena program kami menggunakan versi 1.0.0 dari paket kami dan kami baru saja membuat versi 1.0.1, salah satu dari perintah berikut akan memperbarui kami ke 1.0.1:


 $ go get -u $ go get -u=patch $ go get github.com/robteix/testmod@v1.0.1 

Setelah memulai (misalkan go get -u ), go.mod kami telah berubah:


 module mod require github.com/robteix/testmod v1.0.1 

Versi utama


Menurut spesifikasi versi semantik, versi utama berbeda dari versi minor. Versi besar dapat merusak kompatibilitas. Dari sudut pandang modul Go, versi utama adalah paket yang sama sekali berbeda .


Mungkin terdengar liar pada awalnya, tetapi masuk akal: dua versi perpustakaan yang tidak kompatibel satu sama lain adalah dua perpustakaan yang berbeda.


Mari kita lakukan perubahan besar dalam paket kami. Misalkan, seiring waktu, menjadi jelas bagi kami bahwa API kami terlalu sederhana, terlalu terbatas untuk kasus pengguna pengguna kami, jadi kami perlu mengubah fungsi Hi() untuk menerima bahasa sambutan sebagai parameter:


 package testmod import ( "errors" "fmt" ) // Hi returns a friendly greeting in language lang func Hi(name, lang string) (string, error) { switch lang { case "en": return fmt.Sprintf("Hi, %s!", name), nil case "pt": return fmt.Sprintf("Oi, %s!", name), nil case "es": return fmt.Sprintf("Β‘Hola, %s!", name), nil case "fr": return fmt.Sprintf("Bonjour, %s!", name), nil default: return "", errors.New("unknown language") } } 

Program yang ada menggunakan API kami akan rusak karena mereka a) tidak lulus bahasa sebagai parameter dan b) jangan mengharapkan pengembalian kesalahan. API baru kami tidak lagi kompatibel dengan versi 1.x, jadi penuhi versi 2.0.0.


Saya sebutkan sebelumnya bahwa beberapa versi memiliki fitur, dan sekarang ini masalahnya.
Versi 2 atau yang lebih baru harus mengubah jalur impor. Sekarang ini adalah perpustakaan yang berbeda.


Kami akan melakukan ini dengan menambahkan jalur versi baru ke nama modul kami.


 module github.com/robteix/testmod/v2 

Segala sesuatu yang lain adalah sama: push, beri label bahwa itu v2.0.0 (dan opsional sod cabang v2)


 $ git commit testmod.go -m "Change Hi to allow multilang" $ git checkout -b v2 # optional but recommended $ echo "module github.com/robteix/testmod/v2" > go.mod $ git commit go.mod -m "Bump version to v2" $ git tag v2.0.0 $ git push --tags origin v2 # or master if we don't have a branch 

Pembaruan Versi Utama


Meskipun kami merilis versi baru yang tidak kompatibel dari perpustakaan kami, program yang ada tidak rusak , karena mereka terus menggunakan versi 1.0.1.
go get -u tidak akan mengunduh versi 2.0.0.


Tetapi pada titik tertentu, saya, sebagai pengguna perpustakaan, mungkin ingin meningkatkan ke versi 2.0.0, karena, misalnya, saya adalah salah satu dari pengguna yang membutuhkan dukungan untuk beberapa bahasa.


Untuk memperbarui, saya perlu mengubah program saya sesuai:


 package main import ( "fmt" "github.com/robteix/testmod/v2" ) func main() { g, err := testmod.Hi("Roberto", "pt") if err != nil { panic(err) } fmt.Println(g) } 

Sekarang, ketika saya menjalankan go build , ia "berhenti" dan mengunduh versi 2.0.0 untuk saya. Perhatikan bahwa meskipun jalur impor sekarang diakhiri dengan "v2", Go masih merujuk ke modul dengan nama aslinya ("testmod").


Seperti yang saya katakan, versi utama adalah paket yang berbeda. Dua modul Go ini tidak terhubung dengan cara apa pun. Ini artinya kita dapat memiliki dua versi yang tidak kompatibel dalam satu biner:


 package main import ( "fmt" "github.com/robteix/testmod" testmodML "github.com/robteix/testmod/v2" ) func main() { fmt.Println(testmod.Hi("Roberto")) g, err := testmodML.Hi("Roberto", "pt") if err != nil { panic(err) } fmt.Println(g) } 

Dan ini menghilangkan masalah umum dengan manajemen dependensi ketika dependensi bergantung pada versi berbeda dari perpustakaan yang sama.



Mari kita kembali ke versi sebelumnya, yang hanya menggunakan testmod 2.0.0 - jika kita memeriksa konten go.mod , kita akan melihat sesuatu:


 module mod require github.com/robteix/testmod v1.0.1 require github.com/robteix/testmod/v2 v2.0.0 

Secara default, Go tidak menghapus dependensi dari go.mod hingga Anda memintanya. Jika Anda memiliki dependensi yang tidak lagi diperlukan dan Anda ingin membersihkannya, Anda dapat menggunakan perintah tidy baru:


 $ go mod tidy 

Sekarang kita hanya memiliki dependensi yang benar-benar kita gunakan.


Penjual


Buka modul secara default, abaikan vendor/ direktori. Idenya adalah untuk secara bertahap menyingkirkan penjual 1 . Tetapi jika kita masih ingin menambahkan dependensi "terpisah" ke kontrol versi kita, kita dapat melakukan ini:


 $ go mod vendor 

Tim akan membuat vendor/ direktori di root proyek kami, yang berisi kode sumber semua dependensi.


Namun, go build secara default masih mengabaikan isi direktori ini. Jika Anda ingin mengumpulkan dependensi dari vendor/ direktori, Anda harus secara eksplisit memintanya.


 $ go build -mod vendor 

Saya berasumsi bahwa banyak pengembang yang ingin menggunakan vending akan menjalankan go build , seperti biasa, pada mesin mereka dan menggunakan -mod vendor pada CI mereka.


Sekali lagi, modul Go beralih dari ide penjual untuk menggunakan proksi untuk modul bagi mereka yang tidak ingin bergantung secara langsung pada layanan kontrol versi hulu.


Ada beberapa cara untuk memastikan bahwa go network tidak tersedia (misalnya, menggunakan GOPROXY=off ), tetapi ini adalah topik dari artikel selanjutnya.


Kesimpulan


Artikel itu mungkin terlihat rumit bagi seseorang, tetapi ini karena saya mencoba menjelaskan banyak hal sekaligus. Kenyataannya adalah modul Go umumnya sederhana hari ini - kami, seperti biasa, mengimpor paket ke dalam kode kami, dan tim go mengerjakan sisanya untuk kami. Ketergantungan dimuat secara otomatis selama perakitan.


Modul-modul tersebut juga menghilangkan kebutuhan $GOPATH , yang merupakan batu sandungan bagi pengembang Go baru yang memiliki masalah dalam memahami mengapa mereka harus meletakkan sesuatu di direktori tertentu.


Penjual (tidak resmi) telah ditinggalkan karena menggunakan proxy. 1
Saya dapat membuat artikel terpisah tentang proksi untuk modul Go.


Catatan:


1 Saya pikir ini ungkapan yang terlalu keras dan beberapa orang mungkin memiliki kesan penjual otomatis sedang dihapus sekarang. Ini tidak benar. Penjual masih bekerja, meskipun sedikit berbeda dari sebelumnya. Rupanya, ada keinginan untuk mengganti penjual dengan sesuatu yang lebih baik, misalnya proxy (bukan fakta). Sejauh ini, ini hanya mengejar solusi yang lebih baik. Penjual tidak akan pergi sampai pengganti yang baik ditemukan (jika ada).

Source: https://habr.com/ru/post/id421411/


All Articles