init commit

Signed-off-by: Jess Frazelle <acidburn@microsoft.com>
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..7c73e39
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,50 @@
+###Go###
+
+# Compiled Object files, Static and Dynamic libs (Shared Objects)
+*.o
+*.a
+*.so
+
+# Folders
+_obj
+_test
+
+# Architecture specific extensions/prefixes
+*.[568vq]
+[568vq].out
+
+*.cgo1.go
+*.cgo2.c
+_cgo_defun.c
+_cgo_gotypes.go
+_cgo_export.*
+
+_testmain.go
+
+*.exe
+*.test
+
+
+###OSX###
+
+.DS_Store
+.AppleDouble
+.LSOverride
+
+# Icon must ends with two \r.
+Icon
+
+
+# Thumbnails
+._*
+
+# Files that might appear on external disk
+.Spotlight-V100
+.Trashes
+
+pkg
+cross/
+
+# Go coverage results
+coverage.txt
+profile.out
diff --git a/.travis.yml b/.travis.yml
new file mode 100644
index 0000000..bb43bd6
--- /dev/null
+++ b/.travis.yml
@@ -0,0 +1,13 @@
+language: go
+sudo: false
+go:
+  - 1.10.x
+before_install:
+  - go get github.com/golang/lint/golint
+  - go get honnef.co/go/tools/cmd/staticcheck
+jobs:
+  include:
+    - script: make all
+    - stage: Run Test Coverage
+      script: make cover
+      after_success: bash <(curl -s https://codecov.io/bash)
diff --git a/Gopkg.lock b/Gopkg.lock
new file mode 100644
index 0000000..10ef811
--- /dev/null
+++ b/Gopkg.lock
@@ -0,0 +1,9 @@
+# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'.
+
+
+[solve-meta]
+  analyzer-name = "dep"
+  analyzer-version = 1
+  input-imports = []
+  solver-name = "gps-cdcl"
+  solver-version = 1
diff --git a/Gopkg.toml b/Gopkg.toml
new file mode 100644
index 0000000..6568abb
--- /dev/null
+++ b/Gopkg.toml
@@ -0,0 +1,25 @@
+# Gopkg.toml example
+#
+# Refer to https://github.com/golang/dep/blob/master/docs/Gopkg.toml.md
+# for detailed Gopkg.toml documentation.
+#
+# required = ["github.com/user/thing/cmd/thing"]
+# ignored = ["github.com/user/project/pkgX", "bitbucket.org/user/project/pkgA/pkgY"]
+#
+# [[constraint]]
+#   name = "github.com/user/project"
+#   version = "1.0.0"
+#
+# [[constraint]]
+#   name = "github.com/user/project2"
+#   branch = "dev"
+#   source = "github.com/myfork/project2"
+#
+# [[override]]
+#  name = "github.com/x/y"
+#  version = "2.4.0"
+
+[prune]
+  non-go = true
+  go-tests = true
+  unused-packages = true
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..fd05738
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2018 The Genuinetools Authors
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..62f48a2
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,150 @@
+# Set an output prefix, which is the local directory if not specified
+PREFIX?=$(shell pwd)
+
+# Setup name variables for the package/tool
+NAME := pkg
+PKG := github.com/genuinetools/$(NAME)
+
+# Set any default go build tags
+BUILDTAGS :=
+
+# Set the build dir, where built cross-compiled binaries will be output
+BUILDDIR := ${PREFIX}/cross
+
+# Populate version variables
+# Add to compile time flags
+VERSION := $(shell cat VERSION.txt)
+GITCOMMIT := $(shell git rev-parse --short HEAD)
+GITUNTRACKEDCHANGES := $(shell git status --porcelain --untracked-files=no)
+ifneq ($(GITUNTRACKEDCHANGES),)
+	GITCOMMIT := $(GITCOMMIT)-dirty
+endif
+CTIMEVAR=-X $(PKG)/version.GITCOMMIT=$(GITCOMMIT) -X $(PKG)/version.VERSION=$(VERSION)
+GO_LDFLAGS=-ldflags "-w $(CTIMEVAR)"
+GO_LDFLAGS_STATIC=-ldflags "-w $(CTIMEVAR) -extldflags -static"
+
+# Set our default go compiler
+GO := go
+
+# List the GOOS and GOARCH to build
+GOOSARCHES = darwin/amd64 darwin/386 freebsd/amd64 freebsd/386 linux/arm linux/arm64 linux/amd64 linux/386 solaris/amd64 windows/amd64 windows/386
+
+.PHONY: build
+build: $(NAME) ## Builds a dynamic executable or package
+
+$(NAME): $(wildcard *.go) $(wildcard */*.go) VERSION.txt
+	@echo "+ $@"
+	$(GO) build -tags "$(BUILDTAGS)" ${GO_LDFLAGS} -o $(NAME) ./...
+
+.PHONY: static
+static: ## Builds a static executable
+	@echo "+ $@"
+	CGO_ENABLED=0 $(GO) build \
+				-tags "$(BUILDTAGS) static_build" \
+				${GO_LDFLAGS_STATIC} -o $(NAME) ./...
+
+all: clean build fmt lint test staticcheck vet install ## Runs a clean, build, fmt, lint, test, staticcheck, vet and install
+
+.PHONY: fmt
+fmt: ## Verifies all files have been `gofmt`ed
+	@echo "+ $@"
+	@gofmt -s -l . | grep -v '.pb.go:' | grep -v vendor | tee /dev/stderr
+
+.PHONY: lint
+lint: ## Verifies `golint` passes
+	@echo "+ $@"
+	@golint ./... | grep -v '.pb.go:' | grep -v vendor | tee /dev/stderr
+
+.PHONY: test
+test: ## Runs the go tests
+	@echo "+ $@"
+	@$(GO) test -v -tags "$(BUILDTAGS) cgo" $(shell $(GO) list ./... | grep -v vendor)
+
+.PHONY: vet
+vet: ## Verifies `go vet` passes
+	@echo "+ $@"
+	@$(GO) vet $(shell $(GO) list ./... | grep -v vendor) | grep -v '.pb.go:' | tee /dev/stderr
+
+.PHONY: staticcheck
+staticcheck: ## Verifies `staticcheck` passes
+	@echo "+ $@"
+	@staticcheck $(shell $(GO) list ./... | grep -v vendor) | grep -v '.pb.go:' | tee /dev/stderr
+
+.PHONY: cover
+cover: ## Runs go test with coverage
+	@echo "" > coverage.txt
+	@for d in $(shell $(GO) list ./... | grep -v vendor); do \
+		$(GO) test -race -coverprofile=profile.out -covermode=atomic "$$d"; \
+		if [ -f profile.out ]; then \
+			cat profile.out >> coverage.txt; \
+			rm profile.out; \
+		fi; \
+	done;
+
+.PHONY: install
+install: ## Installs the executable or package
+	@echo "+ $@"
+	$(GO) install -a -tags "$(BUILDTAGS)" ${GO_LDFLAGS} ./...
+
+define buildpretty
+mkdir -p $(BUILDDIR)/$(1)/$(2);
+GOOS=$(1) GOARCH=$(2) CGO_ENABLED=0 $(GO) build \
+	 -o $(BUILDDIR)/$(1)/$(2)/$(NAME) \
+	 -a -tags "$(BUILDTAGS) static_build netgo" \
+	 -installsuffix netgo ${GO_LDFLAGS_STATIC} .;
+md5sum $(BUILDDIR)/$(1)/$(2)/$(NAME) > $(BUILDDIR)/$(1)/$(2)/$(NAME).md5;
+sha256sum $(BUILDDIR)/$(1)/$(2)/$(NAME) > $(BUILDDIR)/$(1)/$(2)/$(NAME).sha256;
+endef
+
+.PHONY: cross
+cross: *.go VERSION.txt ## Builds the cross-compiled binaries, creating a clean directory structure (eg. GOOS/GOARCH/binary)
+	@echo "+ $@"
+	$(foreach GOOSARCH,$(GOOSARCHES), $(call buildpretty,$(subst /,,$(dir $(GOOSARCH))),$(notdir $(GOOSARCH))))
+
+define buildrelease
+GOOS=$(1) GOARCH=$(2) CGO_ENABLED=0 $(GO) build \
+	 -o $(BUILDDIR)/$(NAME)-$(1)-$(2) \
+	 -a -tags "$(BUILDTAGS) static_build netgo" \
+	 -installsuffix netgo ${GO_LDFLAGS_STATIC} .;
+md5sum $(BUILDDIR)/$(NAME)-$(1)-$(2) > $(BUILDDIR)/$(NAME)-$(1)-$(2).md5;
+sha256sum $(BUILDDIR)/$(NAME)-$(1)-$(2) > $(BUILDDIR)/$(NAME)-$(1)-$(2).sha256;
+endef
+
+.PHONY: release
+release: *.go VERSION.txt ## Builds the cross-compiled binaries, naming them in such a way for release (eg. binary-GOOS-GOARCH)
+	@echo "+ $@"
+	$(foreach GOOSARCH,$(GOOSARCHES), $(call buildrelease,$(subst /,,$(dir $(GOOSARCH))),$(notdir $(GOOSARCH))))
+
+.PHONY: bump-version
+BUMP := patch
+bump-version: ## Bump the version in the version file. Set BUMP to [ patch | major | minor ]
+	@$(GO) get -u github.com/jessfraz/junk/sembump # update sembump tool
+	$(eval NEW_VERSION = $(shell sembump --kind $(BUMP) $(VERSION)))
+	@echo "Bumping VERSION.txt from $(VERSION) to $(NEW_VERSION)"
+	echo $(NEW_VERSION) > VERSION.txt
+	@echo "Updating links to download binaries in README.md"
+	sed -i s/$(VERSION)/$(NEW_VERSION)/g README.md
+	git add VERSION.txt README.md
+	git commit -vsam "Bump version to $(NEW_VERSION)"
+	@echo "Run make tag to create and push the tag for new version $(NEW_VERSION)"
+
+.PHONY: tag
+tag: ## Create a new git tag to prepare to build a release
+	git tag -sa $(VERSION) -m "$(VERSION)"
+	@echo "Run git push origin $(VERSION) to push your new tag to GitHub and trigger a travis build."
+
+.PHONY: AUTHORS
+AUTHORS:
+	@$(file >$@,# This file lists all individuals having contributed content to the repository.)
+	@$(file >>$@,# For how it is generated, see `make AUTHORS`.)
+	@echo "$(shell git log --format='\n%aN <%aE>' | LC_ALL=C.UTF-8 sort -uf)" >> $@
+
+.PHONY: clean
+clean: ## Cleanup any build binaries or packages
+	@echo "+ $@"
+	$(RM) $(NAME)
+	$(RM) -r $(BUILDDIR)
+
+.PHONY: help
+help:
+	@grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}'
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..236f767
--- /dev/null
+++ b/README.md
@@ -0,0 +1,8 @@
+# pkg
+
+[![Travis CI](https://img.shields.io/travis/genuinetools/pkg.svg?style=for-the-badge)](https://travis-ci.org/genuinetools/pkg)
+[![GoDoc](https://img.shields.io/badge/godoc-reference-5272B4.svg?style=for-the-badge)](https://godoc.org/github.com/genuinetools/pkg)
+[![Github All Releases](https://img.shields.io/github/downloads/genuinetools/pkg/total.svg?style=for-the-badge)](https://github.com/genuinetools/pkg/releases)
+
+
+A home for various go packages to be imported by other projects.
diff --git a/VERSION.txt b/VERSION.txt
new file mode 100644
index 0000000..ae39fab
--- /dev/null
+++ b/VERSION.txt
@@ -0,0 +1 @@
+v0.0.0
diff --git a/cli/cli.go b/cli/cli.go
new file mode 100644
index 0000000..467015e
--- /dev/null
+++ b/cli/cli.go
@@ -0,0 +1,234 @@
+package cli
+
+import (
+	"bytes"
+	"context"
+	"flag"
+	"fmt"
+	"os"
+	"path/filepath"
+	"strings"
+	"text/tabwriter"
+)
+
+const (
+	// GitCommitKey is the key for the program's GitCommit data.
+	GitCommitKey ContextKey = "program.GitCommit"
+	// VersionKey is the key for the program's Version data.
+	VersionKey ContextKey = "program.Version"
+)
+
+// ContextKey defines the type for holding keys in the context.
+type ContextKey string
+
+// Program defines the struct for holding information about the program.
+type Program struct {
+	// Name of the program. Defaults to path.Base(os.Args[0]).
+	Name string
+	// Description of the program.
+	Description string
+	// Version of the program.
+	Version string
+	// GitCommit information for the program.
+	GitCommit string
+
+	// Commands in the program.
+	Commands []Command
+	// FlagSet holds the common/global flags for the program.
+	FlagSet *flag.FlagSet
+
+	// Before defines a function to execute before any subcommands are run,
+	// but after the context is ready.
+	// If a non-nil error is returned, no subcommands are run.
+	Before func(context.Context) error
+	// After defines a function to execute after any subcommands are run,
+	// but after the subcommand has finished.
+	// It is run even if the subcommand returns an error.
+	After func(context.Context) error
+
+	// Action is the function to execute when no subcommands are specified.
+	Action func(context.Context) error
+}
+
+// Command defines the interface for each command in a program.
+type Command interface {
+	Name() string      // "foobar"
+	Args() string      // "<baz> [quux...]"
+	ShortHelp() string // "Foo the first bar"
+	LongHelp() string  // "Foo the first bar meeting the following conditions..."
+
+	// Hidden indicates whether the command should be hidden from the help output.
+	Hidden() bool
+
+	// Register command specific flags.
+	Register(*flag.FlagSet)
+	// Run executes the function for the command with a context and the command arguments.
+	Run(context.Context, []string) error
+}
+
+// NewProgram creates a new Program with some reasonable defaults for Name,
+// Description, and Version.
+func NewProgram() *Program {
+	return &Program{
+		Name:        filepath.Base(os.Args[0]),
+		Description: "A new command line program.",
+		Version:     "0.0.0",
+	}
+}
+
+// Run is the entry point for the program. It parses the arguments and executes
+// the commands.
+func (p *Program) Run() {
+	// Create the context with the values we need to pass to the version command.
+	ctx := context.WithValue(context.Background(), GitCommitKey, p.GitCommit)
+	ctx = context.WithValue(ctx, VersionKey, p.Version)
+
+	// Append the version command to the list of commands by default.
+	p.Commands = append(p.Commands, &versionCommand{})
+
+	if len(os.Args) <= 1 || len(os.Args) == 2 &&
+		(strings.Contains(strings.ToLower(os.Args[1]), "help") ||
+			strings.ToLower(os.Args[1]) == "-h") {
+		p.usage(ctx)
+		os.Exit(1)
+	}
+
+	// Set the default action to print the usage if it is undefined.
+	if p.Action == nil {
+		p.Action = p.usage
+	}
+
+	if len(os.Args) <= 1 {
+		// Run the main action.
+		if err := p.Action(ctx); err != nil {
+			fmt.Fprintf(os.Stderr, "%v\n", err)
+			os.Exit(1)
+		}
+
+		// Done.
+		return
+	}
+
+	// Iterate over the commands in the program.
+	for _, command := range p.Commands {
+		if name := command.Name(); os.Args[1] == name {
+			// Set the default flagset if our flagset is undefined.
+			if p.FlagSet == nil {
+				p.FlagSet = defaultFlagSet(p.Name)
+			}
+
+			// Register the subcommand flags in with the common/global flags.
+			command.Register(p.FlagSet)
+
+			// Override the usage text to something nicer.
+			p.resetFlagUsage(p.FlagSet, command)
+
+			// Parse the flags the user gave us.
+			if err := p.FlagSet.Parse(os.Args[2:]); err != nil {
+				p.FlagSet.Usage()
+				os.Exit(1)
+			}
+
+			if p.Before != nil {
+				if err := p.Before(ctx); err != nil {
+					fmt.Fprintf(os.Stderr, "%v\n", err)
+					os.Exit(1)
+				}
+			}
+
+			// Run the command with the context and post-flag-processing args.
+			if err := command.Run(ctx, p.FlagSet.Args()); err != nil {
+				fmt.Fprintf(os.Stderr, "%v\n", err)
+
+				if p.After != nil {
+					if err := p.After(ctx); err != nil {
+						fmt.Fprintf(os.Stderr, "%v\n", err)
+					}
+				}
+
+				os.Exit(1)
+			}
+
+			// Run the after function.
+			if p.After != nil {
+				if err := p.After(ctx); err != nil {
+					fmt.Fprintf(os.Stderr, "%v\n", err)
+					os.Exit(1)
+				}
+			}
+
+			// Done.
+			return
+		}
+	}
+
+	fmt.Fprintf(os.Stderr, "%s: no such command\n", os.Args[1])
+	p.usage(ctx)
+	os.Exit(1)
+}
+
+func (p *Program) usage(ctx context.Context) error {
+	fmt.Fprintf(os.Stderr, "Usage: %s <command>\n", p.Name)
+	fmt.Fprintln(os.Stderr)
+
+	// Print information about the common/global flags.
+	if p.FlagSet != nil {
+		p.FlagSet.PrintDefaults()
+		fmt.Fprintln(os.Stderr)
+	}
+
+	// Print information about the commands.
+	fmt.Fprintln(os.Stderr, "Commands:")
+	fmt.Fprintln(os.Stderr)
+
+	w := tabwriter.NewWriter(os.Stderr, 0, 4, 2, ' ', 0)
+	for _, command := range p.Commands {
+		if !command.Hidden() {
+			fmt.Fprintf(w, "\t%s\t%s\n", command.Name(), command.ShortHelp())
+		}
+	}
+	w.Flush()
+
+	fmt.Fprintln(os.Stderr)
+	return nil
+}
+
+func (p *Program) resetFlagUsage(fs *flag.FlagSet, command Command) {
+	var (
+		hasFlags   bool
+		flagBlock  bytes.Buffer
+		flagWriter = tabwriter.NewWriter(&flagBlock, 0, 4, 2, ' ', 0)
+	)
+
+	fs.VisitAll(func(f *flag.Flag) {
+		hasFlags = true
+
+		// Default-empty string vars should read "(default: <none>)"
+		// rather than the comparatively ugly "(default: )".
+		defValue := f.DefValue
+		if defValue == "" {
+			defValue = "<none>"
+		}
+
+		fmt.Fprintf(flagWriter, "\t-%s\t%s (default: %s)\n", f.Name, f.Usage, defValue)
+	})
+
+	flagWriter.Flush()
+
+	fs.Usage = func() {
+		fmt.Fprintf(os.Stderr, "Usage: %s %s %s\n", p.Name, command.Name(), command.Args())
+		fmt.Fprintln(os.Stderr)
+		fmt.Fprintln(os.Stderr, strings.TrimSpace(command.LongHelp()))
+		fmt.Fprintln(os.Stderr)
+		if hasFlags {
+			fmt.Fprintln(os.Stderr, "Flags:")
+			fmt.Fprintln(os.Stderr)
+			fmt.Fprintln(os.Stderr, flagBlock.String())
+		}
+	}
+}
+
+func defaultFlagSet(n string) *flag.FlagSet {
+	// Create the default flagset with a debug flag.
+	return flag.NewFlagSet(n, flag.ExitOnError)
+}
diff --git a/cli/version.go b/cli/version.go
new file mode 100644
index 0000000..9c41e88
--- /dev/null
+++ b/cli/version.go
@@ -0,0 +1,32 @@
+package cli
+
+import (
+	"context"
+	"flag"
+	"fmt"
+	"runtime"
+)
+
+const versionHelp = `Show the version information.`
+
+func (cmd *versionCommand) Name() string      { return "version" }
+func (cmd *versionCommand) Args() string      { return "" }
+func (cmd *versionCommand) ShortHelp() string { return versionHelp }
+func (cmd *versionCommand) LongHelp() string  { return versionHelp }
+func (cmd *versionCommand) Hidden() bool      { return false }
+
+func (cmd *versionCommand) Register(fs *flag.FlagSet) {}
+
+type versionCommand struct{}
+
+func (cmd *versionCommand) Run(ctx context.Context, args []string) error {
+	fmt.Printf(`%s:
+ version     : %s
+ git hash    : %s
+ go version  : %s
+ go compiler : %s
+ platform    : %s/%s
+`, "ship", ctx.Value(VersionKey).(string), ctx.Value(GitCommitKey).(string),
+		runtime.Version(), runtime.Compiler, runtime.GOOS, runtime.GOARCH)
+	return nil
+}