當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}"
使用\$\$可以防止引數被當成環境變數使用。
小結
代理的思維方式可以解決很多問題。
公眾號廣告=。=
相關文章
- 當 Go 遇上了 LuaGo
- 當 Go struct 遇上 MutexGoStructMutex
- go get 安裝gitlab私有包GoGitlab
- Go死鎖——當Channel遇上Mutex時GoMutex
- 當 sendBeacon 遇上 Blob
- 當 Rust 遇上 FedoraRust
- 當class properties遇上decorator
- 當Shell遇上了NodeJSNodeJS
- 當元宇宙遇上梵高元宇宙
- [精選]當PHP遇上Go會怎樣?GO語言彌補了PHP的不足,然後。。。PHPGo
- 當區塊鏈遇上保險區塊鏈
- 當《人民日報》遇上GTA
- 當新零售遇上 ServerlessServer
- 老遊戲遇上新問題:當動森遇上詐騙遊戲
- 【Go】go get 自動代理Go
- go get 超時Go
- 當動態桌面遇上 HTML5HTML
- 當 Swagger 遇上 Torna,瞬間高大上了!Swagger
- 當JSON.parse“遇上”非鍵值對JSON
- 當 Kotlin 遇上 Android KTX,豈止絲滑?KotlinAndroid
- 當金融行業遇上開源技術行業
- 當資料探勘遇上戰略決策
- 當「軟體研發」遇上 AI 大模型AI大模型
- 當餐飲遇上大資料,嗯真香!大資料
- 當頁面渲染遇上邊緣計算
- 當催收遇上疫情,AI能做些什麼?AI
- 當好萊塢遇上國產電影
- 當Spring Cloud遇上Kubernetes,天色都變了SpringCloud
- 當DUBBO遇上Arthas - 排查問題的實踐
- 當RPA遇上AI,會擦出怎樣的火花?AI
- 【新特性速遞】當法語遇上FineUI(Bonjour)!UI
- go get 無法下載Go
- 當微信小程式遇上TensorFlow:Server端實現微信小程式Server
- 當「SPA」應用遇上了膨脹的專案
- 當微信小程式遇上TensorFlow:小程式實現微信小程式
- 當FPS遇上Roguelike:CF手遊的創新之路
- 當 RocketMQ 遇上 Serverless,會碰撞出怎樣的火花?MQServer
- 當char型變數遇上char*型的指標變數指標
- go get報錯connect: connection refusedGo