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"
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" )
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 $ git checkout -b v2 # optional but recommended $ echo > go.mod $ git commit go.mod -m $ 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.
Kami mengatur semuanya
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).