diff --git a/plugins/config/glide.lock b/plugins/config/glide.lock index dc3e9faad..025644fb0 100644 --- a/plugins/config/glide.lock +++ b/plugins/config/glide.lock @@ -1,7 +1,8 @@ -hash: 998d313aef819c8fa68d268d075079d281a7ccab0c3a16f6d8906b20c902f627 -updated: 2017-05-20T15:15:41.795778946-04:00 +hash: 84462ba8482aa8c12a928ab1851f212d558df5036311ec22469fdf86b77cbcad +updated: 2017-07-25T23:17:54.615885635-04:00 imports: - name: github.com/alexquick/godotenv - version: 147552bee9c6196b9388f15ab03fe15721264092 + version: 84c21eea73c643434a7b128dab043ffea44b80d9 - name: github.com/ryanuber/columnize - version: 0fbbb3f0e3fbdc5bae7c6cd5f6c1887ebfb76360 \ No newline at end of file + version: abc90934186a77966e2beeac62ed966aac0561d5 +testImports: [] diff --git a/plugins/config/glide.yaml b/plugins/config/glide.yaml index 62e21eae6..2365d21e1 100644 --- a/plugins/config/glide.yaml +++ b/plugins/config/glide.yaml @@ -1,5 +1,8 @@ -package: . +package: github.com/dokku/dokku/plugins/config +ignore: +- github.com/dokku/dokku/plugins/common +- github.com/onsi/gomega import: - package: github.com/ryanuber/columnize - package: github.com/alexquick/godotenv - version: master + version: ^1.1.0 diff --git a/plugins/config/src/configenv/configenv.go b/plugins/config/src/configenv/configenv.go index 5feef46b7..0b376db9d 100644 --- a/plugins/config/src/configenv/configenv.go +++ b/plugins/config/src/configenv/configenv.go @@ -138,7 +138,7 @@ func LoadGlobal() (*Env, error) { //NewFromString creates an env from the given ENVFILE contents representation func NewFromString(rep string) (*Env, error) { - envMap, err := godotenv.ReadFromReader(strings.NewReader(rep)) + envMap, err := godotenv.ReadString(rep) env := &Env{ "", "", diff --git a/plugins/config/vendor/github.com/alexquick/godotenv/README.md b/plugins/config/vendor/github.com/alexquick/godotenv/README.md index aa6442b50..a6fb4953a 100644 --- a/plugins/config/vendor/github.com/alexquick/godotenv/README.md +++ b/plugins/config/vendor/github.com/alexquick/godotenv/README.md @@ -118,9 +118,15 @@ Contributions are most welcome! The parser itself is pretty stupidly naive and I 4. Push to the branch (`git push origin my-new-feature`) 5. Create new Pull Request +## Releases + +Releases should follow [Semver](http://semver.org/) though the first couple of releases are `v1` and `v1.1`. + +Use [annotated tags for all releases](https://github.com/joho/godotenv/issues/30). Example `git tag -a v1.2.1` + ## CI -Linux: [![wercker status](https://app.wercker.com/status/507594c2ec7e60f19403a568dfea0f78/m "wercker status")](https://app.wercker.com/project/bykey/507594c2ec7e60f19403a568dfea0f78) Windows: [![Build status](https://ci.appveyor.com/api/projects/status/9v40vnfvvgde64u4)](https://ci.appveyor.com/project/joho/godotenv) +Linux: [![Build Status](https://travis-ci.org/joho/godotenv.svg?branch=master)](https://travis-ci.org/joho/godotenv) Windows: [![Build status](https://ci.appveyor.com/api/projects/status/9v40vnfvvgde64u4)](https://ci.appveyor.com/project/joho/godotenv) ## Who? diff --git a/plugins/config/vendor/github.com/alexquick/godotenv/godotenv.go b/plugins/config/vendor/github.com/alexquick/godotenv/godotenv.go index e56663427..e36dbbd9f 100644 --- a/plugins/config/vendor/github.com/alexquick/godotenv/godotenv.go +++ b/plugins/config/vendor/github.com/alexquick/godotenv/godotenv.go @@ -25,6 +25,8 @@ import ( "strings" ) +const doubleQuoteSpecialChars = "\\\n\r\"!$`" + // Load will read your env file(s) and load them into ENV for this process. // // Call this function as close as possible to the start of your program (ideally in main) @@ -93,6 +95,11 @@ func Read(filenames ...string) (envMap map[string]string, err error) { return } +// ReadString parses an environment file from dotenv file contents returning it as a map +func ReadString(content string) (envMap map[string]string, err error) { + return read(strings.NewReader(content)) +} + // Exec loads env vars from the specified filenames (empty map falls back to default) // then executes the cmd specified. // @@ -171,14 +178,10 @@ func readFile(filename string) (envMap map[string]string, err error) { return } defer file.Close() - return ReadFromReader(file) + return read(file) } -func readString(content string) (envMap map[string]string, err error) { - return ReadFromReader(strings.NewReader(content)) -} - -func ReadFromReader(r io.Reader) (envMap map[string]string, err error) { +func read(r io.Reader) (envMap map[string]string, err error) { envMap = make(map[string]string) var lines []string @@ -243,7 +246,7 @@ func parseLine(line string) (key string, value string, err error) { } if len(splitString) != 2 { - err = errors.New("Can't separate key from value for line: " + line) + err = errors.New("Can't separate key from value") return } @@ -264,8 +267,8 @@ func parseValue(value string) string { // trim value = strings.Trim(value, " ") - // check if we've got quoted values - if value != "" { + // check if we've got quoted values or possible escapes + if len(value) > 1 { first := string(value[0:1]) last := string(value[len(value)-1:]) if first == last && strings.ContainsAny(first, `"'`) { @@ -296,9 +299,15 @@ func isIgnoredLine(line string) bool { } func doubleQuoteEscape(line string) string { - line = strings.Replace(line, `\`, `\\`, -1) - line = strings.Replace(line, "\n", `\n`, -1) - line = strings.Replace(line, "\r", `\r`, -1) - line = strings.Replace(line, `"`, `\"`, -1) + for _, c := range doubleQuoteSpecialChars { + toReplace := "\\" + string(c) + if c == '\n' { + toReplace = `\n` + } + if c == '\r' { + toReplace = `\r` + } + line = strings.Replace(line, string(c), toReplace, -1) + } return line } diff --git a/plugins/config/vendor/github.com/alexquick/godotenv/godotenv_test.go b/plugins/config/vendor/github.com/alexquick/godotenv/godotenv_test.go index ee4e5eb3f..8b353c051 100644 --- a/plugins/config/vendor/github.com/alexquick/godotenv/godotenv_test.go +++ b/plugins/config/vendor/github.com/alexquick/godotenv/godotenv_test.go @@ -254,6 +254,10 @@ func TestParsing(t *testing.T) { parseAndCompare(t, `FOO="bar\\\n\ b\az"`, "FOO", "bar\\\n baz") parseAndCompare(t, `FOO="bar\\r\ b\az"`, "FOO", "bar\\r baz") + parseAndCompare(t, `="value"`, "", "value") + parseAndCompare(t, `KEY="`, "KEY", "\"") + parseAndCompare(t, `KEY="value`, "KEY", "\"value") + // it 'throws an error if line format is incorrect' do // expect{env('lol$wut')}.to raise_error(Dotenv::FormatError) badlyFormattedLine := "lol$wut" @@ -309,7 +313,7 @@ func TestErrorParsing(t *testing.T) { func TestWrite(t *testing.T) { writeAndCompare := func(env string, expected string) { - envMap, _ := readString(env) + envMap, _ := ReadString(env) actual, _ := WriteString(envMap) if expected != actual { t.Errorf("Expected '%v' (%v) to write as '%v', got '%v' instead.", env, envMap, expected, actual) @@ -324,8 +328,8 @@ func TestWrite(t *testing.T) { writeAndCompare(`key=va"lu"e`, `key="va\"lu\"e"`) //but single quotes are left alone writeAndCompare(`key=va'lu'e`, `key="va'lu'e"`) - // newlines and backslashes are escaped - writeAndCompare(`foo="ba\n\r\\r!"`, `foo="ba\n\r\\r!"`) + // newlines, backslashes, and some other special chars are escaped + writeAndCompare(`foo="$ba\n\r\\r!"`, `foo="\$ba\n\r\\r\!"`) } func TestRoundtrip(t *testing.T) { @@ -340,7 +344,7 @@ func TestRoundtrip(t *testing.T) { if err != nil { continue } - roundtripped, err := readString(rep) + roundtripped, err := ReadString(rep) if err != nil { continue } diff --git a/plugins/config/vendor/github.com/ryanuber/columnize/README.md b/plugins/config/vendor/github.com/ryanuber/columnize/README.md index e47634fc6..9a583db8d 100644 --- a/plugins/config/vendor/github.com/ryanuber/columnize/README.md +++ b/plugins/config/vendor/github.com/ryanuber/columnize/README.md @@ -56,12 +56,14 @@ config.Delim = "|" config.Glue = " " config.Prefix = "" config.Empty = "" +config.NoTrim = false ``` * `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 +* `NoTrim` is a boolean used to disable the automatic trimming of input values You can then pass the `Config` in using the `Format` method (signature below) to have text formatted to your liking. diff --git a/plugins/config/vendor/github.com/ryanuber/columnize/columnize.go b/plugins/config/vendor/github.com/ryanuber/columnize/columnize.go index 915716a10..527c1d1b3 100644 --- a/plugins/config/vendor/github.com/ryanuber/columnize/columnize.go +++ b/plugins/config/vendor/github.com/ryanuber/columnize/columnize.go @@ -18,8 +18,11 @@ type Config struct { // The string by which columns of output will be prefixed. Prefix string - // A replacement string to replace empty fields + // A replacement string to replace empty fields. Empty string + + // NoTrim disables automatic trimming of inputs. + NoTrim bool } // DefaultConfig returns a *Config with default values. @@ -29,18 +32,22 @@ func DefaultConfig() *Config { Glue: " ", Prefix: "", Empty: "", + NoTrim: false, } } // 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 a == nil { + return b } + if b == nil { + return a + } + + var result Config = *a if b.Delim != "" { result.Delim = b.Delim @@ -54,6 +61,9 @@ func MergeConfig(a, b *Config) *Config { if b.Empty != "" { result.Empty = b.Empty } + if b.NoTrim { + result.NoTrim = true + } return &result } @@ -83,10 +93,13 @@ func stringFormat(c *Config, widths []int, columns int) 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) + separated := strings.Split(line, config.Delim) + elements := make([]interface{}, len(separated)) + for i, field := range separated { + value := field + if !config.NoTrim { + value = strings.TrimSpace(field) + } // Apply the empty value, if configured. if value == "" && config.Empty != "" { diff --git a/plugins/config/vendor/github.com/ryanuber/columnize/columnize_test.go b/plugins/config/vendor/github.com/ryanuber/columnize/columnize_test.go index 89dabaa38..a3194c3d0 100644 --- a/plugins/config/vendor/github.com/ryanuber/columnize/columnize_test.go +++ b/plugins/config/vendor/github.com/ryanuber/columnize/columnize_test.go @@ -2,6 +2,7 @@ package columnize import ( "fmt" + "reflect" "testing" crand "crypto/rand" @@ -155,6 +156,24 @@ func TestVariedInputSpacing(t *testing.T) { } } +func TestVariedInputSpacing_NoTrim(t *testing.T) { + input := []string{ + "Column A|Column B|Column C", + "x|y| z", + } + + config := DefaultConfig() + config.NoTrim = true + 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", @@ -280,27 +299,42 @@ func TestEmptyConfigValues(t *testing.T) { } 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) + for _, tc := range []struct { + desc string + configA *Config + configB *Config + expect *Config + }{ + { + "merges b over a", + &Config{Delim: "a", Glue: "a", Prefix: "a", Empty: "a"}, + &Config{Delim: "b", Glue: "b", Prefix: "b", Empty: "b"}, + &Config{Delim: "b", Glue: "b", Prefix: "b", Empty: "b"}, + }, + { + "merges only non-empty config values", + &Config{Delim: "a", Glue: "a", Prefix: "a", Empty: "a"}, + &Config{Delim: "b", Prefix: "b"}, + &Config{Delim: "b", Glue: "a", Prefix: "b", Empty: "a"}, + }, + { + "takes b if a is nil", + nil, + &Config{Delim: "b", Glue: "b", Prefix: "b", Empty: "b"}, + &Config{Delim: "b", Glue: "b", Prefix: "b", Empty: "b"}, + }, + { + "takes a if b is nil", + &Config{Delim: "a", Glue: "a", Prefix: "a", Empty: "a"}, + nil, + &Config{Delim: "a", Glue: "a", Prefix: "a", Empty: "a"}, + }, + } { + t.Run(tc.desc, func(t *testing.T) { + m := MergeConfig(tc.configA, tc.configB) + if !reflect.DeepEqual(m, tc.expect) { + t.Fatalf("\nexpect:\n%#v\n\nactual:\n%#v", tc.expect, m) + } + }) } }