From 09c2ce3055fe02bf327b04228fa2ee910b7991e0 Mon Sep 17 00:00:00 2001 From: andig Date: Sun, 9 Aug 2026 15:00:31 +0200 Subject: [PATCH] chore: semver-compliant version for dev and nightly builds (#32663) --- .goreleaser-nightly.yml | 5 +- .goreleaser.yml | 3 +- Makefile | 9 ++- assets/js/components/AboutModal.stories.ts | 5 +- assets/js/components/AboutModal.vue | 39 ++++-------- assets/js/components/BottomTabs/Bar.vue | 2 - assets/js/components/BottomTabs/MoreItem.vue | 2 - assets/js/components/BottomTabs/MoreMenu.vue | 3 +- assets/js/types/evcc.ts | 1 - assets/js/utils/version.test.ts | 63 ++++++++++++++++++++ assets/js/utils/version.ts | 22 +++---- assets/js/views/App.vue | 2 - server/http_site_handler.go | 2 - util/version.go | 9 +-- 14 files changed, 103 insertions(+), 64 deletions(-) create mode 100644 assets/js/utils/version.test.ts diff --git a/.goreleaser-nightly.yml b/.goreleaser-nightly.yml index 5ee5ac045..83b2ccd9a 100644 --- a/.goreleaser-nightly.yml +++ b/.goreleaser-nightly.yml @@ -11,7 +11,7 @@ builds: - -trimpath - -tags=release ldflags: - - -X github.com/evcc-io/evcc/util.Version={{ .Tag }} -X github.com/evcc-io/evcc/util.Commit={{ .ShortCommit }} -s -w + - -X github.com/evcc-io/evcc/util.Version={{ .Version }} -X github.com/evcc-io/evcc/util.Commit={{ .ShortCommit }} -s -w env: - CGO_ENABLED=0 goos: @@ -56,7 +56,8 @@ checksum: name_template: "checksums.txt" snapshot: - version_template: '{{ .Version }}{{ if eq (len (split .Version ".")) 2 }}.0{{ end }}+{{ .Timestamp }}' + # semver pre-release of the upcoming version + version_template: "{{ incminor .Version }}-dev+{{ .ShortCommit }}" changelog: sort: asc diff --git a/.goreleaser.yml b/.goreleaser.yml index 9c1beeec9..a392aa64c 100644 --- a/.goreleaser.yml +++ b/.goreleaser.yml @@ -63,7 +63,8 @@ checksum: name_template: "checksums.txt" snapshot: - version_template: "{{ .Tag }}-next" + # semver pre-release of the upcoming version + version_template: "{{ incminor .Version }}-dev+{{ .ShortCommit }}" changelog: sort: asc diff --git a/Makefile b/Makefile index 7f2e3838e..b4dc196d7 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,13 @@ # build vars -TAG_NAME ?= $(shell test -d .git && git describe --abbrev=0 --tags) +# part of the latest tag incremented for untagged builds: minor or patch +BUMP ?= minor SHA ?= $(shell test -d .git && git rev-parse --short HEAD) +GIT_TAG := $(shell test -d .git && git describe --abbrev=0 --tags) +# commits since the latest tag, empty or 0 means the commit is tagged +GIT_DIST := $(shell test -n "$(GIT_TAG)" && git rev-list --count $(GIT_TAG)..HEAD) +NEXT_TAG = $(shell echo $(GIT_TAG) | awk -F. -v b='$(BUMP)' '{if (b == "patch") $$NF++; else {$$2++; $$3=0}} 1' OFS=.) +# untagged builds are semver pre-releases of the upcoming version +TAG_NAME ?= $(if $(filter-out 0,$(GIT_DIST)),$(NEXT_TAG)-dev+$(SHA),$(GIT_TAG)) COMMIT := $(SHA) # hide commit for releases ifeq ($(RELEASE),1) diff --git a/assets/js/components/AboutModal.stories.ts b/assets/js/components/AboutModal.stories.ts index 3a9eac6ee..4b23c3fb1 100644 --- a/assets/js/components/AboutModal.stories.ts +++ b/assets/js/components/AboutModal.stories.ts @@ -47,7 +47,6 @@ export default { component: AboutModal, argTypes: { installed: { control: "text" }, - commit: { control: "text" }, availableVersion: { control: "text" }, releaseNotes: { control: "text" }, hasUpdater: { control: "boolean" }, @@ -95,9 +94,7 @@ StableUpdateWithUpdater.args = { export const Nightly = Template.bind({}); Nightly.args = { - installed: "0.303.1", - commit: "5ce7be4a9f3b2c1d", - availableVersion: "0.303.1", + installed: "0.304.0-dev+5ce7be4", }; export const DevBuild = Template.bind({}); diff --git a/assets/js/components/AboutModal.vue b/assets/js/components/AboutModal.vue index e6469e17c..b0df8929e 100644 --- a/assets/js/components/AboutModal.vue +++ b/assets/js/components/AboutModal.vue @@ -36,25 +36,14 @@ ---
- - - v{{ installed }} - - - + + v{{ installed }} + @@ -77,7 +76,6 @@ export default defineComponent({ startupCompleted: Boolean, evopt: { type: Object as PropType, required: false }, installed: String, - commit: String, availableVersion: String, customBrand: String, }, diff --git a/assets/js/components/BottomTabs/MoreItem.vue b/assets/js/components/BottomTabs/MoreItem.vue index 4b6e4f2f9..e7b0b0b9f 100644 --- a/assets/js/components/BottomTabs/MoreItem.vue +++ b/assets/js/components/BottomTabs/MoreItem.vue @@ -19,7 +19,6 @@ :auth-disabled="authDisabled" :evopt="evopt" :installed="installed" - :commit="commit" :available-version="availableVersion" :custom-brand="customBrand" @close="open = false" @@ -51,7 +50,6 @@ export default defineComponent({ authDisabled: Boolean, evopt: { type: Object as PropType, required: false }, installed: String, - commit: String, availableVersion: String, customBrand: String, }, diff --git a/assets/js/components/BottomTabs/MoreMenu.vue b/assets/js/components/BottomTabs/MoreMenu.vue index 22d32d82d..328b96ec9 100644 --- a/assets/js/components/BottomTabs/MoreMenu.vue +++ b/assets/js/components/BottomTabs/MoreMenu.vue @@ -110,7 +110,6 @@ export default defineComponent({ authDisabled: Boolean, evopt: { type: Object as PropType, required: false }, installed: String, - commit: String, availableVersion: String, customBrand: String, }, @@ -144,7 +143,7 @@ export default defineComponent({ return "bg-warning"; }, versionLabel() { - return getShortVersion(this.installed || "", this.commit); + return getShortVersion(this.installed || ""); }, newVersionAvailable() { return isNewVersionAvailable(this.installed, this.availableVersion); diff --git a/assets/js/types/evcc.ts b/assets/js/types/evcc.ts index 97cd268e5..1e77eb385 100644 --- a/assets/js/types/evcc.ts +++ b/assets/js/types/evcc.ts @@ -8,7 +8,6 @@ declare global { app: any; evcc?: { version: string; - commit: string; customCss: boolean; customLogo: boolean; customBrand: string; diff --git a/assets/js/utils/version.test.ts b/assets/js/utils/version.test.ts new file mode 100644 index 000000000..a48edb1c6 --- /dev/null +++ b/assets/js/utils/version.test.ts @@ -0,0 +1,63 @@ +import { describe, expect, test } from "vite-plus/test"; +import { + isDevelopment, + isNightly, + commitFromVersion, + getReleaseName, + getShortVersion, + isNewVersionAvailable, +} from "./version"; + +const DEV = "0.0.0"; +const NIGHTLY = "0.304.0-dev+abc1234"; +const STABLE = "0.303.1"; + +describe("isDevelopment", () => { + test("only dev builds", () => { + expect(isDevelopment(DEV)).toBe(true); + expect(isDevelopment(NIGHTLY)).toBe(false); + expect(isDevelopment(STABLE)).toBe(false); + }); +}); + +describe("isNightly", () => { + test("only versions with build metadata", () => { + expect(isNightly(DEV)).toBe(false); + expect(isNightly(NIGHTLY)).toBe(true); + expect(isNightly(STABLE)).toBe(false); + }); +}); + +describe("commitFromVersion", () => { + test("extracts build metadata", () => { + expect(commitFromVersion(NIGHTLY)).toBe("abc1234"); + expect(commitFromVersion(STABLE)).toBe(""); + }); +}); + +describe("getReleaseName", () => { + test("maps version to release name", () => { + expect(getReleaseName(DEV)).toBe("development"); + expect(getReleaseName(NIGHTLY)).toBe("nightly"); + expect(getReleaseName(STABLE)).toBe("stable"); + }); +}); + +describe("getShortVersion", () => { + test("formats version", () => { + expect(getShortVersion(DEV)).toBe("dev build"); + expect(getShortVersion(NIGHTLY)).toBe("v0.304.0-dev+abc1234"); + expect(getShortVersion(STABLE)).toBe("v0.303.1"); + }); +}); + +describe("isNewVersionAvailable", () => { + test("never for dev builds", () => { + expect(isNewVersionAvailable(DEV, "0.303.1")).toBe(false); + }); + test("only when available differs", () => { + expect(isNewVersionAvailable(STABLE, "0.303.1")).toBe(false); + expect(isNewVersionAvailable("0.303.0", "0.303.1")).toBe(true); + expect(isNewVersionAvailable(STABLE, undefined)).toBe(false); + }); +}); diff --git a/assets/js/utils/version.ts b/assets/js/utils/version.ts index a17fc1c0f..5fb990d3b 100644 --- a/assets/js/utils/version.ts +++ b/assets/js/utils/version.ts @@ -1,24 +1,24 @@ export function isDevelopment(version: string): boolean { - return version === "[[.Version]]" || version === "0.0.0"; + return version === "0.0.0"; } -export function isNightly(version: string, commit?: string): boolean { - return !isDevelopment(version) && !!commit; +// untagged builds carry the commit as build metadata: 0.304.0-dev+abc1234 +export function isNightly(version: string): boolean { + return version.includes("+"); } -export function getReleaseName(version: string, commit?: string): string { +export function commitFromVersion(version: string): string { + return version.split("+")[1] ?? ""; +} + +export function getReleaseName(version: string): string { if (isDevelopment(version)) return "development"; - if (isNightly(version, commit)) return "nightly"; + if (isNightly(version)) return "nightly"; return "stable"; } -export function shortCommit(commit?: string): string { - return commit?.substring(0, 7) || ""; -} - -export function getShortVersion(version: string, commit?: string): string { +export function getShortVersion(version: string): string { if (isDevelopment(version)) return "dev build"; - if (isNightly(version, commit)) return `v${version} (${shortCommit(commit)})`; return `v${version}`; } diff --git a/assets/js/views/App.vue b/assets/js/views/App.vue index bd75b19e8..44f8a4740 100644 --- a/assets/js/views/App.vue +++ b/assets/js/views/App.vue @@ -105,7 +105,6 @@ export default defineComponent({ aboutModalProps() { return { installed: window.evcc?.version, - commit: window.evcc?.commit, customLogo: this.custom.logo, customBrand: this.custom.brand, customWebsite: this.custom.website, @@ -117,7 +116,6 @@ export default defineComponent({ bottomTabBarProps() { return { installed: window.evcc?.version, - commit: window.evcc?.commit, customBrand: this.custom.brand, ...this.collectProps(BottomTabBar, this.state), }; diff --git a/server/http_site_handler.go b/server/http_site_handler.go index 1b5d70757..1bfd7daf3 100644 --- a/server/http_site_handler.go +++ b/server/http_site_handler.go @@ -45,7 +45,6 @@ func getPreferredLanguage(header string) string { func globalsJsHandler(custom Customization) http.HandlerFunc { globals := struct { Version string `json:"version"` - Commit string `json:"commit"` CustomCss bool `json:"customCss"` CustomLogo bool `json:"customLogo"` Brand string `json:"customBrand"` @@ -54,7 +53,6 @@ func globalsJsHandler(custom Customization) http.HandlerFunc { Phone string `json:"customPhone"` }{ Version: util.Version, - Commit: util.Commit, CustomCss: custom.Css != "", CustomLogo: custom.LogoLight != "", Brand: custom.Brand, diff --git a/util/version.go b/util/version.go index dceab7cf8..3feb56031 100644 --- a/util/version.go +++ b/util/version.go @@ -1,9 +1,6 @@ package util -import ( - "fmt" - "runtime" -) +import "runtime" const DevVersion = "0.0.0" @@ -15,10 +12,8 @@ var ( Commit = "" ) +// FormattedVersion returns the version, untagged builds carry the commit as build metadata func FormattedVersion() string { - if Commit != "" { - return fmt.Sprintf("%s (%s)", Version, Commit) - } return Version }