diff --git a/internal/app/app_test.go b/internal/app/app_test.go index cfe5935b..87c932aa 100644 --- a/internal/app/app_test.go +++ b/internal/app/app_test.go @@ -16,6 +16,7 @@ import ( "unicode" "gitea.dev/tea/internal/clierr" + "gitea.dev/tea/internal/version" "github.com/spf13/cobra" "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"}) require.ErrorContains(t, err, "invalid --jq expression") 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) { diff --git a/internal/version/version.go b/internal/version/version.go index 22cf1646..9dc4ff25 100644 --- a/internal/version/version.go +++ b/internal/version/version.go @@ -1,12 +1,15 @@ // Copyright 2026 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT +// Package version reports the version of the tea build. package version import ( - "fmt" + "regexp" "runtime" + "runtime/debug" "strings" + "time" ) // Version holds the current tea version. @@ -18,16 +21,44 @@ var Version = "development" // Tags holds the build tags used var Tags = "" -// Format returns a human-readable version string including -// Go version and build tags when available. +// release matches the versions of tagged releases, such as 1.0.0 or 1.1.0-rc1; builds between +// 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 { - s := fmt.Sprintf("Version: %s\tgolang: %s", - Version, - strings.ReplaceAll(runtime.Version(), "go", "")) - - if len(Tags) != 0 { - s += fmt.Sprintf("\tbuilt with: %s", strings.ReplaceAll(Tags, " ", ", ")) - } - - return s + info, _ := debug.ReadBuildInfo() + return format(Version, Tags, runtime.Version(), info) +} + +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 } diff --git a/internal/version/version_test.go b/internal/version/version_test.go new file mode 100644 index 00000000..afb0b0f0 --- /dev/null +++ b/internal/version/version_test.go @@ -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)) + }) + } +}