# 穩定打包 Go Binary 成 Debian Package 的選擇

## Context

需求：需要準備一個**穩定的方式**打包某個 Go Package (在 github 上) 編出的執行檔進一個 Debian package ，因為他不在 native 的 apt repository 裡面。

例如打包成這樣

    /opt/mukyu/gotoolbla/bin/gotoolbla
    /opt/mukyu/gotoolbla/man/....

一開始，我是先把 Github release page 裡面的 source code pack 下載下來，搭配 shell script + CMake 處理：

![source_code.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1660574395977/6zgInmzo6.png align="left")

    code.zip
    make_deb.sh
    CMakeLists.txt

雖然對於其他 c/cpp source code package ，尤其是「**來源無法穩定**」的 Package 或者需要**後處理** (git clone with submodule) 才能編譯的 Package，這種做法還算可以。但「一般」（基本上符合 Go 版本規範）的 Golang repository， 不太有這兩個問題。「比較新」的語言： Golang 和 Rust 在 Package 處理的方向上，都預期**依賴的 Library 可以從網路抓取**。

## 方法

所以，這就需要問：

1.  既然把 source code 存放下來的原因是為了避免來源不穩，那 Go Package 是不是不需要考慮這問題？
2.  Go Package 其實仍然有其他 dependency，既然要用 zip 的方式避免網路依賴，那是不是要用 go vendor 進一步把其他 dependency 也下載下來？

所以也想了三個方式：

*   Script + Git Server
*   Source Code Zip: 就是上面的做法
*   Vendor: Source code + Go vendor + zip

### Script + Git Server

用 Git Server 來保存 Go repository。以避免 binary file 出現在 Git Repository 的角度來看，這是最好的選擇。

假如有不少 Go Repository 需要處理，這方案應該是這三種裡面最好的？

### Source Code Zip

某種程度上來說算是個半吊子的方式，因為 Go package 通常會用到其他 repository ，例如 go.mod 裡面那一串 `require` ，所以沒有辦法避免依賴外部資源。

    module ...
    
    require (
    
    	github.com/armon/go-radix v1.0.0
    	github.com/aws/aws-sdk-go v1.43.5
    	github.com/bep/clock v0.3.0
        ...
    )

### Source Code Vendor Zip

    go mod vendor

這樣可以把所有依賴都放在一個資料夾。

不過，假如要用 Vendor 這功能，還需要考量兩點：

*   Go Vendor 是否會被 Deprecate
*   Vendor 的功能本身意圖
*   Source Code 處理的流程

#### Go Vendor 是否會被 Deprecate

Go Vendor 是在 Go Module 功能出現之前暫時用來管理依賴的功能，是否會因為 Go Module 出現後逐漸「消失」，最後被 Golang Deprecate 掉？這很重要，假如我們使用了 Go Vendor 當作管理 Go Package 編成 Debian Package 的方式，然後 Go Vendor 在某一版消失了？？那就很有問題了。

從這討論裡面： [proposal: cmd/go: automatic and partial vendoring in module mode](https://github.com/golang/go/issues/30240)

> Vendor is used because there isn't a better alternative to reviewing dependency changes alongside (and as part of the same process as) changes to one's own code.
> 
> – myitcv

或許可以推斷這功能不容易消失。

#### Vendor 的功能本身意圖

這功能從上面來看，之後大概會被看作一個用來做 Code Review 的功能？這和我們想要的【避免依賴外部資源】意圖有不小的差距。

其次，有人提了一個 [proposal: cmd/go: partial vendoring](https://github.com/golang/go/issues/52604)，目標是可以只把部分指定的外部依賴 Vendor 進來。但是也有人回：

> Perhaps the better solution is to just stop using vendoring and instead support this requirement in another system such as a **read through cache** that never evicts unless explicitly told to do so.
> 
> – johnwmstevens 

這的確很有說服力，畢竟把外部 Code 丟到 Git Repository 不是甚麼好的操作。

#### Source Code 處理的流程

我們需要先下載 Source Code，然後下 `go vendor` ，再壓縮起來，這似乎會和原本 Source Code 有一定的距離。

總之，這方法看起來會隨著時間變成技術債。

## 結論

推薦 Script + Git Server。

假如不太想自己架 Git Server ，並且有信任的 Git Server Provider （ex: Github, GitLab, Bitbucket …），可以把 Repository 放在上面。

## 參考

*   [proposal: cmd/go: automatic and partial vendoring in module mode](https://github.com/golang/go/issues/30240)
*   [proposal: cmd/go: partial vendoring](https://github.com/golang/go/issues/52604)
*   [Debian Go Packaging](https://go-team.pages.debian.net/packaging.html)
