diff --git a/Makefile b/Makefile index 107ee0f..0879460 100644 --- a/Makefile +++ b/Makefile @@ -21,7 +21,7 @@ all: $(addprefix out/trunkver_, $(PLATFORMS)) $(addprefix out/trunkver_, $(PLATFORMS)): out GOOS=$(word 2,$(subst _, ,$@)) \ GOARCH=$(word 3,$(subst _, ,$@)) \ - go build -ldflags "-X main.Version=$(VERSION)" -o $@ ./cmd/trunkver.go + go build -ldflags "-X main.Version=$(VERSION)" -o $@ ./main.go out/smoke: out curl -sL https://github.com/SamirTalwar/smoke/releases/download/v2.4.0/smoke-v2.4.0-Linux-x86_64 -o $@ diff --git a/cmd/trunkver.go b/cmd/trunkver.go index ce49222..6250ec9 100644 --- a/cmd/trunkver.go +++ b/cmd/trunkver.go @@ -1,80 +1,83 @@ -package main +package cmd import ( - "flag" "fmt" - "io" "os" "time" "github.com/crftd-tech/trunkver/internal/ci" "github.com/crftd-tech/trunkver/internal/trunkver" + "github.com/spf13/cobra" ) -var Version string = "0.0.0-HEAD-local" +var Version string = "UNDEFINED" -func main() { - run(os.Stdout, os.Stderr, os.Args) -} +func Execute() { + var rootCmd = &cobra.Command{ + Version: Version, + Use: "trunkver [flags] [base-version]", + Short: "trunkver generates versions for trunk-based apps", + Args: cobra.MaximumNArgs(1), + Run: run, + } -func run(out io.Writer, err io.Writer, args []string) { - flagSet := flag.NewFlagSet("trunkver", flag.ExitOnError) - version := flagSet.Bool("version", false, "Print the version and exit") - ts := flagSet.String("timestamp", "now", "The timestamp to use for the version in RFC3339 format") - sRef := flagSet.String("source-ref", "", "The source ref to use for the version") - bRef := flagSet.String("build-ref", "", "The build ref to use for the version") - prereleaseOnly := flagSet.Bool("prerelease", false, "Build the trunkver as the prerelease part of a version (e.g. for nightly builds)") + rootCmd.SetVersionTemplate("{{.Version}}\n") + rootCmd.Flags().StringP("build-ref", "b", "", "The build ref to use (e.g. $GITHUB_RUN_ID)") + rootCmd.Flags().StringP("source-ref", "s", "", "The source ref to use for the version (e.g. \"g$(git rev-parse --short HEAD)\")") + rootCmd.Flags().StringP("timestamp", "t", "now", "The timestamp to use for the version in RFC3339 format") + rootCmd.Flags().BoolP("prerelease", "p", false, "Build the trunkver as the prerelease part of a semver (e.g. for nightly builds)") - flagSet.Parse(args[1:]) - baseVersion := flagSet.Arg(0) + rootCmd.Execute() +} - if *version { - fmt.Fprintln(err, Version) - return - } +func run(cmd *cobra.Command, args []string) { + var buildRef string = cmd.Flags().Lookup("build-ref").Value.String() + var sourceRef string = cmd.Flags().Lookup("source-ref").Value.String() + var timestamp string = cmd.Flags().Lookup("timestamp").Value.String() + var prerelease bool = cmd.Flags().Lookup("prerelease").Value.String() == "true" ciResult, found := ci.DetectCi() if found { ciData := ciResult.Get() - if *sRef == "" { - *sRef = ciData.SourceRef + if sourceRef == "" { + sourceRef = ciData.SourceRef } - if *bRef == "" { - *bRef = ciData.BuildRef + if buildRef == "" { + buildRef = ciData.BuildRef } } - if *bRef == "" { - fmt.Fprintln(err, "Error: --build-ref missing, your CI might be unsupported. Specify it manually. It should identify the log that was produced during creation of this artifact, e.g. the Job Id in Github Actions") + if buildRef == "" { + fmt.Fprintln(os.Stderr, "Error: --build-ref missing, your CI might be unsupported. It should identify the log that was produced during creation of this artifact, e.g. the Job Id in Github Actions") os.Exit(1) } - if *sRef == "" { - fmt.Fprintln(err, "Error: --source-ref missing, your CI might be unsupported. Specify it manually. It should identify the commit that was used to build this artifact, e.g. \"g${GITHUB_SHA:0:7}\" or \"g$(git rev-parse --short HEAD)\".") + if sourceRef == "" { + fmt.Fprintln(os.Stderr, "Error: --source-ref missing, your CI might be unsupported. It should identify the commit that was used to build this artifact, e.g. \"g${GITHUB_SHA:0:7}\" or \"g$(git rev-parse --short HEAD)\".") os.Exit(1) } var parsedTime time.Time - if *ts == "now" { + if timestamp == "now" { parsedTime = time.Now() } else { var err error - parsedTime, err = time.Parse(time.RFC3339, *ts) + parsedTime, err = time.Parse(time.RFC3339, timestamp) if err != nil { panic(err) } } - if !*prereleaseOnly { - fmt.Fprintln(out, trunkver.FormatMajorTrunkver(parsedTime, *sRef, *bRef)) + if !prerelease { + fmt.Fprintln(os.Stdout, trunkver.FormatMajorTrunkver(parsedTime, sourceRef, buildRef)) return } - var trunkVer string = trunkver.FormatPrereleaseTrunkver(parsedTime, *sRef, *bRef) - if baseVersion == "" { - fmt.Fprintln(out, trunkVer) + var trunkVer string = trunkver.FormatPrereleaseTrunkver(parsedTime, sourceRef, buildRef) + if len(args) == 0 || args[0] == "" { + fmt.Fprintln(os.Stdout, trunkVer) return } - fmt.Fprintln(out, trunkver.MergeWithBaseVersion(baseVersion, trunkVer)) + fmt.Fprintln(os.Stdout, trunkver.MergeWithBaseVersion(args[0], trunkVer)) } diff --git a/cmd/trunkver_test.go b/cmd/trunkver_test.go deleted file mode 100644 index 06d8110..0000000 --- a/cmd/trunkver_test.go +++ /dev/null @@ -1,49 +0,0 @@ -package main - -import ( - "io" - "os" - "testing" - - "github.com/stretchr/testify/assert" -) - -func TestPassAllExplicitArgs(t *testing.T) { - args := []string{ - "trunkver", - "--timestamp", "2024-05-22T16:25:48+02:00", - "--source-ref", "gb4dc0d3", - "--build-ref", "12345", - } - readStdout, writeStdout, _ := os.Pipe() - _, writeStderr, _ := os.Pipe() - run(writeStdout, writeStderr, args) - writeStdout.Close() - writeStderr.Close() - - out, _ := io.ReadAll(readStdout) - assert.Equal(t, - "20240522142548.0.0-gb4dc0d3-12345\n", - string(out), - "Should print the version to stdout", - ) -} - -func TestPrintsVersion(t *testing.T) { - args := []string{ - "trunkver", - "--version", - } - _, writeStdout, _ := os.Pipe() - readStderr, writeStderr, _ := os.Pipe() - run(writeStdout, writeStderr, args) - writeStdout.Close() - writeStderr.Close() - - err, _ := io.ReadAll(readStderr) - assert.Equal(t, - "0.0.0-HEAD-local\n", - string(err), - "Should print the version to stdout", - ) -} diff --git a/go.mod b/go.mod index 4efdd57..72cbf92 100644 --- a/go.mod +++ b/go.mod @@ -4,6 +4,12 @@ go 1.22 require github.com/stretchr/testify v1.9.0 +require ( + github.com/inconshreveable/mousetrap v1.1.0 // indirect + github.com/spf13/cobra v1.8.0 // indirect + github.com/spf13/pflag v1.0.5 // indirect +) + require ( github.com/Masterminds/semver/v3 v3.2.1 github.com/davecgh/go-spew v1.1.1 // indirect diff --git a/go.sum b/go.sum index 7d179a2..473a4a2 100644 --- a/go.sum +++ b/go.sum @@ -2,10 +2,18 @@ github.com/Masterminds/semver/v3 v3.2.1 h1:RN9w6+7QoMeJVGyfmbcgs28Br8cvmnucEXnY0 github.com/Masterminds/semver/v3 v3.2.1/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ= github.com/coreos/go-semver v0.3.1 h1:yi21YpKnrx1gt5R+la8n5WgS0kCrsPp33dmEyHReZr4= github.com/coreos/go-semver v0.3.1/go.mod h1:irMmmIw/7yzSRPWryHsK7EYSg09caPQL03VsM8rvUec= +github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= +github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/spf13/cobra v1.8.0 h1:7aJaZx1B85qltLMc546zn58BxxfZdR/W22ej9CFoEf0= +github.com/spf13/cobra v1.8.0/go.mod h1:WXLWApfZ71AjXPya3WOlMsY9yMs7YeiHhFVlvLyhcho= +github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= +github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= diff --git a/main.go b/main.go new file mode 100644 index 0000000..cf96167 --- /dev/null +++ b/main.go @@ -0,0 +1,9 @@ +package main + +import ( + "github.com/crftd-tech/trunkver/cmd" +) + +func main() { + cmd.Execute() +} diff --git a/smoke.yaml b/smoke.yaml index c4676fa..18614ed 100644 --- a/smoke.yaml +++ b/smoke.yaml @@ -63,7 +63,7 @@ tests: - "g1234567" exit-status: 1 stderr: | - Error: --build-ref missing, your CI might be unsupported. Specify it manually. It should identify the log that was produced during creation of this artifact, e.g. the Job Id in Github Actions + Error: --build-ref missing, your CI might be unsupported. It should identify the log that was produced during creation of this artifact, e.g. the Job Id in Github Actions - name: panic/if-no-source-ref args: @@ -71,7 +71,7 @@ tests: - "1234567" exit-status: 1 stderr: | - Error: --source-ref missing, your CI might be unsupported. Specify it manually. It should identify the commit that was used to build this artifact, e.g. "g${GITHUB_SHA:0:7}" or "g$(git rev-parse --short HEAD)". + Error: --source-ref missing, your CI might be unsupported. It should identify the commit that was used to build this artifact, e.g. "g${GITHUB_SHA:0:7}" or "g$(git rev-parse --short HEAD)". - name: ci/github environment: