diff --git a/.github/workflows/prerelease.yml b/.github/workflows/prerelease.yml index 34e73ac..adec1be 100644 --- a/.github/workflows/prerelease.yml +++ b/.github/workflows/prerelease.yml @@ -28,7 +28,9 @@ jobs: go-version: '1.26.3' - name: Build binaries - run: make release + run: | + SHORT_SHA=$(git rev-parse --short HEAD) + make release VERSION="pre-release-$SHORT_SHA" - name: Create Release and Upload Assets env: diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 273dbcf..67a0bfc 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -29,7 +29,9 @@ jobs: go-version: '1.26.3' - name: Build binaries - run: make release + run: | + TAG_NAME=${GITHUB_REF#refs/tags/} + make release VERSION="$TAG_NAME" - name: Create Release and Upload Assets env: diff --git a/Makefile b/Makefile index 6885717..a63e19a 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,7 @@ .DEFAULT_GOAL := build .PHONY: clean fmt vet lint build run release +VERSION ?= $(shell git describe --tags --always --dirty 2>/dev/null || echo dev) +LDFLAGS := -s -w -X github.com/DataDog/ddtest/internal/buildinfo.Version=$(VERSION) clean: go clean -i -x fmt: @@ -11,14 +13,14 @@ lint: vet test: go test ./... build: test lint - go build -o ddtest main.go + go build -ldflags="$(LDFLAGS)" -o ddtest main.go run: go run main.go release: mkdir -p dist - GOOS=linux GOARCH=amd64 go build -ldflags="-s -w" -o dist/ddtest-linux-amd64 main.go - GOOS=linux GOARCH=arm64 go build -ldflags="-s -w" -o dist/ddtest-linux-arm64 main.go - GOOS=darwin GOARCH=amd64 go build -ldflags="-s -w" -o dist/ddtest-darwin-amd64 main.go - GOOS=darwin GOARCH=arm64 go build -ldflags="-s -w" -o dist/ddtest-darwin-arm64 main.go - GOOS=windows GOARCH=amd64 go build -ldflags="-s -w" -o dist/ddtest-windows-amd64.exe main.go - GOOS=windows GOARCH=arm64 go build -ldflags="-s -w" -o dist/ddtest-windows-arm64.exe main.go + GOOS=linux GOARCH=amd64 go build -ldflags="$(LDFLAGS)" -o dist/ddtest-linux-amd64 main.go + GOOS=linux GOARCH=arm64 go build -ldflags="$(LDFLAGS)" -o dist/ddtest-linux-arm64 main.go + GOOS=darwin GOARCH=amd64 go build -ldflags="$(LDFLAGS)" -o dist/ddtest-darwin-amd64 main.go + GOOS=darwin GOARCH=arm64 go build -ldflags="$(LDFLAGS)" -o dist/ddtest-darwin-arm64 main.go + GOOS=windows GOARCH=amd64 go build -ldflags="$(LDFLAGS)" -o dist/ddtest-windows-amd64.exe main.go + GOOS=windows GOARCH=arm64 go build -ldflags="$(LDFLAGS)" -o dist/ddtest-windows-arm64.exe main.go diff --git a/internal/buildinfo/version.go b/internal/buildinfo/version.go new file mode 100644 index 0000000..3a6b170 --- /dev/null +++ b/internal/buildinfo/version.go @@ -0,0 +1,26 @@ +package buildinfo + +import "runtime/debug" + +const defaultVersion = "dev" + +// Version is set by release builds with -ldflags. +var Version = defaultVersion + +func CurrentVersion() string { + if Version != "" && Version != defaultVersion { + return Version + } + + if info, ok := debug.ReadBuildInfo(); ok { + if info.Main.Version != "" && info.Main.Version != "(devel)" { + return info.Main.Version + } + } + + if Version != "" { + return Version + } + + return defaultVersion +} diff --git a/internal/buildinfo/version_test.go b/internal/buildinfo/version_test.go new file mode 100644 index 0000000..041d82e --- /dev/null +++ b/internal/buildinfo/version_test.go @@ -0,0 +1,29 @@ +package buildinfo + +import "testing" + +func TestCurrentVersionUsesInjectedVersion(t *testing.T) { + originalVersion := Version + t.Cleanup(func() { + Version = originalVersion + }) + + Version = "v1.2.3" + + if got := CurrentVersion(); got != "v1.2.3" { + t.Fatalf("expected injected version, got %q", got) + } +} + +func TestCurrentVersionFallsBackToDefault(t *testing.T) { + originalVersion := Version + t.Cleanup(func() { + Version = originalVersion + }) + + Version = "" + + if got := CurrentVersion(); got != defaultVersion { + t.Fatalf("expected default version, got %q", got) + } +} diff --git a/internal/cmd/cmd.go b/internal/cmd/cmd.go index ea1afd6..3adc05d 100644 --- a/internal/cmd/cmd.go +++ b/internal/cmd/cmd.go @@ -6,7 +6,9 @@ import ( "log/slog" "os" + "github.com/DataDog/ddtest/internal/buildinfo" "github.com/DataDog/ddtest/internal/constants" + "github.com/DataDog/ddtest/internal/git" "github.com/DataDog/ddtest/internal/runner" "github.com/DataDog/ddtest/internal/settings" "github.com/spf13/cobra" @@ -17,9 +19,13 @@ import ( var defaultParallelism = settings.DefaultParallelism() var rootCmd = &cobra.Command{ - Use: "ddtest", - Short: "A test runner from Datadog", - Long: "Command line tool for running tests with Datadog Test Optimization.", + Use: "ddtest", + Short: "A test runner from Datadog", + Long: "Command line tool for running tests with Datadog Test Optimization.", + Version: buildinfo.CurrentVersion(), + PersistentPreRunE: func(cmd *cobra.Command, args []string) error { + return git.CheckAvailable() + }, } var planCmd = &cobra.Command{ @@ -55,6 +61,8 @@ var runCmd = &cobra.Command{ } func init() { + rootCmd.SetVersionTemplate("{{ .Version }}\n") + rootCmd.PersistentFlags().String("platform", "ruby", "Platform that runs tests") rootCmd.PersistentFlags().String("framework", "rspec", "Test framework to use") rootCmd.PersistentFlags().Int("min-parallelism", defaultParallelism, "Minimum number of parallel test processes (default: number of physical CPUs)") diff --git a/internal/cmd/cmd_test.go b/internal/cmd/cmd_test.go index f5ad016..d5c3821 100644 --- a/internal/cmd/cmd_test.go +++ b/internal/cmd/cmd_test.go @@ -2,10 +2,12 @@ package cmd import ( "bytes" + "errors" "os" "strings" "testing" + "github.com/DataDog/ddtest/internal/git" "github.com/DataDog/ddtest/internal/settings" "github.com/spf13/cobra" "github.com/spf13/viper" @@ -134,6 +136,42 @@ func TestExecute(t *testing.T) { } } +func TestVersionFlag(t *testing.T) { + resetBoolFlag := func(name string) { + if rootCmd.Flags().Lookup(name) != nil { + _ = rootCmd.Flags().Set(name, "false") + } + } + resetBoolFlag("help") + resetBoolFlag("version") + + originalLookPathFunc := git.LookPathFunc + git.LookPathFunc = func(file string) (string, error) { + return "", errors.New("git should not be checked for --version") + } + t.Cleanup(func() { + git.LookPathFunc = originalLookPathFunc + resetBoolFlag("help") + resetBoolFlag("version") + rootCmd.SetArgs(nil) + rootCmd.SetOut(os.Stdout) + rootCmd.SetErr(os.Stderr) + }) + + var buf bytes.Buffer + rootCmd.SetOut(&buf) + rootCmd.SetErr(&buf) + rootCmd.SetArgs([]string{"--version"}) + + if err := Execute(); err != nil { + t.Fatalf("Execute() with --version should not return error, got %v", err) + } + + if got, want := buf.String(), rootCmd.Version+"\n"; got != want { + t.Fatalf("expected version output %q, got %q", want, got) + } +} + func TestFlagBinding(t *testing.T) { // Reset viper viper.Reset() diff --git a/main.go b/main.go index 7564fa9..7481842 100644 --- a/main.go +++ b/main.go @@ -5,15 +5,9 @@ import ( "os" "github.com/DataDog/ddtest/internal/cmd" - "github.com/DataDog/ddtest/internal/git" ) func main() { - if err := git.CheckAvailable(); err != nil { - slog.Error(err.Error()) - os.Exit(1) - } - // it doesn't make sense to use ddtest without test optimization mode, // so we just enable it _ = os.Setenv("DD_CIVISIBILITY_ENABLED", "1")