Compare commits

..

31 commits

Author SHA1 Message Date
Nate Jones
b87a4d5637 Adding release script 2022-10-01 18:29:48 -07:00
Nate Jones
3ebdb75ed3 Bump version to v0.6.4 2022-10-01 18:06:26 -07:00
Nate Jones
f925e91395 Switch to using go modules 2022-10-01 18:06:19 -07:00
Nate Jones
15f77275c4 version bump to v0.6.3 2018-04-19 21:13:18 -07:00
Nate Jones
28bcf8034b fix/add vendor json file 2018-04-19 21:12:27 -07:00
Nate Jones
4d5c52e735 version bump to v0.6.2 2018-04-19 20:57:54 -07:00
Nate Jones
2af445f27a vendor deps with govendor 2018-04-19 20:57:06 -07:00
Nate Jones
7b8aa57592 add canonical import path, for docker image building
with centurylink/golang-builder
2018-04-19 19:28:44 -07:00
Nate Jones
b4f269312f bump version to v0.6.1 2018-04-19 08:19:47 -07:00
Nate Jones
f765f7d6cd Merge branch 'master' into treemap 2018-04-16 17:23:49 -07:00
Nate Jones
f63f9dbe37 Merge branch 'fabianlee' 2018-04-16 17:17:27 -07:00
Nate Jones
d30fd054ad Merge branch 'master' into fabianlee 2018-04-16 17:17:13 -07:00
Nate Jones
b84946b83e add note about showing image createdby, small formatting 2018-04-16 17:15:31 -07:00
Nate Jones
85dd6df8b2 conditionalize createdby display, make display options consistent 2018-04-16 17:11:16 -07:00
Nate Jones
4c4b3c154a go fmt 2018-04-16 17:08:57 -07:00
Nate Jones
51c099c18c Merge branch 'master' into fabianlee 2018-04-16 16:33:29 -07:00
Nate Jones
c932e4c843 Merge pull request #36 from nsarafa/graphviz-install-readme-instructions
Graphviz apt-get, and brew installation instructions init
2017-10-12 20:31:52 -07:00
fabianlee
40e1c4e1ce unnecessary comment about param being short d or full dot 2017-05-29 19:17:19 +00:00
nsarafa
651855127c Graphviz apt-get, and brew installation instructions init 2017-05-27 19:12:04 -04:00
fabianlee
edcada1c88 test 2017-05-25 10:52:40 +00:00
fabianlee
1ce624cd37 using names for merge 2017-05-25 10:51:50 +00:00
fabianlee
9e07d53e05 build and push instructions added 2017-05-23 23:52:57 +00:00
fabianlee
c75e40ffd8 more prominent example of using tcp docker client 2017-05-23 23:26:43 +00:00
fabianlee
76415ba88f docs 2017-05-23 23:20:42 +00:00
fabianlee
2df85b6786 document switch for only running containers and package name of graphviz on debian 2017-05-23 23:19:14 +00:00
fabianlee
1f8a6a6f0c updated documentation to force statically linked build, example for remote docker client sending DOCKER_HOST 2017-05-23 23:16:24 +00:00
fabianlee
25acff1b44 switch for only running containers 2017-05-22 16:14:10 +00:00
fabianlee
360c954944 comments 2017-05-22 15:48:18 +00:00
fabianlee
cf340cd457 containers: extra labels for name, removing links for multiply named containers 2017-05-22 15:44:33 +00:00
fabianlee
18ad950cd1 added truncated command and size to make up for idea that since Docker 1.10 content addressability there is usually <missing> id 2017-05-22 01:55:01 +00:00
fabianlee
ff917a13b1 removed canonical import comment which allows forking 2017-05-22 01:50:25 +00:00
7 changed files with 1368 additions and 37 deletions

View file

