feat(version): print the version like gh version

tea version and --version printed "Version: development<TAB>golang:
1.27.2". They now print "tea version 1.0.0 (2026-10-01)", the release
notes URL for tagged releases, and "built with go1.27.2" with the build
tags. Builds from go install take the module version from the build
info, and the date comes from the commit the binary was built from.

Signed-off-by: bircni <bircni@icloud.com>
This commit is contained in:
bircni committed 2026-10-11 23:50:46 +02:00
1 parent 85034c524f
commit 83dd4f7938
3 files changed
+73 -11

No files matched your search

+2 -1
View File
@@ -16,6 +16,7 @@ import (
"unicode" "unicode"
"gitea.dev/tea/internal/clierr" "gitea.dev/tea/internal/clierr"
"gitea.dev/tea/internal/version"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )
@@ -68,7 +69,7 @@ func TestCommandLineEdgeCases(t *testing.T) {
err = Execute(context.Background(), deps, []string{"config", "list", "--jq", ".[", "--remote", "-h"}) err = Execute(context.Background(), deps, []string{"config", "list", "--jq", ".[", "--remote", "-h"})
require.ErrorContains(t, err, "invalid --jq expression") require.ErrorContains(t, err, "invalid --jq expression")
require.NoError(t, Execute(context.Background(), deps, []string{"--version"})) require.NoError(t, Execute(context.Background(), deps, []string{"--version"}))
require.NotContains(t, out.String(), "tea version") require.Equal(t, version.Format()+"\n", out.String())
} }
func TestAliasesAfterGlobalFlagsAndNestedHelp(t *testing.T) { func TestAliasesAfterGlobalFlagsAndNestedHelp(t *testing.T) {
+41 -10
View File
@@ -1,12 +1,15 @@
// Copyright 2026 The Gitea Authors. All rights reserved. // Copyright 2026 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
// Package version reports the version of the tea build.
package version package version
import ( import (
"fmt" "regexp"
"runtime" "runtime"
"runtime/debug"
"strings" "strings"
"time"
) )
// Version holds the current tea version. // Version holds the current tea version.
@@ -18,16 +21,44 @@ var Version = "development"
// Tags holds the build tags used // Tags holds the build tags used
var Tags = "" var Tags = ""
// Format returns a human-readable version string including // release matches the versions of tagged releases, such as 1.0.0 or 1.1.0-rc1; builds between
// Go version and build tags when available. // releases carry a +commit suffix.
var release = regexp.MustCompile(`^\d+\.\d+\.\d+(-[0-9A-Za-z.]+)?$`)
// Format describes the build like gh version: the version and its date, the release notes of a
// release, and the Go version and build tags:
//
// tea version 1.0.0 (2026-10-01)
// https://gitea.com/gitea/tea/releases/tag/v1.0.0
// built with go1.27.2
func Format() string { func Format() string {
s := fmt.Sprintf("Version: %s\tgolang: %s", info, _ := debug.ReadBuildInfo()
Version, return format(Version, Tags, runtime.Version(), info)
strings.ReplaceAll(runtime.Version(), "go", ""))
if len(Tags) != 0 {
s += fmt.Sprintf("\tbuilt with: %s", strings.ReplaceAll(Tags, " ", ", "))
} }
return s func format(version, tags, goVersion string, info *debug.BuildInfo) string {
date := ""
if info != nil {
// go install gitea.dev/tea@v1.0.0 stamps the module version instead of the ldflags.
if version == "development" && info.Main.Version != "" && info.Main.Version != "(devel)" {
version = strings.TrimPrefix(info.Main.Version, "v")
}
for _, setting := range info.Settings {
if setting.Key != "vcs.time" {
continue
}
if built, err := time.Parse(time.RFC3339, setting.Value); err == nil {
date = " (" + built.UTC().Format(time.DateOnly) + ")"
}
}
}
text := "tea version " + version + date + "\n"
if release.MatchString(version) {
text += "https://gitea.com/gitea/tea/releases/tag/v" + version + "\n"
}
text += "built with " + goVersion
if tags = strings.Join(strings.Fields(tags), ", "); tags != "" {
text += " (tags: " + tags + ")"
}
return text
} }
+30
View File
@@ -0,0 +1,30 @@
// Copyright 2026 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package version
import (
"runtime/debug"
"testing"
"github.com/stretchr/testify/require"
)
func TestFormatDescribesTheBuild(t *testing.T) {
stamped := &debug.BuildInfo{Settings: []debug.BuildSetting{{Key: "vcs.time", Value: "2026-10-01T09:30:00Z"}}}
for _, tc := range []struct {
name, version, tags string
info *debug.BuildInfo
want string
}{
{"release", "1.0.0", "", stamped, "tea version 1.0.0 (2026-10-01)\nhttps://gitea.com/gitea/tea/releases/tag/v1.0.0\nbuilt with go1.27.2"},
{"release candidate with tags", "1.1.0-rc1", "sqlite netgo", nil, "tea version 1.1.0-rc1\nhttps://gitea.com/gitea/tea/releases/tag/v1.1.0-rc1\nbuilt with go1.27.2 (tags: sqlite, netgo)"},
{"between releases", "0.16.0+12-gabcdef", "", stamped, "tea version 0.16.0+12-gabcdef (2026-10-01)\nbuilt with go1.27.2"},
{"go install", "development", "", &debug.BuildInfo{Main: debug.Module{Version: "v1.0.1"}}, "tea version 1.0.1\nhttps://gitea.com/gitea/tea/releases/tag/v1.0.1\nbuilt with go1.27.2"},
{"local build", "development", "", &debug.BuildInfo{Main: debug.Module{Version: "(devel)"}}, "tea version development\nbuilt with go1.27.2"},
} {
t.Run(tc.name, func(t *testing.T) {
require.Equal(t, tc.want, format(tc.version, tc.tags, "go1.27.2", tc.info))
})
}
}