Versão de criação automática de aplicativos

193

É possível incrementar um número de versão menor automaticamente cada vez que um aplicativo Go é compilado?

Eu gostaria de definir um número de versão dentro do meu programa, com uma seção de incremento automático:

$ myapp -version
MyApp version 0.5.132

Sendo 0,5 o número da versão que defini e 132 um valor que é incrementado automaticamente cada vez que o binário é compilado.

Isso é possível no Go?

Sebastián Grignoli
fonte

Respostas:

337

O vinculador Go ( link da ferramenta go ) tem uma opção para definir o valor de uma variável de cadeia não inicializada:

-X importpath.name=value
  Set the value of the string variable in importpath named name to

valor. Observe que antes do Go 1.5, essa opção usava dois argumentos separados. Agora, é necessário um argumento dividido no primeiro sinal =.

Como parte do seu processo de construção, você pode definir uma variável de cadeia de versão usando isso. Você pode passar isso pela goferramenta usando -ldflags. Por exemplo, dado o seguinte arquivo de origem:

package main

import "fmt"

var xyz string

func main() {
    fmt.Println(xyz)
}

Então:

$ go run -ldflags "-X main.xyz=abc" main.go
abc

Para definir main.minversiona data e a hora da construção ao criar:

go build -ldflags "-X main.minversion=`date -u +.%Y%m%d.%H%M%S`" service.go

Se você compilar sem inicializar main.minversiondessa maneira, ele conterá a sequência vazia.

axw
fonte
4
Esse valor será salvo no binário se eu usar em go bouildvez de go run?
Sebastián Grignoli 6/07/12
6
go build -ldflags "-X main.minversion `date -u +.%Y%m%d%.H%M%S`" service.go
Sebastián Grignoli 6/07/12
4
O goxc faz isso por você :) Por padrão, ele compila com -ldflags "-Xmain.VERSION xxx -Xmain.BUILD_DATE CurrentDateInISO8601", mas você pode configurar esses nomes de variáveis, se quiser. Veja github.com/laher/goxc ... (isenção de responsabilidade: escrevi goxc)
laher
7
exemplo com o novo 1.5 sintaxe trabalhando para adicionar a variável Buildtimego build -ldflags "-X 'main.buildtime=$(date -u '+%Y-%m-%d %H:%M:%S')'"
xorpaul
26
observe que o nome completo do pacote é necessário. go build -ldflags "-X pkg.version=123"não funcionará enquanto go build -ldflags "-X path/to/pkg.version=123"trabalha como esperado. espero que ajude.
csyangchen
27

Além disso, gostaria de postar um pequeno exemplo de como usar o git e um makefile:

--- Makefile ----

# This how we want to name the binary output
BINARY=gomake

# These are the values we want to pass for VERSION and BUILD
# git tag 1.0.1
# git commit -am "One more change after the tags"
VERSION=`git describe --tags`
BUILD=`date +%FT%T%z`

# Setup the -ldflags option for go build here, interpolate the variable values
LDFLAGS_f1=-ldflags "-w -s -X main.Version=${VERSION} -X main.Build=${BUILD} -X main.Entry=f1"
LDFLAGS_f2=-ldflags "-w -s -X main.Version=${VERSION} -X main.Build=${BUILD} -X main.Entry=f2"

# Builds the project
build:
    go build ${LDFLAGS_f1} -o ${BINARY}_f1
    go build ${LDFLAGS_f2} -o ${BINARY}_f2

# Installs our project: copies binaries
install:
    go install ${LDFLAGS_f1}

# Cleans our project: deletes binaries
clean:
    if [ -f ${BINARY} ] ; then rm ${BINARY} ; fi

.PHONY: clean install

O arquivo make criará dois executáveis. Um está executando a função um, o outro assumirá a função dois como entrada principal:

package main

import (
        "fmt"
)

var (

        Version string
        Build   string
        Entry   string

        funcs = map[string]func() {
                "f1":functionOne,"f2":functionTwo,
        }

)

func functionOne() {
    fmt.Println("This is function one")
}

func functionTwo() {
    fmt.Println("This is function two")
}

func main() {

        fmt.Println("Version: ", Version)
        fmt.Println("Build Time: ", Build)

    funcs[Entry]()

}

Em seguida, basta executar:

make

Você vai ter:

mab@h2470988:~/projects/go/gomake/3/gomake$ ls -al
total 2020
drwxrwxr-x 3 mab mab    4096 Sep  7 22:41 .
drwxrwxr-x 3 mab mab    4096 Aug 16 10:00 ..
drwxrwxr-x 8 mab mab    4096 Aug 17 16:40 .git
-rwxrwxr-x 1 mab mab 1023488 Sep  7 22:41 gomake_f1
-rwxrwxr-x 1 mab mab 1023488 Sep  7 22:41 gomake_f2
-rw-rw-r-- 1 mab mab     399 Aug 16 10:21 main.go
-rw-rw-r-- 1 mab mab     810 Sep  7 22:41 Makefile
mab@h2470988:~/projects/go/gomake/3/gomake$ ./gomake_f1
Version:  1.0.1-1-gfb51187
Build Time:  2016-09-07T22:41:38+0200
This is function one
mab@h2470988:~/projects/go/gomake/3/gomake$ ./gomake_f2
Version:  1.0.1-1-gfb51187
Build Time:  2016-09-07T22:41:39+0200
This is function two
Maciej A. Bednarz
fonte
5
Ou mais simples: basta fazer dois main em dois diretórios diferentes. Essa solução parece ser seriamente superengenharia.
dólmen
26

