Version updates via Makefile

Flamenco now no longer uses the Git tags + hash for the application
version, but an explicit `VERSION` variable in the `Makefile`.

After changing the `VERSION` variable in the `Makefile`, run
`make update-version`.

Not every part of Flamenco looks at this variable, though. Most
importantly: the Blender add-on needs special handling, because that
doesn't just take a version string but a tuple of integers. Running
`make update-version` updates the add-on's `bl_info` dict with the new
version. If the version has any `-blabla` suffix (like `3.0-beta0`) it
will also set the `warning` field to explain that it's not a stable
release.
This commit is contained in:
Sybren A. Stüvel 2022-07-25 14:45:06 +02:00
parent a0b4fc18e6
commit c1a728dc2f
9 changed files with 283 additions and 16 deletions

View File

@ -1,12 +1,11 @@
PKG := git.blender.org/flamenco PKG := git.blender.org/flamenco
VERSION := $(shell git describe --tags --dirty --always)
# Version used in the OpenAPI-generated code shouldn't contain the '-dirty'
# suffix. In the common development workflow, those files will always be dirty
# (because they're only committed after locally working, which means the
# implementation has already been written).
OAPI_VERSION := $(shell git describe --tags --always)
LDFLAGS := -X ${PKG}/internal/appinfo.ApplicationVersion=${VERSION} # To update the version number in all the relevant places, update the VERSION
# variable below and run `make update-version`.
VERSION := 3.0-dev0
GITHASH := $(shell git describe --dirty --always)
LDFLAGS := -X ${PKG}/internal/appinfo.ApplicationVersion=${VERSION} -X ${PKG}/internal/appinfo.ApplicationGitHash=${GITHASH}
BUILD_FLAGS = -ldflags="${LDFLAGS}" BUILD_FLAGS = -ldflags="${LDFLAGS}"
# Package name of the generated Python/JavaScript code for the Flamenco API. # Package name of the generated Python/JavaScript code for the Flamenco API.
@ -76,7 +75,10 @@ webapp-static: addon-packer
./addon-packer -filename ${WEB_STATIC}/flamenco3-addon.zip ./addon-packer -filename ${WEB_STATIC}/flamenco3-addon.zip
@echo "Web app has been installed into ${WEB_STATIC}" @echo "Web app has been installed into ${WEB_STATIC}"
generate: generate-go generate-py generate-js generate:
$(MAKE) generate-go
$(MAKE) generate-py
$(MAKE) generate-js
generate-go: generate-go:
go generate ./pkg/api/... go generate ./pkg/api/...
@ -99,10 +101,10 @@ generate-py:
-g python \ -g python \
-o addon/ \ -o addon/ \
--package-name "${PY_API_PKG_NAME}" \ --package-name "${PY_API_PKG_NAME}" \
--http-user-agent "Flamenco/${OAPI_VERSION} (Blender add-on)" \ --http-user-agent "Flamenco/${VERSION} (Blender add-on)" \
-p generateSourceCodeOnly=true \ -p generateSourceCodeOnly=true \
-p projectName=Flamenco \ -p projectName=Flamenco \
-p packageVersion="${OAPI_VERSION}" > .openapi-generator-py.log -p packageVersion="${VERSION}" > .openapi-generator-py.log
# The generator outputs files so that we can write our own tests. We don't, # The generator outputs files so that we can write our own tests. We don't,
# though, so it's better to just remove those placeholders. # though, so it's better to just remove those placeholders.
@ -130,7 +132,7 @@ generate-js:
-i pkg/api/flamenco-openapi.yaml \ -i pkg/api/flamenco-openapi.yaml \
-g javascript \ -g javascript \
-o web/_tmp-manager-api-javascript \ -o web/_tmp-manager-api-javascript \
--http-user-agent "Flamenco/${OAPI_VERSION} / webbrowser" \ --http-user-agent "Flamenco/${VERSION} / webbrowser" \
-p projectName=flamenco-manager \ -p projectName=flamenco-manager \
-p projectVersion="0.0.0" \ -p projectVersion="0.0.0" \
-p apiPackage="${JS_API_PKG_NAME}" \ -p apiPackage="${JS_API_PKG_NAME}" \
@ -149,11 +151,26 @@ ifeq ($(OS),Windows_NT)
git status --porcelain | grep '^ M web/app/src/manager-api' | cut -d' ' -f3 | xargs unix2dos --keepdate git status --porcelain | grep '^ M web/app/src/manager-api' | cut -d' ' -f3 | xargs unix2dos --keepdate
endif endif
.PHONY:
update-version:
@echo "--- Updating Flamenco version to ${VERSION}"
@echo "--- If this stops with exit status 42, it was already at that version."
@echo
go run ./cmd/update-version ${VERSION}
$(MAKE) generate-py
$(MAKE) generate-js
@echo
@echo 'File replacement done, commit with:'
@echo
@echo 'git commit -m "Bumped version to ${VERSION}" Makefile addon/flamenco/__init__.py'
@echo 'git tag -a -m "Tagged version ${VERSION}" v${VERSION}'
version: version:
@echo "OS : ${OS}"
@echo "Package : ${PKG}" @echo "Package : ${PKG}"
@echo "Version : ${VERSION}" @echo "Version : ${VERSION}"
@echo "OAPI Version: ${OAPI_VERSION}" @echo "Git Hash : ${GITHASH}"
@echo -n "GOOS : "; go env GOOS
@echo -n "GOARCH : "; go env GOARCH
@echo @echo
@env | grep GO @env | grep GO

