1. 從 go-chi 框架撤回所有主版本聊 Go1.16 的新特性

        共 7076字,需瀏覽 15分鐘

         ·

        2021-02-23 18:58

        閱讀本文大概需要 10?分鐘。

        大家好,我是站長(zhǎng) polarisxu。

        在 Reddit 上看到一條消息:

        go-chi is retracting all major versions with go1.16 new retract directive.

        go.mod 的變更如下:

        這利用了 Go 1.16 中 Module 的新特性。在這之前,先一起學(xué)習(xí)下該特性。

        01 retract:撤回版本

        也許不少人沒(méi)有開(kāi)發(fā)過(guò)自己的 Module(模塊),但了解模塊版本撤回還是有必要的,說(shuō)不定哪天就能用到。因此建議你能夠跟著本文操作一遍。

        一般地,模塊作者需要一種方法來(lái)指示不應(yīng)該使用某個(gè)已發(fā)布的模塊版本??赡艹鲇谝韵聨c(diǎn)原因:

        • 發(fā)現(xiàn)了一個(gè)嚴(yán)重的安全漏洞;
        • 發(fā)現(xiàn)了嚴(yán)重的不兼容性或 bug;
        • 這個(gè)版本是偶然發(fā)布的,或是過(guò)早發(fā)布了;

        作者不能簡(jiǎn)單地刪除版本標(biāo)簽(tag),因?yàn)樗鼈兒芸赡茉谀K代理上仍然可用。如果作者能夠從所有代理中刪除一個(gè)版本,那么依賴該版本的下游用戶就會(huì)無(wú)法使用,出問(wèn)題。

        此外,作者在發(fā)布了 go.sum 文件之后也不能更改版本,校驗(yàn)和數(shù)據(jù)庫(kù)會(huì)驗(yàn)證發(fā)布的版本從未更改。

        那怎么辦?作者應(yīng)該能夠撤回模塊版本。撤回的模塊版本是模塊作者明確聲明不應(yīng)該使用的版本。(retract 這個(gè)詞是從學(xué)術(shù)文獻(xiàn)中借來(lái)的:一篇被撤回的研究論文仍然可用,但是它有問(wèn)題,不應(yīng)該成為未來(lái)工作的基礎(chǔ))。

        撤回的版本應(yīng)該在模塊代理和原始代碼庫(kù)中保持可用。依賴已撤回版本的構(gòu)建應(yīng)可以繼續(xù)工作。當(dāng)用戶依賴于一個(gè)已撤回的版本(直接或間接)時(shí),應(yīng)該通知用戶。它也應(yīng)該很難無(wú)意中升級(jí)到一個(gè)已撤回版本。

        為了讓大家更好的理解模塊撤回功能,本文通過(guò)具體的例子來(lái)演示。我們會(huì)創(chuàng)建兩個(gè)模塊:

        • foo,這是一個(gè)模塊,本文將其 push 到 GitHub,完整路徑:https://github.com/polaris1119/foo,你本地試驗(yàn)記得路徑使用你的。本文將發(fā)布該模塊的許多版本。
        • gopher,一個(gè)簡(jiǎn)單的 main 包,使用了上面的模塊,該模塊不會(huì)發(fā)布,只是作為本地模塊;

        注意,請(qǐng)確保 Go 版本是 1.16+。

        本文演示用的操作系統(tǒng)是 MacOS。

        創(chuàng)建 foo 模塊

        $?mkdir?-p?~/foo
        $?cd?~/foo
        $?git?init?-q
        $?git?remote?add?origin?https://github.com/polaris1119/foo
        $?go?mod?init?github.com/polaris1119/foo
        go:?creating?new?go.mod:?module?github.com/polaris1119/foo

        在模塊目錄中創(chuàng)建一個(gè)文件 foo.go,輸入如下內(nèi)容:

        package?foo

        func?Bar()?string?{
        ??return?"這是初始版本"
        }

        注意:在把該模塊代碼提交之前,先在 GitHub 上創(chuàng)建好項(xiàng)目 foo。

        將 foo 模塊的改動(dòng)提交 git 并 push。

        $?git?add?-A
        $?git?commit?-q?-m?"Initial?commit"
        $?git?branch?-M?main
        $?git?push?-u?origin?main

        這是該模塊的初始版本,我們用 v0 語(yǔ)言版本來(lái)標(biāo)記它,表明它還不穩(wěn)定。

        $?git?tag?v0.1.0
        $?git?push?-q?origin?v0.1.0

        foo 的第一個(gè)版本已經(jīng)發(fā)布,我們看看使用它的 gopher 模塊。

        創(chuàng)建 gopher 模塊

        該模塊就放在本地,因此不用設(shè)置 git:

        $?mkdir?~/gopher
        $?cd?~/gopher
        $?go?mod?init?gopher

        創(chuàng)建一個(gè) main.go 文件,內(nèi)容如下:

        package?main

        import?(
        ????"fmt"

        ????"github.com/polaris1119/foo"
        )

        func?main()?{
        ?????fmt.Println(foo.Bar())
        }

        接下來(lái),我們通過(guò) go get 顯示指定依賴 foo 的版本:

        $?go?get?github.com/polaris1119/[email protected]
        go:?downloading?github.com/polaris1119/foo?v0.1.0
        go?get:?added?github.com/polaris1119/foo?v0.1.0

        注意:我本地配置了 GOPROXY=https://goproxy.cn,direct

        然后運(yùn)行:

        $?go?run?.
        這是初始版本

        目前一切正常。

        一個(gè)更好的版本

        經(jīng)過(guò)一段時(shí)間,foo 的功能有一些變化,這里假設(shè) Bar 方法語(yǔ)句變了(一個(gè)兼容的變化)。

        回到 foo 模塊,修改代碼如下:

        package?foo

        func?Bar()?string?{
        ??return?"這是一個(gè)更好的版本"
        }

        提交并推送到 GitHub。

        但現(xiàn)在 foo 模塊還不是穩(wěn)定版,因此不想標(biāo)記為 v1。安全起見(jiàn),我們發(fā)布 v0.2.0:

        $?git?tag?v0.2.0
        $?git?push?-q?origin?v0.2.0

        現(xiàn)在 foo 發(fā)布了 v0.2.0 版本,我們?cè)?gopher 項(xiàng)目嘗試下。

        $?cd?~/gopher
        $?go?get?github.com/polaris1119/[email protected]
        go:?downloading?github.com/polaris1119/foo?v0.2.0
        go?get:?upgraded?github.com/polaris1119/foo?v0.1.0?=>?v0.2.0

        然后 go run 運(yùn)行:

        $?go?run?.
        這是一個(gè)更好的版本

        這時(shí),你發(fā)現(xiàn) “這是一個(gè)更好的版本” 不好,怎么辦?你可以修改掉,然后再發(fā)布一個(gè)新版本。然而,gopher 項(xiàng)目已經(jīng)依賴了 v0.2.0,怎么辦?

        撤回模塊版本

        我們可以在 go.mod 中增加 retract 指令來(lái)撤回某個(gè)模塊版本。

        可以通過(guò)命令實(shí)現(xiàn):(也可以直接修改 go.mod 文件)

        $?go?mod?edit?-retract=v0.2.0

        這時(shí) go.mod 內(nèi)容如下:

        module?github.com/polaris1119/foo

        go?1.16

        retract?v0.2.0

        一般建議在 retract 上加上撤回的原因。go get、go list 等會(huì)顯示這個(gè)原因。

        module?github.com/polaris1119/foo

        go?1.16

        //?Bar?方法返回值不友好
        retract?v0.2.0

        修改下 Bar 的返回內(nèi)容,提交 GitHub 并發(fā)布 v0.3.0:

        func?Bar()?string?{
        ??return?"這是v0.3.0版本"
        }

        v0.3.0 發(fā)布后,回到 gopher 模塊,使用這個(gè)新版本。

        $?cd?~/gopher
        $?go?get?github.com/polaris1119/[email protected]
        go:?downloading?github.com/polaris1119/foo?v0.3.0
        go?get:?upgraded?github.com/polaris1119/foo?v0.2.0?=>?v0.3.0

        這一步確保 https://goproxy.cn 這個(gè)代理知曉了 v0.3.0 版本,這是一種手動(dòng)讓代理拉取你模塊的方法。

        $?go?run?.
        這是v0.3.0版本

        已經(jīng)正常了。

        經(jīng)過(guò)這個(gè)步驟到底發(fā)生了什么?我們通過(guò)以下命令看一下:

        $?go?list?-m?-versions?github.com/polaris1119/foo
        github.com/polaris1119/foo?v0.1.0?v0.3.0

        v0.2.0 不見(jiàn)了。通過(guò)增加 -retracted 選項(xiàng)可以查看撤回的版本:

        $?go?list?-m?-versions?-retracted?github.com/polaris1119/foo
        github.com/polaris1119/foo?v0.1.0?v0.2.0?v0.3.0

        如果我們依賴回收的 v0.2.0 版本會(huì)怎么樣了?

        $?go?get?github.com/polaris1119/[email protected]
        go:?warning:?github.com/polaris1119/[email protected]:?retracted?by?module?author:?Bar?方法返回值不友好
        go:?to?switch?to?the?latest?unretracted?version,?run:
        ?go?get?github.com/polaris1119/foo@latest?go?get:?downgraded?github.com/polaris1119/foo?v0.3.0?=>?v0.2.0

        提示信息還是挺友好的,告知你 v0.2.0 是一個(gè)撤回的版本。

        雖然警告,但 v0.2.0 可以正常使用嗎?

        $?go?run?.
        這是一個(gè)更好的版本

        發(fā)現(xiàn)一切正常。

        有了這個(gè)功能,有一些模塊可能會(huì)使用它。那怎么知曉我們的項(xiàng)目有沒(méi)有依賴撤回的版本呢?使用 go list 命令即可:

        $?go?list?-m?-u?all
        gopher
        github.com/polaris1119/foo?v0.2.0?(retracted)?[v0.3.0]

        我們現(xiàn)在回到最新版本:

        $?go?get?github.com/polaris1119/foo@latest
        go?get:?upgraded?github.com/polaris1119/foo?v0.2.0?=>?v0.3.0

        為 foo 模塊增加功能

        又過(guò)了一段時(shí)間,我們?yōu)?foo 模塊增加了新的功能:

        func?Quz()?string?{
        ????return?"This?is?Quz?function"
        }

        提交到 GitHub,并發(fā)布 v0.4.0,依然是不穩(wěn)定版本。

        $?git?tag?v1.0.0
        $?git?push?-q?origin?v1.0.0

        但很糟糕的是,我不小心發(fā)布了 v1.0.0,這樣會(huì)讓用戶以為你的模塊已經(jīng)是穩(wěn)定版本,但實(shí)際上并不是這樣。所以,我們想撤回 v1.0.0。

        在撤回這個(gè)版本之前,我們應(yīng)該先發(fā)布 v0.4.0 版本:

        $?git?tag?v0.4.0
        $?git?push?-q?origin?v0.4.0

        要撤回 v1.0.0,我們需要發(fā)布 v1.0.1(為什么?因?yàn)槲覀円獙?xiě)入撤回的信息)。不過(guò)這樣一來(lái),我們還得撤回 v1.0.1,死循環(huán)了。。。go module 允許我們指定一個(gè)撤回的版本范圍,這次手動(dòng)編輯 go.mod 文件。

        module?github.com/polaris1119/foo

        go?1.16

        retract?(
        ????//?Bar?方法返回值不友好
        ????v0.2.0

        ????//?v1?提前發(fā)布了
        ????[v1.0.0,?v1.0.1]
        )

        提交這次改動(dòng)到 GitHub,然后再創(chuàng)建 v1.0.1 版本。

        $?git?tag?v1.0.1
        $?git?push?-q?origin?v1.0.1

        接著切到 gopher 模塊。

        為了讓 https://goproxy.cn 知曉 v1.0.0 等版本,我們先獲取它。

        $?go?get?github.com/polaris1119/[email protected]
        $?go?get?github.com/polaris1119/[email protected]
        $?go?get?github.com/polaris1119/[email protected]

        注意,切換到 v1.0.x 版本時(shí),很可能看不到版本撤回的信息,因?yàn)?proxy 可能還沒(méi)自動(dòng)定期更新。一般需要等待一段時(shí)間,比如 1 分鐘。如果沒(méi)有看到警告信息,等待 1 分鐘后再試,應(yīng)該能看到。

        現(xiàn)在列出所有的版本:

        $?go?list?-m?-versions?-retracted?github.com/polaris1119/foo
        github.com/polaris1119/foo?v0.1.0?v0.2.0?v0.3.0?v0.4.0?v1.0.0?v1.0.1

        或只列出未撤回版本:

        $?go?list?-m?-versions?github.com/polaris1119/foo
        github.com/polaris1119/foo?v0.1.0?v0.3.0?v0.4.0

        贊!v0.4.0 是該模塊的新版本。

        這時(shí)可以更新下 gopher,來(lái)使用 foo 的新功能:(加上對(duì) foo.Quz 函數(shù)的調(diào)用)

        $?go?run?.
        這是v0.3.0版本
        This?is?Quz?function

        提示:如果你將來(lái)要發(fā)布 v1 穩(wěn)定版,應(yīng)該從 v1.0.2 開(kāi)始,因?yàn)?v1.0.0 和 v1.0.1 被占用了。

        02 關(guān)于 incompatible

        講解完 retract 指令后,先看本文開(kāi)頭截圖中的另外一個(gè)東西:incompatible。

        在 go-chi 框架中,v2.x.x、v3.x.x 和 v4.x.x 都加上了 incompatible,這是什么意思?

        Go 模塊的版本號(hào)需要遵循 v.. 的格式,當(dāng) major 大于 1 時(shí),版本號(hào)需要體現(xiàn)在模塊名中,比如 Echo 框架:github.com/labstack/echo/v4。

        然而,由于 Go 模塊功能出現(xiàn)較晚(Go1.11 才出現(xiàn)),在它出現(xiàn)之前,很多項(xiàng)目的版本號(hào)已經(jīng)大于 1 了,比如 Echo 框架,這些版本連 go.mod 文件都沒(méi)有,更別提模塊名加上版本號(hào)。于是,這些版本就會(huì)有 incompatible 標(biāo)記。

        因?yàn)槟K名沒(méi)有版本信息,導(dǎo)致無(wú)法判斷版本的兼容性問(wèn)題,比如 v2.x.x 和 v3.x.x 都是 incompatible 的,使用 v2.x.x 的項(xiàng)目,更新依賴時(shí),會(huì)直接升級(jí)到 v3.x.x,這顯然是不行的,因此才標(biāo)記它們?yōu)?incompatible(不兼容)。

        你可以在上面做這個(gè)試驗(yàn):新增版本 v2.0.0,但不修改 go.mod 文件中的 module 名,看看最新版本是否會(huì)帶 incompatible。

        一般不建議項(xiàng)目使用 incompatible,畢竟穩(wěn)定性沒(méi)法保證,它是不符合 Go Module 規(guī)范的。

        03 go-chi 撤回所有主版本

        先介紹下 chi 這個(gè)框架:

        lightweight, idiomatic and composable router for building Go HTTP services

        它主要強(qiáng)調(diào)自己是一個(gè)路由,方便構(gòu)建 HTTP 服務(wù)。它兼容 net/http,沒(méi)有任何第三方依賴。

        簡(jiǎn)單使用示例:

        package?main

        import?(
        ?"net/http"

        ?"github.com/go-chi/chi"
        ?"github.com/go-chi/chi/middleware"
        )

        func?main()?{
        ?r?:=?chi.NewRouter()
        ?r.Use(middleware.Logger)
        ?r.Get("/",?func(w?http.ResponseWriter,?r?*http.Request)?{
        ??w.Write([]byte("welcome"))
        ?})
        ?http.ListenAndServe(":3000",?r)
        }

        有興趣的自己去了解。這里主要說(shuō)下它撤回主版本的事情。

        chi 保證自己有很好的兼容性,而作者特別厭煩模塊名帶版本號(hào),即 github.com/go-chi/chi/v4 這樣的(有強(qiáng)迫癥),但 chi 項(xiàng)目 tag 已經(jīng)到 4.x.x 了,怎么辦?

        從上面截圖看,它一直使用的 incompatible。沒(méi)想過(guò)到 Go1.16 除了 retract 的功能,于是 chi 作者做了一個(gè)決定:在已經(jīng)發(fā)布的版本中,只保留 v1.5.x 系列,其他的全部撤回。

        $?go?list?-m?-versions?github.com/go-chi/chi
        github.com/go-chi/chi?v1.5.0?v1.5.1?v1.5.2?v1.5.3

        沒(méi)有了一大堆帶 incompatible 的版本,世界瞬間清靜了。

        不過(guò)它的這個(gè)決定,有不少人反對(duì),reddit 上也是激烈討論。

        作者表示:https://github.com/go-chi/chi/issues/561

        對(duì)于給您帶來(lái)的不便,我再次表示歉意,但是我為此項(xiàng)目投入了數(shù)年甚至數(shù)千小時(shí)的時(shí)間,對(duì)此我非常感興趣,SIV 是我堅(jiān)決反對(duì)的事情,因此我不會(huì)采納它。盡管許多人不同意,但請(qǐng)記住,這是 OSS,不是以任何方式贊助或付費(fèi)的,您始終可以選擇 fork 它并維護(hù)自己的版本。

        作者很強(qiáng)硬。(誰(shuí)知道 SIV 是什么意思?

        實(shí)際上使用 retract,之前的版本依然可以正常使用。我個(gè)人比較支持 chi 作者的做法。你呢?歡迎交流你的看法。




        往期推薦


        歡迎關(guān)注我

        瀏覽 44
        點(diǎn)贊
        評(píng)論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報(bào)
        評(píng)論
        圖片
        表情
        推薦
        點(diǎn)贊
        評(píng)論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報(bào)
          
          

            1. 好深好烫好大好爽我要视频 | 嗯~用力啊~嗯~c我~啊哈校园 | 久久五月丁香婷 | 国产全黄A级A片一免费女搓女 | 一级A黄片|