Eu tive problemas ao usar o -ldflagsparâmetro ao criar meu aplicativo de linha de comando e projeto de biblioteca combinados, então acabei usando um destino Makefile para gerar um arquivo de origem Go contendo a versão do aplicativo e a data de compilação:

BUILD_DATE := `date +%Y-%m-%d\ %H:%M`
VERSIONFILE := cmd/myapp/version.go

gensrc:
    rm -f $(VERSIONFILE)
    @echo "package main" > $(VERSIONFILE)
    @echo "const (" >> $(VERSIONFILE)
    @echo "  VERSION = \"1.0\"" >> $(VERSIONFILE)
    @echo "  BUILD_DATE = \"$(BUILD_DATE)\"" >> $(VERSIONFILE)
    @echo ")" >> $(VERSIONFILE)

No meu init()método, faço isso:

flag.Usage = func() {
    fmt.Fprintf(os.Stderr, "%s version %s\n", os.Args[0], VERSION)
    fmt.Fprintf(os.Stderr, "built %s\n", BUILD_DATE)
    fmt.Fprintln(os.Stderr, "usage:")
    flag.PrintDefaults()
}

Se você quisesse um número de compilação com aumento atômico em vez de uma data de compilação, no entanto, provavelmente seria necessário criar um arquivo local que contivesse o último número de compilação. Seu Makefile lê o conteúdo do arquivo em uma variável, incrementa-o, insere-o no version.goarquivo em vez da data e grava o novo número da compilação novamente no arquivo.

pegli
fonte
2
Ótima solução. Ainda assim, acho que encontrei o motivo dos problemas de -ldflags. Se o arquivo que contém a variável sendo atualizada por -X não for tocado, a compilação não será acionada e você terá uma versão antiga no binário. Minha solução foi a tocar um pequeno arquivo contendo redefinição de ser única variável via -ldflags "-X ..."
Wojciech Kaczmarek
20

Use ldflagspara definir variáveis ​​emmain pacote:

Com arquivo main.go:

package main

import "fmt"

var (
    version string
    build   string
)

func main() {
    fmt.Println("version=", version)
    fmt.Println("build=", build)
}

Então corra:

go run \
  -ldflags "-X main.version=1.0.0 -X main.build=12082019" \ 
  main.go

Construir:

go build -o mybinary \
  -ldflags "-X main.version=1.0.0 -X 'main.build=$(date)'" \ 
  main.go

Use ldflagspara definir variável em umnon-main pacote:

Com arquivo config.go:

package config

import "fmt"

var (
    Version string
)

func LogVersion() {
    fmt.Println("version=", Version)
}

Você também precisará do arquivo main.go:

package main

import (
    "fmt"
    "github.com/user/repo/config"
}

func main() {
    config.LogVersion()
}

Crie seu binário primeiro:

go build -o mybinary main.go 

Encontre o caminho completo do nome da variável que você deseja definir:

go tool nm <path_to_binary> | grep Version

Execute e construa o binário novamente, mas com o ldflags:

go run \
  -ldflags "-X github.com/user/repo/config.Version=1.0.0" \
  main.go --version       


go build -o mybinary \
  -ldflags "-X github.com/user/repo/config.Version=1.0.0" \
  main.go     

Inspirado em https://github.com/golang/go/wiki/GcToolchainTricks#including-build-information-in-the-executable


Além disso, se você estiver usando goreleaser, leia esta https://goreleaser.com/#using-the-main-version :

Por padrão, o GoReleaser define três ldflags:

main.version: tag atual do Git
main.commit: commit atual do Git SHA
main.date: data de acordo com RFC3339


Se você quiser ver isso em ação: https://github.com/hoto/fuzzy-repo-finder/blob/master/pkg/config/config.go

Andrzej Rehmann
fonte
12

usar multi -ldflags:

$ go build -ldflags "-X name1=value1 -X name2=value2" -o path/to/output
Kimia Zhu
fonte
12

No sistema operacional Windows, conforme o programa abaixo

package main

import "fmt"

var (
    version string
    date    string
)

func main() {
    fmt.Printf("version=%s, date=%s", version, date)
}

Você pode construir usando

go build -ldflags "-X main.version=0.0.1 -X main.date=%date:~10,4%-%date:~4,2%-%date:~7,2%T%time:~0,2%:%time:~3,2%:%time:~6,2%"

O formato da data assume que seu ambiente echo %date%é Fri 07/22/2016e echo %time%é16:21:52.88

Então a saída será: version=0.0.1, date=2016-07-22T16:21:52

Ostati
fonte