Автоматическое создание версий приложений

193

Можно ли автоматически увеличивать вспомогательный номер версии каждый раз при компиляции приложения Go?

Я хотел бы установить номер версии внутри моей программы с автоинкрементным разделом:

$ myapp -version
MyApp version 0.5.132

Будучи равным 0.5 номера версии, которую я установил, и 132 значением, которое увеличивается автоматически при каждой компиляции двоичного файла.

Возможно ли это в Go?

Себастьян Гриньоли
источник

Ответы:

338

В компоновщике Go ( ссылка на инструмент go ) есть опция для установки значения неинициализированной строковой переменной:

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

стоимость. Обратите внимание, что до Go 1.5 эта опция принимала два отдельных аргумента. Теперь требуется один аргумент, разделенный на первый знак =.

Как часть вашего процесса сборки, вы можете установить строковую переменную версии, используя это. Вы можете передать это через goинструмент, используя -ldflags. Например, дан следующий исходный файл:

package main

import "fmt"

var xyz string

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

Затем:

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

Чтобы установить main.minversionдату и время сборки при сборке:

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

Если вы скомпилируете без инициализации main.minversionтаким образом, он будет содержать пустую строку.

AXW
источник
4
Будет ли это значение сохранено в двоичный файл, если я буду использовать go bouildвместо go run?
Себастьян Гриньоли
6
go build -ldflags "-X main.minversion `date -u +.%Y%m%d%.H%M%S`" service.go
Себастьян Гриньоли
4
goxc делает это за вас :) по умолчанию он компилируется с -ldflags "-Xmain.VERSION xxx -Xmain.BUILD_DATE CurrentDateInISO8601", но вы можете настроить эти имена переменных, если хотите. См. Github.com/laher/goxc ... (заявление об отказе: я написал goxc)
laher
7
рабочий пример с новым синтаксисом 1.5 для добавления переменной go build -ldflags "-X 'main.buildtime=$(date -u '+%Y-%m-%d %H:%M:%S')'"
buildtime
26
обратите внимание, что требуется полное имя пакета. go build -ldflags "-X pkg.version=123"не будет работать во время go build -ldflags "-X path/to/pkg.version=123"работы, как ожидалось. Надеюсь, поможет.
csyangchen
27

Кроме того, я хотел бы опубликовать небольшой пример, как использовать git и 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

Файл make создаст два исполняемых файла. Один выполняет функцию один, другой принимает функцию два в качестве основной записи:

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

}

Тогда просто запустите:

make

Ты получишь:

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
Мацей А. Беднарз
источник
5
Или проще: просто сделайте два основных в двух разных каталогах. Это решение, похоже, серьезно переоценено.
дольмен
26

У меня были проблемы с использованием этого -ldflagsпараметра при создании моего смешанного приложения командной строки и проекта библиотеки, поэтому я в конечном итоге использовал цель Makefile для создания исходного файла Go, содержащего версию моего приложения и дату сборки:

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)

В моем init()методе я делаю это:

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

Однако если вы хотите получить атомарно увеличивающийся номер сборки вместо даты сборки, вам, вероятно, потребуется создать локальный файл, содержащий последний номер сборки. Ваш Makefile будет считывать содержимое файла в переменную, увеличивать его, вставлять в version.goфайл вместо даты и записывать новый номер сборки обратно в файл.

Pegli
источник
2
Хорошее решение. Тем не менее, я думаю, что нашел причину проблем с флагами. Если файл, содержащий переменную, обновляемую с помощью -X, не трогается, то компиляция не запускается, и у вас есть старая версия в двоичном файле. Мое решение состояло в том, чтобы прикоснуться к маленькому файлу, содержащему только переменную, сбрасываемую через -ldflags "-X ..."
Войцех Качмарек
20

Используйте ldflagsдля установки переменных в mainпакете:

С файлом main.go:

package main

import "fmt"

var (
    version string
    build   string
)

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

Затем запустите:

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

Телосложение:

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

Используйте ldflagsдля установки переменной в non-mainпакете:

С файлом config.go:

package config

import "fmt"

var (
    Version string
)

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

Вам также понадобится файл main.go:

package main

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

func main() {
    config.LogVersion()
}

Сначала создайте свой бинарный файл:

go build -o mybinary main.go 

Найдите полный путь к имени переменной, которую вы хотите установить:

go tool nm <path_to_binary> | grep Version

Запустите и соберите двоичный файл снова, но с 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     

Вдохновленный https://github.com/golang/go/wiki/GcToolchainTricks#incключ-build-information-in-the-executable


Также, если вы используете, goreleaserпрочитайте это https://goreleaser.com/#using-the-main-version :

Мудрый GoReleaser по умолчанию устанавливает три флага ld:

main.version: текущий тег Git
main.commit: текущий коммит git SHA
main.date: дата согласно RFC3339


Если вы хотите увидеть это в действии: https://github.com/hoto/fuzzy-repo-finder/blob/master/pkg/config/config.go

Анджей Реманн
источник
12

использовать мульти -ldflags:

$ go build -ldflags "-X name1=value1 -X name2=value2" -o path/to/output
Кимия Чжу
источник
12

На ОС Windows приведена программа ниже

package main

import "fmt"

var (
    version string
    date    string
)

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

Вы можете построить с помощью

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

Формат даты предполагает, что ваша среда echo %date%есть Fri 07/22/2016и echo %time%есть16:21:52.88

Тогда вывод будет: version=0.0.1, date=2016-07-22T16:21:52

Ostati
источник