diff --git a/Makefile b/Makefile index 97c73f9d7..d62e6223f 100644 --- a/Makefile +++ b/Makefile @@ -26,6 +26,25 @@ TOOLS_DIR := hack/tools TOOLS_BIN_DIR := $(abspath $(TOOLS_DIR)/bin) BIN_DIR := $(abspath $(ROOT_DIR)/bin) +# Set version variables for LDFLAGS +GIT_VERSION ?= $(shell git describe --tags --always --dirty) +GIT_HASH ?= $(shell git rev-parse HEAD) +DATE_FMT = +'%Y-%m-%dT%H:%M:%SZ' +SOURCE_DATE_EPOCH ?= $(shell git log -1 --pretty=%ct) +ifdef SOURCE_DATE_EPOCH + BUILD_DATE ?= $(shell date -u -d "@$(SOURCE_DATE_EPOCH)" "$(DATE_FMT)" 2>/dev/null || date -u -r "$(SOURCE_DATE_EPOCH)" "$(DATE_FMT)" 2>/dev/null || date -u "$(DATE_FMT)") +else + BUILD_DATE ?= $(shell date "$(DATE_FMT)") +endif +GIT_TREESTATE = "clean" +DIFF = $(shell git diff --quiet >/dev/null 2>&1; if [ $$? -eq 1 ]; then echo "1"; fi) +ifeq ($(DIFF), 1) + GIT_TREESTATE = "dirty" +endif + +SERVER_PKG=github.com/sigstore/fulcio/cmd/app +SERVER_LDFLAGS="-X $(SERVER_PKG).gitVersion=$(GIT_VERSION) -X $(SERVER_PKG).gitCommit=$(GIT_HASH) -X $(SERVER_PKG).gitTreeState=$(GIT_TREESTATE) -X $(SERVER_PKG).buildDate=$(BUILD_DATE)" + # Binaries SWAGGER := $(TOOLS_BIN_DIR)/swagger @@ -43,7 +62,7 @@ gosec: $(GOBIN)/gosec ./... fulcio: $(SRCS) - go build + go build -ldflags $(SERVER_LDFLAGS) test: go test ./... diff --git a/cmd/app/version.go b/cmd/app/version.go new file mode 100644 index 000000000..3e41454ac --- /dev/null +++ b/cmd/app/version.go @@ -0,0 +1,131 @@ +// +// Copyright 2021 The Sigstore Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package app + +import ( + "encoding/json" + "fmt" + "runtime" + "strings" + "text/tabwriter" + + "github.com/pkg/errors" + "github.com/spf13/cobra" +) + +// Base version information. +// +// This is the fallback data used when version information from git is not +// provided via go ldflags (e.g. via Makefile). +var ( + // Output of "git describe". The prerequisite is that the branch should be + // tagged using the correct versioning strategy. + gitVersion = "unknown" + // SHA1 from git, output of $(git rev-parse HEAD) + gitCommit = "unknown" + // State of git tree, either "clean" or "dirty" + gitTreeState = "unknown" + // Build date in ISO8601 format, output of $(date -u +'%Y-%m-%dT%H:%M:%SZ') + buildDate = "unknown" +) + +type versionOptions struct { + json bool +} + +var versionOpts = &versionOptions{} + +// verifyCmd represents the verify command +var versionCmd = &cobra.Command{ + Use: "version", + Short: "fulcio-server version", + RunE: func(cmd *cobra.Command, args []string) error { + return runVersion(versionOpts) + }, +} + +func init() { + versionCmd.PersistentFlags().BoolVarP(&versionOpts.json, "json", "j", false, + "print JSON instead of text") + rootCmd.AddCommand(versionCmd) +} + +func runVersion(opts *versionOptions) error { + v := VersionInfo() + res := v.String() + + if opts.json { + j, err := v.JSONString() + if err != nil { + return errors.Wrap(err, "unable to generate JSON from version info") + } + res = j + } + + fmt.Println(res) + return nil +} + +type Info struct { + GitVersion string + GitCommit string + GitTreeState string + BuildDate string + GoVersion string + Compiler string + Platform string +} + +func VersionInfo() Info { + // These variables typically come from -ldflags settings and in + // their absence fallback to the global defaults set above. + return Info{ + GitVersion: gitVersion, + GitCommit: gitCommit, + GitTreeState: gitTreeState, + BuildDate: buildDate, + GoVersion: runtime.Version(), + Compiler: runtime.Compiler, + Platform: fmt.Sprintf("%s/%s", runtime.GOOS, runtime.GOARCH), + } +} + +// String returns the string representation of the version info +func (i *Info) String() string { + b := strings.Builder{} + w := tabwriter.NewWriter(&b, 0, 0, 2, ' ', 0) + + fmt.Fprintf(w, "GitVersion:\t%s\n", i.GitVersion) + fmt.Fprintf(w, "GitCommit:\t%s\n", i.GitCommit) + fmt.Fprintf(w, "GitTreeState:\t%s\n", i.GitTreeState) + fmt.Fprintf(w, "BuildDate:\t%s\n", i.BuildDate) + fmt.Fprintf(w, "GoVersion:\t%s\n", i.GoVersion) + fmt.Fprintf(w, "Compiler:\t%s\n", i.Compiler) + fmt.Fprintf(w, "Platform:\t%s\n", i.Platform) + + w.Flush() // #nosec + return b.String() +} + +// JSONString returns the JSON representation of the version info +func (i *Info) JSONString() (string, error) { + b, err := json.MarshalIndent(i, "", " ") + if err != nil { + return "", err + } + + return string(b), nil +} diff --git a/go.mod b/go.mod index 2f4ebc4c7..4854ad1eb 100644 --- a/go.mod +++ b/go.mod @@ -23,6 +23,7 @@ require ( github.com/mailru/easyjson v0.7.7 // indirect github.com/miekg/pkcs11 v1.0.3 // indirect github.com/mitchellh/mapstructure v1.4.1 + github.com/pkg/errors v0.9.1 github.com/prometheus/client_golang v1.11.0 github.com/prometheus/common v0.29.0 // indirect github.com/prometheus/procfs v0.7.0 // indirect