diff --git a/Makefile b/Makefile index 1a1aaff5818..ed8ce97ca9a 100644 --- a/Makefile +++ b/Makefile @@ -75,7 +75,7 @@ $(MYGOBIN)/pluginator: # Build from local source. $(MYGOBIN)/kustomize: build-kustomize-api cd kustomize; \ - go install . + go install -ldflags "-X sigs.k8s.io/kustomize/api/provenance.buildDate=$(shell date -u +"%Y-%m-%dT%H:%M:%SZ")" . kustomize: $(MYGOBIN)/kustomize diff --git a/api/Makefile b/api/Makefile index 6a4ce1ed684..6ad1dd016eb 100644 --- a/api/Makefile +++ b/api/Makefile @@ -4,10 +4,10 @@ include ../Makefile-modules.mk test: - go test -v -timeout 45m -cover ./... -ldflags "-X sigs.k8s.io/kustomize/api/provenance.version=v444.333.222" + go test -v -timeout 45m -cover ./... build: - go build -ldflags "-X sigs.k8s.io/kustomize/api/provenance.version=v444.333.222" ./... + go build ./... generate: $(MYGOBIN)/k8scopy $(MYGOBIN)/stringer go generate ./... diff --git a/api/api b/api/api new file mode 100755 index 00000000000..ff0f1bbc3b4 Binary files /dev/null and b/api/api differ diff --git a/api/krusty/managedbylabel_test.go b/api/krusty/managedbylabel_test.go index 8a566c0eead..28b23e49279 100644 --- a/api/krusty/managedbylabel_test.go +++ b/api/krusty/managedbylabel_test.go @@ -13,14 +13,14 @@ const expected = `apiVersion: v1 kind: Service metadata: labels: - app.kubernetes.io/managed-by: kustomize-v444.333.222 + app.kubernetes.io/managed-by: kustomize-unknown name: myService spec: ports: - port: 7002 ` -// This test may failed when running on package tests using the go command because `v444.333.222` is set on makefile. +// This test may fail when running on package tests using the go command because `unknown` is set on makefile. func TestAddManagedbyLabel(t *testing.T) { tests := []struct { kustFile string diff --git a/api/provenance/provenance.go b/api/provenance/provenance.go index 4ad40395a18..f4d93779fab 100644 --- a/api/provenance/provenance.go +++ b/api/provenance/provenance.go @@ -6,42 +6,65 @@ package provenance import ( "fmt" "runtime" + "runtime/debug" "strings" ) var ( version = "unknown" // sha1 from git, output of $(git rev-parse HEAD) - gitCommit = "$Format:%H$" + gitCommit = "unknown" // build date in ISO8601 format, output of $(date -u +'%Y-%m-%dT%H:%M:%SZ') - buildDate = "1970-01-01T00:00:00Z" - goos = runtime.GOOS - goarch = runtime.GOARCH + buildDate = "unknown" ) // Provenance holds information about the build of an executable. type Provenance struct { // Version of the kustomize binary. - Version string `json:"version,omitempty"` + Version string `json:"version,omitempty" yaml:"version,omitempty"` // GitCommit is a git commit - GitCommit string `json:"gitCommit,omitempty"` + GitCommit string `json:"gitCommit,omitempty" yaml:"gitCommit,omitempty"` // BuildDate is date of the build. - BuildDate string `json:"buildDate,omitempty"` + BuildDate string `json:"buildDate,omitempty" yaml:"buildDate,omitempty"` // GoOs holds OS name. - GoOs string `json:"goOs,omitempty"` + GoOs string `json:"goOs,omitempty" yaml:"goOs,omitempty"` // GoArch holds architecture name. - GoArch string `json:"goArch,omitempty"` + GoArch string `json:"goArch,omitempty" yaml:"goArch,omitempty"` + // GoVersion holds Go version. + GoVersion string `json:"goVersion,omitempty" yaml:"goVersion,omitempty"` } // GetProvenance returns an instance of Provenance. func GetProvenance() Provenance { - return Provenance{ - version, - gitCommit, - buildDate, - goos, - goarch, + p := Provenance{ + BuildDate: buildDate, + Version: version, + GitCommit: gitCommit, + GoOs: runtime.GOOS, + GoArch: runtime.GOARCH, + GoVersion: runtime.Version(), } + info, ok := debug.ReadBuildInfo() + if !ok { + return p + } + + // override with values from BuildInfo + if info.Main.Version != "" && version == "unknown" { + p.Version = info.Main.Version + } + + for _, setting := range info.Settings { + switch setting.Key { + case "vcs.revision": + if p.Version == "(devel)" { + p.GitCommit = setting.Value + } + case "GOOS": + p.GoOs = setting.Value + } + } + return p } // Full returns the full provenance stamp. @@ -49,14 +72,9 @@ func (v Provenance) Full() string { return fmt.Sprintf("%+v", v) } -// Short returns the shortened provenance stamp. +// Short returns the semantic version. func (v Provenance) Short() string { - return fmt.Sprintf( - "%v", - Provenance{ - Version: v.Version, - BuildDate: v.BuildDate, - }) + return v.Semver() } // Semver returns the semantic version of kustomize. diff --git a/api/provenance/provenance_test.go b/api/provenance/provenance_test.go new file mode 100644 index 00000000000..77a2591e27a --- /dev/null +++ b/api/provenance/provenance_test.go @@ -0,0 +1,46 @@ +package provenance_test + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "sigs.k8s.io/kustomize/api/provenance" +) + +func TestGetProvenance(t *testing.T) { + p := provenance.GetProvenance() + // These are not set during go test: https://github.com/golang/go/issues/33976 + assert.Equal(t, "unknown", p.Version) + assert.Equal(t, "unknown", p.BuildDate) + assert.Equal(t, "unknown", p.GitCommit) + + // These are set properly during go test + assert.NotEmpty(t, p.GoArch) + assert.NotEmpty(t, p.GoOs) + assert.Contains(t, p.GoVersion, "go1.") +} + +func TestProvenance_Short(t *testing.T) { + p := provenance.GetProvenance() + // The version not set during go test, so this comes from an ldflag: https://github.com/golang/go/issues/33976 + assert.Equal(t, "unknown", p.Short()) + + p.Version = "kustomize/v4.11.12" + assert.Equal(t, "v4.11.12", p.Short()) +} + +func TestProvenance_Full(t *testing.T) { + p := provenance.GetProvenance() + // Most values are not set during go test: https://github.com/golang/go/issues/33976 + assert.Contains(t, p.Full(), "{Version:unknown GitCommit:unknown BuildDate:unknown") + assert.Regexp(t, "GoOs:\\w+ GoArch:\\w+ GoVersion:go1", p.Full()) +} + +func TestProvenance_Semver(t *testing.T) { + p := provenance.GetProvenance() + // The version not set during go test + assert.Equal(t, "unknown", p.Semver()) + + p.Version = "kustomize/v4.11.12" + assert.Equal(t, "v4.11.12", p.Semver()) +} diff --git a/go.work.sum b/go.work.sum index 867c1800e0d..b9297a24a95 100644 --- a/go.work.sum +++ b/go.work.sum @@ -10,17 +10,32 @@ github.com/gobuffalo/here v0.6.0/go.mod h1:wAG085dHOYqUpf+Ap+WOdrPTp5IYcDAs/x7PL github.com/googleapis/gnostic v0.1.0/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY= github.com/markbates/pkger v0.17.1/go.mod h1:0JoVlrol20BSywW79rN3kdFFsE5xYM+rSCQDXbLhiuI= github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8= +github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= +github.com/onsi/ginkgo/v2 v2.1.4/go.mod h1:um6tUpWM/cxCK3/FK8BXqEiUMUwRgSM4JXG47RKZmLU= +github.com/onsi/gomega v1.19.0/go.mod h1:LY+I3pBVzYsTBU1AnDwOSxaYi9WoWiqgwooUqq9yPro= github.com/paulmach/orb v0.1.3/go.mod h1:VFlX/8C+IQ1p6FTRRKzKoOPJnvEtA5G0Veuqwbu//Vk= github.com/qri-io/starlib v0.4.2-0.20200213133954-ff2e8cd5ef8d/go.mod h1:7DPO4domFU579Ga6E61sB9VFNaniPVwJP5C4bBCu3wA= github.com/spf13/cobra v1.0.0/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE= github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE= github.com/stretchr/testify v1.2.3-0.20181224173747-660f15d67dbb/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= go.starlark.net v0.0.0-20190528202925-30ae18b8564f/go.mod h1:c1/X6cHgvdXj6pUlmWKMkuqRnW4K8x2vwt6JAaaircg= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/net v0.0.0-20180218175443-cbe0f9307d01/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.3.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20191022100944-742c48ecaeb7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/tools v0.4.0/go.mod h1:UE5sM2OK9E/d67R0ANs2xJizIymRP5gJU295PvKXxjQ= +golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v3 v3.0.0-20200121175148-a6ecf24a6d71/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/kustomize/commands/version/version.go b/kustomize/commands/version/version.go index dac2aa82770..5f3ca83cd28 100644 --- a/kustomize/commands/version/version.go +++ b/kustomize/commands/version/version.go @@ -4,30 +4,83 @@ package version import ( + "encoding/json" "fmt" "io" + "os" "github.com/spf13/cobra" "sigs.k8s.io/kustomize/api/provenance" + "sigs.k8s.io/kustomize/kyaml/errors" + "sigs.k8s.io/kustomize/kyaml/yaml" ) +type Options struct { + Short bool + Output string + Writer io.Writer +} + // NewCmdVersion makes a new version command. func NewCmdVersion(w io.Writer) *cobra.Command { - var short bool - + o := NewOptions(w) versionCmd := cobra.Command{ Use: "version", Short: "Prints the kustomize version", Example: `kustomize version`, - Run: func(cmd *cobra.Command, args []string) { - if short { - fmt.Fprintln(w, provenance.GetProvenance().Short()) - } else { - fmt.Fprintln(w, provenance.GetProvenance().Full()) + RunE: func(cmd *cobra.Command, args []string) error { + if err := o.Validate(args); err != nil { + return err + } + if err := o.Run(); err != nil { + return err } + return nil }, } - versionCmd.Flags().BoolVar(&short, "short", false, "short form") + versionCmd.Flags().BoolVar(&o.Short, "short", false, "short form") + _ = versionCmd.Flags().MarkDeprecated("short", "and will be removed in the future. The --short output will become the default.") + versionCmd.Flags().StringVarP(&o.Output, "output", "o", o.Output, "One of 'yaml' or 'json'.") return &versionCmd } + +func NewOptions(w io.Writer) *Options { + if w == nil { + w = io.Writer(os.Stdout) + } + return &Options{Writer: w} +} + +func (o *Options) Validate(_ []string) error { + if o.Short { + if o.Output != "" { + return fmt.Errorf("--short and --output are mutually exclusive") + } + } + return nil +} + +func (o *Options) Run() error { + switch o.Output { + case "": + if o.Short { + fmt.Fprintln(o.Writer, provenance.GetProvenance().Short()) + } else { + fmt.Fprintln(o.Writer, provenance.GetProvenance().Full()) + } + case "yaml": + marshalled, err := yaml.Marshal(provenance.GetProvenance()) + if err != nil { + return errors.WrapPrefixf(err, "marshalling provenance to yaml") + } + fmt.Fprintln(o.Writer, string(marshalled)) + case "json": + marshalled, err := json.MarshalIndent(provenance.GetProvenance(), "", " ") + if err != nil { + return errors.WrapPrefixf(err, "marshalling provenance to json") + } + fmt.Fprintln(o.Writer, string(marshalled)) + } + return nil +}