Go Modules 三兩事

Even Chang
6 min readJan 5, 2019

介紹

在 Go 1.11 中 (於 2018 年 8 月釋出),官方加入了實驗性的 package management tool,稱為 Modules。

這一項舉動有幾個重要的目的

  1. 統一既有的第三方套件管理工具(glide, govendor 等等)
  2. 在不久的將來移除自 Go 1.5 時引入的 vendor
  3. 在不久的將來移除 $GOPATH

相信對於第一點,大多數使用 Go 一定時間的人都不需要太多解釋便能理解。社群上有著好幾套看似成熟,實則各有個自的坑的第三方套件管理工具。以 glide 來說,每次想要增加一個 dependency 都要等上好幾分鐘,因為它固定會去更新所有存在的 dependencies。

至於第二跟第三點,就得先了解當時為何會出現 vendor 以及 $GOPATH

我們可以將 $GOPATH 底下看成一個巨大的 Repository,如果以傳聞中 Google 是實踐 Mono Repo 來說的話,似乎就能明白為何 Go 一開始的設計會是如,因為各個 team 維護的專案都能放在同一個 Repo 底下。

然而並不是每個公司或是社群都是走 Mono Repo 的路線,更常見的是,底下有好幾個 projects 擁有相同的 dependency 卻要不同的 version

因此在 Go 1.5 時,在每個專案底下都加入了 vendor 來管理各自的依賴,從此開始,各個 project 終於可以擁有不同版本相同的 dependency 了,然而 vendor 底下的 package 其實也只是 clone 當時 dependency 的樣子,至於後續的升級又得仰賴各個第三方套件實踐。

於是 Go Modules 就這樣誕生了(當然中間還經過了 vgo, dep 等等討論,這邊就不詳述了)

Go Modules 指令介紹

Usage:go mod <command> [arguments]The commands are:download    // 將依賴全部下載到本機中,位置為 $GOPATH/pkg/mod/cache
edit // 編輯 go.mod 例如鎖定某個依賴的版本
graph // 列出專案中哪一個部分使用了某個依賴
init // 建立 go.mod
tidy // 增加遺失的依賴,移除未使用的依賴
vendor // 將既有的 go.mod 依賴全部存在 /vendor 底下
verify // 驗證本地依賴依然符合 go.sum
why // 解釋某個依賴為何存在在 go.mod 中,誰使用了它

如何在一個新的專案使用 Go Modules?

以下以 OSX 為例

// 先確認 Go 的版本已經在 1.11 以上
$ brew upgrade go
$ mkdir gomod-test // 請確定當前位置在 $GOPATH 以外的地方
$ cd gomod-test

其實不一定要在 $GOPATH 以外的地方,只是當前 Go Modules 還在實驗階段,如果是在 $GOPATH 的專案,預設依然會照舊有的 vendor 機制,除非將環境變數 GO111MODULE 該為 on 來強制開啟,但既然 Go Modules 一個重要的性質是去除 $GOPATH ,就讓我們試試看在其他地方開專案吧!

接著初始化 Go Modules

$ go mod init github.com/hieven/gomod-test

便會看到專案底下出現 go.mod 的檔案,而這也是最重要的檔案,之後會紀錄每一個 dependency 以及版本。現在應該長得像這樣

// go.mod
module github.com/hieven/gomod-test

最後讓我們做一個簡單的 hello world

// main.go
package main
import "fmt"func main() {
fmt.Println("hello world")
}

此時還沒有任何改變,但接著我們嘗試加入一個 dependency

package mainimport "fmt"
import "github.com/gofrs/uuid"
func main() {
uuid, _ := uuid.NewV4()

fmt.Println("hello world", uuid)
}

接著運行程式 $ go run main.go 會發現程式神奇的運作了

$ hello world 3f99abff-8404-42ec-b9f6-5fa165e5d447

再來檢查 go.mod ,會發現已經多了一個 dependency

module github.com/hieven/gomod-testrequire (
github.com/gofrs/uuid v3.1.0+incompatible
)

原因是 Go Modules 不僅僅是一個 go mod xxx 的工具而已,同時也整合了既有的 go getgo rungo buildgo test ,每當這些指令運行時,都會去檢查整個 project 底下新的 dependency 並自動更新到 go.mod 底下

既有的專案如何遷移至 Go Modules?

剛好在既有的 project 中分別有用 glide 以及 govendor,所以剛好都試過這兩個的遷移方法,其實非常簡單。只要到 project 底下執行

$ go mod init

便自動會去讀 glide.yaml 或是 vendor/vendor.json 並產生一個 go.mod 。個人目前還沒有遇到問題

如果有興趣的人可以參考我在 go-instagram 中的一個 PR,便是把 glide 轉移成 Go Modules

此外,可以試試看執行

$ go mod tidy

來移除沒使用的依賴,我自己的私人專案在使用 tidy 之後,成功移除了好幾個呢!

如何讓 Travis 能使用 Go Modules?

基本上現在的 Travis 也有支援 Go 1.11 了,所以 Go Modules 自然而然就有了。

唯一要特別注意的是, Travis 底下預設把 project 放在 $GOPATH 底下,所以要在 env 中把 Go Modules 打開才行

具體設定就是要注意這兩行

// .travis.ymlgo:
- "1.11"
env:
- GO111MODULE=on

--

--

Even Chang

目前在英國倫敦的台灣人軟體工程師,曾經待過台灣、新加坡、荷蘭。希望透過分享,來產生更多思維碰撞💥 | LinkedIn: @hieven