Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .goreleaser.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,11 @@ builds:
- arm64
goamd64:
- v1
ldflags:
- -s -w
- -X github.com/jaevans/semvertool/cmd.version={{.Version}}
- -X github.com/jaevans/semvertool/cmd.commit={{.Commit}}
- -X github.com/jaevans/semvertool/cmd.date={{.Date}}

archives:
- format: binary
Expand Down
53 changes: 53 additions & 0 deletions cmd/version.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
/*
Copyright © 2025 James Evans
*/
package cmd

import (
"fmt"

"github.com/spf13/cobra"
)

var (
version = "dev"
commit = "none"
date = "unknown"
)

// versionCmd represents the version command
var versionCmd = &cobra.Command{
Use: "version",
Short: "Print the version information",
Long: `Print the version information for semvertool.

If the tool was built from a tagged release, it shows the version tag.
If it was built from an untagged commit (snapshot), it includes commit information.`,
Run: runVersion,
}

func init() {
rootCmd.AddCommand(versionCmd)
}

func runVersion(cmd *cobra.Command, args []string) {
// If version is "dev", this is a development build (not from goreleaser)
if version == "dev" {
fmt.Printf("semvertool %s (snapshot)\n", version)
fmt.Printf(" commit: %s\n", commit)
fmt.Printf(" built: %s\n", date)
} else {
// This is a goreleaser build
// Check if it's a snapshot build (goreleaser adds commit info for non-tag builds)
// commit will be "none" or empty for tagged releases
if commit != "none" && commit != "" {
// Snapshot build - show version with commit info
fmt.Printf("semvertool %s (snapshot)\n", version)
fmt.Printf(" commit: %s\n", commit)
fmt.Printf(" built: %s\n", date)
} else {
// Tagged release - show clean version
fmt.Printf("semvertool %s\n", version)
}
}
}
108 changes: 108 additions & 0 deletions cmd/version_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
/*
Copyright © 2025 James Evans
*/
package cmd

import (
"bytes"
"io"
"os"
"strings"
"testing"
)

func TestVersionCommand(t *testing.T) {
tests := []struct {
name string
version string
commit string
date string
expectedOutput []string // strings that should be in output
notExpected []string // strings that should NOT be in output
}{
{
name: "dev build",
version: "dev",
commit: "none",
date: "unknown",
expectedOutput: []string{"dev", "snapshot", "commit:", "built:"},
notExpected: []string{},
},
{
name: "tagged release",
version: "v1.2.3",
commit: "",
date: "",
expectedOutput: []string{"v1.2.3"},
notExpected: []string{"snapshot", "commit:", "built:"},
},
{
name: "snapshot with commit",
version: "v0.0.0-next",
commit: "abc123def",
date: "2025-01-01T10:00:00Z",
expectedOutput: []string{"v0.0.0-next", "snapshot", "commit:", "abc123def", "built:", "2025-01-01T10:00:00Z"},
notExpected: []string{},
},
{
name: "tagged release with commit set to none",
version: "v2.0.0",
commit: "none",
date: "2025-01-01T10:00:00Z",
expectedOutput: []string{"v2.0.0"},
notExpected: []string{"snapshot", "commit:", "built:"},
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
// Save original values
origVersion := version
origCommit := commit
origDate := date

// Set test values
version = tt.version
commit = tt.commit
date = tt.date

// Restore original values after test
defer func() {
version = origVersion
commit = origCommit
date = origDate
}()

// Capture output
old := os.Stdout
r, w, _ := os.Pipe()
os.Stdout = w

// Run the command
runVersion(versionCmd, []string{})

// Restore stdout
w.Close()
os.Stdout = old

// Read captured output
var buf bytes.Buffer
_, _ = io.Copy(&buf, r)
output := buf.String()

// Check expected strings are present
for _, expected := range tt.expectedOutput {
if !strings.Contains(output, expected) {
t.Errorf("Expected output to contain %q, got: %s", expected, output)
}
}

// Check unexpected strings are NOT present
for _, notExpected := range tt.notExpected {
if strings.Contains(output, notExpected) {
t.Errorf("Expected output NOT to contain %q, got: %s", notExpected, output)
}
}
})
}
}