當go get遇上gitlab
前言
go get
命令可以說是 golang 開發者最常用的命令了,通過它我們可以輕鬆獲得各種開源倉庫中的包,並且比較方便的在不同的開發機快速部署開發環境。
> 此處應有版本依賴的問題,但聽說新版的 go 會處理。
但作為企業行為,不是所有的程式碼包都適合放在公開的網站上,而開源的又適用於中小型企業的自建 git 倉庫工具中,gitlab 無疑是耀眼的一個,如果配合 docker,一鍵部署簡直不要太舒服。
自建倉庫的go get
其實 golang 在設計的時候是可以支援go get
獲取自建倉庫的,詳細原理網上很多,不羅嗦,簡單講,當執行go get your-web.com/your-project
的時候,go 其實會提交一個HTTP GET
到網址https://you-web.com/your-project?go-get=1
,此時如果這個網址能在head
的meta
標籤中返回以下格式的內容時,就可以告訴go
客戶端應該再到哪裡獲取倉庫。
> 注意,預設情況下 go 會且僅會從 https 的網址獲取資料!
<html>
<head>
<meta content="szyhf/go-dicache git https://github.com/szyhf/go-dicache" name="go-import">
</head>
</html>
其中meta
標籤中的name
是必填項,內容必須是go-import
,而contat
的格式為匯入路徑 VCS型別 倉庫路徑
,例如,上述程式碼的含義就是從https://github.com/szyhf/go-dicache
下載git
倉庫並放到匯入路徑為szyhf/go-dicache
的 $GOPATH 中。
> 至於域名怎麼能訪問,怎麼輸出這個 meta,相信對於各位童鞋來說肯定是不是什麼 problem,跳過
更多說明可以看這裡go get 命令
遇上 gitlab
那麼對於自建倉庫的 gitlab,應該怎麼實現這個功能呢?其實 gitlab 很早就支援了 go get,例如你的 gitlab 網站部署在gitlab.hello.com
,你要 get 的專案是gitlab.hello.com/foo/bar
,那麼直接執行go get gitlab.hello.com/foo/bar
就可以了,gitlab 會自動在返回的網頁中設定合適的meta
標籤的。
但實際使用的時候,我們知道,很多時候我們之所以用自建的gitlab
,是因為這個倉庫見不得光
,說白了,我們自己git clone
的時候還需要輸入一下密碼,go get
顯然也繞不過這個問題。
而預設情況下,gitlab
返回的 meta 標籤中的 url 是https
型別的,而實際上更多時候,我們都是通過ssh
的方式實現獲取倉庫,因此,我們需要對 gitlab 做一定的改造。
當前筆者使用的gitlab
版本是9.3.6
,對go get
的支援是用過 ruby-rails 中的 Middleware 的方式實現的,很傳統,如果懂 ruby 的話可以試試直接改,檔案是gitlab/embedded/service/gitlab-rails/lib/gitlab/middleware/go.rb
,此處不多說。
> 主要考慮要改原始碼不是很優雅,特別是要處理 gitlab 的升級的時候。
此處給一個不需要懂 ruby 的非侵入式方案,因為公司的 gitlab 是搭配 nginx 使用的,所以在處理對 gitlab 的請求的時候,加入以下配置,可以達到一樣的效果:
if ($http_user_agent ~* "go") {
return 200 "<!DOCTYPE html><head><meta content='$host$uri git ssh://git@$host:$uri.git' name='go-import'></head></html>";
}
簡單解釋一下,來自go get
的 HTTP 請求中,User Agent
都是以go
作為開頭的,而且go
也不會跟現在任何主流瀏覽器衝突,所以當發現$http_user_agent
以go
開頭的時候,直接返回一個固定的字串,字串中注意倉庫路徑的拼接要加上ssh://
,要不go1.8
以下的版本無法識別。
> 上述是我第一次的方案,go get gitlab.hello.com/foo/bar
成功,順利按照預期工作。
然後工作了一陣子之後忽然又出現了新的問題,subpackage
。
原因很簡單,當我們在go get
某個專案時,如果這個專案依賴於gitlab.hello.com/foo/bar/you/hu
包,那麼go get
實際提交的請求會變成https://gitlab.hello.com/foo/bar/you/hu
,而實際上並不存在這個倉庫,如果按方案 1 的實現邏輯,會嘗試下載git@gitlab.hello.com/foo/bar/you/hu.git
。
很遺憾,這個倉庫並不存在,真正存在的是gitlab.hello.com/foo/bar.git
,那麼應該怎麼處理呢?結合nginx
的正規表示式重定位的功能,更新的配置如下:
location ~* ^/[^/]+/[^/]+$ {
if ($http_user_agent ~* '^go.*') {
return 200 "<!DOCTYPE html><head><meta content='$host$uri git ssh://git@$host:$uri.git' name='go-import'></head></html>";
}
proxy_cache off;
proxy_pass http://gitlab-workhorse;
}
location ~* ^/(?<holder>[^/]+)/(?<project>[^/]+)/.*$ {
set $goRedirect 'https://$host/$holder/$project?$args';
if ($http_user_agent ~* '^go.*') {
return 301 $goRedirect;
}
proxy_cache off;
proxy_pass http://gitlab-workhorse;
}
其中proxy_cache off;
、proxy_pass http://gitlab-workhorse;
是 gitlab 官方文件中給出的設定。
> 其實很容易理解。
主要來解釋一下兩個location
,首先:
~*
表示開始不區分大小寫地匹配後邊給出的正則。
正則^/[^/]+/[^/]+$
是為了匹配形如/foo/bar
的路徑結構,如果匹配成功,繼續檢查User-Agent
,如果符合go
,則按第一個方案返回結果,如果不符合,則按一般的gitlab
請求進行處理。
正則^/(?<holder>[^/]+)/(?<project>[^/]+)/.*$
是為了匹配形如/foo/bar/you/hu/hu
的結構,其中的小括號表示對其第一二個斜槓之間的字串進行捕捉,並賦值給變數$holder
、$project
,然後判定User-Agent
,如果符合go
,則將請求重定位給/foo/bar
,也就會再交給第一個正則處理,最後獲得一致的結果。
顯然第二個方案比第一個方案複雜了不少,但也都是很標準的nginx
配置邏輯,未必優雅,但還是很實用的。
> 一般來說小型企業的程式碼庫並不會有很高的訪問頻率,哪怕 proxy 稍微慢一點,影響也不大。
Docker
如果使用 docker 版並使用了 docker-compose,配置檔案中的選項可以參考如下:
environment:
GITLAB_OMNIBUS_CONFIG: |
nginx['custom_gitlab_server_config'] = "location ~* ^/[^/]+/[^/]+$$ {\n if ($$http_user_agent ~* '^go.*') {\n return 200 \"<!DOCTYPE html><html><head><meta content='$$host$$uri git ssh://git@$$host:$$uri.git' name='go-import'></head></html>\";\n }\n proxy_cache off;\n proxy_pass http://gitlab-workhorse;\n}\nlocation ~* ^/(?<holder>[^/]+)/(?<project>[^/]+)/.*$$ {\n set $$goRedirect 'https://$$host/$$holder/$$project?$$args';\n if ($$http_user_agent ~* '^go.*') {\n return 301 $$goRedirect;\n }\n proxy_cache off;\n proxy_pass http://gitlab-workhorse;\n}"
> 使用\$\$ 可以防止引數被當成環境變數使用。
小結
代理的思維方式可以解決很多問題。
公眾號廣告=。=
- 加微信實戰群請加微信(註明:實戰群):gocnio
相關文章
- 當 Go 遇上了 LuaGo
- 當 Go struct 遇上 MutexGoStructMutex
- go get 安裝gitlab私有包GoGitlab
- Go死鎖——當Channel遇上Mutex時GoMutex
- 當 Rust 遇上 FedoraRust
- 當UIColor遇上SwiftUISwift
- 當 React 遇上 KendoUIReactUI
- 當UIColor遇上 SwiftUISwift
- 當transition遇上display
- 當 bind 遇上 applyAPP
- 老遊戲遇上新問題:當動森遇上詐騙遊戲
- 當Shell遇上了NodeJSNodeJS
- 當區塊鏈遇上保險區塊鏈
- 當Python字串遇上MySQLPython字串MySql
- 當微信小程式遇上filter~微信小程式Filter
- 當好萊塢遇上國產電影
- [精選]當PHP遇上Go會怎樣?GO語言彌補了PHP的不足,然後。。。PHPGo
- 當新零售遇上 ServerlessServer
- 當12C PDB遇上JDBCJDBC
- 當 JS 遇上物聯網(IoT)JS
- 天生一對,當遊戲遇上MongoDB遊戲MongoDB
- SOA Agents──當網格遇上SOA
- 【Go】go get 自動代理Go
- 當動態桌面遇上 HTML5HTML
- [譯] 當設計模式遇上 Kotlin設計模式Kotlin
- go get 超時Go
- 當催收遇上疫情,AI能做些什麼?AI
- 當頁面渲染遇上邊緣計算
- 當餐飲遇上大資料,嗯真香!大資料
- 當金融行業遇上開源技術行業
- 當資料探勘遇上戰略決策
- 當 jenkins遇上Android Studio 3.0JenkinsAndroid
- 當「軟體研發」遇上 AI 大模型AI大模型
- 【新特性速遞】當法語遇上FineUI(Bonjour)!UI
- 當JSON.parse“遇上”非鍵值對JSON
- 當DUBBO遇上Arthas - 排查問題的實踐
- 當 Swagger 遇上 Torna,瞬間高大上了!Swagger
- go get 無法下載Go