@ -9,14 +9,30 @@ different ways, to help you understand what's going on inside the system.
1. Install dockviz. Either: 1. Install dockviz. Either:
* Download the [latest release](https://github.com/justone/dockviz/releases). * Download the [latest release](https://github.com/justone/dockviz/releases).
* Set up an alias to run it from the (5.8 MB) docker image: * Set up an alias to run it from the (5.8 MB) docker image:
```
# if docker client using local unix socket
alias dockviz="docker run -it --rm -v /var/run/docker.sock:/var/run/docker.sock nate/dockviz"
# if docker client using tcp
alias dockviz="docker run -it --rm -e DOCKER_HOST='tcp://127.0.0.1:2375' nate/dockviz"
```
```
alias dockviz="docker run -it --rm -v /var/run/docker.sock:/var/run/docker.sock nate/dockviz"
```
2. Visualize images by running `dockviz images -t`, which has similar output to `docker images -t`. 2. Visualize images by running `dockviz images -t`, which has similar output to `docker images -t`.
* Image can be visualized as [Graphviz](http://www.graphviz.org), or as a tree or short summary in the terminal. Only Graphviz output has been implemented for containers.
* If you would like to visualize outside the container you will have to install [Graphviz](http://www.graphviz.org) first.
Image can be visualized as [Graphviz](http://www.graphviz.org), or as a tree or short summary in the terminal. Only Graphviz output has been implemented for containers. ```
apt-get update && apt-get install graphviz
```
or
```
brew update && brew install graphviz
```
# Output Examples # Output Examples
@ -25,7 +41,11 @@ Image can be visualized as [Graphviz](http://www.graphviz.org), or as a tree or
Currently, containers are visualized with labelled lines for links. Containers that aren't running are greyed out. Currently, containers are visualized with labelled lines for links. Containers that aren't running are greyed out.
``` ```
# show all containers
$ dockviz containers -d | dot -Tpng -o containers.png $ dockviz containers -d | dot -Tpng -o containers.png
# only show running containers
$ dockviz containers -d -r | dot -Tpng -o containers.png
``` ```
![](sample/containers.png "Container") ![](sample/containers.png "Container")
@ -36,8 +56,6 @@ Image info is visualized with lines indicating parent images:
``` ```
$ dockviz images -d | dot -Tpng -o images.png $ dockviz images -d | dot -Tpng -o images.png
OR
$ dockviz images --dot | dot -Tpng -o images.png
``` ```
![](sample/images.png "Image") ![](sample/images.png "Image")
@ -189,11 +207,15 @@ $ dockviz images -t -i -c
└─316b678ddf48 Size: 70822908 Tags: ubuntu:13.04, ubuntu:raring └─316b678ddf48 Size: 70822908 Tags: ubuntu:13.04, ubuntu:raring
``` ```
It is also possible to show the image's CreatedBy field, for help identifying
image layers when they show up with "<missing>" image Ids.
# Running # Running
Dockviz supports connecting to the Docker daemon directly. It defaults to `unix:///var/run/docker.sock`, but respects the following as well: Dockviz supports connecting to the Docker daemon directly. It defaults to `unix:///var/run/docker.sock`, but respects the following as well:
* The `DOCKER_HOST`, `DOCKER_CERT_PATH`, and `DOCKER_TLS_VERIFY` environment variables, as set up by [boot2docker](http://boot2docker.io/) or [docker-machine](https://docs.docker.com/machine/). * The `DOCKER_HOST`, `DOCKER_CERT_PATH`, and `DOCKER_TLS_VERIFY` environment variables, as set up by [boot2docker](http://boot2docker.io/) or [docker-machine](https://docs.docker.com/machine/).
* Command line arguments (e.g. `--tlscacert`), like those that Docker itself supports. * Command line arguments (e.g. `--tlscacert`), like those that Docker itself supports.
Dockviz also supports receiving Docker image or container json data on standard input. Dockviz also supports receiving Docker image or container json data on standard input.
@ -216,6 +238,27 @@ See the [releases](https://github.com/justone/dockviz/releases) area for binarie
# Build # Build
```bash ```bash
go get ./... # install graphviz in host environment for rendering (Debian example)
sudo apt-get install git graphviz -y
# pull latest code
mkdir -p $GOPATH/src/github.com/nate/ && cd $GOPATH/src/github.com/nate/
git clone https://github.com/nate/dockviz.git && cd dockviz
# force static compilation for go language
export CGO_ENABLED=0
# fetch vendored dependencies
govendor sync
# build
go build go build
# build docker image
docker build --no-cache=true -t "nate/dockviz:1.0" -t "nate/dockviz:latest" .
# push to docker hub
docker login --username=mygituser --password=xxxxxxx
docker push nate/dockviz
``` ```

2
cli.go
View file

@ -20,7 +20,7 @@ type GlobalOptions struct {
var globalOptions GlobalOptions var globalOptions GlobalOptions
var parser = flags.NewParser(&globalOptions, flags.Default) var parser = flags.NewParser(&globalOptions, flags.Default)
var version = "v0.5.0" var version = "v0.6.4"
func main() { func main() {
globalOptions.Version = func() { globalOptions.Version = func() {

View file

@ -24,6 +24,7 @@ type Container struct {
type ContainersCommand struct { type ContainersCommand struct {
Dot bool `short:"d" long:"dot" description:"Show container information as Graphviz dot."` Dot bool `short:"d" long:"dot" description:"Show container information as Graphviz dot."`
NoTruncate bool `short:"n" long:"no-trunc" description:"Don't truncate the container IDs."` NoTruncate bool `short:"n" long:"no-trunc" description:"Don't truncate the container IDs."`
OnlyRunning bool `short:"r" long:"running" description:"Only show running containers, not Exited"`
} }
var containersCommand ContainersCommand var containersCommand ContainersCommand
@ -81,7 +82,7 @@ func (x *ContainersCommand) Execute(args []string) error {
} }
if containersCommand.Dot { if containersCommand.Dot {
fmt.Printf(jsonContainersToDot(containers)) fmt.Printf(jsonContainersToDot(containers, containersCommand.OnlyRunning))
} else { } else {
return fmt.Errorf("Please specify --dot") return fmt.Errorf("Please specify --dot")
} }
@ -115,24 +116,53 @@ func parseContainersJSON(rawJSON []byte) (*[]Container, error) {
return &containers, nil return &containers, nil
} }
func jsonContainersToDot(containers *[]Container) string { func jsonContainersToDot(containers *[]Container,OnlyRunning bool) string {
var buffer bytes.Buffer var buffer bytes.Buffer
buffer.WriteString("digraph docker {\n") buffer.WriteString("digraph docker {\n")
// build list of all primary container names
// this is so we can throw away links to
// non-primary container name
var PrimaryContainerNames map[string]string
PrimaryContainerNames = make(map[string]string)
for _, container := range *containers { for _, container := range *containers {
for _, name := range container.Names {
if strings.Count(name, "/") == 1 {
PrimaryContainerNames[name[1:]] = name[1:]
}
}
}
// stores ony first value of link to avoid duplicates
var LinkMap map[string]string
LinkMap = make(map[string]string)
for _, container := range *containers {
if OnlyRunning && strings.HasPrefix(container.Status,"Exit") { continue }
var containerName string var containerName string
//fmt.Printf("container status/Names %s/%s\n",container.Status,container.Names)
for _, name := range container.Names { for _, name := range container.Names {
if strings.Count(name, "/") == 1 { if strings.Count(name, "/") == 1 {
containerName = name[1:] containerName = name[1:]
} }
} }
for _, name := range container.Names { for _, name := range container.Names {
nameParts := strings.Split(name, "/") nameParts := strings.Split(name, "/")
if len(nameParts) > 2 { if len(nameParts) > 2 {
buffer.WriteString(fmt.Sprintf(" \"%s\" -> \"%s\" [label = \" %s\" ]\n", containerName, nameParts[1], nameParts[len(nameParts)-1])) //fmt.Printf("\t%s to %s\n",containerName,nameParts[1])
// source and dest should be primary container names
if IsPrimaryContainerName(containerName,PrimaryContainerNames) && IsPrimaryContainerName(nameParts[1],PrimaryContainerNames) {
// only create link if none exists already
if _,ok := LinkMap[containerName + "-" + nameParts[1]]; !ok {
LinkMap[containerName + "-" + nameParts[1]] = "exists"
buffer.WriteString(fmt.Sprintf(" \"%s\" -> \"%s\" [label = \" %s\" ]\n", containerName, nameParts[1], nameParts[len(nameParts)-1] ))
}
}
} }
} }
@ -144,7 +174,7 @@ func jsonContainersToDot(containers *[]Container) string {
containerBackground = "paleturquoise" containerBackground = "paleturquoise"
} }
buffer.WriteString(fmt.Sprintf(" \"%s\" [label=\"%s\\n%s\",shape=box,fillcolor=\"%s\",style=\"filled,rounded\"];\n", containerName, containerName, truncate(container.Id, 12), containerBackground)) buffer.WriteString(fmt.Sprintf(" \"%s\" [label=\"%s\\n%s\\n%s\",shape=box,fillcolor=\"%s\",style=\"filled,rounded\"];\n", containerName, container.Image, containerName, truncate(container.Id, 12), containerBackground))
} }
buffer.WriteString("}\n") buffer.WriteString("}\n")
@ -152,6 +182,11 @@ func jsonContainersToDot(containers *[]Container) string {
return buffer.String() return buffer.String()
} }
func IsPrimaryContainerName(Name string,PrimaryContainerNames map[string]string) bool {
_,ok := PrimaryContainerNames[Name]
return ok
}
func init() { func init() {
parser.AddCommand("containers", parser.AddCommand("containers",
"Visualize docker containers.", "Visualize docker containers.",

41
go.mod Normal file
View file

@ -0,0 +1,41 @@
module github.com/justone/dockviz
go 1.19
require (
github.com/fsouza/go-dockerclient v1.8.3
github.com/jessevdk/go-flags v1.5.0
)
require (
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect
github.com/Microsoft/go-winio v0.6.0 // indirect
github.com/Microsoft/hcsshim v0.9.4 // indirect
github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 // indirect
github.com/containerd/cgroups v1.0.4 // indirect
github.com/containerd/containerd v1.6.8 // indirect
github.com/containerd/continuity v0.3.0 // indirect
github.com/docker/docker v20.10.18+incompatible // indirect
github.com/docker/go-connections v0.4.0 // indirect
github.com/docker/go-units v0.5.0 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
github.com/google/go-cmp v0.5.9 // indirect
github.com/gotestyourself/gotestyourself v2.2.0+incompatible // indirect
github.com/moby/sys/mount v0.3.3 // indirect
github.com/moby/sys/mountinfo v0.6.2 // indirect
github.com/moby/term v0.0.0-20220808134915-39b0c02b01ae // indirect
github.com/morikuni/aec v1.0.0 // indirect
github.com/opencontainers/go-digest v1.0.0 // indirect
github.com/opencontainers/image-spec v1.1.0-rc1 // indirect
github.com/opencontainers/runc v1.1.4 // indirect
github.com/opencontainers/selinux v1.10.2 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/sirupsen/logrus v1.9.0 // indirect
go.opencensus.io v0.23.0 // indirect
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 // indirect
golang.org/x/net v0.0.0-20220930213112-107f3e3c3b0b // indirect
golang.org/x/sys v0.0.0-20220928140112-f11e5e49a4ec // indirect
golang.org/x/tools v0.1.12 // indirect
gotest.tools v2.2.0+incompatible // indirect
)

1097
go.sum Normal file

File diff suppressed because it is too large Load diff

100
images.go
View file

@ -26,19 +26,21 @@ type Image struct {
} }
type ImagesCommand struct { type ImagesCommand struct {
Dot bool `short:"d" long:"dot" description:"Show image information as Graphviz dot. You can add a start image id or name -d/--dot [id/name]"` Dot bool `short:"d" long:"dot" description:"Show image information as Graphviz dot. You can add a start image id or name -d/--dot [id/name]"`
Tree bool `short:"t" long:"tree" description:"Show image information as tree. You can add a start image id or name -t/--tree [id/name]"` Tree bool `short:"t" long:"tree" description:"Show image information as tree. You can add a start image id or name -t/--tree [id/name]"`
Short bool `short:"s" long:"short" description:"Show short summary of images (repo name and list of tags)."` Short bool `short:"s" long:"short" description:"Show short summary of images (repo name and list of tags)."`
NoTruncate bool `short:"n" long:"no-trunc" description:"Don't truncate the image IDs (only works with tree mode)."` NoTruncate bool `short:"n" long:"no-trunc" description:"Don't truncate the image IDs (only works with tree mode)."`
Incremental bool `short:"i" long:"incremental" description:"Display image size as incremental rather than cumulative."` Incremental bool `short:"i" long:"incremental" description:"Display image size as incremental rather than cumulative."`
OnlyLabelled bool `short:"l" long:"only-labelled" description:"Print only labelled images/containers."` OnlyLabelled bool `short:"l" long:"only-labelled" description:"Print only labelled images/containers."`
NoHuman bool `short:"c" long:"no-human" description:"Don't humanize the sizes."` ShowCreatedBy bool `long:"show-created-by" description:"Show the image 'CreatedBy' to help identify layers."`
NoHuman bool `short:"c" long:"no-human" description:"Don't humanize the sizes."`
} }
type DisplayOpts struct { type DisplayOpts struct {
NoTruncate bool NoTruncate bool
Incremental bool Incremental bool
NoHuman bool NoHuman bool
ShowCreatedBy bool
} }
var imagesCommand ImagesCommand var imagesCommand ImagesCommand
@ -156,16 +158,17 @@ func (x *ImagesCommand) Execute(args []string) error {
*images, imagesByParent = filterImages(images, &imagesByParent) *images, imagesByParent = filterImages(images, &imagesByParent)
} }
dispOpts := DisplayOpts{
imagesCommand.NoTruncate,
imagesCommand.Incremental,
imagesCommand.NoHuman,
imagesCommand.ShowCreatedBy,
}
if imagesCommand.Tree { if imagesCommand.Tree {
dispOpts := DisplayOpts{
imagesCommand.NoTruncate,
imagesCommand.Incremental,
imagesCommand.NoHuman,
}
fmt.Print(jsonToTree(roots, imagesByParent, dispOpts)) fmt.Print(jsonToTree(roots, imagesByParent, dispOpts))
} }
if imagesCommand.Dot { if imagesCommand.Dot {
fmt.Print(jsonToDot(roots, imagesByParent)) fmt.Print(jsonToDot(roots, imagesByParent, dispOpts))
} }
} else if imagesCommand.Short { } else if imagesCommand.Short {
@ -282,11 +285,11 @@ func jsonToTree(images []Image, byParent map[string][]Image, dispOpts DisplayOpt
return buffer.String() return buffer.String()
} }
func jsonToDot(roots []Image, byParent map[string][]Image) string { func jsonToDot(roots []Image, byParent map[string][]Image, dispOpts DisplayOpts) string {
var buffer bytes.Buffer var buffer bytes.Buffer
buffer.WriteString("digraph docker {\n") buffer.WriteString("digraph docker {\n")
imagesToDot(&buffer, roots, byParent) imagesToDot(&buffer, roots, byParent, dispOpts)
buffer.WriteString(" base [style=invisible]\n}\n") buffer.WriteString(" base [style=invisible]\n}\n")
return buffer.String() return buffer.String()
@ -399,10 +402,12 @@ func PrintTreeNode(buffer *bytes.Buffer, image Image, dispOpts DisplayOpts, pref
buffer.WriteString(fmt.Sprintf("%s%s %s: %s", prefix, imageID, sizeLabel, sizeStr)) buffer.WriteString(fmt.Sprintf("%s%s %s: %s", prefix, imageID, sizeLabel, sizeStr))
if image.RepoTags[0] != "<none>:<none>" { if image.RepoTags[0] != "<none>:<none>" {
buffer.WriteString(fmt.Sprintf(" Tags: %s\n", strings.Join(image.RepoTags, ", "))) buffer.WriteString(fmt.Sprintf(" Tags: %s", strings.Join(image.RepoTags, ", ")))
} else {
buffer.WriteString(fmt.Sprintf("\n"))
} }
if dispOpts.ShowCreatedBy {
buffer.WriteString(fmt.Sprintf(" (%s)", SanitizeCommand(image.CreatedBy, 100)))
}
buffer.WriteString(fmt.Sprintf("\n"))
} }
func megabytes(bytes int64) float64 { func megabytes(bytes int64) float64 {
@ -457,20 +462,45 @@ func parseImagesJSON(rawJSON []byte) (*[]Image, error) {
return &images, nil return &images, nil
} }
func imagesToDot(buffer *bytes.Buffer, images []Image, byParent map[string][]Image) { func imagesToDot(buffer *bytes.Buffer, images []Image, byParent map[string][]Image, dispOpts DisplayOpts) {
for _, image := range images { for _, image := range images {
if image.ParentId == "" { if image.ParentId == "" {
buffer.WriteString(fmt.Sprintf(" base -> \"%s\" [style=invis]\n", truncate(image.Id, 12))) buffer.WriteString(fmt.Sprintf(" base -> \"%s\" [style=invis]\n", truncate(image.Id, 12)))
} else { } else {
buffer.WriteString(fmt.Sprintf(" \"%s\" -> \"%s\"\n", truncate(image.ParentId, 12), truncate(image.Id, 12))) buffer.WriteString(fmt.Sprintf(" \"%s\" -> \"%s\"\n", truncate(image.ParentId, 12), truncate(image.Id, 12)))
} }
if image.RepoTags[0] != "<none>:<none>" { if image.RepoTags[0] != "<none>:<none>" {
buffer.WriteString(fmt.Sprintf(" \"%s\" [label=\"%s\\n%s\",area=%f,shape=box,fillcolor=\"paleturquoise\",style=\"filled,rounded\"];\n", truncate(image.Id, 12), truncate(stripPrefix(image.OrigId), 12), strings.Join(image.RepoTags, "\\n"), megabytes(image.Size))) buffer.WriteString(fmt.Sprintf(" \"%s\" [label=\"%s\\n%s\",area=%f,shape=box,fillcolor=\"paleturquoise\",style=\"filled,rounded\"];\n", truncate(image.Id, 12), truncate(stripPrefix(image.OrigId), 12), strings.Join(image.RepoTags, "\\n"), megabytes(image.Size)))
} else { } else {
buffer.WriteString(fmt.Sprintf(" \"%s\" [label=\"%s\",area=%f]\n", truncate(image.Id, 12), truncate(stripPrefix(image.OrigId), 12), megabytes(image.Size))) labelParts := []string{truncate(stripPrefix(image.OrigId), 12)}
if dispOpts.ShowCreatedBy {
labelParts = append(labelParts, SanitizeCommand(image.CreatedBy, 30))
}
var size int64
var sizeLabel string
if dispOpts.Incremental {
sizeLabel = "Size"
size = image.Size
} else {
sizeLabel = "Virtual Size"
size = image.VirtualSize
}
var sizeStr string
if dispOpts.NoHuman {
sizeStr = strconv.FormatInt(size, 10)
} else {
sizeStr = humanSize(size)
}
labelParts = append(labelParts, fmt.Sprintf("%s: %s", sizeLabel, sizeStr))
buffer.WriteString(fmt.Sprintf(" \"%s\" [label=\"%s\",area=%f]\n", truncate(image.Id, 12), strings.Join(labelParts, "\n"), megabytes(image.Size)))
} }
if subimages, exists := byParent[image.Id]; exists { if subimages, exists := byParent[image.Id]; exists {
imagesToDot(buffer, subimages, byParent) imagesToDot(buffer, subimages, byParent, dispOpts)
} }
} }
} }
@ -506,6 +536,28 @@ func jsonToShort(images *[]Image) string {
return buffer.String() return buffer.String()
} }
func SanitizeCommand(CommandStr string, MaxLength int) string {
temp := CommandStr
// remove prefixes that don't add meaning
if strings.HasPrefix(temp, "/bin/sh -c") {
temp = strings.TrimSpace(temp[10:])
}
if strings.HasPrefix(temp, "#(nop)") {
temp = strings.TrimSpace(temp[6:])
}
// remove double and single quotes which make dot format invalid
temp = strings.Replace(temp, "\"", " ", -1)
temp = strings.Replace(temp, "'", " ", -1)
// remove double spaces inside
temp = strings.Join(strings.Fields(temp), " ")
return truncate(temp, MaxLength)
}
func init() { func init() {
parser.AddCommand("images", parser.AddCommand("images",
"Visualize docker images.", "Visualize docker images.",

63
release.sh Executable file
View file

@ -0,0 +1,63 @@
#!/bin/bash
ORG=justone
NAME=dockviz
ARCHS="darwin/amd64 linux/amd64 windows/amd64"
set -ex
if [[ ! $(type -P gox) ]]; then
echo "Error: gox not found."
echo "To fix: run 'go get github.com/mitchellh/gox', and/or add \$GOPATH/bin to \$PATH"
exit 1
fi
if [[ -z $GITHUB_TOKEN ]]; then
echo "Error: GITHUB_TOKEN not set."
exit 1
fi
if [[ ! $(type -P github-release) ]]; then
echo "Error: github-release not found."
exit 1
fi
VER=$1
if [[ -z $VER ]]; then
echo "Need to specify version."
exit 1
fi
PRE_ARG=
if [[ $VER =~ pre ]]; then
PRE_ARG="--pre-release"
fi
# git tag $VER
echo "Building $VER"
echo
rm -v ${NAME}* || true
gox -ldflags "-X main.version=$VER" -osarch="$ARCHS"
echo "* " > desc
echo "" >> desc
echo "\`\`\`" >> desc
echo "$ sha1sum ${NAME}_*" >> desc
sha1sum ${NAME}_* >> desc
echo "$ sha256sum ${NAME}_*" >> desc
sha256sum ${NAME}_* >> desc
echo "\`\`\`" >> desc
vi desc
git push --tags
sleep 2
cat desc | github-release release $PRE_ARG --user ${ORG} --repo ${NAME} --tag $VER --name $VER --description -
for file in ${NAME}_*; do
github-release upload --user ${ORG} --repo ${NAME} --tag $VER --name $file --file $file
done