View File

@ -12,6 +12,7 @@ bl_info = {
"doc_url": "https://www.flamenco.io/", "doc_url": "https://www.flamenco.io/",
"category": "System", "category": "System",
"support": "COMMUNITY", "support": "COMMUNITY",
"warning": "This is version 3.0-dev0 of the add-on, which is not a stable release",
} }
from pathlib import Path from pathlib import Path

View File

@ -72,6 +72,7 @@ func main() {
log.Logger = log.Output(output) log.Logger = log.Output(output)
log.Info(). log.Info().
Str("version", appinfo.ApplicationVersion). Str("version", appinfo.ApplicationVersion).
Str("git", appinfo.ApplicationGitHash).
Str("os", runtime.GOOS). Str("os", runtime.GOOS).
Str("arch", runtime.GOARCH). Str("arch", runtime.GOARCH).
Msgf("starting %v", appinfo.ApplicationName) Msgf("starting %v", appinfo.ApplicationName)

View File

@ -61,6 +61,7 @@ func main() {
log.Info(). log.Info().
Str("version", appinfo.ApplicationVersion). Str("version", appinfo.ApplicationVersion).
Str("git", appinfo.ApplicationGitHash).
Str("OS", runtime.GOOS). Str("OS", runtime.GOOS).
Str("ARCH", runtime.GOARCH). Str("ARCH", runtime.GOARCH).
Int("pid", os.Getpid()). Int("pid", os.Getpid()).

View File

@ -0,0 +1,65 @@
package main
import (
"fmt"
"strings"
"github.com/rs/zerolog/log"
)
const addonVersionFile = "addon/flamenco/__init__.py"
// updateAddon changes the version number in the Blender add-on.
// Returns whether the file actually changed.
func updateAddon() bool {
// The add-on needs a (x, y, z) tuple as version, and doesn't support suffixes
// like `-dev0` or `-beta3`.
splitOnDash := strings.SplitN(cliArgs.newVersion, "-", 2)
versionWithoutSuffix := splitOnDash[0]
var warning string
if len(splitOnDash) >= 2 {
log.Warn().Msg("versions of form `x.y.z-something` will put a warning in the bl_info about the `-something`")
warning = fmt.Sprintf("This is version %s of the add-on, which is not a stable release", cliArgs.newVersion)
}
versionParts := strings.Split(versionWithoutSuffix, ".")
var versionTuple string
switch len(versionParts) {
case 0:
log.Fatal().Str("versionWithoutSuffix", versionWithoutSuffix).Msg("no dot-separated version number found")
case 1:
log.Warn().Strs("versionParts", versionParts).Msg("only a major version found, may be the wrong syntax")
versionTuple = fmt.Sprintf("(%s, 0)", versionParts[0])
case 2:
log.Debug().Strs("versionParts", versionParts).Msg("major.minor version found, this is expected")
versionTuple = fmt.Sprintf("(%s, %s)", versionParts[0], versionParts[1])
case 3:
log.Debug().Strs("versionParts", versionParts).Msg("major.minor.micro version found, this is expected")
versionTuple = fmt.Sprintf("(%s, %s, %s)", versionParts[0], versionParts[1], versionParts[2])
default:
log.Warn().Strs("versionParts", versionParts).Msg("more than three (major, minor, micro) version parts found, using only the first three")
versionTuple = fmt.Sprintf("(%s, %s, %s)", versionParts[0], versionParts[1], versionParts[2])
}
var blinfoOpened, blinfoClosed bool
replacer := func(line string) string {
switch {
case !blinfoOpened && strings.HasPrefix(line, "bl_info = {"):
blinfoOpened = true
case blinfoOpened && strings.HasPrefix(line, "}"):
blinfoClosed = true
case blinfoOpened && !blinfoClosed && strings.HasPrefix(line, " \"version\":"):
return fmt.Sprintf(" \"version\": %s,", versionTuple)
case blinfoOpened && !blinfoClosed && strings.HasPrefix(line, " \"warning\":"):
return fmt.Sprintf(" \"warning\": %q,", warning)
}
return line
}
fileWasChanged, err := updateLines(addonVersionFile, replacer)
if err != nil {
log.Fatal().Err(err).Msg("error updating add-on")
}
return fileWasChanged
}

View File

@ -0,0 +1,63 @@
package main
import (
"fmt"
"os"
"strings"
"github.com/rs/zerolog/log"
)
// updateLines calls replacer() on each line of the given file, and replaces it
// with the returned value.
// Returns whether the file changed at all.
func updateLines(filename string, replacer func(string) string) (bool, error) {
logger := log.With().Str("filename", filename).Logger()
logger.Info().Msg("updating file")
// Read the file contents:
input, err := os.ReadFile(filename)
if err != nil {
return false, fmt.Errorf("reading from %s: %w", filename, err)
}
// Replace the lines:
anythingChanged := false
lines := strings.Split(string(input), "\n")
for idx := range lines {
replaced := replacer(lines[idx])
if replaced == lines[idx] {
continue
}
logger.Info().
Str("old", strings.TrimSpace(lines[idx])).
Str("new", strings.TrimSpace(replaced)).
Msg("replacing line")
lines[idx] = replaced
anythingChanged = true
}
if !anythingChanged {
logger.Info().Msg("file did not change, will not touch it")
return false, nil
}
// Write the file contents to a temporary location:
output := strings.Join(lines, "\n")
tempname := filename + "~"
err = os.WriteFile(tempname, []byte(output), 0644)
if err != nil {
return false, fmt.Errorf("writing to %s: %w", tempname, err)
}
// Move the temporary file onto the input filename:
if err := os.Remove(filename); err != nil {
return false, fmt.Errorf("removing %s: %w", filename, err)
}
if err := os.Rename(tempname, filename); err != nil {
return false, fmt.Errorf("renaming %s to %s: %w", tempname, filename, err)
}
return true, nil
}

View File

@ -0,0 +1,87 @@
package main
// SPDX-License-Identifier: GPL-3.0-or-later
import (
"flag"
"fmt"
"os"
"time"
"github.com/mattn/go-colorable"
"github.com/rs/zerolog"
"github.com/rs/zerolog/log"
)
var cliArgs struct {
// Logging level flags.
quiet, debug, trace bool
newVersion string
updateMakefile bool
}
func main() {
parseCliArgs()
output := zerolog.ConsoleWriter{Out: colorable.NewColorableStdout(), TimeFormat: time.RFC3339}
log.Logger = log.Output(output)
configLogLevel()
log.Info().Str("version", cliArgs.newVersion).Msg("updating Flamenco version")
var anyFileWasChanged bool
if cliArgs.updateMakefile {
anyFileWasChanged = anyFileWasChanged || updateMakefile()
}
anyFileWasChanged = anyFileWasChanged || updateAddon()
if !anyFileWasChanged {
log.Warn().Msg("nothing changed")
os.Exit(42)
return
}
// Lot the result & some easy-to-copy Git commands:
commitMsg := fmt.Sprintf("Bumped version to %s", cliArgs.newVersion)
tagMsg := fmt.Sprintf("Tagged version %s", cliArgs.newVersion)
log.Info().Msg("file replacement done, commit with:")
log.Info().Msgf("git commit -m %q %s %s", commitMsg, makefileFile, addonVersionFile)
log.Info().Msgf("git tag -a -m %q v%s", tagMsg, cliArgs.newVersion)
}
func parseCliArgs() {
flag.BoolVar(&cliArgs.quiet, "quiet", false, "Only log warning-level and worse.")
flag.BoolVar(&cliArgs.debug, "debug", false, "Enable debug-level logging.")
flag.BoolVar(&cliArgs.trace, "trace", false, "Enable trace-level logging.")
flag.BoolVar(&cliArgs.updateMakefile, "makefile", false,
"Also update the Makefile. Normally this application is invoked from the Makefile itself, "+
"and thus it does not change that file without this CLI argument.")
flag.Parse()
cliArgs.newVersion = flag.Arg(0)
if cliArgs.newVersion == "" {
os.Stderr.WriteString(fmt.Sprintf("Usage: %s [-quiet|-debug|-trace] {new Flamenco version number}\n", os.Args[0]))
os.Stderr.WriteString("\n")
flag.PrintDefaults()
os.Stderr.WriteString("\n")
os.Stderr.WriteString("This program updates Makefile and some other files to set the new Flamenco version.\n")
os.Stderr.WriteString("\n")
os.Exit(47)
}
}
func configLogLevel() {
var logLevel zerolog.Level
switch {
case cliArgs.trace:
logLevel = zerolog.TraceLevel
case cliArgs.debug:
logLevel = zerolog.DebugLevel
case cliArgs.quiet:
logLevel = zerolog.WarnLevel
default:
logLevel = zerolog.InfoLevel
}
zerolog.SetGlobalLevel(logLevel)
}

View File

@ -0,0 +1,27 @@
package main
import (
"fmt"
"strings"
"github.com/rs/zerolog/log"
)
const makefileFile = "Makefile"
// updateMakefile changes the version number in Makefile.
// Returns whether the file actually changed.
func updateMakefile() bool {
replacer := func(line string) string {
if !strings.HasPrefix(line, "VERSION := ") {
return line
}
return fmt.Sprintf("VERSION := %q", cliArgs.newVersion)
}
fileWasChanged, err := updateLines(makefileFile, replacer)
if err != nil {
log.Fatal().Err(err).Msg("error updating Makefile")
}
return fileWasChanged
}

View File

@ -5,11 +5,16 @@ package appinfo
import "fmt" import "fmt"
// ApplicationName contains the application name. // ApplicationName contains the application name.
const ApplicationName = "Flamenco 3" const ApplicationName = "Flamenco"
// ApplicationVersion has the version number, and is set during the build. // ApplicationVersion is the version number of the application.
// It is set during the build.
var ApplicationVersion = "set-during-build" var ApplicationVersion = "set-during-build"
// ApplicationGitHash has the Git hash of the commit used to create this build.
// It is set during the build.
var ApplicationGitHash = "set-during-build"
// FormattedApplicationInfo returns the application name & version as single string. // FormattedApplicationInfo returns the application name & version as single string.
func FormattedApplicationInfo() string { func FormattedApplicationInfo() string {
return fmt.Sprintf("%s %s", ApplicationName, ApplicationVersion) return fmt.Sprintf("%s %s", ApplicationName, ApplicationVersion)
@ -17,5 +22,5 @@ func FormattedApplicationInfo() string {
// UserAgent returns the application name & version suitable for the HTTP User-Agent header. // UserAgent returns the application name & version suitable for the HTTP User-Agent header.
func UserAgent() string { func UserAgent() string {
return fmt.Sprintf("%s/%s", ApplicationName, ApplicationVersion) return fmt.Sprintf("%s/%s (%s)", ApplicationName, ApplicationVersion, ApplicationGitHash)
} }