Take the version from the git tag, not a constant (closes #80)
check / check (push) Waiting to run
check / check (push) Waiting to run
`mfer version`, `mfer --version` and the banner print the revision the build stamps into main.Gitrev or, when it stamped none (as after `go install` of a tag), the main module's version from runtime/debug.ReadBuildInfo. The banner no longer shows a release date. mfer.Version and mfer.ReleaseDate go, with main.Version and the version field and parameter in internal/cli that nothing read. A program using the library reads the module version from its own build information. The README says where the version comes from. Judgement call: a binary with no build information, or none holding a module version, prints "unknown", the word script/build already uses when git describe gives nothing. Model: opus-5-5
This commit was merged in pull request #204.
This commit is contained in:
@@ -26,8 +26,7 @@ func noColorEnvSet() bool {
|
||||
// Use DefaultRunOptions for standard CLI execution, or construct manually for testing.
|
||||
type RunOptions struct {
|
||||
Appname string // Application name displayed in help and version output
|
||||
Version string // Version string (typically set at build time)
|
||||
Gitrev string // Git revision hash (typically set at build time)
|
||||
Gitrev string // Revision stamped in at build time, or empty
|
||||
Args []string // Command-line arguments (typically os.Args)
|
||||
Stdin io.Reader // Standard input stream
|
||||
Stdout io.Writer // Standard output stream
|
||||
@@ -36,10 +35,9 @@ type RunOptions struct {
|
||||
}
|
||||
|
||||
// DefaultRunOptions returns RunOptions configured for normal CLI execution.
|
||||
func DefaultRunOptions(appname, version, gitrev string) *RunOptions {
|
||||
func DefaultRunOptions(appname, gitrev string) *RunOptions {
|
||||
return &RunOptions{
|
||||
Appname: appname,
|
||||
Version: version,
|
||||
Gitrev: gitrev,
|
||||
Args: os.Args,
|
||||
Stdin: os.Stdin,
|
||||
@@ -50,15 +48,14 @@ func DefaultRunOptions(appname, version, gitrev string) *RunOptions {
|
||||
}
|
||||
|
||||
// Run creates and runs the CLI application with default options.
|
||||
func Run(appname, version, gitrev string) int {
|
||||
return RunWithOptions(DefaultRunOptions(appname, version, gitrev))
|
||||
func Run(appname, gitrev string) int {
|
||||
return RunWithOptions(DefaultRunOptions(appname, gitrev))
|
||||
}
|
||||
|
||||
// RunWithOptions creates and runs the CLI application with the given options.
|
||||
func RunWithOptions(opts *RunOptions) int {
|
||||
m := &CLIApp{
|
||||
appname: opts.Appname,
|
||||
version: opts.Version,
|
||||
gitrev: opts.Gitrev,
|
||||
exitCode: 0,
|
||||
maxManifestSize: mfer.MaxManifestSize,
|
||||
|
||||
@@ -11,6 +11,7 @@ import (
|
||||
"net/http/httptest"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime/debug"
|
||||
"slices"
|
||||
"strings"
|
||||
"sync"
|
||||
@@ -27,6 +28,7 @@ import (
|
||||
|
||||
const (
|
||||
testApp = "mfer"
|
||||
testGitrev = "abc123"
|
||||
testDir = "/testdir"
|
||||
testFile1 = "/testdir/file1.txt"
|
||||
testMF = "/testdir/test.mf"
|
||||
@@ -94,8 +96,7 @@ func TestBuild(t *testing.T) {
|
||||
func testOpts(args []string, fs afero.Fs) *RunOptions {
|
||||
return &RunOptions{
|
||||
Appname: testApp,
|
||||
Version: "1.0.0",
|
||||
Gitrev: "abc123",
|
||||
Gitrev: testGitrev,
|
||||
Args: args,
|
||||
Stdin: &bytes.Buffer{},
|
||||
Stdout: &bytes.Buffer{},
|
||||
@@ -137,10 +138,35 @@ func TestVersionCommand(t *testing.T) {
|
||||
exitCode := runCLI(opts)
|
||||
|
||||
assert.Equal(t, 0, exitCode)
|
||||
assert.Equal(t, "mfer version "+testGitrev+"\n", testStdout(t, opts))
|
||||
}
|
||||
|
||||
stdout := testStdout(t, opts)
|
||||
assert.Contains(t, stdout, mfer.Version)
|
||||
assert.Contains(t, stdout, "abc123")
|
||||
// TestVersionFromBuildInfo covers a binary whose build stamped no Gitrev,
|
||||
// as `go install` builds it: mfer version prints the main module's version
|
||||
// from the build information instead.
|
||||
func TestVersionFromBuildInfo(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
info, ok := debug.ReadBuildInfo()
|
||||
require.True(t, ok)
|
||||
require.NotEmpty(t, info.Main.Version)
|
||||
|
||||
opts := testOpts([]string{testApp, cmdVersion}, afero.NewMemMapFs())
|
||||
opts.Gitrev = ""
|
||||
|
||||
require.Equal(t, 0, runCLI(opts))
|
||||
assert.Equal(t, "mfer version "+info.Main.Version+"\n", testStdout(t, opts))
|
||||
}
|
||||
|
||||
// TestModuleVersionEmpty covers build information that holds no module
|
||||
// version, as in a build with GO111MODULE=off, and a binary with no build
|
||||
// information at all. Both report "unknown": an empty version would print
|
||||
// nothing after "mfer version" and drop the --version flag.
|
||||
func TestModuleVersionEmpty(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
assert.Equal(t, "unknown", moduleVersion(&debug.BuildInfo{}, true))
|
||||
assert.Equal(t, "unknown", moduleVersion(nil, false))
|
||||
}
|
||||
|
||||
// TestVFlagCollision covers the -v/--verbose vs --version flag interaction
|
||||
@@ -166,7 +192,7 @@ func TestVFlagCollision(t *testing.T) {
|
||||
exitCode := runCLI(opts)
|
||||
|
||||
assert.Equal(t, 0, exitCode, "stderr: %s", testStderr(t, opts))
|
||||
assert.Contains(t, testStdout(t, opts), mfer.Version)
|
||||
assert.Contains(t, testStdout(t, opts), testGitrev)
|
||||
assert.NotContains(t, testStderr(t, opts), "two forms of the same flag")
|
||||
})
|
||||
}
|
||||
@@ -746,6 +772,9 @@ func TestBannerOutput(t *testing.T) {
|
||||
stdout := testStdout(t, opts)
|
||||
assert.Contains(t, stdout, "___")
|
||||
assert.Contains(t, stdout, "\\")
|
||||
|
||||
// The banner shows the version mfer version prints, and nothing after it.
|
||||
assert.Contains(t, stdout, "mfer by @sneak: "+testGitrev+"\n")
|
||||
}
|
||||
|
||||
func TestUnknownCommand(t *testing.T) {
|
||||
|
||||
+18
-7
@@ -6,12 +6,12 @@ import (
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"runtime/debug"
|
||||
"time"
|
||||
|
||||
"github.com/spf13/afero"
|
||||
"github.com/urfave/cli/v3"
|
||||
"sneak.berlin/go/mfer/internal/log"
|
||||
"sneak.berlin/go/mfer/mfer"
|
||||
)
|
||||
|
||||
// Command and flag names shared across command definitions and tests.
|
||||
@@ -58,7 +58,6 @@ var errUnexpectedArgument = errors.New("unexpected argument")
|
||||
//nolint:revive // established name used throughout the codebase and tests
|
||||
type CLIApp struct {
|
||||
appname string
|
||||
version string
|
||||
gitrev string
|
||||
startupTime time.Time
|
||||
exitCode int
|
||||
@@ -87,13 +86,26 @@ const banner = `
|
||||
\ \:\ \ \:\ \ \::/ \ \:\
|
||||
\__\/ \__\/ \__\/ \__\/`
|
||||
|
||||
// VersionString returns the version and git revision formatted for display.
|
||||
// VersionString returns the version mfer reports: the revision the build
|
||||
// stamped into main.Gitrev or, when it stamped none, as with `go install`,
|
||||
// the main module's version from the binary's build information.
|
||||
func (mfa *CLIApp) VersionString() string {
|
||||
if mfa.gitrev != "" {
|
||||
return fmt.Sprintf("%s (%s)", mfer.Version, mfa.gitrev)
|
||||
return mfa.gitrev
|
||||
}
|
||||
|
||||
return mfer.Version
|
||||
return moduleVersion(debug.ReadBuildInfo())
|
||||
}
|
||||
|
||||
// moduleVersion returns the main module's version from the binary's build
|
||||
// information, or "unknown" when the binary has none or it holds no version,
|
||||
// as in a build with GO111MODULE=off.
|
||||
func moduleVersion(info *debug.BuildInfo, ok bool) string {
|
||||
if !ok || info.Main.Version == "" {
|
||||
return "unknown"
|
||||
}
|
||||
|
||||
return info.Main.Version
|
||||
}
|
||||
|
||||
// printVersion writes the version line shared by the --version flag and the
|
||||
@@ -106,8 +118,7 @@ func (mfa *CLIApp) printBanner() {
|
||||
if log.GetLevel() <= log.InfoLevel {
|
||||
_, _ = fmt.Fprintln(mfa.Stdout, banner)
|
||||
_, _ = fmt.Fprintf(mfa.Stdout,
|
||||
" mfer by @sneak: v%s released %s\n",
|
||||
mfer.Version, mfer.ReleaseDate)
|
||||
" mfer by @sneak: %s\n", mfa.VersionString())
|
||||
_, _ = fmt.Fprintln(mfa.Stdout, " https://sneak.berlin/go/mfer")
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user