mirror of
https://github.com/jimeh/casecmp.git
synced 2026-02-19 10:26:40 +00:00
Compare commits
20 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 93589c3261 | |||
| 874fcfdfe5 | |||
| 48dd4d7e7d | |||
| 645ba113b4 | |||
| 232ba27c86 | |||
| 75542c3748 | |||
| 63ed2d9b30 | |||
| 66de20267f | |||
| 6ac719e953 | |||
| 2e02f76886 | |||
| 15fb686368 | |||
| c16b23b685 | |||
| 47ef4267fa | |||
| 0a09a1eaf0 | |||
| f882e14b51 | |||
| a4dcfa68d5 | |||
| 1c8b1a4d9d | |||
| 3e321d8ef5 | |||
| c614bd9642 | |||
| cf03ebb20b |
70
.circleci/config.yml
Normal file
70
.circleci/config.yml
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
version: 2
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
docker:
|
||||||
|
- image: circleci/golang:1.10
|
||||||
|
working_directory: /go/src/github.com/jimeh/casecmp
|
||||||
|
steps:
|
||||||
|
- checkout
|
||||||
|
- run:
|
||||||
|
name: Install tools
|
||||||
|
command: |
|
||||||
|
curl -L https://github.com/golang/dep/raw/master/install.sh | sh
|
||||||
|
- run:
|
||||||
|
name: Install dependencies
|
||||||
|
command: dep ensure
|
||||||
|
- run:
|
||||||
|
name: Build binary
|
||||||
|
command: make
|
||||||
|
- run:
|
||||||
|
name: Start service
|
||||||
|
command: ./bin/casecmp --port=8080
|
||||||
|
background: true
|
||||||
|
- run:
|
||||||
|
name: Validate service is working
|
||||||
|
command: |
|
||||||
|
curl --retry 10 --retry-delay 1 --retry-connrefused \
|
||||||
|
http://localhost:8080/
|
||||||
|
release:
|
||||||
|
docker:
|
||||||
|
- image: circleci/golang:1.10
|
||||||
|
working_directory: /go/src/github.com/jimeh/casecmp
|
||||||
|
steps:
|
||||||
|
- checkout
|
||||||
|
- run:
|
||||||
|
name: Install tools
|
||||||
|
command: |
|
||||||
|
curl -L https://github.com/golang/dep/raw/master/install.sh | sh
|
||||||
|
go get github.com/goreleaser/goreleaser
|
||||||
|
- run:
|
||||||
|
name: Install dependencies
|
||||||
|
command: dep ensure
|
||||||
|
- run:
|
||||||
|
name: Run goreleaser
|
||||||
|
command: goreleaser
|
||||||
|
workflows:
|
||||||
|
version: 2
|
||||||
|
build:
|
||||||
|
jobs:
|
||||||
|
- build:
|
||||||
|
filters:
|
||||||
|
branches:
|
||||||
|
only: /.*/
|
||||||
|
tags:
|
||||||
|
ignore: /^[0-9]+(\.[0-9]+)*/
|
||||||
|
build-and-release:
|
||||||
|
jobs:
|
||||||
|
- build:
|
||||||
|
filters:
|
||||||
|
branches:
|
||||||
|
ignore: /.*/
|
||||||
|
tags:
|
||||||
|
only: /^[0-9]+(\.[0-9]+)*/
|
||||||
|
- release:
|
||||||
|
requires:
|
||||||
|
- build
|
||||||
|
filters:
|
||||||
|
branches:
|
||||||
|
ignore: /.*/
|
||||||
|
tags:
|
||||||
|
only: /^[0-9]+(\.[0-9]+)*/
|
||||||
3
.dockerignore
Normal file
3
.dockerignore
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
bin/*
|
||||||
|
dist/*
|
||||||
|
releases/*
|
||||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -1,2 +1,3 @@
|
|||||||
bin/*
|
bin/*
|
||||||
|
dist/*
|
||||||
releases/*
|
releases/*
|
||||||
|
|||||||
34
.goreleaser.yml
Normal file
34
.goreleaser.yml
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
project_name: casecmp
|
||||||
|
builds:
|
||||||
|
- main: main.go
|
||||||
|
binary: casecmp
|
||||||
|
flags: -a
|
||||||
|
env:
|
||||||
|
- CGO_ENABLED=0
|
||||||
|
goos:
|
||||||
|
- darwin
|
||||||
|
- freebsd
|
||||||
|
- linux
|
||||||
|
- windows
|
||||||
|
goarch:
|
||||||
|
- amd64
|
||||||
|
- 386
|
||||||
|
- arm
|
||||||
|
goarm:
|
||||||
|
- 6
|
||||||
|
- 7
|
||||||
|
archive:
|
||||||
|
name_template: '{{ .ProjectName }}-{{ .Version }}_{{ .Os }}_{{ .Arch }}{{ if .Arm
|
||||||
|
}}v{{ .Arm }}{{ end }}'
|
||||||
|
format: tar.gz
|
||||||
|
wrap_in_directory: true
|
||||||
|
format_overrides:
|
||||||
|
- goos: windows
|
||||||
|
format: zip
|
||||||
|
before:
|
||||||
|
hooks:
|
||||||
|
- make clean
|
||||||
|
git:
|
||||||
|
short_hash: true
|
||||||
|
snapshot:
|
||||||
|
name_template: snapshot-{{.Commit}}
|
||||||
20
Dockerfile
20
Dockerfile
@@ -1,12 +1,22 @@
|
|||||||
FROM golang:1.9-alpine as builder
|
FROM golang:1.10-alpine as builder
|
||||||
ADD . /go/src/github.com/jimeh/casecmp
|
RUN set -e \
|
||||||
|
&& apk add --no-cache git \
|
||||||
|
&& wget -O - https://github.com/golang/dep/raw/master/install.sh | sh
|
||||||
|
|
||||||
WORKDIR /go/src/github.com/jimeh/casecmp
|
WORKDIR /go/src/github.com/jimeh/casecmp
|
||||||
RUN CGO_ENABLED=0 go build -a -o /casecmp \
|
ADD . /go/src/github.com/jimeh/casecmp
|
||||||
-ldflags "-X main.Version=$(cat VERSION)"
|
RUN set -e \
|
||||||
|
&& dep ensure \
|
||||||
|
&& CGO_ENABLED=0 go build -a -o /casecmp \
|
||||||
|
-ldflags "-s -w \
|
||||||
|
-X main.version=$(cat VERSION) \
|
||||||
|
-X main.commit=$(git show --format='%h' --no-patch)\
|
||||||
|
-X main.date=$(date +%Y-%m-%dT%T%z)"
|
||||||
|
|
||||||
|
|
||||||
FROM scratch
|
FROM scratch
|
||||||
COPY --from=builder /casecmp /
|
COPY --from=builder /casecmp /
|
||||||
ENV PORT 8080
|
ENV PORT 8080
|
||||||
EXPOSE 8080
|
EXPOSE 8080
|
||||||
WORKDIR /
|
WORKDIR /
|
||||||
CMD ["/casecmp"]
|
ENTRYPOINT ["/casecmp"]
|
||||||
|
|||||||
30
Gopkg.lock
generated
Normal file
30
Gopkg.lock
generated
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'.
|
||||||
|
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
branch = "master"
|
||||||
|
name = "github.com/alecthomas/template"
|
||||||
|
packages = [
|
||||||
|
".",
|
||||||
|
"parse"
|
||||||
|
]
|
||||||
|
revision = "a0175ee3bccc567396460bf5acd36800cb10c49c"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
branch = "master"
|
||||||
|
name = "github.com/alecthomas/units"
|
||||||
|
packages = ["."]
|
||||||
|
revision = "2efee857e7cfd4f3d0138cc3cbb1b4966962b93a"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
name = "gopkg.in/alecthomas/kingpin.v2"
|
||||||
|
packages = ["."]
|
||||||
|
revision = "947dcec5ba9c011838740e680966fd7087a71d0d"
|
||||||
|
version = "v2.2.6"
|
||||||
|
|
||||||
|
[solve-meta]
|
||||||
|
analyzer-name = "dep"
|
||||||
|
analyzer-version = 1
|
||||||
|
inputs-digest = "fb2e356a0e1ee12f41b1c72b97ebfd1a4eaf20d8fbaadadd4fd91c8b82793779"
|
||||||
|
solver-name = "gps-cdcl"
|
||||||
|
solver-version = 1
|
||||||
8
Gopkg.toml
Normal file
8
Gopkg.toml
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
[[constraint]]
|
||||||
|
name = "gopkg.in/alecthomas/kingpin.v2"
|
||||||
|
version = "2.2.5"
|
||||||
|
|
||||||
|
[prune]
|
||||||
|
non-go = true
|
||||||
|
go-tests = true
|
||||||
|
unused-packages = true
|
||||||
13
LICENSE
Normal file
13
LICENSE
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
||||||
|
Version 2, December 2004
|
||||||
|
|
||||||
|
Copyright (C) 2018 Jim Myhrberg
|
||||||
|
|
||||||
|
Everyone is permitted to copy and distribute verbatim or modified
|
||||||
|
copies of this license document, and changing it is allowed as long
|
||||||
|
as the name is changed.
|
||||||
|
|
||||||
|
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
||||||
|
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||||
|
|
||||||
|
0. You just DO WHAT THE FUCK YOU WANT TO.
|
||||||
82
Makefile
82
Makefile
@@ -1,79 +1,31 @@
|
|||||||
DEV_DEPS = github.com/kardianos/govendor
|
|
||||||
|
|
||||||
NAME = casecmp
|
NAME = casecmp
|
||||||
BINARY = bin/${NAME}
|
BINARY = bin/${NAME}
|
||||||
SOURCES = $(shell find . -name '*.go' -o -name 'VERSION' -o -name 'README.md')
|
VERSION ?= $(shell cat VERSION)
|
||||||
VERSION = $(shell cat VERSION)
|
SOURCES = $(shell find . \
|
||||||
RELEASE_DIR = releases
|
-name '*.go' \
|
||||||
RELEASE_TARGETS = \
|
-o -name 'Makefile' \
|
||||||
$(RELEASE_DIR)/$(NAME)-$(VERSION)_darwin_386.tar.gz \
|
-o -name 'VERSION')
|
||||||
$(RELEASE_DIR)/$(NAME)-$(VERSION)_darwin_amd64.tar.gz \
|
|
||||||
$(RELEASE_DIR)/$(NAME)-$(VERSION)_freebsd_386.tar.gz \
|
|
||||||
$(RELEASE_DIR)/$(NAME)-$(VERSION)_freebsd_amd64.tar.gz \
|
|
||||||
$(RELEASE_DIR)/$(NAME)-$(VERSION)_freebsd_arm.tar.gz \
|
|
||||||
$(RELEASE_DIR)/$(NAME)-$(VERSION)_linux_386.tar.gz \
|
|
||||||
$(RELEASE_DIR)/$(NAME)-$(VERSION)_linux_amd64.tar.gz \
|
|
||||||
$(RELEASE_DIR)/$(NAME)-$(VERSION)_linux_arm.tar.gz \
|
|
||||||
$(RELEASE_DIR)/$(NAME)-$(VERSION)_windows_386.zip \
|
|
||||||
$(RELEASE_DIR)/$(NAME)-$(VERSION)_windows_amd64.zip
|
|
||||||
RELEASE_ASSETS = \
|
|
||||||
README.md
|
|
||||||
|
|
||||||
$(BINARY): $(SOURCES)
|
$(BINARY): $(SOURCES)
|
||||||
go build -o ${BINARY} -ldflags "-X main.Version=${VERSION}"
|
CGO_ENABLED=0 go build -a -o ${BINARY} -ldflags \ "\
|
||||||
|
-s -w \
|
||||||
|
-X main.version=${VERSION} \
|
||||||
|
-X main.commit=$(shell git show --format="%h" --no-patch) \
|
||||||
|
-X main.date=$(shell date +%Y-%m-%dT%T%z)"
|
||||||
|
|
||||||
.PHONY: build
|
.PHONY: build
|
||||||
build: $(BINARY)
|
build: $(BINARY)
|
||||||
|
|
||||||
.PHONY: clean
|
|
||||||
clean:
|
|
||||||
$(eval BIN_DIR := $(shell dirname ${BINARY}))
|
|
||||||
if [ -f ${BINARY} ]; then rm ${BINARY}; fi; \
|
|
||||||
if [ -d ${BIN_DIR} ]; then rmdir ${BIN_DIR}; fi
|
|
||||||
|
|
||||||
.PHONY: run
|
.PHONY: run
|
||||||
run: $(BINARY)
|
run: $(BINARY)
|
||||||
$(BINARY)
|
$(BINARY)
|
||||||
|
|
||||||
.PHONY: deps
|
.PHONY: clean
|
||||||
deps:
|
clean:
|
||||||
@govendor sync
|
$(eval BIN_DIR := $(shell dirname ${BINARY}))
|
||||||
|
if [ -f ${BINARY} ]; then rm ${BINARY}; fi
|
||||||
.PHONY: dev-deps
|
if [ -d ${BIN_DIR} ]; then rmdir ${BIN_DIR}; fi
|
||||||
dev-deps:
|
|
||||||
@$(foreach DEP,$(DEV_DEPS),go get $(DEP);)
|
|
||||||
|
|
||||||
.PHONY: update-dev-deps
|
|
||||||
update-dev-deps:
|
|
||||||
@$(foreach DEP,$(DEV_DEPS),go get -u $(DEP);)
|
|
||||||
|
|
||||||
.PHONY: release
|
|
||||||
release: $(RELEASE_TARGETS)
|
|
||||||
|
|
||||||
$(RELEASE_DIR)/$(NAME)-$(VERSION)_%.tar.gz: $(SOURCES)
|
|
||||||
$(eval OS := $(word 1, $(subst _, ,$*)))
|
|
||||||
$(eval ARCH := $(word 2, $(subst _, ,$*)))
|
|
||||||
$(eval TARGET := $(NAME)-$(VERSION)_$*)
|
|
||||||
mkdir -p "$(TARGET)" \
|
|
||||||
&& env GOOS=$(OS) GOARCH=$(ARCH) CGO_ENABLED=0 go build -a \
|
|
||||||
-o "$(TARGET)/$(NAME)" -ldflags "-X main.Version=$(VERSION)" \
|
|
||||||
&& cp $(RELEASE_ASSETS) "$(TARGET)/" \
|
|
||||||
&& tar -cvzf "$@" "$(TARGET)" \
|
|
||||||
&& cd "$(TARGET)" && rm "$(NAME)" $(RELEASE_ASSETS) && cd .. \
|
|
||||||
&& rmdir "$(TARGET)"
|
|
||||||
|
|
||||||
$(RELEASE_DIR)/$(NAME)-$(VERSION)_windows_%.zip: $(SOURCES)
|
|
||||||
$(eval TARGET := $(NAME)-$(VERSION)_windows_$*)
|
|
||||||
mkdir -p "$(TARGET)" \
|
|
||||||
&& env GOOS=windows GOARCH=$* CGO_ENABLED=0 go build -a \
|
|
||||||
-o "$(TARGET)/$(NAME).exe" -ldflags "-X main.Version=$(VERSION)" \
|
|
||||||
&& cp $(RELEASE_ASSETS) "$(TARGET)/" \
|
|
||||||
&& zip -r "$@" "$(TARGET)" \
|
|
||||||
&& cd "$(TARGET)" && rm "$(NAME).exe" $(RELEASE_ASSETS) && cd .. \
|
|
||||||
&& rmdir "$(TARGET)"
|
|
||||||
|
|
||||||
.PHONY: docker
|
.PHONY: docker
|
||||||
docker: clean deps
|
docker:
|
||||||
$(eval REPO := $(shell whoami)/$(NAME))
|
docker build -t "$(shell whoami)/$(NAME)" .
|
||||||
docker build -t "$(REPO):latest" . \
|
|
||||||
&& docker tag "$(REPO):latest" "$(REPO):$(VERSION)"
|
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ Case-insensitive string comparison, as an API. Because ¯\\_(ツ)\_/¯
|
|||||||
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
||||||
Version 2, December 2004
|
Version 2, December 2004
|
||||||
|
|
||||||
Copyright (C) 2017 Jim Myhrberg
|
Copyright (C) 2018 Jim Myhrberg
|
||||||
|
|
||||||
Everyone is permitted to copy and distribute verbatim or modified
|
Everyone is permitted to copy and distribute verbatim or modified
|
||||||
copies of this license document, and changing it is allowed as long
|
copies of this license document, and changing it is allowed as long
|
||||||
|
|||||||
83
main.go
83
main.go
@@ -1,8 +1,8 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
|
||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
@@ -11,47 +11,57 @@ import (
|
|||||||
"gopkg.in/alecthomas/kingpin.v2"
|
"gopkg.in/alecthomas/kingpin.v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Name of application.
|
var (
|
||||||
var Name = "casecmp"
|
name = "casecmp"
|
||||||
|
version = "dev"
|
||||||
// Version gets populated with version at build-time.
|
commit = "unknown"
|
||||||
var Version string
|
date = "unknown"
|
||||||
|
defaultPort = "8080"
|
||||||
// DefaultPort that service runs on.
|
)
|
||||||
var DefaultPort = "8080"
|
|
||||||
|
|
||||||
// Argument parsing setup.
|
// Argument parsing setup.
|
||||||
var (
|
var (
|
||||||
port = kingpin.Flag("port", "Port to listen to.").Short('p').
|
portFlag = kingpin.Flag("port", "Port to listen to.").Short('p').
|
||||||
Default("").String()
|
Default("").String()
|
||||||
bind = kingpin.Flag("bind", "Bind address.").Short('b').
|
bindFlag = kingpin.Flag("bind", "Bind address.").Short('b').
|
||||||
Default("0.0.0.0").String()
|
Default("0.0.0.0").String()
|
||||||
version = kingpin.Flag("version", "Print version info.").
|
versionFlag = kingpin.Flag("version", "Print version info.").
|
||||||
Short('v').Bool()
|
Short('v').Bool()
|
||||||
)
|
)
|
||||||
|
|
||||||
func indexHandler(w http.ResponseWriter, r *http.Request) {
|
func indexHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
resp := Name + " " + Version + "\n" +
|
scheme := "http"
|
||||||
"\n" +
|
if r.TLS != nil {
|
||||||
"Case-insensitive string comparison, as an API. Because ¯\\_(ツ)_/¯\n" +
|
scheme = "https"
|
||||||
"\n" +
|
}
|
||||||
"Example:\n" +
|
|
||||||
"curl -X POST -F \"a=Foo Bar\" -F \"b=FOO BAR\" " +
|
|
||||||
"http://" + r.Host + "/\n" +
|
|
||||||
"curl -X POST http://" + r.Host + "/?a=Foo%%20Bar&b=FOO%%20BAR"
|
|
||||||
|
|
||||||
io.WriteString(w, resp)
|
_, err := fmt.Fprintf(w, `%s %s
|
||||||
|
|
||||||
|
Case-insensitive string comparison, as an API. Because ¯\_(ツ)_/¯
|
||||||
|
|
||||||
|
Example usage:
|
||||||
|
curl -X POST -F "a=Foo Bar" -F "b=FOO BAR" %s://%s/
|
||||||
|
curl -X POST "%s://%s/?a=Foo+Bar&b=FOO+BAR"`,
|
||||||
|
name, version, scheme, r.Host, scheme, r.Host)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func casecmpHandler(w http.ResponseWriter, r *http.Request) {
|
func casecmpHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
a := r.FormValue("a")
|
a := r.FormValue("a")
|
||||||
b := r.FormValue("b")
|
b := r.FormValue("b")
|
||||||
|
|
||||||
resp := "0"
|
resp := "0"
|
||||||
|
|
||||||
if strings.EqualFold(string(a), string(b)) {
|
if strings.EqualFold(string(a), string(b)) {
|
||||||
resp = "1"
|
resp = "1"
|
||||||
}
|
}
|
||||||
fmt.Fprintf(w, resp)
|
_, err := fmt.Fprintf(w, resp)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func rootHandler(w http.ResponseWriter, r *http.Request) {
|
func rootHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
@@ -68,30 +78,37 @@ func rootHandler(w http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func printVersion() {
|
func printVersion() {
|
||||||
fmt.Println(Name + " " + Version)
|
var buffer bytes.Buffer
|
||||||
|
buffer.WriteString(fmt.Sprintf("%s %s", name, version))
|
||||||
|
|
||||||
|
if commit != "unknown" {
|
||||||
|
buffer.WriteString(fmt.Sprintf(" (%s)", commit))
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Println(buffer.String())
|
||||||
}
|
}
|
||||||
|
|
||||||
func startServer() {
|
func startServer() {
|
||||||
http.HandleFunc("/", rootHandler)
|
http.HandleFunc("/", rootHandler)
|
||||||
|
|
||||||
if *port == "" {
|
if *portFlag == "" {
|
||||||
envPort := os.Getenv("PORT")
|
envPort := os.Getenv("PORT")
|
||||||
if envPort != "" {
|
if envPort != "" {
|
||||||
*port = envPort
|
*portFlag = envPort
|
||||||
} else {
|
} else {
|
||||||
*port = DefaultPort
|
*portFlag = defaultPort
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
address := *bind + ":" + *port
|
address := *bindFlag + ":" + *portFlag
|
||||||
fmt.Println("Listening on " + address)
|
fmt.Printf("Listening on %s\n", address)
|
||||||
log.Fatal(http.ListenAndServe(address, nil))
|
log.Fatal(http.ListenAndServe(address, nil))
|
||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
kingpin.Parse()
|
kingpin.Parse()
|
||||||
|
|
||||||
if *version {
|
if *versionFlag {
|
||||||
printVersion()
|
printVersion()
|
||||||
} else {
|
} else {
|
||||||
startServer()
|
startServer()
|
||||||
|
|||||||
25
vendor/github.com/alecthomas/template/README.md
generated
vendored
25
vendor/github.com/alecthomas/template/README.md
generated
vendored
@@ -1,25 +0,0 @@
|
|||||||
# Go's `text/template` package with newline elision
|
|
||||||
|
|
||||||
This is a fork of Go 1.4's [text/template](http://golang.org/pkg/text/template/) package with one addition: a backslash immediately after a closing delimiter will delete all subsequent newlines until a non-newline.
|
|
||||||
|
|
||||||
eg.
|
|
||||||
|
|
||||||
```
|
|
||||||
{{if true}}\
|
|
||||||
hello
|
|
||||||
{{end}}\
|
|
||||||
```
|
|
||||||
|
|
||||||
Will result in:
|
|
||||||
|
|
||||||
```
|
|
||||||
hello\n
|
|
||||||
```
|
|
||||||
|
|
||||||
Rather than:
|
|
||||||
|
|
||||||
```
|
|
||||||
\n
|
|
||||||
hello\n
|
|
||||||
\n
|
|
||||||
```
|
|
||||||
11
vendor/github.com/alecthomas/units/README.md
generated
vendored
11
vendor/github.com/alecthomas/units/README.md
generated
vendored
@@ -1,11 +0,0 @@
|
|||||||
# Units - Helpful unit multipliers and functions for Go
|
|
||||||
|
|
||||||
The goal of this package is to have functionality similar to the [time](http://golang.org/pkg/time/) package.
|
|
||||||
|
|
||||||
It allows for code like this:
|
|
||||||
|
|
||||||
```go
|
|
||||||
n, err := ParseBase2Bytes("1KB")
|
|
||||||
// n == 1024
|
|
||||||
n = units.Mebibyte * 512
|
|
||||||
```
|
|
||||||
674
vendor/gopkg.in/alecthomas/kingpin.v2/README.md
generated
vendored
674
vendor/gopkg.in/alecthomas/kingpin.v2/README.md
generated
vendored
@@ -1,674 +0,0 @@
|
|||||||
# Kingpin - A Go (golang) command line and flag parser
|
|
||||||
[](http://godoc.org/github.com/alecthomas/kingpin) [](https://travis-ci.org/alecthomas/kingpin) [](https://gitter.im/alecthomas/Lobby)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<!-- MarkdownTOC -->
|
|
||||||
|
|
||||||
- [Overview](#overview)
|
|
||||||
- [Features](#features)
|
|
||||||
- [User-visible changes between v1 and v2](#user-visible-changes-between-v1-and-v2)
|
|
||||||
- [Flags can be used at any point after their definition.](#flags-can-be-used-at-any-point-after-their-definition)
|
|
||||||
- [Short flags can be combined with their parameters](#short-flags-can-be-combined-with-their-parameters)
|
|
||||||
- [API changes between v1 and v2](#api-changes-between-v1-and-v2)
|
|
||||||
- [Versions](#versions)
|
|
||||||
- [V2 is the current stable version](#v2-is-the-current-stable-version)
|
|
||||||
- [V1 is the OLD stable version](#v1-is-the-old-stable-version)
|
|
||||||
- [Change History](#change-history)
|
|
||||||
- [Examples](#examples)
|
|
||||||
- [Simple Example](#simple-example)
|
|
||||||
- [Complex Example](#complex-example)
|
|
||||||
- [Reference Documentation](#reference-documentation)
|
|
||||||
- [Displaying errors and usage information](#displaying-errors-and-usage-information)
|
|
||||||
- [Sub-commands](#sub-commands)
|
|
||||||
- [Custom Parsers](#custom-parsers)
|
|
||||||
- [Repeatable flags](#repeatable-flags)
|
|
||||||
- [Boolean Values](#boolean-values)
|
|
||||||
- [Default Values](#default-values)
|
|
||||||
- [Place-holders in Help](#place-holders-in-help)
|
|
||||||
- [Consuming all remaining arguments](#consuming-all-remaining-arguments)
|
|
||||||
- [Bash/ZSH Shell Completion](#bashzsh-shell-completion)
|
|
||||||
- [Supporting -h for help](#supporting--h-for-help)
|
|
||||||
- [Custom help](#custom-help)
|
|
||||||
|
|
||||||
<!-- /MarkdownTOC -->
|
|
||||||
|
|
||||||
## Overview
|
|
||||||
|
|
||||||
Kingpin is a [fluent-style](http://en.wikipedia.org/wiki/Fluent_interface),
|
|
||||||
type-safe command-line parser. It supports flags, nested commands, and
|
|
||||||
positional arguments.
|
|
||||||
|
|
||||||
Install it with:
|
|
||||||
|
|
||||||
$ go get gopkg.in/alecthomas/kingpin.v2
|
|
||||||
|
|
||||||
It looks like this:
|
|
||||||
|
|
||||||
```go
|
|
||||||
var (
|
|
||||||
verbose = kingpin.Flag("verbose", "Verbose mode.").Short('v').Bool()
|
|
||||||
name = kingpin.Arg("name", "Name of user.").Required().String()
|
|
||||||
)
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
kingpin.Parse()
|
|
||||||
fmt.Printf("%v, %s\n", *verbose, *name)
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
More [examples](https://github.com/alecthomas/kingpin/tree/master/_examples) are available.
|
|
||||||
|
|
||||||
Second to parsing, providing the user with useful help is probably the most
|
|
||||||
important thing a command-line parser does. Kingpin tries to provide detailed
|
|
||||||
contextual help if `--help` is encountered at any point in the command line
|
|
||||||
(excluding after `--`).
|
|
||||||
|
|
||||||
## Features
|
|
||||||
|
|
||||||
- Help output that isn't as ugly as sin.
|
|
||||||
- Fully [customisable help](#custom-help), via Go templates.
|
|
||||||
- Parsed, type-safe flags (`kingpin.Flag("f", "help").Int()`)
|
|
||||||
- Parsed, type-safe positional arguments (`kingpin.Arg("a", "help").Int()`).
|
|
||||||
- Parsed, type-safe, arbitrarily deep commands (`kingpin.Command("c", "help")`).
|
|
||||||
- Support for required flags and required positional arguments (`kingpin.Flag("f", "").Required().Int()`).
|
|
||||||
- Support for arbitrarily nested default commands (`command.Default()`).
|
|
||||||
- Callbacks per command, flag and argument (`kingpin.Command("c", "").Action(myAction)`).
|
|
||||||
- POSIX-style short flag combining (`-a -b` -> `-ab`).
|
|
||||||
- Short-flag+parameter combining (`-a parm` -> `-aparm`).
|
|
||||||
- Read command-line from files (`@<file>`).
|
|
||||||
- Automatically generate man pages (`--help-man`).
|
|
||||||
|
|
||||||
## User-visible changes between v1 and v2
|
|
||||||
|
|
||||||
### Flags can be used at any point after their definition.
|
|
||||||
|
|
||||||
Flags can be specified at any point after their definition, not just
|
|
||||||
*immediately after their associated command*. From the chat example below, the
|
|
||||||
following used to be required:
|
|
||||||
|
|
||||||
```
|
|
||||||
$ chat --server=chat.server.com:8080 post --image=~/Downloads/owls.jpg pics
|
|
||||||
```
|
|
||||||
|
|
||||||
But the following will now work:
|
|
||||||
|
|
||||||
```
|
|
||||||
$ chat post --server=chat.server.com:8080 --image=~/Downloads/owls.jpg pics
|
|
||||||
```
|
|
||||||
|
|
||||||
### Short flags can be combined with their parameters
|
|
||||||
|
|
||||||
Previously, if a short flag was used, any argument to that flag would have to
|
|
||||||
be separated by a space. That is no longer the case.
|
|
||||||
|
|
||||||
## API changes between v1 and v2
|
|
||||||
|
|
||||||
- `ParseWithFileExpansion()` is gone. The new parser directly supports expanding `@<file>`.
|
|
||||||
- Added `FatalUsage()` and `FatalUsageContext()` for displaying an error + usage and terminating.
|
|
||||||
- `Dispatch()` renamed to `Action()`.
|
|
||||||
- Added `ParseContext()` for parsing a command line into its intermediate context form without executing.
|
|
||||||
- Added `Terminate()` function to override the termination function.
|
|
||||||
- Added `UsageForContextWithTemplate()` for printing usage via a custom template.
|
|
||||||
- Added `UsageTemplate()` for overriding the default template to use. Two templates are included:
|
|
||||||
1. `DefaultUsageTemplate` - default template.
|
|
||||||
2. `CompactUsageTemplate` - compact command template for larger applications.
|
|
||||||
|
|
||||||
## Versions
|
|
||||||
|
|
||||||
Kingpin uses [gopkg.in](https://gopkg.in/alecthomas/kingpin) for versioning.
|
|
||||||
|
|
||||||
The current stable version is [gopkg.in/alecthomas/kingpin.v2](https://gopkg.in/alecthomas/kingpin.v2). The previous version, [gopkg.in/alecthomas/kingpin.v1](https://gopkg.in/alecthomas/kingpin.v1), is deprecated and in maintenance mode.
|
|
||||||
|
|
||||||
### [V2](https://gopkg.in/alecthomas/kingpin.v2) is the current stable version
|
|
||||||
|
|
||||||
Installation:
|
|
||||||
|
|
||||||
```sh
|
|
||||||
$ go get gopkg.in/alecthomas/kingpin.v2
|
|
||||||
```
|
|
||||||
|
|
||||||
### [V1](https://gopkg.in/alecthomas/kingpin.v1) is the OLD stable version
|
|
||||||
|
|
||||||
Installation:
|
|
||||||
|
|
||||||
```sh
|
|
||||||
$ go get gopkg.in/alecthomas/kingpin.v1
|
|
||||||
```
|
|
||||||
|
|
||||||
## Change History
|
|
||||||
|
|
||||||
- *2015-09-19* -- Stable v2.1.0 release.
|
|
||||||
- Added `command.Default()` to specify a default command to use if no other
|
|
||||||
command matches. This allows for convenient user shortcuts.
|
|
||||||
- Exposed `HelpFlag` and `VersionFlag` for further customisation.
|
|
||||||
- `Action()` and `PreAction()` added and both now support an arbitrary
|
|
||||||
number of callbacks.
|
|
||||||
- `kingpin.SeparateOptionalFlagsUsageTemplate`.
|
|
||||||
- `--help-long` and `--help-man` (hidden by default) flags.
|
|
||||||
- Flags are "interspersed" by default, but can be disabled with `app.Interspersed(false)`.
|
|
||||||
- Added flags for all simple builtin types (int8, uint16, etc.) and slice variants.
|
|
||||||
- Use `app.Writer(os.Writer)` to specify the default writer for all output functions.
|
|
||||||
- Dropped `os.Writer` prefix from all printf-like functions.
|
|
||||||
|
|
||||||
- *2015-05-22* -- Stable v2.0.0 release.
|
|
||||||
- Initial stable release of v2.0.0.
|
|
||||||
- Fully supports interspersed flags, commands and arguments.
|
|
||||||
- Flags can be present at any point after their logical definition.
|
|
||||||
- Application.Parse() terminates if commands are present and a command is not parsed.
|
|
||||||
- Dispatch() -> Action().
|
|
||||||
- Actions are dispatched after all values are populated.
|
|
||||||
- Override termination function (defaults to os.Exit).
|
|
||||||
- Override output stream (defaults to os.Stderr).
|
|
||||||
- Templatised usage help, with default and compact templates.
|
|
||||||
- Make error/usage functions more consistent.
|
|
||||||
- Support argument expansion from files by default (with @<file>).
|
|
||||||
- Fully public data model is available via .Model().
|
|
||||||
- Parser has been completely refactored.
|
|
||||||
- Parsing and execution has been split into distinct stages.
|
|
||||||
- Use `go generate` to generate repeated flags.
|
|
||||||
- Support combined short-flag+argument: -fARG.
|
|
||||||
|
|
||||||
- *2015-01-23* -- Stable v1.3.4 release.
|
|
||||||
- Support "--" for separating flags from positional arguments.
|
|
||||||
- Support loading flags from files (ParseWithFileExpansion()). Use @FILE as an argument.
|
|
||||||
- Add post-app and post-cmd validation hooks. This allows arbitrary validation to be added.
|
|
||||||
- A bunch of improvements to help usage and formatting.
|
|
||||||
- Support arbitrarily nested sub-commands.
|
|
||||||
|
|
||||||
- *2014-07-08* -- Stable v1.2.0 release.
|
|
||||||
- Pass any value through to `Strings()` when final argument.
|
|
||||||
Allows for values that look like flags to be processed.
|
|
||||||
- Allow `--help` to be used with commands.
|
|
||||||
- Support `Hidden()` flags.
|
|
||||||
- Parser for [units.Base2Bytes](https://github.com/alecthomas/units)
|
|
||||||
type. Allows for flags like `--ram=512MB` or `--ram=1GB`.
|
|
||||||
- Add an `Enum()` value, allowing only one of a set of values
|
|
||||||
to be selected. eg. `Flag(...).Enum("debug", "info", "warning")`.
|
|
||||||
|
|
||||||
- *2014-06-27* -- Stable v1.1.0 release.
|
|
||||||
- Bug fixes.
|
|
||||||
- Always return an error (rather than panicing) when misconfigured.
|
|
||||||
- `OpenFile(flag, perm)` value type added, for finer control over opening files.
|
|
||||||
- Significantly improved usage formatting.
|
|
||||||
|
|
||||||
- *2014-06-19* -- Stable v1.0.0 release.
|
|
||||||
- Support [cumulative positional](#consuming-all-remaining-arguments) arguments.
|
|
||||||
- Return error rather than panic when there are fatal errors not caught by
|
|
||||||
the type system. eg. when a default value is invalid.
|
|
||||||
- Use gokpg.in.
|
|
||||||
|
|
||||||
- *2014-06-10* -- Place-holder streamlining.
|
|
||||||
- Renamed `MetaVar` to `PlaceHolder`.
|
|
||||||
- Removed `MetaVarFromDefault`. Kingpin now uses [heuristics](#place-holders-in-help)
|
|
||||||
to determine what to display.
|
|
||||||
|
|
||||||
## Examples
|
|
||||||
|
|
||||||
### Simple Example
|
|
||||||
|
|
||||||
Kingpin can be used for simple flag+arg applications like so:
|
|
||||||
|
|
||||||
```
|
|
||||||
$ ping --help
|
|
||||||
usage: ping [<flags>] <ip> [<count>]
|
|
||||||
|
|
||||||
Flags:
|
|
||||||
--debug Enable debug mode.
|
|
||||||
--help Show help.
|
|
||||||
-t, --timeout=5s Timeout waiting for ping.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
<ip> IP address to ping.
|
|
||||||
[<count>] Number of packets to send
|
|
||||||
$ ping 1.2.3.4 5
|
|
||||||
Would ping: 1.2.3.4 with timeout 5s and count 0
|
|
||||||
```
|
|
||||||
|
|
||||||
From the following source:
|
|
||||||
|
|
||||||
```go
|
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"gopkg.in/alecthomas/kingpin.v2"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
debug = kingpin.Flag("debug", "Enable debug mode.").Bool()
|
|
||||||
timeout = kingpin.Flag("timeout", "Timeout waiting for ping.").Default("5s").OverrideDefaultFromEnvar("PING_TIMEOUT").Short('t').Duration()
|
|
||||||
ip = kingpin.Arg("ip", "IP address to ping.").Required().IP()
|
|
||||||
count = kingpin.Arg("count", "Number of packets to send").Int()
|
|
||||||
)
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
kingpin.Version("0.0.1")
|
|
||||||
kingpin.Parse()
|
|
||||||
fmt.Printf("Would ping: %s with timeout %s and count %d\n", *ip, *timeout, *count)
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Complex Example
|
|
||||||
|
|
||||||
Kingpin can also produce complex command-line applications with global flags,
|
|
||||||
subcommands, and per-subcommand flags, like this:
|
|
||||||
|
|
||||||
```
|
|
||||||
$ chat --help
|
|
||||||
usage: chat [<flags>] <command> [<flags>] [<args> ...]
|
|
||||||
|
|
||||||
A command-line chat application.
|
|
||||||
|
|
||||||
Flags:
|
|
||||||
--help Show help.
|
|
||||||
--debug Enable debug mode.
|
|
||||||
--server=127.0.0.1 Server address.
|
|
||||||
|
|
||||||
Commands:
|
|
||||||
help [<command>]
|
|
||||||
Show help for a command.
|
|
||||||
|
|
||||||
register <nick> <name>
|
|
||||||
Register a new user.
|
|
||||||
|
|
||||||
post [<flags>] <channel> [<text>]
|
|
||||||
Post a message to a channel.
|
|
||||||
|
|
||||||
$ chat help post
|
|
||||||
usage: chat [<flags>] post [<flags>] <channel> [<text>]
|
|
||||||
|
|
||||||
Post a message to a channel.
|
|
||||||
|
|
||||||
Flags:
|
|
||||||
--image=IMAGE Image to post.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
<channel> Channel to post to.
|
|
||||||
[<text>] Text to post.
|
|
||||||
|
|
||||||
$ chat post --image=~/Downloads/owls.jpg pics
|
|
||||||
...
|
|
||||||
```
|
|
||||||
|
|
||||||
From this code:
|
|
||||||
|
|
||||||
```go
|
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"os"
|
|
||||||
"strings"
|
|
||||||
"gopkg.in/alecthomas/kingpin.v2"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
app = kingpin.New("chat", "A command-line chat application.")
|
|
||||||
debug = app.Flag("debug", "Enable debug mode.").Bool()
|
|
||||||
serverIP = app.Flag("server", "Server address.").Default("127.0.0.1").IP()
|
|
||||||
|
|
||||||
register = app.Command("register", "Register a new user.")
|
|
||||||
registerNick = register.Arg("nick", "Nickname for user.").Required().String()
|
|
||||||
registerName = register.Arg("name", "Name of user.").Required().String()
|
|
||||||
|
|
||||||
post = app.Command("post", "Post a message to a channel.")
|
|
||||||
postImage = post.Flag("image", "Image to post.").File()
|
|
||||||
postChannel = post.Arg("channel", "Channel to post to.").Required().String()
|
|
||||||
postText = post.Arg("text", "Text to post.").Strings()
|
|
||||||
)
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
switch kingpin.MustParse(app.Parse(os.Args[1:])) {
|
|
||||||
// Register user
|
|
||||||
case register.FullCommand():
|
|
||||||
println(*registerNick)
|
|
||||||
|
|
||||||
// Post message
|
|
||||||
case post.FullCommand():
|
|
||||||
if *postImage != nil {
|
|
||||||
}
|
|
||||||
text := strings.Join(*postText, " ")
|
|
||||||
println("Post:", text)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Reference Documentation
|
|
||||||
|
|
||||||
### Displaying errors and usage information
|
|
||||||
|
|
||||||
Kingpin exports a set of functions to provide consistent errors and usage
|
|
||||||
information to the user.
|
|
||||||
|
|
||||||
Error messages look something like this:
|
|
||||||
|
|
||||||
<app>: error: <message>
|
|
||||||
|
|
||||||
The functions on `Application` are:
|
|
||||||
|
|
||||||
Function | Purpose
|
|
||||||
---------|--------------
|
|
||||||
`Errorf(format, args)` | Display a printf formatted error to the user.
|
|
||||||
`Fatalf(format, args)` | As with Errorf, but also call the termination handler.
|
|
||||||
`FatalUsage(format, args)` | As with Fatalf, but also print contextual usage information.
|
|
||||||
`FatalUsageContext(context, format, args)` | As with Fatalf, but also print contextual usage information from a `ParseContext`.
|
|
||||||
`FatalIfError(err, format, args)` | Conditionally print an error prefixed with format+args, then call the termination handler
|
|
||||||
|
|
||||||
There are equivalent global functions in the kingpin namespace for the default
|
|
||||||
`kingpin.CommandLine` instance.
|
|
||||||
|
|
||||||
### Sub-commands
|
|
||||||
|
|
||||||
Kingpin supports nested sub-commands, with separate flag and positional
|
|
||||||
arguments per sub-command. Note that positional arguments may only occur after
|
|
||||||
sub-commands.
|
|
||||||
|
|
||||||
For example:
|
|
||||||
|
|
||||||
```go
|
|
||||||
var (
|
|
||||||
deleteCommand = kingpin.Command("delete", "Delete an object.")
|
|
||||||
deleteUserCommand = deleteCommand.Command("user", "Delete a user.")
|
|
||||||
deleteUserUIDFlag = deleteUserCommand.Flag("uid", "Delete user by UID rather than username.")
|
|
||||||
deleteUserUsername = deleteUserCommand.Arg("username", "Username to delete.")
|
|
||||||
deletePostCommand = deleteCommand.Command("post", "Delete a post.")
|
|
||||||
)
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
switch kingpin.Parse() {
|
|
||||||
case "delete user":
|
|
||||||
case "delete post":
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Custom Parsers
|
|
||||||
|
|
||||||
Kingpin supports both flag and positional argument parsers for converting to
|
|
||||||
Go types. For example, some included parsers are `Int()`, `Float()`,
|
|
||||||
`Duration()` and `ExistingFile()` (see [parsers.go](./parsers.go) for a complete list of included parsers).
|
|
||||||
|
|
||||||
Parsers conform to Go's [`flag.Value`](http://godoc.org/flag#Value)
|
|
||||||
interface, so any existing implementations will work.
|
|
||||||
|
|
||||||
For example, a parser for accumulating HTTP header values might look like this:
|
|
||||||
|
|
||||||
```go
|
|
||||||
type HTTPHeaderValue http.Header
|
|
||||||
|
|
||||||
func (h *HTTPHeaderValue) Set(value string) error {
|
|
||||||
parts := strings.SplitN(value, ":", 2)
|
|
||||||
if len(parts) != 2 {
|
|
||||||
return fmt.Errorf("expected HEADER:VALUE got '%s'", value)
|
|
||||||
}
|
|
||||||
(*http.Header)(h).Add(parts[0], parts[1])
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h *HTTPHeaderValue) String() string {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
As a convenience, I would recommend something like this:
|
|
||||||
|
|
||||||
```go
|
|
||||||
func HTTPHeader(s Settings) (target *http.Header) {
|
|
||||||
target = &http.Header{}
|
|
||||||
s.SetValue((*HTTPHeaderValue)(target))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
You would use it like so:
|
|
||||||
|
|
||||||
```go
|
|
||||||
headers = HTTPHeader(kingpin.Flag("header", "Add a HTTP header to the request.").Short('H'))
|
|
||||||
```
|
|
||||||
|
|
||||||
### Repeatable flags
|
|
||||||
|
|
||||||
Depending on the `Value` they hold, some flags may be repeated. The
|
|
||||||
`IsCumulative() bool` function on `Value` tells if it's safe to call `Set()`
|
|
||||||
multiple times or if an error should be raised if several values are passed.
|
|
||||||
|
|
||||||
The built-in `Value`s returning slices and maps, as well as `Counter` are
|
|
||||||
examples of `Value`s that make a flag repeatable.
|
|
||||||
|
|
||||||
### Boolean values
|
|
||||||
|
|
||||||
Boolean values are uniquely managed by Kingpin. Each boolean flag will have a negative complement:
|
|
||||||
`--<name>` and `--no-<name>`.
|
|
||||||
|
|
||||||
### Default Values
|
|
||||||
|
|
||||||
The default value is the zero value for a type. This can be overridden with
|
|
||||||
the `Default(value...)` function on flags and arguments. This function accepts
|
|
||||||
one or several strings, which are parsed by the value itself, so they *must*
|
|
||||||
be compliant with the format expected.
|
|
||||||
|
|
||||||
### Place-holders in Help
|
|
||||||
|
|
||||||
The place-holder value for a flag is the value used in the help to describe
|
|
||||||
the value of a non-boolean flag.
|
|
||||||
|
|
||||||
The value provided to PlaceHolder() is used if provided, then the value
|
|
||||||
provided by Default() if provided, then finally the capitalised flag name is
|
|
||||||
used.
|
|
||||||
|
|
||||||
Here are some examples of flags with various permutations:
|
|
||||||
|
|
||||||
--name=NAME // Flag(...).String()
|
|
||||||
--name="Harry" // Flag(...).Default("Harry").String()
|
|
||||||
--name=FULL-NAME // flag(...).PlaceHolder("FULL-NAME").Default("Harry").String()
|
|
||||||
|
|
||||||
### Consuming all remaining arguments
|
|
||||||
|
|
||||||
A common command-line idiom is to use all remaining arguments for some
|
|
||||||
purpose. eg. The following command accepts an arbitrary number of
|
|
||||||
IP addresses as positional arguments:
|
|
||||||
|
|
||||||
./cmd ping 10.1.1.1 192.168.1.1
|
|
||||||
|
|
||||||
Such arguments are similar to [repeatable flags](#repeatable-flags), but for
|
|
||||||
arguments. Therefore they use the same `IsCumulative() bool` function on the
|
|
||||||
underlying `Value`, so the built-in `Value`s for which the `Set()` function
|
|
||||||
can be called several times will consume multiple arguments.
|
|
||||||
|
|
||||||
To implement the above example with a custom `Value`, we might do something
|
|
||||||
like this:
|
|
||||||
|
|
||||||
```go
|
|
||||||
type ipList []net.IP
|
|
||||||
|
|
||||||
func (i *ipList) Set(value string) error {
|
|
||||||
if ip := net.ParseIP(value); ip == nil {
|
|
||||||
return fmt.Errorf("'%s' is not an IP address", value)
|
|
||||||
} else {
|
|
||||||
*i = append(*i, ip)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (i *ipList) String() string {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
func (i *ipList) IsCumulative() bool {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
func IPList(s Settings) (target *[]net.IP) {
|
|
||||||
target = new([]net.IP)
|
|
||||||
s.SetValue((*ipList)(target))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
And use it like so:
|
|
||||||
|
|
||||||
```go
|
|
||||||
ips := IPList(kingpin.Arg("ips", "IP addresses to ping."))
|
|
||||||
```
|
|
||||||
|
|
||||||
### Bash/ZSH Shell Completion
|
|
||||||
|
|
||||||
By default, all flags and commands/subcommands generate completions
|
|
||||||
internally.
|
|
||||||
|
|
||||||
Out of the box, CLI tools using kingpin should be able to take advantage
|
|
||||||
of completion hinting for flags and commands. By specifying
|
|
||||||
`--completion-bash` as the first argument, your CLI tool will show
|
|
||||||
possible subcommands. By ending your argv with `--`, hints for flags
|
|
||||||
will be shown.
|
|
||||||
|
|
||||||
To allow your end users to take advantage you must package a
|
|
||||||
`/etc/bash_completion.d` script with your distribution (or the equivalent
|
|
||||||
for your target platform/shell). An alternative is to instruct your end
|
|
||||||
user to source a script from their `bash_profile` (or equivalent).
|
|
||||||
|
|
||||||
Fortunately Kingpin makes it easy to generate or source a script for use
|
|
||||||
with end users shells. `./yourtool --completion-script-bash` and
|
|
||||||
`./yourtool --completion-script-zsh` will generate these scripts for you.
|
|
||||||
|
|
||||||
**Installation by Package**
|
|
||||||
|
|
||||||
For the best user experience, you should bundle your pre-created
|
|
||||||
completion script with your CLI tool and install it inside
|
|
||||||
`/etc/bash_completion.d` (or equivalent). A good suggestion is to add
|
|
||||||
this as an automated step to your build pipeline, in the implementation
|
|
||||||
is improved for bug fixed.
|
|
||||||
|
|
||||||
**Installation by `bash_profile`**
|
|
||||||
|
|
||||||
Alternatively, instruct your users to add an additional statement to
|
|
||||||
their `bash_profile` (or equivalent):
|
|
||||||
|
|
||||||
```
|
|
||||||
eval "$(your-cli-tool --completion-script-bash)"
|
|
||||||
```
|
|
||||||
|
|
||||||
Or for ZSH
|
|
||||||
|
|
||||||
```
|
|
||||||
eval "$(your-cli-tool --completion-script-zsh)"
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Additional API
|
|
||||||
To provide more flexibility, a completion option API has been
|
|
||||||
exposed for flags to allow user defined completion options, to extend
|
|
||||||
completions further than just EnumVar/Enum.
|
|
||||||
|
|
||||||
|
|
||||||
**Provide Static Options**
|
|
||||||
|
|
||||||
When using an `Enum` or `EnumVar`, users are limited to only the options
|
|
||||||
given. Maybe we wish to hint possible options to the user, but also
|
|
||||||
allow them to provide their own custom option. `HintOptions` gives
|
|
||||||
this functionality to flags.
|
|
||||||
|
|
||||||
```
|
|
||||||
app := kingpin.New("completion", "My application with bash completion.")
|
|
||||||
app.Flag("port", "Provide a port to connect to").
|
|
||||||
Required().
|
|
||||||
HintOptions("80", "443", "8080").
|
|
||||||
IntVar(&c.port)
|
|
||||||
```
|
|
||||||
|
|
||||||
**Provide Dynamic Options**
|
|
||||||
Consider the case that you needed to read a local database or a file to
|
|
||||||
provide suggestions. You can dynamically generate the options
|
|
||||||
|
|
||||||
```
|
|
||||||
func listHosts() []string {
|
|
||||||
// Provide a dynamic list of hosts from a hosts file or otherwise
|
|
||||||
// for bash completion. In this example we simply return static slice.
|
|
||||||
|
|
||||||
// You could use this functionality to reach into a hosts file to provide
|
|
||||||
// completion for a list of known hosts.
|
|
||||||
return []string{"sshhost.example", "webhost.example", "ftphost.example"}
|
|
||||||
}
|
|
||||||
|
|
||||||
app := kingpin.New("completion", "My application with bash completion.")
|
|
||||||
app.Flag("flag-1", "").HintAction(listHosts).String()
|
|
||||||
```
|
|
||||||
|
|
||||||
**EnumVar/Enum**
|
|
||||||
When using `Enum` or `EnumVar`, any provided options will be automatically
|
|
||||||
used for bash autocompletion. However, if you wish to provide a subset or
|
|
||||||
different options, you can use `HintOptions` or `HintAction` which will override
|
|
||||||
the default completion options for `Enum`/`EnumVar`.
|
|
||||||
|
|
||||||
|
|
||||||
**Examples**
|
|
||||||
You can see an in depth example of the completion API within
|
|
||||||
`examples/completion/main.go`
|
|
||||||
|
|
||||||
|
|
||||||
### Supporting -h for help
|
|
||||||
|
|
||||||
`kingpin.CommandLine.HelpFlag.Short('h')`
|
|
||||||
|
|
||||||
### Custom help
|
|
||||||
|
|
||||||
Kingpin v2 supports templatised help using the text/template library (actually, [a fork](https://github.com/alecthomas/template)).
|
|
||||||
|
|
||||||
You can specify the template to use with the [Application.UsageTemplate()](http://godoc.org/gopkg.in/alecthomas/kingpin.v2#Application.UsageTemplate) function.
|
|
||||||
|
|
||||||
There are four included templates: `kingpin.DefaultUsageTemplate` is the default,
|
|
||||||
`kingpin.CompactUsageTemplate` provides a more compact representation for more complex command-line structures,
|
|
||||||
`kingpin.SeparateOptionalFlagsUsageTemplate` looks like the default template, but splits required
|
|
||||||
and optional command flags into separate lists, and `kingpin.ManPageTemplate` is used to generate man pages.
|
|
||||||
|
|
||||||
See the above templates for examples of usage, and the the function [UsageForContextWithTemplate()](https://github.com/alecthomas/kingpin/blob/master/usage.go#L198) method for details on the context.
|
|
||||||
|
|
||||||
#### Default help template
|
|
||||||
|
|
||||||
```
|
|
||||||
$ go run ./examples/curl/curl.go --help
|
|
||||||
usage: curl [<flags>] <command> [<args> ...]
|
|
||||||
|
|
||||||
An example implementation of curl.
|
|
||||||
|
|
||||||
Flags:
|
|
||||||
--help Show help.
|
|
||||||
-t, --timeout=5s Set connection timeout.
|
|
||||||
-H, --headers=HEADER=VALUE
|
|
||||||
Add HTTP headers to the request.
|
|
||||||
|
|
||||||
Commands:
|
|
||||||
help [<command>...]
|
|
||||||
Show help.
|
|
||||||
|
|
||||||
get url <url>
|
|
||||||
Retrieve a URL.
|
|
||||||
|
|
||||||
get file <file>
|
|
||||||
Retrieve a file.
|
|
||||||
|
|
||||||
post [<flags>] <url>
|
|
||||||
POST a resource.
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Compact help template
|
|
||||||
|
|
||||||
```
|
|
||||||
$ go run ./examples/curl/curl.go --help
|
|
||||||
usage: curl [<flags>] <command> [<args> ...]
|
|
||||||
|
|
||||||
An example implementation of curl.
|
|
||||||
|
|
||||||
Flags:
|
|
||||||
--help Show help.
|
|
||||||
-t, --timeout=5s Set connection timeout.
|
|
||||||
-H, --headers=HEADER=VALUE
|
|
||||||
Add HTTP headers to the request.
|
|
||||||
|
|
||||||
Commands:
|
|
||||||
help [<command>...]
|
|
||||||
get [<flags>]
|
|
||||||
url <url>
|
|
||||||
file <file>
|
|
||||||
post [<flags>] <url>
|
|
||||||
```
|
|
||||||
3
vendor/gopkg.in/alecthomas/kingpin.v2/app.go
generated
vendored
3
vendor/gopkg.in/alecthomas/kingpin.v2/app.go
generated
vendored
@@ -402,6 +402,9 @@ func (a *Application) setDefaults(context *ParseContext) error {
|
|||||||
flagElements := map[string]*ParseElement{}
|
flagElements := map[string]*ParseElement{}
|
||||||
for _, element := range context.Elements {
|
for _, element := range context.Elements {
|
||||||
if flag, ok := element.Clause.(*FlagClause); ok {
|
if flag, ok := element.Clause.(*FlagClause); ok {
|
||||||
|
if flag.name == "help" {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
flagElements[flag.name] = element
|
flagElements[flag.name] = element
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
18
vendor/gopkg.in/alecthomas/kingpin.v2/parser.go
generated
vendored
18
vendor/gopkg.in/alecthomas/kingpin.v2/parser.go
generated
vendored
@@ -153,6 +153,10 @@ func (p *ParseContext) EOL() bool {
|
|||||||
return p.Peek().Type == TokenEOL
|
return p.Peek().Type == TokenEOL
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (p *ParseContext) Error() bool {
|
||||||
|
return p.Peek().Type == TokenError
|
||||||
|
}
|
||||||
|
|
||||||
// Next token in the parse context.
|
// Next token in the parse context.
|
||||||
func (p *ParseContext) Next() *Token {
|
func (p *ParseContext) Next() *Token {
|
||||||
if len(p.peek) > 0 {
|
if len(p.peek) > 0 {
|
||||||
@@ -266,9 +270,12 @@ func (p *ParseContext) matchedCmd(cmd *CmdClause) {
|
|||||||
|
|
||||||
// Expand arguments from a file. Lines starting with # will be treated as comments.
|
// Expand arguments from a file. Lines starting with # will be treated as comments.
|
||||||
func ExpandArgsFromFile(filename string) (out []string, err error) {
|
func ExpandArgsFromFile(filename string) (out []string, err error) {
|
||||||
|
if filename == "" {
|
||||||
|
return nil, fmt.Errorf("expected @ file to expand arguments from")
|
||||||
|
}
|
||||||
r, err := os.Open(filename)
|
r, err := os.Open(filename)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, fmt.Errorf("failed to open arguments file %q: %s", filename, err)
|
||||||
}
|
}
|
||||||
defer r.Close()
|
defer r.Close()
|
||||||
scanner := bufio.NewScanner(r)
|
scanner := bufio.NewScanner(r)
|
||||||
@@ -280,6 +287,9 @@ func ExpandArgsFromFile(filename string) (out []string, err error) {
|
|||||||
out = append(out, line)
|
out = append(out, line)
|
||||||
}
|
}
|
||||||
err = scanner.Err()
|
err = scanner.Err()
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to read arguments from %q: %s", filename, err)
|
||||||
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -291,7 +301,7 @@ func parse(context *ParseContext, app *Application) (err error) {
|
|||||||
ignoreDefault := context.ignoreDefault
|
ignoreDefault := context.ignoreDefault
|
||||||
|
|
||||||
loop:
|
loop:
|
||||||
for !context.EOL() {
|
for !context.EOL() && !context.Error() {
|
||||||
token := context.Peek()
|
token := context.Peek()
|
||||||
|
|
||||||
switch token.Type {
|
switch token.Type {
|
||||||
@@ -365,6 +375,10 @@ loop:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if context.Error() {
|
||||||
|
return fmt.Errorf("%s", context.Peek().Value)
|
||||||
|
}
|
||||||
|
|
||||||
if !context.EOL() {
|
if !context.EOL() {
|
||||||
return fmt.Errorf("unexpected %s", context.Peek())
|
return fmt.Errorf("unexpected %s", context.Peek())
|
||||||
}
|
}
|
||||||
|
|||||||
25
vendor/gopkg.in/alecthomas/kingpin.v2/values.json
generated
vendored
25
vendor/gopkg.in/alecthomas/kingpin.v2/values.json
generated
vendored
@@ -1,25 +0,0 @@
|
|||||||
[
|
|
||||||
{"type": "bool", "parser": "strconv.ParseBool(s)"},
|
|
||||||
{"type": "string", "parser": "s, error(nil)", "format": "string(*f.v)", "plural": "Strings"},
|
|
||||||
{"type": "uint", "parser": "strconv.ParseUint(s, 0, 64)", "plural": "Uints"},
|
|
||||||
{"type": "uint8", "parser": "strconv.ParseUint(s, 0, 8)"},
|
|
||||||
{"type": "uint16", "parser": "strconv.ParseUint(s, 0, 16)"},
|
|
||||||
{"type": "uint32", "parser": "strconv.ParseUint(s, 0, 32)"},
|
|
||||||
{"type": "uint64", "parser": "strconv.ParseUint(s, 0, 64)"},
|
|
||||||
{"type": "int", "parser": "strconv.ParseFloat(s, 64)", "plural": "Ints"},
|
|
||||||
{"type": "int8", "parser": "strconv.ParseInt(s, 0, 8)"},
|
|
||||||
{"type": "int16", "parser": "strconv.ParseInt(s, 0, 16)"},
|
|
||||||
{"type": "int32", "parser": "strconv.ParseInt(s, 0, 32)"},
|
|
||||||
{"type": "int64", "parser": "strconv.ParseInt(s, 0, 64)"},
|
|
||||||
{"type": "float64", "parser": "strconv.ParseFloat(s, 64)"},
|
|
||||||
{"type": "float32", "parser": "strconv.ParseFloat(s, 32)"},
|
|
||||||
{"name": "Duration", "type": "time.Duration", "no_value_parser": true},
|
|
||||||
{"name": "IP", "type": "net.IP", "no_value_parser": true},
|
|
||||||
{"name": "TCPAddr", "Type": "*net.TCPAddr", "plural": "TCPList", "no_value_parser": true},
|
|
||||||
{"name": "ExistingFile", "Type": "string", "plural": "ExistingFiles", "no_value_parser": true},
|
|
||||||
{"name": "ExistingDir", "Type": "string", "plural": "ExistingDirs", "no_value_parser": true},
|
|
||||||
{"name": "ExistingFileOrDir", "Type": "string", "plural": "ExistingFilesOrDirs", "no_value_parser": true},
|
|
||||||
{"name": "Regexp", "Type": "*regexp.Regexp", "parser": "regexp.Compile(s)"},
|
|
||||||
{"name": "ResolvedIP", "Type": "net.IP", "parser": "resolveHost(s)", "help": "Resolve a hostname or IP to an IP."},
|
|
||||||
{"name": "HexBytes", "Type": "[]byte", "parser": "hex.DecodeString(s)", "help": "Bytes as a hex string."}
|
|
||||||
]
|
|
||||||
31
vendor/vendor.json
vendored
31
vendor/vendor.json
vendored
@@ -1,31 +0,0 @@
|
|||||||
{
|
|
||||||
"comment": "",
|
|
||||||
"ignore": "test",
|
|
||||||
"package": [
|
|
||||||
{
|
|
||||||
"checksumSHA1": "KmjnydoAbofMieIWm+it5OWERaM=",
|
|
||||||
"path": "github.com/alecthomas/template",
|
|
||||||
"revision": "a0175ee3bccc567396460bf5acd36800cb10c49c",
|
|
||||||
"revisionTime": "2016-04-05T07:15:01Z"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"checksumSHA1": "3wt0pTXXeS+S93unwhGoLIyGX/Q=",
|
|
||||||
"path": "github.com/alecthomas/template/parse",
|
|
||||||
"revision": "a0175ee3bccc567396460bf5acd36800cb10c49c",
|
|
||||||
"revisionTime": "2016-04-05T07:15:01Z"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"checksumSHA1": "fCc3grA7vIxfBru7R3SqjcW+oLI=",
|
|
||||||
"path": "github.com/alecthomas/units",
|
|
||||||
"revision": "2efee857e7cfd4f3d0138cc3cbb1b4966962b93a",
|
|
||||||
"revisionTime": "2015-10-22T06:55:26Z"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"checksumSHA1": "3SZTatHIy9OTKc95YlVfXKnoySg=",
|
|
||||||
"path": "gopkg.in/alecthomas/kingpin.v2",
|
|
||||||
"revision": "1087e65c9441605df944fb12c33f0fe7072d18ca",
|
|
||||||
"revisionTime": "2017-07-27T04:22:29Z"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"rootPath": "github.com/jimeh/casecmp"
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user