Aplicación automática de versiones de compilación

193

¿Es posible incrementar automáticamente un número de versión menor cada vez que se compila una aplicación Go?

Me gustaría establecer un número de versión dentro de mi programa, con una sección de autoincremento:

$ myapp -version
MyApp version 0.5.132

Siendo 0.5 el número de versión que configuré, y 132 un valor que se incrementa automáticamente cada vez que se compila el binario.

¿Es esto posible en Go?

Sebastián Grignoli
fuente

Respuestas:

337

El enlazador Go ( enlace de herramienta go ) tiene una opción para establecer el valor de una variable de cadena no inicializada:

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

valor. Tenga en cuenta que antes de ir a 1.5 esta opción tomó dos argumentos separados. Ahora se necesita un argumento dividido en el primer signo =.

Como parte de su proceso de compilación, puede establecer una variable de cadena de versión con esto. Puede pasar esto a través de la goherramienta usando -ldflags. Por ejemplo, dado el siguiente archivo fuente:

package main

import "fmt"

var xyz string

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

Luego:

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

Para establecer main.minversionla fecha y hora de compilación al compilar:

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

Si compila sin inicializar main.minversionde esta manera, contendrá la cadena vacía.

axw
fuente
44
¿Se guardará ese valor en el binario si lo uso en go bouildlugar de go run?
Sebastián Grignoli
66
go build -ldflags "-X main.minversion `date -u +.%Y%m%d%.H%M%S`" service.go
Sebastián Grignoli
44
goxc hace esto por usted :) de forma predeterminada, se compila con -ldflags "-Xmain.VERSION xxx -Xmain.BUILD_DATE CurrentDateInISO8601", pero puede configurar esos nombres de variables si lo desea. Ver github.com/laher/goxc ... (descargo de responsabilidad: escribí goxc)
laher
77
ejemplo de trabajo con la nueva sintaxis 1.5 para agregar la variable go build -ldflags "-X 'main.buildtime=$(date -u '+%Y-%m-%d %H:%M:%S')'"
buildtime
26
observe que se requiere el nombre completo del paquete. go build -ldflags "-X pkg.version=123"no funcionará mientras go build -ldflags "-X path/to/pkg.version=123"trabaje como se esperaba. Espero eso ayude.
csyangchen
27

Además, me gustaría publicar un pequeño ejemplo de cómo usar git y un archivo MAKE:

--- 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

El archivo make creará dos ejecutables. Uno está ejecutando la función uno, el otro tomará la función dos 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]()

}

Entonces solo corre:

make

Conseguirás:

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
fuente
55
O más simple: simplemente haga dos principales en dos directorios diferentes. Esta solución parece estar seriamente diseñada en exceso.
dolmen
26

Tuve problemas para usar el -ldflagsparámetro al compilar mi proyecto mixto de aplicación de línea de comandos y biblioteca, así que terminé usando un destino Makefile para generar un archivo fuente Go que contiene la versión de mi aplicación y la fecha de compilación:

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)

En mi init()método, hago esto:

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()
}

Sin embargo, si desea un número de compilación que aumente atómicamente en lugar de una fecha de compilación, probablemente necesitará crear un archivo local que contenga el último número de compilación. Su Makefile leería el contenido del archivo en una variable, lo incrementaría, lo insertaría en el version.goarchivo en lugar de la fecha y volvería a escribir el nuevo número de compilación en el archivo.

pegli
fuente
2
Buena solución Aún así, creo que he encontrado la razón de los problemas de -ldflags. Si no se toca el archivo que contiene la variable que está siendo actualizada por -X, entonces la compilación no se dispara y usted tiene una versión anterior en el binario. Mi solución fue tocar un pequeño archivo que contiene solo la variable que se restablece a través de -ldflags "-X ..."
Wojciech Kaczmarek
20

Use ldflagspara establecer variables en el mainpaquete:

Con archivo main.go:

package main

import "fmt"

var (
    version string
    build   string
)

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

Entonces corre:

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 establecer la variable en un non-mainpaquete:

Con archivo config.go:

package config

import "fmt"

var (
    Version string
)

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

También necesitará el archivo main.go:

package main

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

func main() {
    config.LogVersion()
}

Construye tu binario primero:

go build -o mybinary main.go 

Encuentre la ruta completa del nombre de la variable que desea establecer:

go tool nm <path_to_binary> | grep Version

Ejecuta y construye el binario nuevamente pero con 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 en https://github.com/golang/go/wiki/GcToolchainTricks#incuding-build-information-in-the-executable


Además, si está utilizando goreleaser, lea este https://goreleaser.com/#using-the-main-version :

El GoReleaser sabio predeterminado establece tres ldflags:

main.version: etiqueta Git actual
main.commit: commit git actual SHA
main.date: fecha según RFC3339


Si quieres ver esto en acción: https://github.com/hoto/fuzzy-repo-finder/blob/master/pkg/config/config.go

Andrzej Rehmann
fuente
12

usar multi -ldflags:

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

En el sistema operativo Windows dado el programa a continuación

package main

import "fmt"

var (
    version string
    date    string
)

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

Puedes 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%"

El formato de fecha supone que su entorno echo %date%es Fri 07/22/2016y echo %time%es16:21:52.88

Entonces la salida será: version=0.0.1, date=2016-07-22T16:21:52

Ostati
fuente