mirror of
https://github.com/dokku/dokku.git
synced 2025-12-16 03:57:43 +01:00
convert repo plugin to golang
This commit is contained in:
@@ -14,3 +14,8 @@ indent_size = 4
|
||||
insert_final_newline = true
|
||||
indent_style = tab
|
||||
indent_size = 4
|
||||
|
||||
[*.go]
|
||||
insert_final_newline = true
|
||||
indent_style = space
|
||||
indent_size = 4
|
||||
|
||||
19
Makefile
19
Makefile
@@ -9,6 +9,9 @@ DOKKU_LIB_ROOT ?= /var/lib/dokku
|
||||
PLUGINS_PATH ?= ${DOKKU_LIB_ROOT}/plugins
|
||||
CORE_PLUGINS_PATH ?= ${DOKKU_LIB_ROOT}/core-plugins
|
||||
|
||||
export GO_REPO_ROOT := /go/src/github.com/dokku/dokku
|
||||
export BUILD_IMAGE := golang:1.7.1
|
||||
|
||||
# If the first argument is "vagrant-dokku"...
|
||||
ifeq (vagrant-dokku,$(firstword $(MAKECMDGOALS)))
|
||||
# use the rest as arguments for "vagrant-dokku"
|
||||
@@ -23,7 +26,8 @@ else
|
||||
BUILD_STACK_TARGETS = build-in-docker
|
||||
endif
|
||||
|
||||
.PHONY: all apt-update install version copyfiles man-db plugins dependencies sshcommand plugn docker aufs stack count dokku-installer vagrant-acl-add vagrant-dokku
|
||||
.PHONY: all apt-update install version copyfiles man-db plugins dependencies sshcommand plugn docker aufs stack count dokku-installer vagrant-acl-add vagrant-dokku go-build force
|
||||
force :;
|
||||
|
||||
include tests.mk
|
||||
include deb.mk
|
||||
@@ -50,7 +54,16 @@ package_cloud:
|
||||
packer:
|
||||
packer build contrib/packer.json
|
||||
|
||||
go-build: force
|
||||
basedir=$(PWD); \
|
||||
for dir in plugins/*; do \
|
||||
if [ -e $$dir/Makefile ]; then \
|
||||
$(MAKE) -C $$dir || exit $$? ;\
|
||||
fi ;\
|
||||
done
|
||||
|
||||
copyfiles:
|
||||
$(MAKE) go-build || exit 1
|
||||
cp dokku /usr/local/bin/dokku
|
||||
mkdir -p ${CORE_PLUGINS_PATH} ${PLUGINS_PATH}
|
||||
rm -rf ${CORE_PLUGINS_PATH}/*
|
||||
@@ -62,11 +75,13 @@ copyfiles:
|
||||
rm -rf ${CORE_PLUGINS_PATH}/$$plugin && \
|
||||
rm -rf ${PLUGINS_PATH}/$$plugin && \
|
||||
cp -R plugins/$$plugin ${CORE_PLUGINS_PATH}/available && \
|
||||
rm -rf ${CORE_PLUGINS_PATH}/available/$$plugin/src && \
|
||||
ln -s ${CORE_PLUGINS_PATH}/available/$$plugin ${PLUGINS_PATH}/available; \
|
||||
find /var/lib/dokku/ -xtype l -delete;\
|
||||
PLUGIN_PATH=${CORE_PLUGINS_PATH} plugn enable $$plugin ;\
|
||||
PLUGIN_PATH=${PLUGINS_PATH} plugn enable $$plugin ;\
|
||||
done
|
||||
done
|
||||
find ./plugins/* -type f -executable -exec file -i '{}' \; | grep 'x-executable; charset=binary' | awk -F: '{ print $$1 }' | xargs rm -f
|
||||
chown dokku:dokku -R ${PLUGINS_PATH} ${CORE_PLUGINS_PATH} || true
|
||||
$(MAKE) addman
|
||||
|
||||
|
||||
149
plugins/common/common.go
Normal file
149
plugins/common/common.go
Normal file
@@ -0,0 +1,149 @@
|
||||
package common
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
sh "github.com/codeskyblue/go-sh"
|
||||
)
|
||||
|
||||
// DokkuCmd represents a shell command to be run for dokku
|
||||
type DokkuCmd struct {
|
||||
Env map[string]string
|
||||
Command *exec.Cmd
|
||||
CommandString string
|
||||
Args []string
|
||||
ShowOutput bool
|
||||
}
|
||||
|
||||
// NewDokkuCmd creates a new DokkuCmd
|
||||
func NewDokkuCmd(command string) *DokkuCmd {
|
||||
items := strings.Split(command, " ")
|
||||
cmd := items[0]
|
||||
args := items[1:]
|
||||
return &DokkuCmd{
|
||||
Command: exec.Command(cmd, args...),
|
||||
CommandString: command,
|
||||
Args: args,
|
||||
ShowOutput: true,
|
||||
}
|
||||
}
|
||||
|
||||
// Execute is a lightweight wrapper around exec.Command
|
||||
func (dc *DokkuCmd) Execute() bool {
|
||||
env := os.Environ()
|
||||
for k, v := range dc.Env {
|
||||
env = append(env, fmt.Sprintf("%s=%s", k, v))
|
||||
}
|
||||
dc.Command.Env = env
|
||||
if dc.ShowOutput {
|
||||
dc.Command.Stdout = os.Stdout
|
||||
dc.Command.Stderr = os.Stderr
|
||||
}
|
||||
err := dc.Command.Run()
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// VerifyAppName verifies app name format and app existence"
|
||||
func VerifyAppName(appName string) (err error) {
|
||||
dokkuRoot := MustGetEnv("DOKKU_ROOT")
|
||||
appRoot := strings.Join([]string{dokkuRoot, appName}, "/")
|
||||
_, err = os.Stat(appRoot)
|
||||
if os.IsNotExist(err) {
|
||||
return fmt.Errorf("App %s does not exist: %v\n", appName, err)
|
||||
}
|
||||
r, _ := regexp.Compile("^[a-z].*")
|
||||
if !r.MatchString(appName) {
|
||||
return fmt.Errorf("App name (%s) must begin with lowercase alphanumeric character\n", appName)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// MustGetEnv returns env variable or fails if it's not set
|
||||
func MustGetEnv(key string) string {
|
||||
dokkuRoot := os.Getenv(key)
|
||||
if dokkuRoot == "" {
|
||||
LogFail(fmt.Sprintf("%s not set!", key))
|
||||
}
|
||||
return dokkuRoot
|
||||
}
|
||||
|
||||
// LogFail is the failure log formatter
|
||||
// prints text to stderr and exits with status 1
|
||||
func LogFail(text string) {
|
||||
fmt.Fprintln(os.Stderr, fmt.Sprintf("FAILED: %s", text))
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// GetDeployingAppImageName returns deploying image identifier for a given app, tag tuple. validate if tag is presented
|
||||
func GetDeployingAppImageName(args ...string) (imageName string) {
|
||||
argArray := make([]string, 3)
|
||||
for idx, arg := range args {
|
||||
argArray[idx] = arg
|
||||
}
|
||||
appName := argArray[0]
|
||||
imageTag := argArray[1]
|
||||
imageRepo := argArray[2]
|
||||
|
||||
if appName == "" {
|
||||
LogFail("(GetDeployingAppImageName) APP must not be empty")
|
||||
}
|
||||
|
||||
b, err := sh.Command("plugn", "trigger", "deployed-app-repository", appName).Output()
|
||||
if err != nil {
|
||||
LogFail(err.Error())
|
||||
}
|
||||
imageRemoteRepository := string(b[:])
|
||||
|
||||
b, err = sh.Command("plugn", "trigger", "deployed-app-image-tag", appName).Output()
|
||||
if err != nil {
|
||||
LogFail(err.Error())
|
||||
}
|
||||
newImageTag := string(b[:])
|
||||
|
||||
b, err = sh.Command("plugn", "trigger", "deployed-app-image-repo", appName).Output()
|
||||
if err != nil {
|
||||
LogFail(err.Error())
|
||||
}
|
||||
newImageRepo := string(b[:])
|
||||
|
||||
if newImageRepo != "" {
|
||||
imageRepo = newImageRepo
|
||||
}
|
||||
if newImageTag != "" {
|
||||
imageTag = newImageTag
|
||||
}
|
||||
if imageRepo == "" {
|
||||
imageRepo = GetAppImageRepo(appName)
|
||||
}
|
||||
if imageTag == "" {
|
||||
imageTag = "latest"
|
||||
}
|
||||
|
||||
imageName = fmt.Sprintf("%s%s:%s", imageRemoteRepository, imageRepo, imageTag)
|
||||
if !VerifyImage(imageName) {
|
||||
LogFail(fmt.Sprintf("app image (%s) not found", imageName))
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// GetAppImageRepo is the central definition of a dokku image repo pattern
|
||||
func GetAppImageRepo(appName string) string {
|
||||
return strings.Join([]string{"dokku", appName}, "/")
|
||||
}
|
||||
|
||||
// VerifyImage returns true if docker image exists in local repo
|
||||
func VerifyImage(image string) bool {
|
||||
imageCmd := NewDokkuCmd(fmt.Sprintf("docker inspect %s", image))
|
||||
imageCmd.ShowOutput = false
|
||||
if imageCmd.Execute() {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
38
plugins/common/common_test.go
Normal file
38
plugins/common/common_test.go
Normal file
@@ -0,0 +1,38 @@
|
||||
package common
|
||||
|
||||
import (
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
. "github.com/onsi/gomega"
|
||||
)
|
||||
|
||||
func TestGetEnv(t *testing.T) {
|
||||
RegisterTestingT(t)
|
||||
Expect(MustGetEnv("DOKKU_ROOT")).To(Equal("/home/dokku"))
|
||||
}
|
||||
|
||||
func TestGetAppImageRepo(t *testing.T) {
|
||||
RegisterTestingT(t)
|
||||
Expect(GetAppImageRepo("testapp")).To(Equal("dokku/testapp"))
|
||||
}
|
||||
|
||||
func TestVerifyImageInvalid(t *testing.T) {
|
||||
RegisterTestingT(t)
|
||||
Expect(VerifyImage("testapp")).To(Equal(false))
|
||||
}
|
||||
|
||||
func TestVerifyAppNameInvalid(t *testing.T) {
|
||||
RegisterTestingT(t)
|
||||
err := VerifyAppName("1994testApp")
|
||||
Expect(err).To(HaveOccurred())
|
||||
}
|
||||
|
||||
func TestVerifyAppName(t *testing.T) {
|
||||
RegisterTestingT(t)
|
||||
dir := "/home/dokku/testApp"
|
||||
os.MkdirAll(dir, 0644)
|
||||
err := VerifyAppName("testApp")
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
os.RemoveAll(dir)
|
||||
}
|
||||
8
plugins/common/glide.lock
generated
Normal file
8
plugins/common/glide.lock
generated
Normal file
@@ -0,0 +1,8 @@
|
||||
hash: e438ca8cf1bdca3e9c984bb1ee70c0d58be9da52cbe13a079a7c6cbc14210a37
|
||||
updated: 2017-01-03T17:17:27.973373328-08:00
|
||||
imports:
|
||||
- name: github.com/codegangsta/inject
|
||||
version: 33e0aa1cb7c019ccc3fbe049a8262a6403d30504
|
||||
- name: github.com/codeskyblue/go-sh
|
||||
version: ceb46ec4630a726eeed51d0a70066c9e1102c48f
|
||||
testImports: []
|
||||
3
plugins/common/glide.yaml
Normal file
3
plugins/common/glide.yaml
Normal file
@@ -0,0 +1,3 @@
|
||||
package: .
|
||||
import:
|
||||
- package: github.com/codeskyblue/go-sh
|
||||
2
plugins/common/vendor/github.com/codegangsta/inject/.gitignore
generated
vendored
Normal file
2
plugins/common/vendor/github.com/codegangsta/inject/.gitignore
generated
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
inject
|
||||
inject.test
|
||||
20
plugins/common/vendor/github.com/codegangsta/inject/LICENSE
generated
vendored
Normal file
20
plugins/common/vendor/github.com/codegangsta/inject/LICENSE
generated
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2013 Jeremy Saenz
|
||||
|
||||
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.
|
||||
92
plugins/common/vendor/github.com/codegangsta/inject/README.md
generated
vendored
Normal file
92
plugins/common/vendor/github.com/codegangsta/inject/README.md
generated
vendored
Normal file
@@ -0,0 +1,92 @@
|
||||
# inject
|
||||
--
|
||||
import "github.com/codegangsta/inject"
|
||||
|
||||
Package inject provides utilities for mapping and injecting dependencies in
|
||||
various ways.
|
||||
|
||||
Language Translations:
|
||||
* [简体中文](translations/README_zh_cn.md)
|
||||
|
||||
## Usage
|
||||
|
||||
#### func InterfaceOf
|
||||
|
||||
```go
|
||||
func InterfaceOf(value interface{}) reflect.Type
|
||||
```
|
||||
InterfaceOf dereferences a pointer to an Interface type. It panics if value is
|
||||
not an pointer to an interface.
|
||||
|
||||
#### type Applicator
|
||||
|
||||
```go
|
||||
type Applicator interface {
|
||||
// Maps dependencies in the Type map to each field in the struct
|
||||
// that is tagged with 'inject'. Returns an error if the injection
|
||||
// fails.
|
||||
Apply(interface{}) error
|
||||
}
|
||||
```
|
||||
|
||||
Applicator represents an interface for mapping dependencies to a struct.
|
||||
|
||||
#### type Injector
|
||||
|
||||
```go
|
||||
type Injector interface {
|
||||
Applicator
|
||||
Invoker
|
||||
TypeMapper
|
||||
// SetParent sets the parent of the injector. If the injector cannot find a
|
||||
// dependency in its Type map it will check its parent before returning an
|
||||
// error.
|
||||
SetParent(Injector)
|
||||
}
|
||||
```
|
||||
|
||||
Injector represents an interface for mapping and injecting dependencies into
|
||||
structs and function arguments.
|
||||
|
||||
#### func New
|
||||
|
||||
```go
|
||||
func New() Injector
|
||||
```
|
||||
New returns a new Injector.
|
||||
|
||||
#### type Invoker
|
||||
|
||||
```go
|
||||
type Invoker interface {
|
||||
// Invoke attempts to call the interface{} provided as a function,
|
||||
// providing dependencies for function arguments based on Type. Returns
|
||||
// a slice of reflect.Value representing the returned values of the function.
|
||||
// Returns an error if the injection fails.
|
||||
Invoke(interface{}) ([]reflect.Value, error)
|
||||
}
|
||||
```
|
||||
|
||||
Invoker represents an interface for calling functions via reflection.
|
||||
|
||||
#### type TypeMapper
|
||||
|
||||
```go
|
||||
type TypeMapper interface {
|
||||
// Maps the interface{} value based on its immediate type from reflect.TypeOf.
|
||||
Map(interface{}) TypeMapper
|
||||
// Maps the interface{} value based on the pointer of an Interface provided.
|
||||
// This is really only useful for mapping a value as an interface, as interfaces
|
||||
// cannot at this time be referenced directly without a pointer.
|
||||
MapTo(interface{}, interface{}) TypeMapper
|
||||
// Provides a possibility to directly insert a mapping based on type and value.
|
||||
// This makes it possible to directly map type arguments not possible to instantiate
|
||||
// with reflect like unidirectional channels.
|
||||
Set(reflect.Type, reflect.Value) TypeMapper
|
||||
// Returns the Value that is mapped to the current type. Returns a zeroed Value if
|
||||
// the Type has not been mapped.
|
||||
Get(reflect.Type) reflect.Value
|
||||
}
|
||||
```
|
||||
|
||||
TypeMapper represents an interface for mapping interface{} values based on type.
|
||||
187
plugins/common/vendor/github.com/codegangsta/inject/inject.go
generated
vendored
Normal file
187
plugins/common/vendor/github.com/codegangsta/inject/inject.go
generated
vendored
Normal file
@@ -0,0 +1,187 @@
|
||||
// Package inject provides utilities for mapping and injecting dependencies in various ways.
|
||||
package inject
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
// Injector represents an interface for mapping and injecting dependencies into structs
|
||||
// and function arguments.
|
||||
type Injector interface {
|
||||
Applicator
|
||||
Invoker
|
||||
TypeMapper
|
||||
// SetParent sets the parent of the injector. If the injector cannot find a
|
||||
// dependency in its Type map it will check its parent before returning an
|
||||
// error.
|
||||
SetParent(Injector)
|
||||
}
|
||||
|
||||
// Applicator represents an interface for mapping dependencies to a struct.
|
||||
type Applicator interface {
|
||||
// Maps dependencies in the Type map to each field in the struct
|
||||
// that is tagged with 'inject'. Returns an error if the injection
|
||||
// fails.
|
||||
Apply(interface{}) error
|
||||
}
|
||||
|
||||
// Invoker represents an interface for calling functions via reflection.
|
||||
type Invoker interface {
|
||||
// Invoke attempts to call the interface{} provided as a function,
|
||||
// providing dependencies for function arguments based on Type. Returns
|
||||
// a slice of reflect.Value representing the returned values of the function.
|
||||
// Returns an error if the injection fails.
|
||||
Invoke(interface{}) ([]reflect.Value, error)
|
||||
}
|
||||
|
||||
// TypeMapper represents an interface for mapping interface{} values based on type.
|
||||
type TypeMapper interface {
|
||||
// Maps the interface{} value based on its immediate type from reflect.TypeOf.
|
||||
Map(interface{}) TypeMapper
|
||||
// Maps the interface{} value based on the pointer of an Interface provided.
|
||||
// This is really only useful for mapping a value as an interface, as interfaces
|
||||
// cannot at this time be referenced directly without a pointer.
|
||||
MapTo(interface{}, interface{}) TypeMapper
|
||||
// Provides a possibility to directly insert a mapping based on type and value.
|
||||
// This makes it possible to directly map type arguments not possible to instantiate
|
||||
// with reflect like unidirectional channels.
|
||||
Set(reflect.Type, reflect.Value) TypeMapper
|
||||
// Returns the Value that is mapped to the current type. Returns a zeroed Value if
|
||||
// the Type has not been mapped.
|
||||
Get(reflect.Type) reflect.Value
|
||||
}
|
||||
|
||||
type injector struct {
|
||||
values map[reflect.Type]reflect.Value
|
||||
parent Injector
|
||||
}
|
||||
|
||||
// InterfaceOf dereferences a pointer to an Interface type.
|
||||
// It panics if value is not an pointer to an interface.
|
||||
func InterfaceOf(value interface{}) reflect.Type {
|
||||
t := reflect.TypeOf(value)
|
||||
|
||||
for t.Kind() == reflect.Ptr {
|
||||
t = t.Elem()
|
||||
}
|
||||
|
||||
if t.Kind() != reflect.Interface {
|
||||
panic("Called inject.InterfaceOf with a value that is not a pointer to an interface. (*MyInterface)(nil)")
|
||||
}
|
||||
|
||||
return t
|
||||
}
|
||||
|
||||
// New returns a new Injector.
|
||||
func New() Injector {
|
||||
return &injector{
|
||||
values: make(map[reflect.Type]reflect.Value),
|
||||
}
|
||||
}
|
||||
|
||||
// Invoke attempts to call the interface{} provided as a function,
|
||||
// providing dependencies for function arguments based on Type.
|
||||
// Returns a slice of reflect.Value representing the returned values of the function.
|
||||
// Returns an error if the injection fails.
|
||||
// It panics if f is not a function
|
||||
func (inj *injector) Invoke(f interface{}) ([]reflect.Value, error) {
|
||||
t := reflect.TypeOf(f)
|
||||
|
||||
var in = make([]reflect.Value, t.NumIn()) //Panic if t is not kind of Func
|
||||
for i := 0; i < t.NumIn(); i++ {
|
||||
argType := t.In(i)
|
||||
val := inj.Get(argType)
|
||||
if !val.IsValid() {
|
||||
return nil, fmt.Errorf("Value not found for type %v", argType)
|
||||
}
|
||||
|
||||
in[i] = val
|
||||
}
|
||||
|
||||
return reflect.ValueOf(f).Call(in), nil
|
||||
}
|
||||
|
||||
// Maps dependencies in the Type map to each field in the struct
|
||||
// that is tagged with 'inject'.
|
||||
// Returns an error if the injection fails.
|
||||
func (inj *injector) Apply(val interface{}) error {
|
||||
v := reflect.ValueOf(val)
|
||||
|
||||
for v.Kind() == reflect.Ptr {
|
||||
v = v.Elem()
|
||||
}
|
||||
|
||||
if v.Kind() != reflect.Struct {
|
||||
return nil // Should not panic here ?
|
||||
}
|
||||
|
||||
t := v.Type()
|
||||
|
||||
for i := 0; i < v.NumField(); i++ {
|
||||
f := v.Field(i)
|
||||
structField := t.Field(i)
|
||||
if f.CanSet() && (structField.Tag == "inject" || structField.Tag.Get("inject") != "") {
|
||||
ft := f.Type()
|
||||
v := inj.Get(ft)
|
||||
if !v.IsValid() {
|
||||
return fmt.Errorf("Value not found for type %v", ft)
|
||||
}
|
||||
|
||||
f.Set(v)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Maps the concrete value of val to its dynamic type using reflect.TypeOf,
|
||||
// It returns the TypeMapper registered in.
|
||||
func (i *injector) Map(val interface{}) TypeMapper {
|
||||
i.values[reflect.TypeOf(val)] = reflect.ValueOf(val)
|
||||
return i
|
||||
}
|
||||
|
||||
func (i *injector) MapTo(val interface{}, ifacePtr interface{}) TypeMapper {
|
||||
i.values[InterfaceOf(ifacePtr)] = reflect.ValueOf(val)
|
||||
return i
|
||||
}
|
||||
|
||||
// Maps the given reflect.Type to the given reflect.Value and returns
|
||||
// the Typemapper the mapping has been registered in.
|
||||
func (i *injector) Set(typ reflect.Type, val reflect.Value) TypeMapper {
|
||||
i.values[typ] = val
|
||||
return i
|
||||
}
|
||||
|
||||
func (i *injector) Get(t reflect.Type) reflect.Value {
|
||||
val := i.values[t]
|
||||
|
||||
if val.IsValid() {
|
||||
return val
|
||||
}
|
||||
|
||||
// no concrete types found, try to find implementors
|
||||
// if t is an interface
|
||||
if t.Kind() == reflect.Interface {
|
||||
for k, v := range i.values {
|
||||
if k.Implements(t) {
|
||||
val = v
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Still no type found, try to look it up on the parent
|
||||
if !val.IsValid() && i.parent != nil {
|
||||
val = i.parent.Get(t)
|
||||
}
|
||||
|
||||
return val
|
||||
|
||||
}
|
||||
|
||||
func (i *injector) SetParent(parent Injector) {
|
||||
i.parent = parent
|
||||
}
|
||||
159
plugins/common/vendor/github.com/codegangsta/inject/inject_test.go
generated
vendored
Normal file
159
plugins/common/vendor/github.com/codegangsta/inject/inject_test.go
generated
vendored
Normal file
@@ -0,0 +1,159 @@
|
||||
package inject_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/codegangsta/inject"
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
type SpecialString interface {
|
||||
}
|
||||
|
||||
type TestStruct struct {
|
||||
Dep1 string `inject:"t" json:"-"`
|
||||
Dep2 SpecialString `inject`
|
||||
Dep3 string
|
||||
}
|
||||
|
||||
type Greeter struct {
|
||||
Name string
|
||||
}
|
||||
|
||||
func (g *Greeter) String() string {
|
||||
return "Hello, My name is" + g.Name
|
||||
}
|
||||
|
||||
/* Test Helpers */
|
||||
func expect(t *testing.T, a interface{}, b interface{}) {
|
||||
if a != b {
|
||||
t.Errorf("Expected %v (type %v) - Got %v (type %v)", b, reflect.TypeOf(b), a, reflect.TypeOf(a))
|
||||
}
|
||||
}
|
||||
|
||||
func refute(t *testing.T, a interface{}, b interface{}) {
|
||||
if a == b {
|
||||
t.Errorf("Did not expect %v (type %v) - Got %v (type %v)", b, reflect.TypeOf(b), a, reflect.TypeOf(a))
|
||||
}
|
||||
}
|
||||
|
||||
func Test_InjectorInvoke(t *testing.T) {
|
||||
injector := inject.New()
|
||||
expect(t, injector == nil, false)
|
||||
|
||||
dep := "some dependency"
|
||||
injector.Map(dep)
|
||||
dep2 := "another dep"
|
||||
injector.MapTo(dep2, (*SpecialString)(nil))
|
||||
dep3 := make(chan *SpecialString)
|
||||
dep4 := make(chan *SpecialString)
|
||||
typRecv := reflect.ChanOf(reflect.RecvDir, reflect.TypeOf(dep3).Elem())
|
||||
typSend := reflect.ChanOf(reflect.SendDir, reflect.TypeOf(dep4).Elem())
|
||||
injector.Set(typRecv, reflect.ValueOf(dep3))
|
||||
injector.Set(typSend, reflect.ValueOf(dep4))
|
||||
|
||||
_, err := injector.Invoke(func(d1 string, d2 SpecialString, d3 <-chan *SpecialString, d4 chan<- *SpecialString) {
|
||||
expect(t, d1, dep)
|
||||
expect(t, d2, dep2)
|
||||
expect(t, reflect.TypeOf(d3).Elem(), reflect.TypeOf(dep3).Elem())
|
||||
expect(t, reflect.TypeOf(d4).Elem(), reflect.TypeOf(dep4).Elem())
|
||||
expect(t, reflect.TypeOf(d3).ChanDir(), reflect.RecvDir)
|
||||
expect(t, reflect.TypeOf(d4).ChanDir(), reflect.SendDir)
|
||||
})
|
||||
|
||||
expect(t, err, nil)
|
||||
}
|
||||
|
||||
func Test_InjectorInvokeReturnValues(t *testing.T) {
|
||||
injector := inject.New()
|
||||
expect(t, injector == nil, false)
|
||||
|
||||
dep := "some dependency"
|
||||
injector.Map(dep)
|
||||
dep2 := "another dep"
|
||||
injector.MapTo(dep2, (*SpecialString)(nil))
|
||||
|
||||
result, err := injector.Invoke(func(d1 string, d2 SpecialString) string {
|
||||
expect(t, d1, dep)
|
||||
expect(t, d2, dep2)
|
||||
return "Hello world"
|
||||
})
|
||||
|
||||
expect(t, result[0].String(), "Hello world")
|
||||
expect(t, err, nil)
|
||||
}
|
||||
|
||||
func Test_InjectorApply(t *testing.T) {
|
||||
injector := inject.New()
|
||||
|
||||
injector.Map("a dep").MapTo("another dep", (*SpecialString)(nil))
|
||||
|
||||
s := TestStruct{}
|
||||
err := injector.Apply(&s)
|
||||
expect(t, err, nil)
|
||||
|
||||
expect(t, s.Dep1, "a dep")
|
||||
expect(t, s.Dep2, "another dep")
|
||||
expect(t, s.Dep3, "")
|
||||
}
|
||||
|
||||
func Test_InterfaceOf(t *testing.T) {
|
||||
iType := inject.InterfaceOf((*SpecialString)(nil))
|
||||
expect(t, iType.Kind(), reflect.Interface)
|
||||
|
||||
iType = inject.InterfaceOf((**SpecialString)(nil))
|
||||
expect(t, iType.Kind(), reflect.Interface)
|
||||
|
||||
// Expecting nil
|
||||
defer func() {
|
||||
rec := recover()
|
||||
refute(t, rec, nil)
|
||||
}()
|
||||
iType = inject.InterfaceOf((*testing.T)(nil))
|
||||
}
|
||||
|
||||
func Test_InjectorSet(t *testing.T) {
|
||||
injector := inject.New()
|
||||
typ := reflect.TypeOf("string")
|
||||
typSend := reflect.ChanOf(reflect.SendDir, typ)
|
||||
typRecv := reflect.ChanOf(reflect.RecvDir, typ)
|
||||
|
||||
// instantiating unidirectional channels is not possible using reflect
|
||||
// http://golang.org/src/pkg/reflect/value.go?s=60463:60504#L2064
|
||||
chanRecv := reflect.MakeChan(reflect.ChanOf(reflect.BothDir, typ), 0)
|
||||
chanSend := reflect.MakeChan(reflect.ChanOf(reflect.BothDir, typ), 0)
|
||||
|
||||
injector.Set(typSend, chanSend)
|
||||
injector.Set(typRecv, chanRecv)
|
||||
|
||||
expect(t, injector.Get(typSend).IsValid(), true)
|
||||
expect(t, injector.Get(typRecv).IsValid(), true)
|
||||
expect(t, injector.Get(chanSend.Type()).IsValid(), false)
|
||||
}
|
||||
|
||||
func Test_InjectorGet(t *testing.T) {
|
||||
injector := inject.New()
|
||||
|
||||
injector.Map("some dependency")
|
||||
|
||||
expect(t, injector.Get(reflect.TypeOf("string")).IsValid(), true)
|
||||
expect(t, injector.Get(reflect.TypeOf(11)).IsValid(), false)
|
||||
}
|
||||
|
||||
func Test_InjectorSetParent(t *testing.T) {
|
||||
injector := inject.New()
|
||||
injector.MapTo("another dep", (*SpecialString)(nil))
|
||||
|
||||
injector2 := inject.New()
|
||||
injector2.SetParent(injector)
|
||||
|
||||
expect(t, injector2.Get(inject.InterfaceOf((*SpecialString)(nil))).IsValid(), true)
|
||||
}
|
||||
|
||||
func TestInjectImplementors(t *testing.T) {
|
||||
injector := inject.New()
|
||||
g := &Greeter{"Jeremy"}
|
||||
injector.Map(g)
|
||||
|
||||
expect(t, injector.Get(inject.InterfaceOf((*fmt.Stringer)(nil))).IsValid(), true)
|
||||
}
|
||||
85
plugins/common/vendor/github.com/codegangsta/inject/translations/README_zh_cn.md
generated
vendored
Normal file
85
plugins/common/vendor/github.com/codegangsta/inject/translations/README_zh_cn.md
generated
vendored
Normal file
@@ -0,0 +1,85 @@
|
||||
# inject
|
||||
--
|
||||
import "github.com/codegangsta/inject"
|
||||
|
||||
inject包提供了多种对实体的映射和依赖注入方式。
|
||||
|
||||
## 用法
|
||||
|
||||
#### func InterfaceOf
|
||||
|
||||
```go
|
||||
func InterfaceOf(value interface{}) reflect.Type
|
||||
```
|
||||
函数InterfaceOf返回指向接口类型的指针。如果传入的value值不是指向接口的指针,将抛出一个panic异常。
|
||||
|
||||
#### type Applicator
|
||||
|
||||
```go
|
||||
type Applicator interface {
|
||||
// 在Type map中维持对结构体中每个域的引用并用'inject'来标记
|
||||
// 如果注入失败将会返回一个error.
|
||||
Apply(interface{}) error
|
||||
}
|
||||
```
|
||||
|
||||
Applicator接口表示到结构体的依赖映射关系。
|
||||
|
||||
#### type Injector
|
||||
|
||||
```go
|
||||
type Injector interface {
|
||||
Applicator
|
||||
Invoker
|
||||
TypeMapper
|
||||
// SetParent用来设置父injector. 如果在当前injector的Type map中找不到依赖,
|
||||
// 将会继续从它的父injector中找,直到返回error.
|
||||
SetParent(Injector)
|
||||
}
|
||||
```
|
||||
|
||||
Injector接口表示对结构体、函数参数的映射和依赖注入。
|
||||
|
||||
#### func New
|
||||
|
||||
```go
|
||||
func New() Injector
|
||||
```
|
||||
New创建并返回一个Injector.
|
||||
|
||||
#### type Invoker
|
||||
|
||||
```go
|
||||
type Invoker interface {
|
||||
// Invoke尝试将interface{}作为一个函数来调用,并基于Type为函数提供参数。
|
||||
// 它将返回reflect.Value的切片,其中存放原函数的返回值。
|
||||
// 如果注入失败则返回error.
|
||||
Invoke(interface{}) ([]reflect.Value, error)
|
||||
}
|
||||
```
|
||||
|
||||
Invoker接口表示通过反射进行函数调用。
|
||||
|
||||
#### type TypeMapper
|
||||
|
||||
```go
|
||||
type TypeMapper interface {
|
||||
// 基于调用reflect.TypeOf得到的类型映射interface{}的值。
|
||||
Map(interface{}) TypeMapper
|
||||
// 基于提供的接口的指针映射interface{}的值。
|
||||
// 该函数仅用来将一个值映射为接口,因为接口无法不通过指针而直接引用到。
|
||||
MapTo(interface{}, interface{}) TypeMapper
|
||||
// 为直接插入基于类型和值的map提供一种可能性。
|
||||
// 它使得这一类直接映射成为可能:无法通过反射直接实例化的类型参数,如单向管道。
|
||||
Set(reflect.Type, reflect.Value) TypeMapper
|
||||
// 返回映射到当前类型的Value. 如果Type没被映射,将返回对应的零值。
|
||||
Get(reflect.Type) reflect.Value
|
||||
}
|
||||
```
|
||||
|
||||
TypeMapper接口用来表示基于类型到接口值的映射。
|
||||
|
||||
|
||||
## 译者
|
||||
|
||||
张强 (qqbunny@yeah.net)
|
||||
3
plugins/common/vendor/github.com/codegangsta/inject/update_readme.sh
generated
vendored
Normal file
3
plugins/common/vendor/github.com/codegangsta/inject/update_readme.sh
generated
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
#!/bin/bash
|
||||
go get github.com/robertkrimen/godocdown/godocdown
|
||||
godocdown > README.md
|
||||
202
plugins/common/vendor/github.com/codeskyblue/go-sh/LICENSE
generated
vendored
Normal file
202
plugins/common/vendor/github.com/codeskyblue/go-sh/LICENSE
generated
vendored
Normal file
@@ -0,0 +1,202 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "{}"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright {yyyy} {name of copyright owner}
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
69
plugins/common/vendor/github.com/codeskyblue/go-sh/OLD_README.md
generated
vendored
Normal file
69
plugins/common/vendor/github.com/codeskyblue/go-sh/OLD_README.md
generated
vendored
Normal file
@@ -0,0 +1,69 @@
|
||||
## OLD README
|
||||
First give you a full example, I will explain every command below.
|
||||
|
||||
session := sh.NewSession()
|
||||
session.Env["PATH"] = "/usr/bin:/bin"
|
||||
session.Stdout = os.Stdout
|
||||
session.Stderr = os.Stderr
|
||||
session.Alias("ll", "ls", "-l")
|
||||
session.ShowCMD = true // enable for debug
|
||||
var err error
|
||||
err = session.Call("ll", "/")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
ret, err := session.Capture("pwd", sh.Dir("/home")) # wraper of session.Call
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
# ret is "/home\n"
|
||||
fmt.Println(ret)
|
||||
|
||||
create a new Session
|
||||
|
||||
session := sh.NewSession()
|
||||
|
||||
use alias like this
|
||||
|
||||
session.Alias("ll", "ls", "-l") # like alias ll='ls -l'
|
||||
|
||||
set current env like this
|
||||
|
||||
session.Env["BUILD_ID"] = "123" # like export BUILD_ID=123
|
||||
|
||||
set current directory
|
||||
|
||||
session.Set(sh.Dir("/")) # like cd /
|
||||
|
||||
pipe is also supported
|
||||
|
||||
session.Command("echo", "hello\tworld").Command("cut", "-f2")
|
||||
// output should be "world"
|
||||
session.Run()
|
||||
|
||||
test, the build in command support
|
||||
|
||||
session.Test("d", "dir") // test dir
|
||||
session.Test("f", "file) // test regular file
|
||||
|
||||
with `Alias Env Set Call Capture Command` a shell scripts can be easily converted into golang program. below is a shell script.
|
||||
|
||||
#!/bin/bash -
|
||||
#
|
||||
export PATH=/usr/bin:/bin
|
||||
alias ll='ls -l'
|
||||
cd /usr
|
||||
if test -d "local"
|
||||
then
|
||||
ll local | awk '{print $1, $NF}'
|
||||
fi
|
||||
|
||||
convert to golang, will be
|
||||
|
||||
s := sh.NewSession()
|
||||
s.Env["PATH"] = "/usr/bin:/bin"
|
||||
s.Set(sh.Dir("/usr"))
|
||||
s.Alias("ll", "ls", "-l")
|
||||
if s.Test("d", "local") {
|
||||
s.Command("ll", "local").Command("awk", "{print $1, $NF}").Run()
|
||||
}
|
||||
85
plugins/common/vendor/github.com/codeskyblue/go-sh/README.md
generated
vendored
Normal file
85
plugins/common/vendor/github.com/codeskyblue/go-sh/README.md
generated
vendored
Normal file
@@ -0,0 +1,85 @@
|
||||
## go-sh
|
||||
[](https://app.wercker.com/project/bykey/009acbd4f00ccc6de7e2554e12a50d84)
|
||||
[](http://gowalker.org/github.com/codeskyblue/go-sh)
|
||||
|
||||
*If you depend on the old api, see tag: v.0.1*
|
||||
|
||||
install: `go get github.com/codeskyblue/go-sh`
|
||||
|
||||
Pipe Example:
|
||||
|
||||
package main
|
||||
|
||||
import "github.com/codeskyblue/go-sh"
|
||||
|
||||
func main() {
|
||||
sh.Command("echo", "hello\tworld").Command("cut", "-f2").Run()
|
||||
}
|
||||
|
||||
Because I like os/exec, `go-sh` is very much modelled after it. However, `go-sh` provides a better experience.
|
||||
|
||||
These are some of its features:
|
||||
|
||||
* keep the variable environment (e.g. export)
|
||||
* alias support (e.g. alias in shell)
|
||||
* remember current dir
|
||||
* pipe command
|
||||
* shell build-in commands echo & test
|
||||
* timeout support
|
||||
|
||||
Examples are important:
|
||||
|
||||
sh: echo hello
|
||||
go: sh.Command("echo", "hello").Run()
|
||||
|
||||
sh: export BUILD_ID=123
|
||||
go: s = sh.NewSession().SetEnv("BUILD_ID", "123")
|
||||
|
||||
sh: alias ll='ls -l'
|
||||
go: s = sh.NewSession().Alias('ll', 'ls', '-l')
|
||||
|
||||
sh: (cd /; pwd)
|
||||
go: sh.Command("pwd", sh.Dir("/")).Run()
|
||||
|
||||
sh: test -d data || mkdir data
|
||||
go: if ! sh.Test("dir", "data") { sh.Command("mkdir", "data").Run() }
|
||||
|
||||
sh: cat first second | awk '{print $1}'
|
||||
go: sh.Command("cat", "first", "second").Command("awk", "{print $1}").Run()
|
||||
|
||||
sh: count=$(echo "one two three" | wc -w)
|
||||
go: count, err := sh.Echo("one two three").Command("wc", "-w").Output()
|
||||
|
||||
sh(in ubuntu): timeout 1s sleep 3
|
||||
go: c := sh.Command("sleep", "3"); c.Start(); c.WaitTimeout(time.Second) # default SIGKILL
|
||||
go: out, err := sh.Command("sleep", "3").SetTimeout(time.Second).Output() # set session timeout and get output)
|
||||
|
||||
sh: echo hello | cat
|
||||
go: out, err := sh.Command("cat").SetInput("hello").Output()
|
||||
|
||||
sh: cat # read from stdin
|
||||
go: out, err := sh.Command("cat").SetStdin(os.Stdin).Output()
|
||||
|
||||
If you need to keep env and dir, it is better to create a session
|
||||
|
||||
session := sh.NewSession()
|
||||
session.SetEnv("BUILD_ID", "123")
|
||||
session.SetDir("/")
|
||||
# then call cmd
|
||||
session.Command("echo", "hello").Run()
|
||||
# set ShowCMD to true for easily debug
|
||||
session.ShowCMD = true
|
||||
|
||||
for more information, it better to see docs.
|
||||
[](http://gowalker.org/github.com/codeskyblue/go-sh)
|
||||
|
||||
### contribute
|
||||
If you love this project, starring it will encourage the coder. Pull requests are welcome.
|
||||
|
||||
support the author: [alipay](https://me.alipay.com/goskyblue)
|
||||
|
||||
### thanks
|
||||
this project is based on <http://github.com/codegangsta/inject>. thanks for the author.
|
||||
|
||||
# the reason to use Go shell
|
||||
Sometimes we need to write shell scripts, but shell scripts are not good at working cross platform, Go, on the other hand, is good at that. Is there a good way to use Go to write shell like scripts? Using go-sh we can do this now.
|
||||
41
plugins/common/vendor/github.com/codeskyblue/go-sh/example/example1.go
generated
vendored
Normal file
41
plugins/common/vendor/github.com/codeskyblue/go-sh/example/example1.go
generated
vendored
Normal file
@@ -0,0 +1,41 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
|
||||
"github.com/codeskyblue/go-sh"
|
||||
)
|
||||
|
||||
func main() {
|
||||
sh.Command("echo", "hello").Run()
|
||||
out, err := sh.Command("echo", "hello").Output()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
fmt.Println("output is", string(out))
|
||||
|
||||
var a int
|
||||
sh.Command("echo", "2").UnmarshalJSON(&a)
|
||||
fmt.Println("a =", a)
|
||||
|
||||
s := sh.NewSession()
|
||||
s.Alias("hi", "echo", "hi")
|
||||
s.Command("hi", "boy").Run()
|
||||
|
||||
fmt.Print("pwd = ")
|
||||
s.Command("pwd", sh.Dir("/")).Run()
|
||||
|
||||
if !sh.Test("dir", "data") {
|
||||
sh.Command("echo", "mkdir", "data").Run()
|
||||
}
|
||||
|
||||
sh.Command("echo", "hello", "world").
|
||||
Command("awk", `{print "second arg is "$2}`).Run()
|
||||
s.ShowCMD = true
|
||||
s.Command("echo", "hello", "world").
|
||||
Command("awk", `{print "second arg is "$2}`).Run()
|
||||
|
||||
s.SetEnv("BUILD_ID", "123").Command("bash", "-c", "echo $BUILD_ID").Run()
|
||||
s.Command("bash", "-c", "echo current shell is $SHELL").Run()
|
||||
}
|
||||
7
plugins/common/vendor/github.com/codeskyblue/go-sh/example/less/less.go
generated
vendored
Normal file
7
plugins/common/vendor/github.com/codeskyblue/go-sh/example/less/less.go
generated
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
package main
|
||||
|
||||
import "github.com/codeskyblue/go-sh"
|
||||
|
||||
func main() {
|
||||
sh.Command("less", "less.go").Run()
|
||||
}
|
||||
17
plugins/common/vendor/github.com/codeskyblue/go-sh/example/tail/tailf.go
generated
vendored
Normal file
17
plugins/common/vendor/github.com/codeskyblue/go-sh/example/tail/tailf.go
generated
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
|
||||
"github.com/codeskyblue/go-sh"
|
||||
)
|
||||
|
||||
func main() {
|
||||
flag.Parse()
|
||||
if flag.NArg() != 1 {
|
||||
fmt.Println("Usage: PROGRAM <file>")
|
||||
return
|
||||
}
|
||||
sh.Command("tail", "-f", flag.Arg(0)).Run()
|
||||
}
|
||||
23
plugins/common/vendor/github.com/codeskyblue/go-sh/example/timeout/timeout.go
generated
vendored
Normal file
23
plugins/common/vendor/github.com/codeskyblue/go-sh/example/timeout/timeout.go
generated
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
sh "github.com/codeskyblue/go-sh"
|
||||
)
|
||||
|
||||
func main() {
|
||||
c := sh.Command("sleep", "3")
|
||||
c.Start()
|
||||
err := c.WaitTimeout(time.Second * 1)
|
||||
if err != nil {
|
||||
fmt.Printf("timeout should happend: %v\n", err)
|
||||
}
|
||||
// timeout should be a session
|
||||
out, err := sh.Command("sleep", "2").SetTimeout(time.Second).Output()
|
||||
fmt.Printf("output:(%s), err(%v)\n", string(out), err)
|
||||
|
||||
out, err = sh.Command("echo", "hello").SetTimeout(time.Second).Output()
|
||||
fmt.Printf("output:(%s), err(%v)\n", string(out), err)
|
||||
}
|
||||
28
plugins/common/vendor/github.com/codeskyblue/go-sh/example_test.go
generated
vendored
Normal file
28
plugins/common/vendor/github.com/codeskyblue/go-sh/example_test.go
generated
vendored
Normal file
@@ -0,0 +1,28 @@
|
||||
package sh_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/codeskyblue/go-sh"
|
||||
)
|
||||
|
||||
func ExampleCommand() {
|
||||
out, err := sh.Command("echo", "hello").Output()
|
||||
fmt.Println(string(out), err)
|
||||
}
|
||||
|
||||
func ExampleCommandPipe() {
|
||||
out, err := sh.Command("echo", "-n", "hi").Command("wc", "-c").Output()
|
||||
fmt.Println(string(out), err)
|
||||
}
|
||||
|
||||
func ExampleCommandSetDir() {
|
||||
out, err := sh.Command("pwd", sh.Dir("/")).Output()
|
||||
fmt.Println(string(out), err)
|
||||
}
|
||||
|
||||
func ExampleTest() {
|
||||
if sh.Test("dir", "mydir") {
|
||||
fmt.Println("mydir exists")
|
||||
}
|
||||
}
|
||||
148
plugins/common/vendor/github.com/codeskyblue/go-sh/pipe.go
generated
vendored
Normal file
148
plugins/common/vendor/github.com/codeskyblue/go-sh/pipe.go
generated
vendored
Normal file
@@ -0,0 +1,148 @@
|
||||
package sh
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"encoding/xml"
|
||||
"errors"
|
||||
"io"
|
||||
"os"
|
||||
"strings"
|
||||
"syscall"
|
||||
"time"
|
||||
)
|
||||
|
||||
var ErrExecTimeout = errors.New("execute timeout")
|
||||
|
||||
// unmarshal shell output to decode json
|
||||
func (s *Session) UnmarshalJSON(data interface{}) (err error) {
|
||||
bufrw := bytes.NewBuffer(nil)
|
||||
s.Stdout = bufrw
|
||||
if err = s.Run(); err != nil {
|
||||
return
|
||||
}
|
||||
return json.NewDecoder(bufrw).Decode(data)
|
||||
}
|
||||
|
||||
// unmarshal command output into xml
|
||||
func (s *Session) UnmarshalXML(data interface{}) (err error) {
|
||||
bufrw := bytes.NewBuffer(nil)
|
||||
s.Stdout = bufrw
|
||||
if err = s.Run(); err != nil {
|
||||
return
|
||||
}
|
||||
return xml.NewDecoder(bufrw).Decode(data)
|
||||
}
|
||||
|
||||
// start command
|
||||
func (s *Session) Start() (err error) {
|
||||
s.started = true
|
||||
var rd *io.PipeReader
|
||||
var wr *io.PipeWriter
|
||||
var length = len(s.cmds)
|
||||
if s.ShowCMD {
|
||||
var cmds = make([]string, 0, 4)
|
||||
for _, cmd := range s.cmds {
|
||||
cmds = append(cmds, strings.Join(cmd.Args, " "))
|
||||
}
|
||||
s.writePrompt(strings.Join(cmds, " | "))
|
||||
}
|
||||
for index, cmd := range s.cmds {
|
||||
if index == 0 {
|
||||
cmd.Stdin = s.Stdin
|
||||
} else {
|
||||
cmd.Stdin = rd
|
||||
}
|
||||
if index != length {
|
||||
rd, wr = io.Pipe() // create pipe
|
||||
cmd.Stdout = wr
|
||||
cmd.Stderr = os.Stderr
|
||||
}
|
||||
if index == length-1 {
|
||||
cmd.Stdout = s.Stdout
|
||||
cmd.Stderr = s.Stderr
|
||||
}
|
||||
err = cmd.Start()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Should be call after Start()
|
||||
// only catch the last command error
|
||||
func (s *Session) Wait() (err error) {
|
||||
for _, cmd := range s.cmds {
|
||||
err = cmd.Wait()
|
||||
wr, ok := cmd.Stdout.(*io.PipeWriter)
|
||||
if ok {
|
||||
wr.Close()
|
||||
}
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (s *Session) Kill(sig os.Signal) {
|
||||
for _, cmd := range s.cmds {
|
||||
if cmd.Process != nil {
|
||||
cmd.Process.Signal(sig)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Session) WaitTimeout(timeout time.Duration) (err error) {
|
||||
select {
|
||||
case <-time.After(timeout):
|
||||
s.Kill(syscall.SIGKILL)
|
||||
return ErrExecTimeout
|
||||
case err = <-Go(s.Wait):
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
func Go(f func() error) chan error {
|
||||
ch := make(chan error)
|
||||
go func() {
|
||||
ch <- f()
|
||||
}()
|
||||
return ch
|
||||
}
|
||||
|
||||
func (s *Session) Run() (err error) {
|
||||
if err = s.Start(); err != nil {
|
||||
return
|
||||
}
|
||||
if s.timeout != time.Duration(0) {
|
||||
return s.WaitTimeout(s.timeout)
|
||||
}
|
||||
return s.Wait()
|
||||
}
|
||||
|
||||
func (s *Session) Output() (out []byte, err error) {
|
||||
oldout := s.Stdout
|
||||
defer func() {
|
||||
s.Stdout = oldout
|
||||
}()
|
||||
stdout := bytes.NewBuffer(nil)
|
||||
s.Stdout = stdout
|
||||
err = s.Run()
|
||||
out = stdout.Bytes()
|
||||
return
|
||||
}
|
||||
|
||||
func (s *Session) CombinedOutput() (out []byte, err error) {
|
||||
oldout := s.Stdout
|
||||
olderr := s.Stderr
|
||||
defer func() {
|
||||
s.Stdout = oldout
|
||||
s.Stderr = olderr
|
||||
}()
|
||||
stdout := bytes.NewBuffer(nil)
|
||||
s.Stdout = stdout
|
||||
s.Stderr = stdout
|
||||
|
||||
err = s.Run()
|
||||
out = stdout.Bytes()
|
||||
return
|
||||
}
|
||||
126
plugins/common/vendor/github.com/codeskyblue/go-sh/pipe_test.go
generated
vendored
Normal file
126
plugins/common/vendor/github.com/codeskyblue/go-sh/pipe_test.go
generated
vendored
Normal file
@@ -0,0 +1,126 @@
|
||||
package sh
|
||||
|
||||
import (
|
||||
"encoding/xml"
|
||||
"io"
|
||||
"os"
|
||||
"os/exec"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestUnmarshalJSON(t *testing.T) {
|
||||
var a int
|
||||
s := NewSession()
|
||||
s.ShowCMD = true
|
||||
err := s.Command("echo", []string{"1"}).UnmarshalJSON(&a)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
if a != 1 {
|
||||
t.Errorf("expect a tobe 1, but got %d", a)
|
||||
}
|
||||
}
|
||||
|
||||
func TestUnmarshalXML(t *testing.T) {
|
||||
s := NewSession()
|
||||
xmlSample := `<?xml version="1.0" encoding="utf-8"?>
|
||||
<server version="1" />`
|
||||
type server struct {
|
||||
XMLName xml.Name `xml:"server"`
|
||||
Version string `xml:"version,attr"`
|
||||
}
|
||||
data := &server{}
|
||||
s.Command("echo", xmlSample).UnmarshalXML(data)
|
||||
if data.Version != "1" {
|
||||
t.Error(data)
|
||||
}
|
||||
}
|
||||
|
||||
func TestPipe(t *testing.T) {
|
||||
s := NewSession()
|
||||
s.ShowCMD = true
|
||||
s.Call("echo", "hello")
|
||||
err := s.Command("echo", "hi").Command("cat", "-n").Start()
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
err = s.Wait()
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
out, err := s.Command("echo", []string{"hello"}).Output()
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
if string(out) != "hello\n" {
|
||||
t.Error("capture wrong output:", out)
|
||||
}
|
||||
s.Command("echo", []string{"hello\tworld"}).Command("cut", []string{"-f2"}).Run()
|
||||
}
|
||||
|
||||
func TestPipeCommand(t *testing.T) {
|
||||
c1 := exec.Command("echo", "good")
|
||||
rd, wr := io.Pipe()
|
||||
c1.Stdout = wr
|
||||
c2 := exec.Command("cat", "-n")
|
||||
c2.Stdout = os.Stdout
|
||||
c2.Stdin = rd
|
||||
c1.Start()
|
||||
c2.Start()
|
||||
|
||||
c1.Wait()
|
||||
wc, ok := c1.Stdout.(io.WriteCloser)
|
||||
if ok {
|
||||
wc.Close()
|
||||
}
|
||||
c2.Wait()
|
||||
}
|
||||
|
||||
func TestPipeInput(t *testing.T) {
|
||||
s := NewSession()
|
||||
s.ShowCMD = true
|
||||
s.SetInput("first line\nsecond line\n")
|
||||
out, err := s.Command("grep", "second").Output()
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
if string(out) != "second line\n" {
|
||||
t.Error("capture wrong output:", out)
|
||||
}
|
||||
}
|
||||
|
||||
func TestTimeout(t *testing.T) {
|
||||
s := NewSession()
|
||||
err := s.Command("sleep", "2").Start()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
err = s.WaitTimeout(time.Second)
|
||||
if err != ErrExecTimeout {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSetTimeout(t *testing.T) {
|
||||
s := NewSession()
|
||||
s.SetTimeout(time.Second)
|
||||
defer s.SetTimeout(0)
|
||||
err := s.Command("sleep", "2").Run()
|
||||
if err != ErrExecTimeout {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCombinedOutput(t *testing.T) {
|
||||
s := NewSession()
|
||||
bytes, err := s.Command("sh", "-c", "echo stderr >&2 ; echo stdout").CombinedOutput()
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
stringOutput := string(bytes)
|
||||
if !(strings.Contains(stringOutput, "stdout") && strings.Contains(stringOutput, "stderr")) {
|
||||
t.Errorf("expect output from both output streams, got '%s'", strings.TrimSpace(stringOutput))
|
||||
}
|
||||
}
|
||||
200
plugins/common/vendor/github.com/codeskyblue/go-sh/sh.go
generated
vendored
Normal file
200
plugins/common/vendor/github.com/codeskyblue/go-sh/sh.go
generated
vendored
Normal file
@@ -0,0 +1,200 @@
|
||||
/*
|
||||
Package go-sh is intented to make shell call with golang more easily.
|
||||
Some usage is more similar to os/exec, eg: Run(), Output(), Command(name, args...)
|
||||
|
||||
But with these similar function, pipe is added in and this package also got shell-session support.
|
||||
|
||||
Why I love golang so much, because the usage of golang is simple, but the power is unlimited. I want to make this pakcage got the sample style like golang.
|
||||
|
||||
// just like os/exec
|
||||
sh.Command("echo", "hello").Run()
|
||||
|
||||
// support pipe
|
||||
sh.Command("echo", "hello").Command("wc", "-c").Run()
|
||||
|
||||
// create a session to store dir and env
|
||||
sh.NewSession().SetDir("/").Command("pwd")
|
||||
|
||||
// shell buildin command - "test"
|
||||
sh.Test("dir", "mydir")
|
||||
|
||||
// like shell call: (cd /; pwd)
|
||||
sh.Command("pwd", sh.Dir("/")) same with sh.Command(sh.Dir("/"), "pwd")
|
||||
|
||||
// output to json and xml easily
|
||||
v := map[string] int {}
|
||||
err = sh.Command("echo", `{"number": 1}`).UnmarshalJSON(&v)
|
||||
*/
|
||||
package sh
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"os/exec"
|
||||
"reflect"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/codegangsta/inject"
|
||||
)
|
||||
|
||||
type Dir string
|
||||
|
||||
type Session struct {
|
||||
inj inject.Injector
|
||||
alias map[string][]string
|
||||
cmds []*exec.Cmd
|
||||
dir Dir
|
||||
started bool
|
||||
Env map[string]string
|
||||
Stdin io.Reader
|
||||
Stdout io.Writer
|
||||
Stderr io.Writer
|
||||
ShowCMD bool // enable for debug
|
||||
timeout time.Duration
|
||||
}
|
||||
|
||||
func (s *Session) writePrompt(args ...interface{}) {
|
||||
var ps1 = fmt.Sprintf("[golang-sh]$")
|
||||
args = append([]interface{}{ps1}, args...)
|
||||
fmt.Fprintln(s.Stderr, args...)
|
||||
}
|
||||
|
||||
func NewSession() *Session {
|
||||
env := make(map[string]string)
|
||||
for _, key := range []string{"PATH"} {
|
||||
env[key] = os.Getenv(key)
|
||||
}
|
||||
s := &Session{
|
||||
inj: inject.New(),
|
||||
alias: make(map[string][]string),
|
||||
dir: Dir(""),
|
||||
Stdin: strings.NewReader(""),
|
||||
Stdout: os.Stdout,
|
||||
Stderr: os.Stderr,
|
||||
Env: env,
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
func InteractiveSession() *Session {
|
||||
s := NewSession()
|
||||
s.SetStdin(os.Stdin)
|
||||
return s
|
||||
}
|
||||
|
||||
func Command(name string, a ...interface{}) *Session {
|
||||
s := NewSession()
|
||||
return s.Command(name, a...)
|
||||
}
|
||||
|
||||
func Echo(in string) *Session {
|
||||
s := NewSession()
|
||||
return s.SetInput(in)
|
||||
}
|
||||
|
||||
func (s *Session) Alias(alias, cmd string, args ...string) {
|
||||
v := []string{cmd}
|
||||
v = append(v, args...)
|
||||
s.alias[alias] = v
|
||||
}
|
||||
|
||||
func (s *Session) Command(name string, a ...interface{}) *Session {
|
||||
var args = make([]string, 0)
|
||||
var sType = reflect.TypeOf("")
|
||||
|
||||
// init cmd, args, dir, envs
|
||||
// if not init, program may panic
|
||||
s.inj.Map(name).Map(args).Map(s.dir).Map(map[string]string{})
|
||||
for _, v := range a {
|
||||
switch reflect.TypeOf(v) {
|
||||
case sType:
|
||||
args = append(args, v.(string))
|
||||
default:
|
||||
s.inj.Map(v)
|
||||
}
|
||||
}
|
||||
if len(args) != 0 {
|
||||
s.inj.Map(args)
|
||||
}
|
||||
s.inj.Invoke(s.appendCmd)
|
||||
return s
|
||||
}
|
||||
|
||||
// combine Command and Run
|
||||
func (s *Session) Call(name string, a ...interface{}) error {
|
||||
return s.Command(name, a...).Run()
|
||||
}
|
||||
|
||||
/*
|
||||
func (s *Session) Exec(cmd string, args ...string) error {
|
||||
return s.Call(cmd, args)
|
||||
}
|
||||
*/
|
||||
|
||||
func (s *Session) SetEnv(key, value string) *Session {
|
||||
s.Env[key] = value
|
||||
return s
|
||||
}
|
||||
|
||||
func (s *Session) SetDir(dir string) *Session {
|
||||
s.dir = Dir(dir)
|
||||
return s
|
||||
}
|
||||
|
||||
func (s *Session) SetInput(in string) *Session {
|
||||
s.Stdin = strings.NewReader(in)
|
||||
return s
|
||||
}
|
||||
|
||||
func (s *Session) SetStdin(r io.Reader) *Session {
|
||||
s.Stdin = r
|
||||
return s
|
||||
}
|
||||
|
||||
func (s *Session) SetTimeout(d time.Duration) *Session {
|
||||
s.timeout = d
|
||||
return s
|
||||
}
|
||||
|
||||
func newEnviron(env map[string]string, inherit bool) []string { //map[string]string {
|
||||
environ := make([]string, 0, len(env))
|
||||
if inherit {
|
||||
for _, line := range os.Environ() {
|
||||
for k, _ := range env {
|
||||
if strings.HasPrefix(line, k+"=") {
|
||||
goto CONTINUE
|
||||
}
|
||||
}
|
||||
environ = append(environ, line)
|
||||
CONTINUE:
|
||||
}
|
||||
}
|
||||
for k, v := range env {
|
||||
environ = append(environ, k+"="+v)
|
||||
}
|
||||
return environ
|
||||
}
|
||||
|
||||
func (s *Session) appendCmd(cmd string, args []string, cwd Dir, env map[string]string) {
|
||||
if s.started {
|
||||
s.started = false
|
||||
s.cmds = make([]*exec.Cmd, 0)
|
||||
}
|
||||
for k, v := range s.Env {
|
||||
if _, ok := env[k]; !ok {
|
||||
env[k] = v
|
||||
}
|
||||
}
|
||||
environ := newEnviron(s.Env, true) // true: inherit sys-env
|
||||
v, ok := s.alias[cmd]
|
||||
if ok {
|
||||
cmd = v[0]
|
||||
args = append(v[1:], args...)
|
||||
}
|
||||
c := exec.Command(cmd, args...)
|
||||
c.Env = environ
|
||||
c.Dir = string(cwd)
|
||||
s.cmds = append(s.cmds, c)
|
||||
}
|
||||
106
plugins/common/vendor/github.com/codeskyblue/go-sh/sh_test.go
generated
vendored
Normal file
106
plugins/common/vendor/github.com/codeskyblue/go-sh/sh_test.go
generated
vendored
Normal file
@@ -0,0 +1,106 @@
|
||||
package sh
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"runtime"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestAlias(t *testing.T) {
|
||||
s := NewSession()
|
||||
s.Alias("gr", "echo", "hi")
|
||||
out, err := s.Command("gr", "sky").Output()
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
if string(out) != "hi sky\n" {
|
||||
t.Errorf("expect 'hi sky' but got:%s", string(out))
|
||||
}
|
||||
}
|
||||
|
||||
func ExampleSession_Command() {
|
||||
s := NewSession()
|
||||
out, err := s.Command("echo", "hello").Output()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
fmt.Println(string(out))
|
||||
// Output: hello
|
||||
}
|
||||
|
||||
func ExampleSession_Command_pipe() {
|
||||
s := NewSession()
|
||||
out, err := s.Command("echo", "hello", "world").Command("awk", "{print $2}").Output()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
fmt.Println(string(out))
|
||||
// Output: world
|
||||
}
|
||||
|
||||
func ExampleSession_Alias() {
|
||||
s := NewSession()
|
||||
s.Alias("alias_echo_hello", "echo", "hello")
|
||||
out, err := s.Command("alias_echo_hello", "world").Output()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
fmt.Println(string(out))
|
||||
// Output: hello world
|
||||
}
|
||||
|
||||
func TestEcho(t *testing.T) {
|
||||
out, err := Echo("one two three").Command("wc", "-w").Output()
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
if strings.TrimSpace(string(out)) != "3" {
|
||||
t.Errorf("expect '3' but got:%s", string(out))
|
||||
}
|
||||
}
|
||||
|
||||
func TestSession(t *testing.T) {
|
||||
if runtime.GOOS == "windows" {
|
||||
t.Log("ignore test on windows")
|
||||
return
|
||||
}
|
||||
session := NewSession()
|
||||
session.ShowCMD = true
|
||||
err := session.Call("pwd")
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
out, err := session.SetDir("/").Command("pwd").Output()
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
if string(out) != "/\n" {
|
||||
t.Errorf("expect /, but got %s", string(out))
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
#!/bin/bash -
|
||||
#
|
||||
export PATH=/usr/bin:/bin
|
||||
alias ll='ls -l'
|
||||
cd /usr
|
||||
if test -d "local"
|
||||
then
|
||||
ll local | awk '{print $1, $NF}' | grep bin
|
||||
fi
|
||||
*/
|
||||
func Example(t *testing.T) {
|
||||
s := NewSession()
|
||||
//s.ShowCMD = true
|
||||
s.Env["PATH"] = "/usr/bin:/bin"
|
||||
s.SetDir("/bin")
|
||||
s.Alias("ll", "ls", "-l")
|
||||
|
||||
if s.Test("d", "local") {
|
||||
//s.Command("ll", []string{"local"}).Command("awk", []string{"{print $1, $NF}"}).Command("grep", []string{"bin"}).Run()
|
||||
s.Command("ll", "local").Command("awk", "{print $1, $NF}").Command("grep", "bin").Run()
|
||||
}
|
||||
}
|
||||
64
plugins/common/vendor/github.com/codeskyblue/go-sh/test.go
generated
vendored
Normal file
64
plugins/common/vendor/github.com/codeskyblue/go-sh/test.go
generated
vendored
Normal file
@@ -0,0 +1,64 @@
|
||||
package sh
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
func filetest(name string, modemask os.FileMode) (match bool, err error) {
|
||||
fi, err := os.Stat(name)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
match = (fi.Mode() & modemask) == modemask
|
||||
return
|
||||
}
|
||||
|
||||
func (s *Session) pwd() string {
|
||||
dir := string(s.dir)
|
||||
if dir == "" {
|
||||
dir, _ = os.Getwd()
|
||||
}
|
||||
return dir
|
||||
}
|
||||
|
||||
func (s *Session) abspath(name string) string {
|
||||
if filepath.IsAbs(name) {
|
||||
return name
|
||||
}
|
||||
return filepath.Join(s.pwd(), name)
|
||||
}
|
||||
|
||||
func init() {
|
||||
//log.SetFlags(log.Lshortfile | log.LstdFlags)
|
||||
}
|
||||
|
||||
// expression can be dir, file, link
|
||||
func (s *Session) Test(expression string, argument string) bool {
|
||||
var err error
|
||||
var fi os.FileInfo
|
||||
fi, err = os.Lstat(s.abspath(argument))
|
||||
switch expression {
|
||||
case "d", "dir":
|
||||
return err == nil && fi.IsDir()
|
||||
case "f", "file":
|
||||
return err == nil && fi.Mode().IsRegular()
|
||||
case "x", "executable":
|
||||
/*
|
||||
fmt.Println(expression, argument)
|
||||
if err == nil {
|
||||
fmt.Println(fi.Mode())
|
||||
}
|
||||
*/
|
||||
return err == nil && fi.Mode()&os.FileMode(0100) != 0
|
||||
case "L", "link":
|
||||
return err == nil && fi.Mode()&os.ModeSymlink != 0
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// expression can be d,dir, f,file, link
|
||||
func Test(exp string, arg string) bool {
|
||||
s := NewSession()
|
||||
return s.Test(exp, arg)
|
||||
}
|
||||
58
plugins/common/vendor/github.com/codeskyblue/go-sh/test_test.go
generated
vendored
Normal file
58
plugins/common/vendor/github.com/codeskyblue/go-sh/test_test.go
generated
vendored
Normal file
@@ -0,0 +1,58 @@
|
||||
package sh_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/codeskyblue/go-sh"
|
||||
)
|
||||
|
||||
var s = sh.NewSession()
|
||||
|
||||
type T struct{ *testing.T }
|
||||
|
||||
func NewT(t *testing.T) *T {
|
||||
return &T{t}
|
||||
}
|
||||
|
||||
func (t *T) checkTest(exp string, arg string, result bool) {
|
||||
r := s.Test(exp, arg)
|
||||
if r != result {
|
||||
t.Errorf("test -%s %s, %v != %v", exp, arg, r, result)
|
||||
}
|
||||
}
|
||||
|
||||
func TestTest(i *testing.T) {
|
||||
t := NewT(i)
|
||||
t.checkTest("d", "../go-sh", true)
|
||||
t.checkTest("d", "./yymm", false)
|
||||
|
||||
// file test
|
||||
t.checkTest("f", "testdata/hello.txt", true)
|
||||
t.checkTest("f", "testdata/xxxxx", false)
|
||||
t.checkTest("f", "testdata/yymm", false)
|
||||
|
||||
// link test
|
||||
t.checkTest("link", "testdata/linkfile", true)
|
||||
t.checkTest("link", "testdata/xxxxxlinkfile", false)
|
||||
t.checkTest("link", "testdata/hello.txt", false)
|
||||
|
||||
// executable test
|
||||
t.checkTest("x", "testdata/executable", true)
|
||||
t.checkTest("x", "testdata/xxxxx", false)
|
||||
t.checkTest("x", "testdata/hello.txt", false)
|
||||
}
|
||||
|
||||
func ExampleShellTest(t *testing.T) {
|
||||
// test -L
|
||||
sh.Test("link", "testdata/linkfile")
|
||||
sh.Test("L", "testdata/linkfile")
|
||||
// test -f
|
||||
sh.Test("file", "testdata/file")
|
||||
sh.Test("f", "testdata/file")
|
||||
// test -x
|
||||
sh.Test("executable", "testdata/binfile")
|
||||
sh.Test("x", "testdata/binfile")
|
||||
// test -d
|
||||
sh.Test("dir", "testdata/dir")
|
||||
sh.Test("d", "testdata/dir")
|
||||
}
|
||||
0
plugins/common/vendor/github.com/codeskyblue/go-sh/testdata/executable
generated
vendored
Executable file
0
plugins/common/vendor/github.com/codeskyblue/go-sh/testdata/executable
generated
vendored
Executable file
0
plugins/common/vendor/github.com/codeskyblue/go-sh/testdata/hello.txt
generated
vendored
Normal file
0
plugins/common/vendor/github.com/codeskyblue/go-sh/testdata/hello.txt
generated
vendored
Normal file
1
plugins/common/vendor/github.com/codeskyblue/go-sh/testdata/linkfile
generated
vendored
Symbolic link
1
plugins/common/vendor/github.com/codeskyblue/go-sh/testdata/linkfile
generated
vendored
Symbolic link
@@ -0,0 +1 @@
|
||||
hello.txt
|
||||
28
plugins/common/vendor/github.com/codeskyblue/go-sh/wercker.yml
generated
vendored
Normal file
28
plugins/common/vendor/github.com/codeskyblue/go-sh/wercker.yml
generated
vendored
Normal file
@@ -0,0 +1,28 @@
|
||||
box: wercker/golang
|
||||
# Build definition
|
||||
build:
|
||||
# The steps that will be executed on build
|
||||
steps:
|
||||
# Sets the go workspace and places you package
|
||||
# at the right place in the workspace tree
|
||||
- setup-go-workspace
|
||||
|
||||
# Gets the dependencies
|
||||
- script:
|
||||
name: go get
|
||||
code: |
|
||||
cd $WERCKER_SOURCE_DIR
|
||||
go version
|
||||
go get -t .
|
||||
|
||||
# Build the project
|
||||
- script:
|
||||
name: go build
|
||||
code: |
|
||||
go build .
|
||||
|
||||
# Test the project
|
||||
- script:
|
||||
name: go test
|
||||
code: |
|
||||
go test -v ./...
|
||||
14
plugins/repo/Makefile
Normal file
14
plugins/repo/Makefile
Normal file
@@ -0,0 +1,14 @@
|
||||
.PHONY: build-in-docker build commands subcommands
|
||||
|
||||
build-in-docker: clean
|
||||
docker run --rm \
|
||||
-v $$PWD/../..:$(GO_REPO_ROOT) \
|
||||
-w $(GO_REPO_ROOT)/plugins/repo \
|
||||
$(BUILD_IMAGE) \
|
||||
bash -c "make build" || exit $$?
|
||||
|
||||
build:
|
||||
cd src && go build -a -o ../commands
|
||||
|
||||
clean:
|
||||
rm -f commands
|
||||
58
plugins/repo/src/commands.go
Normal file
58
plugins/repo/src/commands.go
Normal file
@@ -0,0 +1,58 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
columnize "github.com/ryanuber/columnize"
|
||||
)
|
||||
|
||||
const (
|
||||
helpHeader = `Usage: dokku repo[:COMMAND]
|
||||
|
||||
Runs commands that interact with the app's repo
|
||||
|
||||
Additional commands:`
|
||||
|
||||
helpContent = `
|
||||
repo:gc <app>, Runs 'git gc --aggressive' against the application's repo
|
||||
repo:purge-cache <app>, Deletes the contents of the build cache stored in the repository
|
||||
`
|
||||
)
|
||||
|
||||
func main() {
|
||||
flag.Usage = usage
|
||||
flag.Parse()
|
||||
|
||||
cmd := flag.Arg(0)
|
||||
switch cmd {
|
||||
case "repo:help":
|
||||
usage()
|
||||
case "help":
|
||||
fmt.Print(helpContent)
|
||||
case "repo:gc":
|
||||
gitGC()
|
||||
case "repo:purge-cache":
|
||||
purgeCache()
|
||||
default:
|
||||
dokkuNotImplementExitCode, err := strconv.Atoi(os.Getenv("DOKKU_NOT_IMPLEMENTED_EXIT"))
|
||||
if err != nil {
|
||||
fmt.Println("failed to parse DOKKU_NOT_IMPLEMENTED_EXIT")
|
||||
dokkuNotImplementExitCode = 10
|
||||
}
|
||||
os.Exit(dokkuNotImplementExitCode)
|
||||
}
|
||||
}
|
||||
|
||||
func usage() {
|
||||
config := columnize.DefaultConfig()
|
||||
config.Delim = ","
|
||||
config.Prefix = "\t"
|
||||
config.Empty = ""
|
||||
content := strings.Split(helpContent, "\n")[1:]
|
||||
fmt.Println(helpHeader)
|
||||
fmt.Println(columnize.Format(content, config))
|
||||
}
|
||||
29
plugins/repo/src/gc.go
Normal file
29
plugins/repo/src/gc.go
Normal file
@@ -0,0 +1,29 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"strings"
|
||||
|
||||
common "github.com/dokku/dokku/plugins/common"
|
||||
)
|
||||
|
||||
// runs 'git gc --aggressive' against the application's repo
|
||||
func gitGC() {
|
||||
flag.Parse()
|
||||
appName := flag.Arg(1)
|
||||
if appName == "" {
|
||||
common.LogFail("Please specify an app to run the command on")
|
||||
}
|
||||
err := common.VerifyAppName(appName)
|
||||
if err != nil {
|
||||
common.LogFail(err.Error())
|
||||
}
|
||||
|
||||
appRoot := strings.Join([]string{common.MustGetEnv("DOKKU_ROOT"), appName}, "/")
|
||||
cmdEnv := map[string]string{
|
||||
"GIT_DIR": appRoot,
|
||||
}
|
||||
gitGcCmd := common.NewDokkuCmd("git gc --aggressive")
|
||||
gitGcCmd.Env = cmdEnv
|
||||
gitGcCmd.Execute()
|
||||
}
|
||||
6
plugins/repo/src/glide.lock
generated
Normal file
6
plugins/repo/src/glide.lock
generated
Normal file
@@ -0,0 +1,6 @@
|
||||
hash: 1ddab5de41d1514c2722bd7e24758ad4b60bf6956eb5b9b925fa071a1427f149
|
||||
updated: 2017-01-03T17:16:50.97156327-08:00
|
||||
imports:
|
||||
- name: github.com/ryanuber/columnize
|
||||
version: 0fbbb3f0e3fbdc5bae7c6cd5f6c1887ebfb76360
|
||||
testImports: []
|
||||
3
plugins/repo/src/glide.yaml
Normal file
3
plugins/repo/src/glide.yaml
Normal file
@@ -0,0 +1,3 @@
|
||||
package: .
|
||||
import:
|
||||
- package: github.com/ryanuber/columnize
|
||||
36
plugins/repo/src/purge-cache.go
Normal file
36
plugins/repo/src/purge-cache.go
Normal file
@@ -0,0 +1,36 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
common "github.com/dokku/dokku/plugins/common"
|
||||
)
|
||||
|
||||
// deletes the contents of the build cache stored in the repository
|
||||
func purgeCache() {
|
||||
flag.Parse()
|
||||
appName := flag.Arg(1)
|
||||
if appName == "" {
|
||||
common.LogFail("Please specify an app to run the command on")
|
||||
}
|
||||
err := common.VerifyAppName(appName)
|
||||
if err != nil {
|
||||
common.LogFail(err.Error())
|
||||
}
|
||||
|
||||
cacheDir := strings.Join([]string{common.MustGetEnv("DOKKU_ROOT"), appName, "cache"}, "/")
|
||||
dokkuGlobalRunArgs := common.MustGetEnv("DOKKU_GLOBAL_RUN_ARGS")
|
||||
image := common.GetDeployingAppImageName(appName)
|
||||
if info, _ := os.Stat(cacheDir); info != nil && info.IsDir() {
|
||||
purgeCacheCmd := common.NewDokkuCmd(strings.Join([]string{"docker run --rm", dokkuGlobalRunArgs,
|
||||
"-v", strings.Join([]string{cacheDir, ":/cache"}, ""), image,
|
||||
`find /cache -depth -mindepth 1 -maxdepth 1 -exec rm -Rf {} ;`}, " "))
|
||||
purgeCacheCmd.Execute()
|
||||
err := os.MkdirAll(cacheDir, 0644)
|
||||
if err != nil {
|
||||
common.LogFail(err.Error())
|
||||
}
|
||||
}
|
||||
}
|
||||
3
plugins/repo/src/vendor/github.com/ryanuber/columnize/.travis.yml
generated
vendored
Normal file
3
plugins/repo/src/vendor/github.com/ryanuber/columnize/.travis.yml
generated
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
language: go
|
||||
go:
|
||||
- tip
|
||||
20
plugins/repo/src/vendor/github.com/ryanuber/columnize/LICENSE
generated
vendored
Normal file
20
plugins/repo/src/vendor/github.com/ryanuber/columnize/LICENSE
generated
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
Copyright (c) 2016 Ryan Uber
|
||||
|
||||
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.
|
||||
69
plugins/repo/src/vendor/github.com/ryanuber/columnize/README.md
generated
vendored
Normal file
69
plugins/repo/src/vendor/github.com/ryanuber/columnize/README.md
generated
vendored
Normal file
@@ -0,0 +1,69 @@
|
||||
Columnize
|
||||
=========
|
||||
|
||||
Easy column-formatted output for golang
|
||||
|
||||
[](https://travis-ci.org/ryanuber/columnize)
|
||||
[](https://godoc.org/github.com/ryanuber/columnize)
|
||||
|
||||
Columnize is a really small Go package that makes building CLI's a little bit
|
||||
easier. In some CLI designs, you want to output a number similar items in a
|
||||
human-readable way with nicely aligned columns. However, figuring out how wide
|
||||
to make each column is a boring problem to solve and eats your valuable time.
|
||||
|
||||
Here is an example:
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/ryanuber/columnize"
|
||||
)
|
||||
|
||||
func main() {
|
||||
output := []string{
|
||||
"Name | Gender | Age",
|
||||
"Bob | Male | 38",
|
||||
"Sally | Female | 26",
|
||||
}
|
||||
result := columnize.SimpleFormat(output)
|
||||
fmt.Println(result)
|
||||
}
|
||||
```
|
||||
|
||||
As you can see, you just pass in a list of strings. And the result:
|
||||
|
||||
```
|
||||
Name Gender Age
|
||||
Bob Male 38
|
||||
Sally Female 26
|
||||
```
|
||||
|
||||
Columnize is tolerant of missing or empty fields, or even empty lines, so
|
||||
passing in extra lines for spacing should show up as you would expect.
|
||||
|
||||
Configuration
|
||||
=============
|
||||
|
||||
Columnize is configured using a `Config`, which can be obtained by calling the
|
||||
`DefaultConfig()` method. You can then tweak the settings in the resulting
|
||||
`Config`:
|
||||
|
||||
```
|
||||
config := columnize.DefaultConfig()
|
||||
config.Delim = "|"
|
||||
config.Glue = " "
|
||||
config.Prefix = ""
|
||||
config.Empty = ""
|
||||
```
|
||||
|
||||
* `Delim` is the string by which columns of **input** are delimited
|
||||
* `Glue` is the string by which columns of **output** are delimited
|
||||
* `Prefix` is a string by which each line of **output** is prefixed
|
||||
* `Empty` is a string used to replace blank values found in output
|
||||
|
||||
You can then pass the `Config` in using the `Format` method (signature below) to
|
||||
have text formatted to your liking.
|
||||
|
||||
See the [godoc](https://godoc.org/github.com/ryanuber/columnize) page for usage.
|
||||
178
plugins/repo/src/vendor/github.com/ryanuber/columnize/columnize.go
generated
vendored
Normal file
178
plugins/repo/src/vendor/github.com/ryanuber/columnize/columnize.go
generated
vendored
Normal file
@@ -0,0 +1,178 @@
|
||||
package columnize
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Config can be used to tune certain parameters which affect the way
|
||||
// in which Columnize will format output text.
|
||||
type Config struct {
|
||||
// The string by which the lines of input will be split.
|
||||
Delim string
|
||||
|
||||
// The string by which columns of output will be separated.
|
||||
Glue string
|
||||
|
||||
// The string by which columns of output will be prefixed.
|
||||
Prefix string
|
||||
|
||||
// A replacement string to replace empty fields
|
||||
Empty string
|
||||
}
|
||||
|
||||
// DefaultConfig returns a *Config with default values.
|
||||
func DefaultConfig() *Config {
|
||||
return &Config{
|
||||
Delim: "|",
|
||||
Glue: " ",
|
||||
Prefix: "",
|
||||
Empty: "",
|
||||
}
|
||||
}
|
||||
|
||||
// MergeConfig merges two config objects together and returns the resulting
|
||||
// configuration. Values from the right take precedence over the left side.
|
||||
func MergeConfig(a, b *Config) *Config {
|
||||
var result Config = *a
|
||||
|
||||
// Return quickly if either side was nil
|
||||
if a == nil || b == nil {
|
||||
return &result
|
||||
}
|
||||
|
||||
if b.Delim != "" {
|
||||
result.Delim = b.Delim
|
||||
}
|
||||
if b.Glue != "" {
|
||||
result.Glue = b.Glue
|
||||
}
|
||||
if b.Prefix != "" {
|
||||
result.Prefix = b.Prefix
|
||||
}
|
||||
if b.Empty != "" {
|
||||
result.Empty = b.Empty
|
||||
}
|
||||
|
||||
return &result
|
||||
}
|
||||
|
||||
// stringFormat, given a set of column widths and the number of columns in
|
||||
// the current line, returns a sprintf-style format string which can be used
|
||||
// to print output aligned properly with other lines using the same widths set.
|
||||
func stringFormat(c *Config, widths []int, columns int) string {
|
||||
// Create the buffer with an estimate of the length
|
||||
buf := bytes.NewBuffer(make([]byte, 0, (6+len(c.Glue))*columns))
|
||||
|
||||
// Start with the prefix, if any was given. The buffer will not return an
|
||||
// error so it does not need to be handled
|
||||
buf.WriteString(c.Prefix)
|
||||
|
||||
// Create the format string from the discovered widths
|
||||
for i := 0; i < columns && i < len(widths); i++ {
|
||||
if i == columns-1 {
|
||||
buf.WriteString("%s\n")
|
||||
} else {
|
||||
fmt.Fprintf(buf, "%%-%ds%s", widths[i], c.Glue)
|
||||
}
|
||||
}
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
// elementsFromLine returns a list of elements, each representing a single
|
||||
// item which will belong to a column of output.
|
||||
func elementsFromLine(config *Config, line string) []interface{} {
|
||||
seperated := strings.Split(line, config.Delim)
|
||||
elements := make([]interface{}, len(seperated))
|
||||
for i, field := range seperated {
|
||||
value := strings.TrimSpace(field)
|
||||
|
||||
// Apply the empty value, if configured.
|
||||
if value == "" && config.Empty != "" {
|
||||
value = config.Empty
|
||||
}
|
||||
elements[i] = value
|
||||
}
|
||||
return elements
|
||||
}
|
||||
|
||||
// runeLen calculates the number of visible "characters" in a string
|
||||
func runeLen(s string) int {
|
||||
l := 0
|
||||
for _ = range s {
|
||||
l++
|
||||
}
|
||||
return l
|
||||
}
|
||||
|
||||
// widthsFromLines examines a list of strings and determines how wide each
|
||||
// column should be considering all of the elements that need to be printed
|
||||
// within it.
|
||||
func widthsFromLines(config *Config, lines []string) []int {
|
||||
widths := make([]int, 0, 8)
|
||||
|
||||
for _, line := range lines {
|
||||
elems := elementsFromLine(config, line)
|
||||
for i := 0; i < len(elems); i++ {
|
||||
l := runeLen(elems[i].(string))
|
||||
if len(widths) <= i {
|
||||
widths = append(widths, l)
|
||||
} else if widths[i] < l {
|
||||
widths[i] = l
|
||||
}
|
||||
}
|
||||
}
|
||||
return widths
|
||||
}
|
||||
|
||||
// Format is the public-facing interface that takes a list of strings and
|
||||
// returns nicely aligned column-formatted text.
|
||||
func Format(lines []string, config *Config) string {
|
||||
conf := MergeConfig(DefaultConfig(), config)
|
||||
widths := widthsFromLines(conf, lines)
|
||||
|
||||
// Estimate the buffer size
|
||||
glueSize := len(conf.Glue)
|
||||
var size int
|
||||
for _, w := range widths {
|
||||
size += w + glueSize
|
||||
}
|
||||
size *= len(lines)
|
||||
|
||||
// Create the buffer
|
||||
buf := bytes.NewBuffer(make([]byte, 0, size))
|
||||
|
||||
// Create a cache for the string formats
|
||||
fmtCache := make(map[int]string, 16)
|
||||
|
||||
// Create the formatted output using the format string
|
||||
for _, line := range lines {
|
||||
elems := elementsFromLine(conf, line)
|
||||
|
||||
// Get the string format using cache
|
||||
numElems := len(elems)
|
||||
stringfmt, ok := fmtCache[numElems]
|
||||
if !ok {
|
||||
stringfmt = stringFormat(conf, widths, numElems)
|
||||
fmtCache[numElems] = stringfmt
|
||||
}
|
||||
|
||||
fmt.Fprintf(buf, stringfmt, elems...)
|
||||
}
|
||||
|
||||
// Get the string result
|
||||
result := buf.String()
|
||||
|
||||
// Remove trailing newline without removing leading/trailing space
|
||||
if n := len(result); n > 0 && result[n-1] == '\n' {
|
||||
result = result[:n-1]
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// SimpleFormat is a convenience function to format text with the defaults.
|
||||
func SimpleFormat(lines []string) string {
|
||||
return Format(lines, nil)
|
||||
}
|
||||
306
plugins/repo/src/vendor/github.com/ryanuber/columnize/columnize_test.go
generated
vendored
Normal file
306
plugins/repo/src/vendor/github.com/ryanuber/columnize/columnize_test.go
generated
vendored
Normal file
@@ -0,0 +1,306 @@
|
||||
package columnize
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
crand "crypto/rand"
|
||||
)
|
||||
|
||||
func TestListOfStringsInput(t *testing.T) {
|
||||
input := []string{
|
||||
"Column A | Column B | Column C",
|
||||
"x | y | z",
|
||||
}
|
||||
|
||||
config := DefaultConfig()
|
||||
output := Format(input, config)
|
||||
|
||||
expected := "Column A Column B Column C\n"
|
||||
expected += "x y z"
|
||||
|
||||
if output != expected {
|
||||
t.Fatalf("\nexpected:\n%s\n\ngot:\n%s", expected, output)
|
||||
}
|
||||
}
|
||||
|
||||
func TestEmptyLinesOutput(t *testing.T) {
|
||||
input := []string{
|
||||
"Column A | Column B | Column C",
|
||||
"",
|
||||
"x | y | z",
|
||||
}
|
||||
|
||||
config := DefaultConfig()
|
||||
output := Format(input, config)
|
||||
|
||||
expected := "Column A Column B Column C\n"
|
||||
expected += "\n"
|
||||
expected += "x y z"
|
||||
|
||||
if output != expected {
|
||||
t.Fatalf("\nexpected:\n%s\n\ngot:\n%s", expected, output)
|
||||
}
|
||||
}
|
||||
|
||||
func TestLeadingSpacePreserved(t *testing.T) {
|
||||
input := []string{
|
||||
"| Column B | Column C",
|
||||
"x | y | z",
|
||||
}
|
||||
|
||||
config := DefaultConfig()
|
||||
output := Format(input, config)
|
||||
|
||||
expected := " Column B Column C\n"
|
||||
expected += "x y z"
|
||||
|
||||
if output != expected {
|
||||
t.Fatalf("\nexpected:\n%s\n\ngot:\n%s", expected, output)
|
||||
}
|
||||
}
|
||||
|
||||
func TestColumnWidthCalculator(t *testing.T) {
|
||||
input := []string{
|
||||
"Column A | Column B | Column C",
|
||||
"Longer than A | Longer than B | Longer than C",
|
||||
"short | short | short",
|
||||
}
|
||||
|
||||
config := DefaultConfig()
|
||||
output := Format(input, config)
|
||||
|
||||
expected := "Column A Column B Column C\n"
|
||||
expected += "Longer than A Longer than B Longer than C\n"
|
||||
expected += "short short short"
|
||||
|
||||
if output != expected {
|
||||
printableProof := fmt.Sprintf("\nGot: %+q", output)
|
||||
printableProof += fmt.Sprintf("\nExpected: %+q", expected)
|
||||
t.Fatalf("\n%s", printableProof)
|
||||
}
|
||||
}
|
||||
|
||||
func TestColumnWidthCalculatorNonASCII(t *testing.T) {
|
||||
input := []string{
|
||||
"Column A | Column B | Column C",
|
||||
"⌘⌘⌘⌘⌘⌘⌘⌘ | Longer than B | Longer than C",
|
||||
"short | short | short",
|
||||
}
|
||||
|
||||
config := DefaultConfig()
|
||||
output := Format(input, config)
|
||||
|
||||
expected := "Column A Column B Column C\n"
|
||||
expected += "⌘⌘⌘⌘⌘⌘⌘⌘ Longer than B Longer than C\n"
|
||||
expected += "short short short"
|
||||
|
||||
if output != expected {
|
||||
printableProof := fmt.Sprintf("\nGot: %+q", output)
|
||||
printableProof += fmt.Sprintf("\nExpected: %+q", expected)
|
||||
t.Fatalf("\n%s", printableProof)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkColumnWidthCalculator(b *testing.B) {
|
||||
// Generate the input
|
||||
input := []string{
|
||||
"UUID A | UUID B | UUID C | Column D | Column E",
|
||||
}
|
||||
|
||||
format := "%s|%s|%s|%s"
|
||||
short := "short"
|
||||
|
||||
uuid := func() string {
|
||||
buf := make([]byte, 16)
|
||||
if _, err := crand.Read(buf); err != nil {
|
||||
panic(fmt.Errorf("failed to read random bytes: %v", err))
|
||||
}
|
||||
|
||||
return fmt.Sprintf("%08x-%04x-%04x-%04x-%12x",
|
||||
buf[0:4],
|
||||
buf[4:6],
|
||||
buf[6:8],
|
||||
buf[8:10],
|
||||
buf[10:16])
|
||||
}
|
||||
|
||||
for i := 0; i < 1000; i++ {
|
||||
l := fmt.Sprintf(format, uuid()[:8], uuid()[:12], uuid(), short, short)
|
||||
input = append(input, l)
|
||||
}
|
||||
|
||||
config := DefaultConfig()
|
||||
b.ResetTimer()
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
Format(input, config)
|
||||
}
|
||||
}
|
||||
|
||||
func TestVariedInputSpacing(t *testing.T) {
|
||||
input := []string{
|
||||
"Column A |Column B| Column C",
|
||||
"x|y| z",
|
||||
}
|
||||
|
||||
config := DefaultConfig()
|
||||
output := Format(input, config)
|
||||
|
||||
expected := "Column A Column B Column C\n"
|
||||
expected += "x y z"
|
||||
|
||||
if output != expected {
|
||||
t.Fatalf("\nexpected:\n%s\n\ngot:\n%s", expected, output)
|
||||
}
|
||||
}
|
||||
|
||||
func TestUnmatchedColumnCounts(t *testing.T) {
|
||||
input := []string{
|
||||
"Column A | Column B | Column C",
|
||||
"Value A | Value B",
|
||||
"Value A | Value B | Value C | Value D",
|
||||
}
|
||||
|
||||
config := DefaultConfig()
|
||||
output := Format(input, config)
|
||||
|
||||
expected := "Column A Column B Column C\n"
|
||||
expected += "Value A Value B\n"
|
||||
expected += "Value A Value B Value C Value D"
|
||||
|
||||
if output != expected {
|
||||
t.Fatalf("\nexpected:\n%s\n\ngot:\n%s", expected, output)
|
||||
}
|
||||
}
|
||||
|
||||
func TestAlternateDelimiter(t *testing.T) {
|
||||
input := []string{
|
||||
"Column | A % Column | B % Column | C",
|
||||
"Value A % Value B % Value C",
|
||||
}
|
||||
|
||||
config := DefaultConfig()
|
||||
config.Delim = "%"
|
||||
output := Format(input, config)
|
||||
|
||||
expected := "Column | A Column | B Column | C\n"
|
||||
expected += "Value A Value B Value C"
|
||||
|
||||
if output != expected {
|
||||
t.Fatalf("\nexpected:\n%s\n\ngot:\n%s", expected, output)
|
||||
}
|
||||
}
|
||||
|
||||
func TestAlternateSpacingString(t *testing.T) {
|
||||
input := []string{
|
||||
"Column A | Column B | Column C",
|
||||
"x | y | z",
|
||||
}
|
||||
|
||||
config := DefaultConfig()
|
||||
config.Glue = " "
|
||||
output := Format(input, config)
|
||||
|
||||
expected := "Column A Column B Column C\n"
|
||||
expected += "x y z"
|
||||
|
||||
if output != expected {
|
||||
t.Fatalf("\nexpected:\n%s\n\ngot:\n%s", expected, output)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSimpleFormat(t *testing.T) {
|
||||
input := []string{
|
||||
"Column A | Column B | Column C",
|
||||
"x | y | z",
|
||||
}
|
||||
|
||||
output := SimpleFormat(input)
|
||||
|
||||
expected := "Column A Column B Column C\n"
|
||||
expected += "x y z"
|
||||
|
||||
if output != expected {
|
||||
t.Fatalf("\nexpected:\n%s\n\ngot:\n%s", expected, output)
|
||||
}
|
||||
}
|
||||
|
||||
func TestAlternatePrefixString(t *testing.T) {
|
||||
input := []string{
|
||||
"Column A | Column B | Column C",
|
||||
"x | y | z",
|
||||
}
|
||||
|
||||
config := DefaultConfig()
|
||||
config.Prefix = " "
|
||||
output := Format(input, config)
|
||||
|
||||
expected := " Column A Column B Column C\n"
|
||||
expected += " x y z"
|
||||
|
||||
if output != expected {
|
||||
t.Fatalf("\nexpected:\n%s\n\ngot:\n%s", expected, output)
|
||||
}
|
||||
}
|
||||
|
||||
func TestEmptyFieldReplacement(t *testing.T) {
|
||||
input := []string{
|
||||
"Column A | Column B | Column C",
|
||||
"x | | z",
|
||||
}
|
||||
|
||||
config := DefaultConfig()
|
||||
config.Empty = "<none>"
|
||||
output := Format(input, config)
|
||||
|
||||
expected := "Column A Column B Column C\n"
|
||||
expected += "x <none> z"
|
||||
|
||||
if output != expected {
|
||||
t.Fatalf("\nexpected:\n%s\n\ngot:\n%s", expected, output)
|
||||
}
|
||||
}
|
||||
|
||||
func TestEmptyConfigValues(t *testing.T) {
|
||||
input := []string{
|
||||
"Column A | Column B | Column C",
|
||||
"x | y | z",
|
||||
}
|
||||
|
||||
config := Config{}
|
||||
output := Format(input, &config)
|
||||
|
||||
expected := "Column A Column B Column C\n"
|
||||
expected += "x y z"
|
||||
|
||||
if output != expected {
|
||||
t.Fatalf("\nexpected:\n%s\n\ngot:\n%s", expected, output)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMergeConfig(t *testing.T) {
|
||||
conf1 := &Config{Delim: "a", Glue: "a", Prefix: "a", Empty: "a"}
|
||||
conf2 := &Config{Delim: "b", Glue: "b", Prefix: "b", Empty: "b"}
|
||||
conf3 := &Config{Delim: "c", Prefix: "c"}
|
||||
|
||||
m := MergeConfig(conf1, conf2)
|
||||
if m.Delim != "b" || m.Glue != "b" || m.Prefix != "b" || m.Empty != "b" {
|
||||
t.Fatalf("bad: %#v", m)
|
||||
}
|
||||
|
||||
m = MergeConfig(conf1, conf3)
|
||||
if m.Delim != "c" || m.Glue != "a" || m.Prefix != "c" || m.Empty != "a" {
|
||||
t.Fatalf("bad: %#v", m)
|
||||
}
|
||||
|
||||
m = MergeConfig(conf1, nil)
|
||||
if m.Delim != "a" || m.Glue != "a" || m.Prefix != "a" || m.Empty != "a" {
|
||||
t.Fatalf("bad: %#v", m)
|
||||
}
|
||||
|
||||
m = MergeConfig(conf1, &Config{})
|
||||
if m.Delim != "a" || m.Glue != "a" || m.Prefix != "a" || m.Empty != "a" {
|
||||
t.Fatalf("bad: %#v", m)
|
||||
}
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
set -eo pipefail; [[ $DOKKU_TRACE ]] && set -x
|
||||
source "$PLUGIN_CORE_AVAILABLE_PATH/common/functions"
|
||||
|
||||
repo_purge_cache() {
|
||||
declare desc="deletes the contents of the build cache stored in the repository"
|
||||
local cmd="repo:purge-cache"
|
||||
[[ -z $2 ]] && dokku_log_fail "Please specify an app to run the command on"
|
||||
local APP="$2";
|
||||
verify_app_name "$APP"
|
||||
|
||||
local IMAGE=$(get_deploying_app_image_name "$APP"); local CACHE_DIR="$DOKKU_ROOT/$APP/cache"
|
||||
|
||||
if [[ -d $CACHE_DIR ]]; then
|
||||
docker run "$DOKKU_GLOBAL_RUN_ARGS" --rm -v "$CACHE_DIR:/cache" "$IMAGE" find /cache -depth -mindepth 1 -maxdepth 1 -exec rm -Rf {} \; || true
|
||||
mkdir -p "$CACHE_DIR" || true
|
||||
fi
|
||||
}
|
||||
|
||||
repo_purge_cache "$@"
|
||||
14
tests.mk
14
tests.mk
@@ -73,8 +73,18 @@ lint:
|
||||
@echo linting...
|
||||
@$(QUIET) find . -not -path '*/\.*' -not -path './debian/*' -type f | xargs file | grep text | awk -F ':' '{ print $$1 }' | xargs head -n1 | egrep -B1 "bash" | grep "==>" | awk '{ print $$2 }' | xargs shellcheck -e SC2034
|
||||
|
||||
unit-tests:
|
||||
@echo running unit tests...
|
||||
go-tests:
|
||||
@echo running go unit tests...
|
||||
docker run --rm -ti \
|
||||
-e DOKKU_ROOT=/home/dokku \
|
||||
-v $$PWD:$(GO_REPO_ROOT) \
|
||||
-w $(GO_REPO_ROOT) \
|
||||
$(BUILD_IMAGE) \
|
||||
bash -c "go get github.com/onsi/gomega && \
|
||||
go list ./... | grep -v /vendor/ | grep -v /tests/apps/ | xargs go test -v -p 1 -race" || exit $$?
|
||||
|
||||
unit-tests: go-tests
|
||||
@echo running bats unit tests...
|
||||
ifndef UNIT_TEST_BATCH
|
||||
@$(QUIET) bats tests/unit
|
||||
else
|
||||
|
||||
Reference in New Issue
Block a user