Go - 基于go:embed的軟件版本維護(hù)方案

1. 概述

版本管理,是軟件的必需功能之一,對于軟件的開發(fā)、部署和維護(hù)都至關(guān)重要。
關(guān)于Golang程序的版本管理,流行的做法是基于ldflags,感覺比較hack。個(gè)人比較認(rèn)可go embed,代碼可讀性更強(qiáng)。
這里分別介紹一下兩種做法,大家可以根據(jù)需要,靈活選擇。

2. 基于ldflags

2.1. 基本原理

go build,可以用-gcflagsgo編譯器傳入?yún)?shù),也就是傳給go tool compile的參數(shù),因此可以用go tool compile --help查看所有可用的參數(shù)。
go build,用-ldflagsgo鏈接器傳入?yún)?shù),實(shí)際是給go tool link的參數(shù),可以用go tool link --help查看可用的參數(shù)。
常用-X來指定版本號(hào)等編譯時(shí)才決定的參數(shù)值。例如,代碼中定義var buildVer string,然后,在編譯時(shí)用go build -ldflags "-X main.buildVer=1.0" ...來賦值。
注意-X只能給string類型變量賦值。

% go tool link --help
...
  -X definition
        add string value definition of the form importpath.name=value
  -s disable symbol table
  -w disable DWARF generation
...
  • -X importpath.name=value 編譯期設(shè)置變量的值
  • -s disable symbol table 禁用符號(hào)表
  • -w disable DWARF generation 禁用調(diào)試信息

2.2. 實(shí)現(xiàn)

package main

import (
    "fmt"
)

var (
    VERSION string
)

func main() {
    fmt.Printf("Version:[%s]\n", VERSION)
}
% go build -ldflags "-X main.VERSION=v1.0.0-alpha1" test_version.go
% ./test_version 
Version:[v1.0.0-alpha1]

參考

3. 基于go embed

3.1. 基本原理

Go編譯的程序非常適合部署,如果沒有通過CGO引用其它的庫的話,我們一般編譯出來的可執(zhí)行二進(jìn)制文件都是單個(gè)的文件,非常適合復(fù)制和部署。在實(shí)際使用中,除了二進(jìn)制文件,可能還需要一些配置文件,或者靜態(tài)文件,比如html模板、靜態(tài)的圖片、CSS、javascript等文件,如何這些文件也能打進(jìn)到二進(jìn)制文件中,那就太美妙,我們只需復(fù)制、按照單個(gè)的可執(zhí)行文件即可。

一些開源的項(xiàng)目很久以前就開始做這方面的工作,比如gobuffalo/packrmarkbates/pkger、rakyll/statikknadh/stuffbin等等,但是不管怎么說這些都是第三方提供的功能,如果Go官方能內(nèi)建支持就好了。2019末一個(gè)提案被提出issue#35950,期望Go官方編譯器支持嵌入靜態(tài)文件。后來Russ Cox專門寫了一個(gè)設(shè)計(jì)文檔Go command support for embedded static assets, 并最終實(shí)現(xiàn)了它。

Go 1.16中包含了go embed的功能,支持嵌入為string, byte sliceembed.FS三種類型,這三種類型的別名(alias)和命名類型(如type S string)都不可以。

直接看下官方給的例子:

package main

import (
    "embed"
)

//go:embed hello.txt
var s string

//go:embed hello.txt
var b []byte

//go:embed hello.txt
var f embed.FS

func main() {
    print(s)
    print(string(b))
    data, _ := f.ReadFile("hello.txt")
    print(string(data))
}

Go embed 簡明教程 (colobu.com)
Go 1.16 推出 Embedding Files - 小惡魔 - AppleBOY (wu-boy.com)

3.2. 實(shí)現(xiàn)

基于以上例子,我們很容易設(shè)計(jì)一套軟件版本維護(hù)方案。

% tree .
.
├── dockerfile   # 兩階段的docker image生成方法
├── go.mod
├── go.sum
├── main.go      # go:embed 嵌入version
├── makefile     # 執(zhí)行g(shù)o:build
├── release.sh   # release,先執(zhí)行bumpversion,再執(zhí)行docker build
├── src
│   ├── controllers
│   │   ├── contract
│   │   ├── routes.go
│   │   └── v1
│   ├── libs
│   ├── models
│   ├── services
│   └── version.txt          # 軟件版本文件
├── .bumpversion.cfg         # 軟件版本文件
  • 依賴

安裝bumpversionpip install bumpversion

  • .bumpversion.cfg
[bumpversion]
current_version = 0.0.11

[bumpversion:file:src/version.txt]
search = {current_version}
replace = {new_version}
  • src/version.txt
0.0.11
  • main.go
...
//go:embed src/version.txt
var version string
...
  • release.sh
#!/bin/bash

set -e

if [ $# -ne 1 ]
then
  echo "Bumpversion options in [patch/minor/major]"
  exit 1
fi

# pip install bumpversion
OLD_VERSION=$(cat src/version.txt)
bumpversion --allow-dirty --no-tag --no-commit "$1"
VERSION=$(cat src/version.txt)
echo "Bump Version: $OLD_VERSION -> $VERSION"

DOCKER_IMAGE_NAME=registry.cn-beijing.aliyuncs.com/shuzhang/master:$VERSION

docker build . \
  -t "$DOCKER_IMAGE_NAME"

docker push "$DOCKER_IMAGE_NAME"
  • dockerfile
FROM golang:1.20 AS builder

ENV GOPROXY=https://goproxy.cn,direct
#ENV GOPROXY=https://mirrors.aliyun.com/goproxy/,direct

WORKDIR /code
COPY . /code

RUN make

##########################

FROM debian:10.9
LABEL maintainer="xxx@163.com"

# ENV GOMAXPROCS=8
ENV PATH=/app:$PATH

WORKDIR /app
COPY --from=builder /code/bin /app/
COPY conf /app/conf

ENTRYPOINT ["master"]
  • makefile
BINARY_FILE=bin/master

all: test build

compile:
    echo "Compiling for every OS and Platform"
    GOOS=linux GOARCH=amd64 go build -o ${BINARY_FILE}-linux-amd64 main.go
#   GOOS=freebsd GOARCH=amd64 go build -o ${BINARY_FILE}-freebsd-amd64 main.go
#   GOOS=windows GOARCH=amd64 go build -o ${BINARY_FILE}-windows-amd64 main.go
#   GOOS=darwin GOARCH=amd64 go build -o ${BINARY_FILE}-darwin-amd64 main.go

build:
    go build -o ${BINARY_FILE} main.go

test:
    go test -v ./...

run:
    go run main.go

install: build
    cp ./${BINARY_FILE} ${GOPATH}/bin

deps:
    go mod download

clean:
    go clean
    rm ${BINARY_FILE}*

3.3. 使用方法

  • 開發(fā)或debug,直接makemake compile即可
  • 發(fā)布的話,直接bash release.sh。docker image的版本號(hào)和golang二進(jìn)制程序的版本一致,贊!

4. 綜上

go:embed,是golang1.16引入的新功能,它確保部署簡單和程序的完整性。
其應(yīng)用場景很多,例如配置文件、網(wǎng)站的靜態(tài)文件、golang模板文件、數(shù)據(jù)庫遷移等。
大家可以根據(jù)需求使用。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容