這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。
本文譯自 Go += Package Versioning, Go & Versioning 的第 1 部分, 著作權@歸原文所有.
我們需要將包版本控制添加到 Go.
更確切地說, 我們需要將軟體包版本的概念添加到 Go 開發人員和我們的工具的工作詞彙表中, 以便在彼此交談時準確地確定應該構建, 運行或分析哪個程式.
go 命令需要能夠告訴開發人員具體哪些版本的軟體包在特定構建中, 反之亦然.
版本控制可以讓我們啟用可重複構建, 所以如果我告訴你試用我的程式的最新版本, 我知道你將不僅獲得My Code的最新版本,
而且還包括My Code所依賴包的精確的同一版本, 所以你和我將構建完全等價的二進位.
版本控制還可以讓我們確保明天程式的構建方式與今天的完全相同. 即使有更新版本的依賴, go 命令也不應該使用它們除非被要求這樣做.
儘管我們必須添加版本控制, 但我們也不能移除當前 go 命令的最佳部分: 簡單, 速度以及易懂. 當前, 許多程式員大多不關注版本控制, 而且大部分都工作良好.
如果我們正確地獲得了模型和預設值, 我們應該能夠以這樣的方式添加版本控制, 即程式員仍然大多不關注版本控制, 並且一切都更好, 而且更易於理解. 現有的工作流程應該儘可能少地改變.
發布新版本應該非常簡單. 一般來說, 版本管理工作必須從後台淡出, 而不是日常關注.
簡而言之, 我們需要添加軟體包版本控制, 但我們還不能破壞 go get.
這篇文章草擬了一個做這件事的恰到好處的提案, 以及今天就可以嘗試的原型示範, 希望這將成為最終 go 命令整合的基礎. 我打算將這篇文章作為有效討論什麼可行以及什麼不可行的開始.
基於這一討論, 我將對提案和原型進行調整, 然後我將提交一份正式的 Go 提案, 作為可選功能整合到 Go 1.11 中.
這個提案保留了 go get 最好的部分, 增加了可重複構建, 採用了語義版本化, 消除了 vendoring, 棄用了 GOPATH 轉而採用了基於項目的工作流程, 並且提供了一個從 dep 和其前任們的平滑遷移.
也就是說, 這個提案還處於早期階段. 如果細節還不正確, 我們會花時間在整合進 Go 主發行版之前修複它們.
背景
在我們看這個提案之前, 讓我們看看我們做到了什麼. 這可能有點長, 但曆史對當下有重要的啟示, 並有助於理解提案為什麼會作出改變.
如果你不耐煩, 請跳到提案或閱讀附帶的樣本部落格文章.
Makefiles, goinstall, 以及 go get
2009 年 11 月, Go 的最初版本是一個編譯器, 連結器和一些庫. 你必須運行 6g 和 6l 編譯和連結你的程式, 並且我們包含了樣本 makefile(s).
在大多數情況下, 有一個最小化的 gobuild 封裝可以構建單個包並寫入一個合適的 makefile. 沒有既定的方式與其他人分享代碼.
我們知道還需要更多的東西, 但是我們已經發布了我們所有的東西, 並計劃在社區中完成剩下的工作.
2010 年 2 月, 我們提出了 goinstall 一個新的零配置命令,
用於從源碼控制倉庫(如 Bitbucket 和 GitHub )下載軟體包.
Goinstall 引入了今天開發人員認為理所當然的匯入路徑約定. 由於當時沒有任何代碼遵循這些約定, 所以 goinstall 起初只能處理除標準庫之外沒有任何其他包匯入的軟體包.
但是開發人員很快從他們自己的各種命名方案轉移到了我們今天所知道的統一約定, 並且發行的 Go 包集合發展成為一個連貫的生態系統.
Goinstall 也消除了 makefile 以及與之相關的使用者定義構建變動的複雜性.
儘管對於包作者在每次構建期間不能產生代碼來說有時不方便, 但這種簡化對於包使用者來說非常重要: 使用者不必擔心首次安裝與構建之前使用的包作者相同的一組工具.
簡化對於工具來說也是至關重要的. makefile 是編譯包的一個必要的步驟, 反向工程如何應用不同的工具, 如 go vet 或程式碼完成, 到相同的包, 可能會非常困難.
即使正確地獲得構建依賴關係, 以便在必要時重新構建包, 並且只在必要時重新構建包, 這對於任意 makefile 非常困難.
雖然有些人在靈活性被剝奪的時候表示反對, 但回想起來, 這些好處遠遠超過了不便之處.
2011 年 12 月, 作為 Go 1 準備工作的一部分, 我們推出了 go 命令, go get 替換了 gobuild .
總體而言, go get 具有變革意義, 可讓 Go 開發人員共用原始碼並構建彼此的工作, 並通過在 go 命令內部隔離構建系統的細節來啟用工具.
但是 go get 缺少任何版本控制的概念. 很明顯在第一次 goinstall 討論中我們需要做一些關於版本控制的內容.
不幸的是, 至少對於我們在 Go 團隊中, 我們不清楚究竟該怎麼做. 當 go get 需要一個包時, 它總是擷取最新的副本, 將下載和更新操作委託給像 Git 或 Mercurial 這樣的版本控制系統.
軟體包版本的這種無知導致了至少兩個顯著的缺陷.
版本和 API 穩定性
go get 的第一個顯著缺點是, 如果沒有版本控制的概念, 它就不能告訴使用者對給定更新期望發生什麼樣的變化.
2013 年 11 月, Go 1.2 添加了一個關於包版本控制的常見問題集條目, 提供了基本的建議 (Go 1.10也沒有變):
打算供公眾使用的軟體包應該盡量保持向後相容性.
Go 1 相容性準則在這裡是一個很好的參考: 不要刪除匯出的名稱, 鼓勵標記的複合文字等等.
如果需要不同的功能, 請添加新名稱而不是更改舊名稱. 如果需要完全打破相容性, 請建立新的匯入路徑的軟體包.
2014 年 3 月, Gustavo Niemeyer 建立了 gopkg.in, 廣而告之為 "Go語言的穩定API".
該網域名稱是一個版本感知的 GitHub 重新導向器, 允許匯入路徑, 如 gopkg.in/yaml.v1 和 gopkg.in/yaml.v2 引用單個 Git 倉庫的不同提交 (可能位於不同分支上) .
在語義版本化之後, 包作者需要在進行重大更改時引入一個新的主要版本, 以便較早版本的 v1 匯入路徑可以作為先前版本的替代版本, 而 v2 匯入路徑可能是完全不同的 API.
2015 年 8 月, Dave Cheney 提出採用語義版本化的提案.
這在接下來的幾個月裡引發了一場有趣的討論, 其中每個人似乎都認為, 使用語義版本戳記代碼似乎是一個好主意, 但沒有人知道下一步: 這些版本應該使用什麼工具 ?
任何關於語義版本的討論都不可避免地包含引用海勒姆法則的反駁, 其中指出:
擁有足夠數量的 API 使用者, 無論你在合約中承諾什麼, 都無關緊要. 系統中所有可觀察到的行為都將被某人依賴.
雖然海勒姆法則在經驗上是真實的, 但是語義版本化仍然是一個有用的方法來構建對發布之間關係的期望.
從 1.2.3 更新到 1.2.4 不應該破壞你的代碼, 而從 1.2.3 更新到 2.0.0 是可能的.
如果代碼在更新到 1.2.4 後停止工作, 作者很可能會歡迎 bug 報告並在 1.2.5 中發布修複.
如果你的代碼在更新到 2.0.0 之後停止工作(甚至編譯), 那麼這種改變更有可能是故意的, 並且在 2.0.1 中修複你的代碼的機會相對較少.
我沒有從海勒姆法則中得出結論: 語義版本化是不可能的, 我認為構建應該小心地使用作者所做的每個依賴的完全相同的版本, 除非被迫. 也就是說, 構建應該預設為儘可能重現.
Vendoring 以及可重複性構建
go get 的第二個顯著的缺點是, 如果沒有版本控制的概念, 它不能確保甚至不能表達可重複構建的想法. 無法確定你的使用者正在編譯你的代碼所依賴的相同版本.
2013 年 11 月, Go 1.2 常見問題集還添加了以下基本建議:
如果你使用的是外部提供的軟體包, 並擔心它可能會以意想不到的方式更改, 最簡單的解決方案是將其複製到本地倉庫 (這是 Google 內部採取的方法).
將副本儲存在新的匯入路徑下, 以將其標識為本機複本.
例如, 你可能會複製 "original.com/pkg" 到 "you.com/external/original.com/pkg". Keith Rarick 的 goven 是協助實現這一過程自動化的工具之一.
Goven, Keith Rarick 於 2012 年 3 月開始建立, 它將依賴項複製到你的倉庫中, 並更新其中的所有匯入路徑以反映新位置.
以這種方式修改依賴項的原始碼對於構建它而言是必要的, 但也是不幸的.
這些修改使得它難以與使用該依賴關係的其他複製代碼進行比較和合并更新的副本和所需的更新.
Keith 在 2013 年 9 月宣布了 godep: "一個凍結軟體包依賴關係的新工具".
godep 的主要進展是添加我們現在所理解的 Go vendoring - 即將依賴複製到項目中, 而不修改源檔案 - 沒有直接工具鏈通過以某種方式設定 GOPATH 來支援.
2014 年 10 月, Keith 建議在 Go 工具鏈中增加對 "外部軟體包" 概念的支援, 以便工具可以更好地理解使用該約定的項目.
屆時, 有許多類似於 godep 的努力. Matt Farina 寫了一篇部落格文章: "Glide In The Sea of Go Package Managers", 將最新的包管理器特別是 glide 與 godep 進行比較.
2015 年 4 月, Dave Cheney 介紹了 gb 一個 "基於項目的構建工具...允許通過原始碼 vendoring 重複構建", 而且無需重新匯入.
(gb 的另一個動機是避免將代碼儲存在 GOPATH 中的特定目錄中, 這對於許多開發人員的工作流程來說不太適合).
那年春天, Jason Buberel 對 Go 包管理進行了調查, 以瞭解可以採取什麼措施來統一這些多重努力, 避免重複和浪費工作.
他的調查在 Go 團隊中清楚地表明, go 命令需要直接支援 vendoring 而不是重寫 import .
與此同時, Daniel Theophanes 開始了一個檔案格式規範, 以描述 vendor 目錄中確切的出處和代碼版本.
2015 年 6 月, 我們接受 Keith 的提案作為 Go 1.5 vendor 實驗方式, 在 Go 1.5 中可選, 並在 Go 1.6 中預設啟用.
我們鼓勵所有 vendoring 工具作者與 Daniel 合作採用單一中繼資料檔案格式.
將 vendoring 概念納入 Go 工具鏈允許程式分析工具如 go vet 使用 vendoring 更好地理解項目, 現在有十幾個 Go 軟體包管理器或 vendoring 工具來管理 vendor 目錄.
另一方面, 因為這些工具都使用不同的中繼資料檔案格式, 所以它們不能互操作, 並且不能輕鬆共用關於依賴需求的資訊.
從根本上說, vendoring 是解決軟體包版本問題的不完整方案. 它只提供可重複性構建. 它沒有協助理解軟體包版本並決定使用哪個版本的軟體包.
軟體包管理器如 glide 和 dep 將版本控制的概念隱式地添加到 Go 中, 通過以某種方式設定 vendor 目錄而無需直接的工具鏈支援.
因此, Go 生態系統中的許多工具無法正確識別版本. 很明顯, Go 需要對軟體包版本提供直接的工具鏈支援.
官方包管理實驗
在 2016 年 GopherCon 大會上, 一群有趣的 gophers 在 Hack Day (現在是 Community Day) 聚在一起, 圍繞 Go 包管理進行廣泛的討論.
其中一個成果是成立了一個委員會和一個包管理工作諮詢小組, 目標是為 Go 包管理建立一個新工具.
願景是為了統一和取代現有的工具, 但它仍然在直接工具鏈之外使用 vendor 目錄來實現.
由 Peter Bourgon 組織的委員會 Andrew Gerrand, Ed Muller, Jessie Frazelle 和 Sam Boyer 起草了一份規範, 然後由 Sam 領導, 將其實施為 dep.
有關背景資訊, 請參閱 Sam 的 2016 年 2 月發布的文章: "所以你想寫一個包管理器", 他 2016 年 12 月發布的 "Go 的依賴管理傳奇" 和他 2017 年 7 月的 GopherCon 演講 "Go包管理的新時代".
Dep 有許多用途: 它是對當前可用的實踐的重要改進, 它是朝著解決方案邁出的重要一步, 也是一個實驗 - 我們稱之為 "官方實驗"
- 協助我們更多地瞭解什麼是對 Go 開發人員友好的以及哪些不是.
但 dep 不是最終整合了包版本控制的 go 命令的直接原型. 它是一種強大的, 幾乎任意靈活的方式來探索設計空間, 在構建 Go 程式時扮演像 makefiles 一樣的角色.
但是, 一旦我們更好地理解設計空間並將其縮小到必須支援的幾個關鍵特性, 它將協助 Go 生態系統移除其他特性, 降低表現力, 採用強制性約定,
使 Go 程式碼程式庫更加均勻並且更容易理解並使工具更易於構建.
這篇文章是 dep 之後下一步的開始: 最終 go 命令整合原型的初稿, 即 goinstall 的包管理的等價物.
原型是我們所稱的獨立命令 vgo. 它是 go 命令的直接替代品, 但它增加了對軟體包版本控制的支援.
這是一個新的實驗, 我們將看到我們可以從中學到什麼. 就像我們介紹 goinstall 的那樣, 當前一些代碼和項目已經可以使用 vgo, 其他項目需要變更才能相容.
我們將拿走一些控制和表現力, 就像我們拿走了 makefile 一樣, 為簡化系統和消除使用者複雜性服務.
通常,我們正在尋找早期採用者來協助我們進行實驗 vgo, 以便我們儘可能地從使用者那裡學習.
開始嘗試 vgo 並不意味著結束對 dep 的支援. 我們將保持 dep 可用, 直到完成 go 命令整合的路徑被確定, 實施並且大體上可用.
我們也將儘可能順利地完成從最終過渡 dep 到 go 命令整合的各種形式. 尚未轉化到 dep 的項目仍然可以從中獲益.
(注意, godep 和 glide 已經結束了活躍的開發, 鼓勵遷移到 dep). 其他項目不妨隨即轉移到 vgo, 如果它已滿足需求.
提案
添加版本到 go 命令的提案有四個步驟. 首先, 採用 Go FAQ 和 gopkg.in 暗示的匯入相容性規則; 也就是說, 建立一個預期, 即具有給定匯入路徑的軟體包的新版本應該與舊版本向後相容.
其次, 使用簡單的新演算法(稱為最小版本選擇)來選擇在給定構建中使用哪些版本.
第三, 引入 Go module 的概念, Go module 是一組版本為單個版本的軟體包, 並聲明了它們的依賴關係必須滿足的最低要求.
第四, 定義如何將所有這些改造成現有的 go 命令, 以便基本的工作流程從今天不再發生顯著變化. 本節的其餘部分將介紹這些步驟中的每一個. 本周其他部落格文章將更詳細地介紹.
匯入 (Import) 相容性規則
封裝管理系統中幾乎所有的痛苦都是由於試圖馴服不相容而導致的.
例如, 大多數系統允許程式包 B 聲明它需要程式包 D 6 或更高版本, 然後允許程式包 C 聲明它需要D 2, 3 或 4, 但不是 5 或更高版本.
如果你正在編寫軟體包 A, 並且你想同時使用 B 和 C, 那麼你運氣不好: 沒有一個 D 版本可以選擇將 B 和 C 一起構建到 A 中.
你什麼也做不了: 這些系統說 B 和 C 這樣做是可以接受的 - 他們有效地鼓勵它 - 所以就這樣你被卡住了.
這個提案要求包作者遵循匯入相容性規則, 而不是設計一個不可避免導致大型程式無法構建的系統:
如果舊軟體包和新軟體包具有相同的匯入路徑,
則新軟體包必須與舊軟體包向後相容.
這條規則是對前面引用的 Go FAQ 中的建議的重申. 常見問題引文最後說到: "如果需要完全的破壞相容性, 請使用新的匯入路徑建立一個新包".
今天的開發人員希望使用語義版本來表達這樣一個破壞, 所以我們將語義版本整合到了我們的提案中.
具體來說, 主要版本 2 和更高版本可以通過在路徑中包含版本來使用, 如下所示:
import "github.com/go-yaml/yaml/v2"
建立 v2.0.0, 在語義版本化中表示一個重大破壞, 因此按照匯入相容性的要求, 建立具有新匯入路徑的新包.
由於每個主要版本都有不同的匯入路徑, 因此給定的 Go 可執行檔可能包含每個主要版本中的一個. 這是預期的和可取的.
它保持程式的構建, 並允許一個非常大的程式部分獨立地從 v1 更新到 v2.
期望包作者遵循匯入相容性規則, 可以避免嘗試馴服不相容性, 使整個系統指數級更簡單, 並且軟體包生態系統更少片段化.
當然, 實際上, 儘管作者做出了最大的努力, 但在同一個主要版本中的更新偶爾會破壞使用者使用.
因此, 使用升級速度不太快的升級機制很重要. 這將引領我們到下一步.
最小版本選擇
今天幾乎所有的軟體包管理器, 包括 dep 和 cargo, 使用最新允許的版本參與構建包.
我認為這是錯誤的違約, 有兩個重要原因.
首先, "最新允許版本"的含義可能因外來事件而改變, 即新版本正在發布. 也許今晚有人會推出一些新版本的依賴項, 然後明天你再運行今天的相同命令序列會產生不同的結果.
其次,為了覆蓋這個預設值, 開發人員花時間告訴包管理器 "不, 不要使用 X", 然後包管理器花時間尋找一種不使用 X 的方法.
這個提案採取了不同的方法, 我稱之為最小版本選擇. 它預設使用構建中涉及的每個包的最舊允許版本. 這個決定從今天到明天不會改變, 因為不會發布舊版本.
更好的是, 為了覆蓋這個預設值,開發人員花時間告訴包管理器, "不, 至少使用 Y", 然後包管理器可以輕鬆地決定使用哪個版本.
我稱之為最小版本選擇, 因為所選擇的版本是最小的, 也因為整個系統也可能是最小的, 避免了現有系統的幾乎所有複雜性.
最小版本選擇允許模組僅指定其相依模組的最低需求. 它為升級和降級操作提供了定義明確的獨特答案, 並且這些操作的實施效率很高.
它還允許正在構建的整個模組的作者指定要排除的依賴項版本, 或者指定將特定的依賴項版本替換為本地檔案系統中的 forked 副本或作為其自己的模組發布.
當模組被構建為其他模組的依賴項時, 這些排除和替換不適用. 這使使用者可以完全控制自己的程式的構建方式, 而不是其他人的程式構建方式.
最小版本選擇預設提供可重複的版本, 無需鎖定檔案.
匯入相容性是簡化版本選擇的關鍵. 使用者不會說 "不, 這太新了", 他們只會說 "不, 這太舊了".
在這種情況下, 解決方案很明確: 使用(最低限度)更新的版本. 新版本同意成為老版本的可接受替代品.
定義 Go 模組
Go 模組是一組共用公用匯入路徑首碼的軟體包, 稱為模組路徑. 該模組是版本控制的單元, 模組版本被編寫為語義版本字串.
在開發使用 Git 時, 開發人員將通過向模組的 Git 倉庫添加標籤(tag)來定義模組的新語義版本.
儘管強烈推薦使用語義版本, 但也支援引用特定的提交(commits).
一個模組在一個稱作 go.mod 的新檔案中定義了它所依賴的其他模組的最低版本要求. 例如, 這裡是一個簡單的 go.mod 檔案:
// My hello, world.module "rsc.io/hello"require ( "golang.org/x/text" v0.0.0-20180208041248-4e4a3210bb54 "rsc.io/quote" v1.5.2)
該檔案定義了一個由路徑標識的模組, 該模組 rsc.io/hello 本身依賴於另外兩個模組: golang.org/x/text 和 rsc.io/quote.
模組本身的構建將始終使用 go.mod 檔案中列出的特定版本的必需依賴項. 作為更大構建的一部分, 如果構建中的其他地方需要它, 它將只使用更新的版本.
期望包作者使用語義版本戳記(tag)發布, vgo 鼓勵使用標記版本, 而不是任意提交. rsc.io/quote 模組服務於 github.com/rsc/quote, 已經標記了版本, 包括 v1.5.2.
但是 golang.org/x/text 模組尚未提供標籤版本. 要命名未標記的提交, 偽版本 v0.0.0-yyyymmddhhmmss-commit 標識在給定日期進行的特定提交.
在語義版本控制中, 這個字串對應於一個 v0.0.0 預發行版, 預發行版標識符為 yyyymmddhhmmss - commit.
語義版本優先規則在 v0.0.0 或更高版本之前排序此類預發布, 並且它們通過字串比較來排序預發布. 在偽版本文法中放置日期在前可確保字串比較匹配日期比較.
除了 requirements 之外, go.mod 檔案還可以指定上一節中提到的排除 (exclusions) 和替換 (replacements),
但這些僅在直接構建模組時才應用, 而不是在作為較大程式的一部分構建模組時應用. 這些例子示範了所有這些.
Goinstall 和舊的 go get 直接調用版本控制工具, 如 git 和 hg 直接下載代碼, 導致許多問題, 其中包括片段化.
例如, 使用者沒有 bzr 無法下載儲存在 Bazaar 倉庫中的代碼.
相比之下, 模組始終下載通過 HTTP 提供的 zip 歸檔檔案. 之前, go get 有特殊外殼為流行代碼託管網站選擇版本控制命令.
現在, vgo 有特殊外殼可以使用這些託管網站的 API 來擷取檔案.
將模組統一表示為 zip 壓縮檔可以實現模組下載代理的簡單協議和實現.
公司或個人可以出於任何原因運行代理, 包括安全性, 並希望能夠在刪除原始檔案的情況下從快取複本進行工作.
通過使用代理來確保可用性, 在 go.mod 中定義要使用的代碼, vendor 目錄不再需要.
go 命令
go 命令必須更新以使用模組. 一個顯著的變化是, 普通的構建命令, 如 go build, go install, go run 和 go test,
將按需解決新依賴, 只需要使用 golang.org/x/text 全新的模組增加匯入到 Go 原始碼並且構建代碼.
但是, 最重要的變化是 GOPATH 作為 Go 代碼工作的必需位置的結束.
因為該 go.mod 檔案包含完整的模組路徑, 並且還定義了正在使用的每個依賴項的版本,
所以帶有 go.mod 檔案的目錄會將分類樹的根標記為獨立的工作空間, 與其他任何此類目錄分開.
現在你只是 git clone, cd, 開始寫. 無處不可. 不需要 GOPATH.
接下來
我還發布了 "A Tour of Versioned Go" 來展示 vgo 的使用方式.
看這篇文章, 瞭解如何下載和實驗 vgo. 我會在整個一周發布更多資訊, 以添加我在本文中跳過的詳細資料.
我鼓勵對這個文章和其他人的評論發表意見, 我也會試著去看看 Go subreddit 和 golang-nuts 郵件清單.
周五, 我將發布 FAQ 作為系列文章的最後一篇博文(至少現在). 下周我會提交一份正式的 Go 提案.
請嘗試 vgo. 在存放庫中開始標記版本. 建立並簽入 go.mod 檔案.
請注意, 如果啟動並執行是一個包含空的 go.mod 的倉庫, 但是,
它有一個現成的 dep, glide, glock, godep, godeps, govend, govendor 或 gvt 設定檔, vgo 將用它來填充 go.mod 檔案.
我為 Go 添加版本到它的工作詞彙中這一姍姍來遲的一步感到興奮.
開發人員在使用 Go 時遇到的一些最常見的問題是缺乏可重複的構建, go get 完全忽略發布標籤,
GOPATH 無法理解包的多個版本以及想要或需要在 GOPATH 之外的來源目錄中工作. 這裡提出的設計消除了所有這些問題, 以及更多.
即便如此, 我確定有些細節是錯誤的. 我希望我們的使用者能夠通過嘗試新的 vgo 原型並參與富有成效的討論來協助我們實現這一設計.
我希望 Go 1.11 為 Go 模組提供初步支援, 作為一種技術預覽, 然後我希望 Go 1.12 能夠提供官方支援.
在稍後的一些版本中, 我們將刪除對舊的無版本的 go get 支援. 不過, 這是一個激進的時間表, 如果獲得正確的功能意味著等待以後的發布, 我們會.
我非常關心從舊的 go get 和無數的 vendoring 工具到新的模組系統的過渡.
對於我來說, 這個過程就和獲得正確的功能同樣重要. 如果成功的轉換意味著等待以後的發布, 我們會.
感謝 Peter Bourgon, Jess Frazelle, Andrew Gerrand 和 Ed Mueller 以及 Sam Boyer 在包管理委員會的工作以及去年的許多有益討論.
還要感謝 Dave Cheney, Gustavo Niemeyer, Keith Rarick 和 Daniel Theophanes 對 Go 和包版本的關鍵貢獻.
再次感謝 Sam Boyer 創造 dep, 並感謝他和 dep 的所有貢獻者. 感謝所有曾經在許多早期的 vendoring 工具上建立或工作過的人.
最後, 感謝所有能夠協助我們推進此提案的人, 找到並解決問題, 並儘可能順利地將軟體包版本控制添加到 Go.