diff --git a/config.go b/config.go index 1f98b167..d7f44460 100644 --- a/config.go +++ b/config.go @@ -12,6 +12,7 @@ import ( "net" "os" "path/filepath" + "strings" "time" ) @@ -33,6 +34,7 @@ var ( // // See loadConfig for details on the configuration load process. type config struct { + ShowVersion bool `short:"V" long:"version" description:"Display version information and exit"` ConfigFile string `short:"C" long:"configfile" description:"Path to configuration file"` DbDir string `short:"b" long:"dbdir" description:"Directory to store database"` AddPeers []string `short:"a" long:"addpeer" description:"Add a peer to connect with at startup"` @@ -177,7 +179,7 @@ func loadConfig() (*config, []string, error) { } // Pre-parse the command line options to see if an alternative config - // file was specified. + // file or the version flag was specified. preCfg := cfg preParser := flags.NewParser(&preCfg, flags.Default) _, err := preParser.Parse() @@ -188,6 +190,14 @@ func loadConfig() (*config, []string, error) { return nil, nil, err } + // Show the version and exit if the version flag was specified. + if preCfg.ShowVersion { + appName := filepath.Base(os.Args[0]) + appName = strings.TrimSuffix(appName, filepath.Ext(appName)) + fmt.Println(appName, "version", version()) + os.Exit(0) + } + // Load additional config from file. parser := flags.NewParser(&cfg, flags.Default) err = parser.ParseIniFile(preCfg.ConfigFile) diff --git a/version.go b/version.go new file mode 100644 index 00000000..cb52e57b --- /dev/null +++ b/version.go @@ -0,0 +1,72 @@ +// Copyright (c) 2013 Conformal Systems LLC. +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package main + +import ( + "bytes" + "fmt" + "strings" +) + +// semanticAlphabet +const semanticAlphabet = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz-" + +// These constants define the application version and follow the semantic +// versioning 2.0.0 spec (http://semver.org/). +const ( + appMajor uint = 0 + appMinor uint = 1 + appPatch uint = 0 + + // appPreRelease MUST only contain characters from semanticAlphabet + // per the semantic versioning spec. + appPreRelease = "alpha" +) + +// appBuild is defined as a variable so it can be overridden during the build +// process with '-ldflags "-X main.appBuild foo' if needed. It MUST only +// contain characters from semanticAlphabet per the semantic versioning spec. +var appBuild string + +// version returns the application version as a properly formed string per the +// semantic versioning 2.0.0 spec (http://semver.org/). +func version() string { + // Start with the major, minor, and path versions. + version := fmt.Sprintf("%d.%d.%d", appMajor, appMinor, appPatch) + + // Append pre-release version if there is one. The hyphen called for + // by the semantic versioning spec is automatically appended and should + // not be contained in the pre-release string. The pre-release version + // is not appended if it contains invalid characters. + preRelease := normalizeVerString(appPreRelease) + if preRelease != "" { + version = fmt.Sprintf("%s-%s", version, preRelease) + } + + // Append build metadata if there is any. The plus called for + // by the semantic versioning spec is automatically appended and should + // not be contained in the build metadata string. The build metadata + // string is not appended if it contains invalid characters. + build := normalizeVerString(appBuild) + if build != "" { + version = fmt.Sprintf("%s+%s", version, build) + } + + return version +} + +// normalizeVerString returns the passed string stripped of all characters which +// are not valid according to the semantic versioning guidelines for pre-release +// version and build metadata strings. In particular they MUST only contain +// characters in semanticAlphabet. +func normalizeVerString(str string) string { + var result bytes.Buffer + for _, r := range str { + if strings.ContainsRune(semanticAlphabet, r) { + result.WriteRune(r) + } + } + return result.String() +}