Compare commits

...

46 Commits

Author SHA1 Message Date
41e88df24e Add action to publish binaries on each release 2020-11-11 15:02:30 +11:00
65218afbaa Add a go.mod file (#123) 2020-11-11 14:54:56 +11:00
aa035a1808 Non amd tests (#122)
* Add non-amd64 tests

* Try shifting env around

* Update input to match action

* Include git

* Comment out IBM Z for now and quieten apt-get
2020-11-11 12:54:49 +11:00
23296b91aa Merge pull request #121 from joho/github-actions
Setup GitHub actions for CI
2020-11-11 11:33:11 +11:00
e57c08db27 Add CI badge to readme 2020-11-11 11:31:34 +11:00
573e9186b2 Matrix test across mac/win/linux 2020-11-11 11:24:40 +11:00
5451d82b77 Add Barebones github actions test 2020-11-11 11:19:52 +11:00
0da8ce72f0 Merge pull request #115 from adombeck/master
Fix typo
2020-11-08 12:01:29 +11:00
3e4069b9b2 Merge pull request #109 from mniak/pr
Write ints without quotes
2020-11-08 11:58:27 +11:00
a4d9cf1d6d Merge pull request #120 from santosh653/master
PR_AddingPowerSupport_golang-github-joho-godotenv
2020-10-16 06:51:22 +11:00
7fbe752d59 Update .travis.yml
adding power support.
2020-10-15 02:09:51 -04:00
46ee0dcae8 Update .travis.yml
Adding arch: amd64
2020-10-15 00:58:50 -04:00
bc7d5cd181 Fix typo 2020-09-04 23:35:59 +02:00
6e653f9adf add newline back 2020-06-26 15:39:50 -03:00
fccdfd265d Fix package name 2020-06-26 15:39:06 -03:00
f4e7418908 Remove go.mod 2020-06-26 15:36:35 -03:00
63ea8bf09b Change package name back to joho/godotenv 2020-06-26 15:35:47 -03:00
29b5be9cdc Rename and implement int-able without quotes 2020-06-26 15:22:04 -03:00
d6ee6871f2 Merge pull request #95 from orxobo/Write-adjustment
Fixed Write bugs
2020-03-02 07:46:15 +11:00
dbcf4b53b8 Fixed Write bugs
This should address a fix #93 and #94
This has not been addressed in any tests.
2020-02-11 17:05:34 +10:00
b09de681dc Merge pull request #90 from djherbis/master
#89 move regexp.MustCompile to globals
2019-10-21 07:25:22 +11:00
992ab0ec47 #89 move regexp.MustCompile to globals 2019-10-18 10:35:15 -04:00
5c0e6c6ab1 Merge pull request #69 from hairyhenderson/ignore-leading-whitespace
Fixing a couple whitespace bugs: ignoring leading whitespace, and supporting more kinds of empty lines
2019-02-04 15:41:09 +11:00
61baafa627 Merge branch 'master' into ignore-leading-whitespace 2019-02-04 15:28:23 +11:00
823f94bb9a Merge pull request #70 from hairyhenderson/support-keys-beginning-with-export-66
Support key names beginning with 'export'
2019-02-04 15:26:24 +11:00
263a1dda9d Support key names beginning with 'export'
Signed-off-by: Dave Henderson <dhenderson@gmail.com>
2019-02-03 23:11:51 -05:00
79711eebaf Ignoring leading whitespace
Signed-off-by: Dave Henderson <dhenderson@gmail.com>
2019-02-03 22:39:38 -05:00
69ed1d913a Merge pull request #63 from mdanzinger/patch-1
Update cmd.go
2018-11-21 06:47:48 +11:00
2841430efc An even tinier grammar fix
I read `.env` aloud as dotenv so the A makes sense.
2018-11-21 06:41:23 +11:00
5917dd2291 Update cmd.go
Tiny little grammatical error.
2018-11-20 13:25:32 -05:00
c0b86d615e Merge pull request #60 from coolaj86/precedence
explicitly state env precedence and convention
2018-09-27 15:32:03 +10:00
3896766f7d explicitly state env precedence and convention 2018-09-24 21:35:43 -06:00
23d116af35 Merge pull request #58 from lucastetreault/master
feat(Expand Variables): Custom variable expansion instead of Go's os.Expand
2018-09-11 18:04:31 +10:00
2d8b3aab88 feat(Expand Variables): Custom variable expansion instead of Go's os.Expand
Copy over the tests from https://github.com/bkeepers/dotenv/blob/master/spec/dotenv/parser_spec.rb
related to expanding variables and implement the required changes. I also realized as part of this
that this implementation was not handling values in single quotes properly (e.g.: not the same was
as the ruby package mentionned) so that has been fixed as well along with the related tests.

Fixes: #52
2018-09-11 00:55:10 -06:00
1709ab122c Merge pull request #54 from egorse/master
The value expand fallback to actual ENV values
2018-04-05 15:36:34 +10:00
8ad714e304 The value expand fallback to actual ENV values 2018-03-31 23:18:36 +03:00
6bb0851667 Merge pull request #47 from sachaos/feature-expand-variables-on-value
Support variable substitution in dotenv files
2018-01-15 13:49:21 +11:00
06e67b5ef3 Update my homepage link 2017-12-23 10:35:39 +11:00
2707e9ff66 Fix test, $ should not be escaped 2017-11-20 23:41:46 +09:00
50c29652a0 Expand variables on parseValue 2017-11-20 23:20:38 +09:00
33977c2d8d Add test for substitutions 2017-11-20 23:20:19 +09:00
9be76b3741 Pass envMap to parseLine & parseValue 2017-11-20 22:21:39 +09:00
6d367c18ed Merge pull request #46 from joho/fix_quoting_parser_bug
WIP Parsing bug with nested quotes
2017-11-10 12:03:15 +11:00
05be8ccbf7 Try and replicate reported bug #45 2017-11-10 10:38:18 +11:00
0f92a24bb0 Merge pull request #44 from dvrkps/patch-1
travis: update go version
2017-11-02 13:11:18 +11:00
b7bbb3624e travis: update go version 2017-11-01 07:18:32 +01:00
10 changed files with 313 additions and 37 deletions

62
.github/workflows/ci.yml vendored Normal file
View File

@ -0,0 +1,62 @@
name: CI
on: [push]
jobs:
test:
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
go: [ '1.15', '1.14' ]
os: [ ubuntu-latest, macOS-latest, windows-latest ]
name: ${{ matrix.os }} Go ${{ matrix.go }} Tests
steps:
- uses: actions/checkout@v2
- name: Setup go
uses: actions/setup-go@v2
with:
go-version: ${{ matrix.go }}
- run: go test
test-non-amd64:
strategy:
fail-fast: false
matrix:
arch:
# For some reasons this is segfaulting on go env
# - name: IBM Z and LinuxONE
# architecture: "s390x"
- name: POWER8
architecture: "ppc64le"
runs-on: ubuntu-latest
name: Test on ${{ matrix.arch.name }}
steps:
- uses: actions/checkout@v2
- uses: uraimo/run-on-arch-action@master
with:
arch: ${{ matrix.arch.architecture }}
distro: ubuntu20.04
env: | # YAML pipe
GOARCH: ${{ matrix.arch.architecture }}
CGO_ENABLED: 0
run: |
apt-get update
apt-get install -q -y curl wget git
latestGo=$(curl "https://golang.org/VERSION?m=text")
wget "https://dl.google.com/go/${latestGo}.linux-${GOARCH}.tar.gz"
rm -f $(which go)
rm -rf /usr/local/go
tar -C /usr/local -xzf "${latestGo}.linux-${GOARCH}.tar.gz"
export PATH=/usr/local/go/bin:$PATH
printf "Using go at: $(which go)\n"
printf "Go version: $(go version)\n"
printf "\n\nGo environment:\n\n"
go env
printf "\n\nSystem environment:\n\n"
env
go get -insecure -v -t -d ./...
go test ./...
cd ./cmd/godotenv
go build -trimpath -ldflags="-w -s" -v

42
.github/workflows/release.yml vendored Normal file
View File

@ -0,0 +1,42 @@
on:
push:
tags:
- 'v*' # Push events to matching v*, i.e. v1.0, v20.15.10
name: Upload Release Assets
jobs:
build:
name: Upload Release Assets
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v2
- name: Generate build files
uses: thatisuday/go-cross-build@v1
with:
platforms: 'linux/amd64, linux/ppc64le, darwin/amd64, windows/amd64'
package: 'cmd/godotenv'
name: 'godotenv'
compress: 'true'
dest: 'dist'
- name: Create Release
id: create_release
uses: actions/create-release@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
tag_name: ${{ github.ref }}
release_name: Release ${{ github.ref }}
draft: false
prerelease: ${{ contains(github.ref, 'pre') }}
- name: Upload Release Assets
id: upload-release-asset
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
file: dist/*
overwrite: true
file_glob: true

View File

@ -1,8 +0,0 @@
language: go
go:
- 1.8
os:
- linux
- osx

View File

@ -1,4 +1,4 @@
# GoDotEnv [![Build Status](https://travis-ci.org/joho/godotenv.svg?branch=master)](https://travis-ci.org/joho/godotenv) [![Build status](https://ci.appveyor.com/api/projects/status/9v40vnfvvgde64u4?svg=true)](https://ci.appveyor.com/project/joho/godotenv) [![Go Report Card](https://goreportcard.com/badge/github.com/joho/godotenv)](https://goreportcard.com/report/github.com/joho/godotenv) # GoDotEnv ![CI](https://github.com/joho/godotenv/workflows/CI/badge.svg) [![Go Report Card](https://goreportcard.com/badge/github.com/joho/godotenv)](https://goreportcard.com/report/github.com/joho/godotenv)
A Go (golang) port of the Ruby dotenv project (which loads env vars from a .env file) A Go (golang) port of the Ruby dotenv project (which loads env vars from a .env file)
@ -110,6 +110,31 @@ content := getRemoteFileContent()
myEnv, err := godotenv.Unmarshal(content) myEnv, err := godotenv.Unmarshal(content)
``` ```
### Precedence & Conventions
Existing envs take precedence of envs that are loaded later.
The [convention](https://github.com/bkeepers/dotenv#what-other-env-files-can-i-use)
for managing multiple environments (i.e. development, test, production)
is to create an env named `{YOURAPP}_ENV` and load envs in this order:
```go
env := os.Getenv("FOO_ENV")
if "" == env {
env = "development"
}
godotenv.Load(".env." + env + ".local")
if "test" != env {
godotenv.Load(".env.local")
}
godotenv.Load(".env." + env)
godotenv.Load() // The Original .env
```
If you need to, you can also use `godotenv.Overload()` to defy this convention
and overwrite existing envs instead of only supplanting them. Use with caution.
### Command Mode ### Command Mode
Assuming you've installed the command as above and you've got `$GOPATH/bin` in your `$PATH` Assuming you've installed the command as above and you've got `$GOPATH/bin` in your `$PATH`
@ -160,4 +185,4 @@ Linux: [![Build Status](https://travis-ci.org/joho/godotenv.svg?branch=master)](
## Who? ## Who?
The original library [dotenv](https://github.com/bkeepers/dotenv) was written by [Brandon Keepers](http://opensoul.org/), and this port was done by [John Barton](http://whoisjohnbarton.com) based off the tests/fixtures in the original library. The original library [dotenv](https://github.com/bkeepers/dotenv) was written by [Brandon Keepers](http://opensoul.org/), and this port was done by [John Barton](https://johnbarton.co/) based off the tests/fixtures in the original library.

View File

@ -19,7 +19,7 @@ func main() {
flag.Parse() flag.Parse()
usage := ` usage := `
Run a process with a env setup from a .env file Run a process with an env setup from a .env file
godotenv [-f ENV_FILE_PATHS] COMMAND_ARGS godotenv [-f ENV_FILE_PATHS] COMMAND_ARGS

View File

@ -6,3 +6,4 @@ OPTION_E="1"
OPTION_F="2" OPTION_F="2"
OPTION_G="" OPTION_G=""
OPTION_H="\n" OPTION_H="\n"
OPTION_I = "echo 'asd'"

View File

@ -0,0 +1,5 @@
OPTION_A=1
OPTION_B=${OPTION_A}
OPTION_C=$OPTION_B
OPTION_D=${OPTION_A}${OPTION_B}
OPTION_E=${OPTION_NOT_DEFINED}

3
go.mod Normal file
View File

@ -0,0 +1,3 @@
module github.com/joho/godotenv
go 1.12

View File

@ -22,6 +22,7 @@ import (
"os/exec" "os/exec"
"regexp" "regexp"
"sort" "sort"
"strconv"
"strings" "strings"
) )
@ -112,7 +113,7 @@ func Parse(r io.Reader) (envMap map[string]string, err error) {
for _, fullLine := range lines { for _, fullLine := range lines {
if !isIgnoredLine(fullLine) { if !isIgnoredLine(fullLine) {
var key, value string var key, value string
key, value, err = parseLine(fullLine) key, value, err = parseLine(fullLine, envMap)
if err != nil { if err != nil {
return return
@ -147,15 +148,20 @@ func Exec(filenames []string, cmd string, cmdArgs []string) error {
// Write serializes the given environment and writes it to a file // Write serializes the given environment and writes it to a file
func Write(envMap map[string]string, filename string) error { func Write(envMap map[string]string, filename string) error {
content, error := Marshal(envMap) content, err := Marshal(envMap)
if error != nil { if err != nil {
return error return err
} }
file, error := os.Create(filename) file, err := os.Create(filename)
if error != nil { if err != nil {
return error return err
} }
_, err := file.WriteString(content) defer file.Close()
_, err = file.WriteString(content)
if err != nil {
return err
}
file.Sync()
return err return err
} }
@ -164,7 +170,11 @@ func Write(envMap map[string]string, filename string) error {
func Marshal(envMap map[string]string) (string, error) { func Marshal(envMap map[string]string) (string, error) {
lines := make([]string, 0, len(envMap)) lines := make([]string, 0, len(envMap))
for k, v := range envMap { for k, v := range envMap {
lines = append(lines, fmt.Sprintf(`%s="%s"`, k, doubleQuoteEscape(v))) if d, err := strconv.Atoi(v); err == nil {
lines = append(lines, fmt.Sprintf(`%s=%d`, k, d))
} else {
lines = append(lines, fmt.Sprintf(`%s="%s"`, k, doubleQuoteEscape(v)))
}
} }
sort.Strings(lines) sort.Strings(lines)
return strings.Join(lines, "\n"), nil return strings.Join(lines, "\n"), nil
@ -209,7 +219,9 @@ func readFile(filename string) (envMap map[string]string, err error) {
return Parse(file) return Parse(file)
} }
func parseLine(line string) (key string, value string, err error) { var exportRegex = regexp.MustCompile(`^\s*(?:export\s+)?(.*?)\s*$`)
func parseLine(line string, envMap map[string]string) (key string, value string, err error) {
if len(line) == 0 { if len(line) == 0 {
err = errors.New("zero length string") err = errors.New("zero length string")
return return
@ -256,27 +268,40 @@ func parseLine(line string) (key string, value string, err error) {
if strings.HasPrefix(key, "export") { if strings.HasPrefix(key, "export") {
key = strings.TrimPrefix(key, "export") key = strings.TrimPrefix(key, "export")
} }
key = strings.Trim(key, " ") key = strings.TrimSpace(key)
key = exportRegex.ReplaceAllString(splitString[0], "$1")
// Parse the value // Parse the value
value = parseValue(splitString[1]) value = parseValue(splitString[1], envMap)
return return
} }
func parseValue(value string) string { var (
singleQuotesRegex = regexp.MustCompile(`\A'(.*)'\z`)
doubleQuotesRegex = regexp.MustCompile(`\A"(.*)"\z`)
escapeRegex = regexp.MustCompile(`\\.`)
unescapeCharsRegex = regexp.MustCompile(`\\([^$])`)
)
func parseValue(value string, envMap map[string]string) string {
// trim // trim
value = strings.Trim(value, " ") value = strings.Trim(value, " ")
// check if we've got quoted values or possible escapes // check if we've got quoted values or possible escapes
if len(value) > 1 { if len(value) > 1 {
first := string(value[0:1]) singleQuotes := singleQuotesRegex.FindStringSubmatch(value)
last := string(value[len(value)-1:])
if first == last && strings.ContainsAny(first, `"'`) { doubleQuotes := doubleQuotesRegex.FindStringSubmatch(value)
if singleQuotes != nil || doubleQuotes != nil {
// pull the quotes off the edges // pull the quotes off the edges
value = value[1 : len(value)-1] value = value[1 : len(value)-1]
// handle escapes }
escapeRegex := regexp.MustCompile(`\\.`)
if doubleQuotes != nil {
// expand newlines
value = escapeRegex.ReplaceAllStringFunc(value, func(match string) string { value = escapeRegex.ReplaceAllStringFunc(value, func(match string) string {
c := strings.TrimPrefix(match, `\`) c := strings.TrimPrefix(match, `\`)
switch c { switch c {
@ -285,17 +310,41 @@ func parseValue(value string) string {
case "r": case "r":
return "\r" return "\r"
default: default:
return c return match
} }
}) })
// unescape characters
value = unescapeCharsRegex.ReplaceAllString(value, "$1")
}
if singleQuotes == nil {
value = expandVariables(value, envMap)
} }
} }
return value return value
} }
var expandVarRegex = regexp.MustCompile(`(\\)?(\$)(\()?\{?([A-Z0-9_]+)?\}?`)
func expandVariables(v string, m map[string]string) string {
return expandVarRegex.ReplaceAllStringFunc(v, func(s string) string {
submatch := expandVarRegex.FindStringSubmatch(s)
if submatch == nil {
return s
}
if submatch[1] == "\\" || submatch[2] == "(" {
return submatch[0][1:]
} else if submatch[4] != "" {
return m[submatch[4]]
}
return s
})
}
func isIgnoredLine(line string) bool { func isIgnoredLine(line string) bool {
trimmedLine := strings.Trim(line, " \n\t") trimmedLine := strings.TrimSpace(line)
return len(trimmedLine) == 0 || strings.HasPrefix(trimmedLine, "#") return len(trimmedLine) == 0 || strings.HasPrefix(trimmedLine, "#")
} }

View File

@ -5,13 +5,14 @@ import (
"fmt" "fmt"
"os" "os"
"reflect" "reflect"
"strings"
"testing" "testing"
) )
var noopPresets = make(map[string]string) var noopPresets = make(map[string]string)
func parseAndCompare(t *testing.T, rawEnvLine string, expectedKey string, expectedValue string) { func parseAndCompare(t *testing.T, rawEnvLine string, expectedKey string, expectedValue string) {
key, value, _ := parseLine(rawEnvLine) key, value, _ := parseLine(rawEnvLine, noopPresets)
if key != expectedKey || value != expectedValue { if key != expectedKey || value != expectedValue {
t.Errorf("Expected '%v' to parse as '%v' => '%v', got '%v' => '%v' instead", rawEnvLine, expectedKey, expectedValue, key, value) t.Errorf("Expected '%v' to parse as '%v' => '%v', got '%v' => '%v' instead", rawEnvLine, expectedKey, expectedValue, key, value)
} }
@ -161,7 +162,7 @@ func TestLoadExportedEnv(t *testing.T) {
envFileName := "fixtures/exported.env" envFileName := "fixtures/exported.env"
expectedValues := map[string]string{ expectedValues := map[string]string{
"OPTION_A": "2", "OPTION_A": "2",
"OPTION_B": "\n", "OPTION_B": "\\n",
} }
loadEnvAndCompareValues(t, Load, envFileName, expectedValues, noopPresets) loadEnvAndCompareValues(t, Load, envFileName, expectedValues, noopPresets)
@ -182,16 +183,94 @@ func TestLoadQuotedEnv(t *testing.T) {
"OPTION_A": "1", "OPTION_A": "1",
"OPTION_B": "2", "OPTION_B": "2",
"OPTION_C": "", "OPTION_C": "",
"OPTION_D": "\n", "OPTION_D": "\\n",
"OPTION_E": "1", "OPTION_E": "1",
"OPTION_F": "2", "OPTION_F": "2",
"OPTION_G": "", "OPTION_G": "",
"OPTION_H": "\n", "OPTION_H": "\n",
"OPTION_I": "echo 'asd'",
} }
loadEnvAndCompareValues(t, Load, envFileName, expectedValues, noopPresets) loadEnvAndCompareValues(t, Load, envFileName, expectedValues, noopPresets)
} }
func TestSubstitutions(t *testing.T) {
envFileName := "fixtures/substitutions.env"
expectedValues := map[string]string{
"OPTION_A": "1",
"OPTION_B": "1",
"OPTION_C": "1",
"OPTION_D": "11",
"OPTION_E": "",
}
loadEnvAndCompareValues(t, Load, envFileName, expectedValues, noopPresets)
}
func TestExpanding(t *testing.T) {
tests := []struct {
name string
input string
expected map[string]string
}{
{
"expands variables found in values",
"FOO=test\nBAR=$FOO",
map[string]string{"FOO": "test", "BAR": "test"},
},
{
"parses variables wrapped in brackets",
"FOO=test\nBAR=${FOO}bar",
map[string]string{"FOO": "test", "BAR": "testbar"},
},
{
"expands undefined variables to an empty string",
"BAR=$FOO",
map[string]string{"BAR": ""},
},
{
"expands variables in double quoted strings",
"FOO=test\nBAR=\"quote $FOO\"",
map[string]string{"FOO": "test", "BAR": "quote test"},
},
{
"does not expand variables in single quoted strings",
"BAR='quote $FOO'",
map[string]string{"BAR": "quote $FOO"},
},
{
"does not expand escaped variables",
`FOO="foo\$BAR"`,
map[string]string{"FOO": "foo$BAR"},
},
{
"does not expand escaped variables",
`FOO="foo\${BAR}"`,
map[string]string{"FOO": "foo${BAR}"},
},
{
"does not expand escaped variables",
"FOO=test\nBAR=\"foo\\${FOO} ${FOO}\"",
map[string]string{"FOO": "test", "BAR": "foo${FOO} test"},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
env, err := Parse(strings.NewReader(tt.input))
if err != nil {
t.Errorf("Error: %s", err.Error())
}
for k, v := range tt.expected {
if strings.Compare(env[k], v) != 0 {
t.Errorf("Expected: %s, Actual: %s", v, env[k])
}
}
})
}
}
func TestActualEnvVarsAreLeftAlone(t *testing.T) { func TestActualEnvVarsAreLeftAlone(t *testing.T) {
os.Clearenv() os.Clearenv()
os.Setenv("OPTION_A", "actualenv") os.Setenv("OPTION_A", "actualenv")
@ -233,7 +312,14 @@ func TestParsing(t *testing.T) {
// parses export keyword // parses export keyword
parseAndCompare(t, "export OPTION_A=2", "OPTION_A", "2") parseAndCompare(t, "export OPTION_A=2", "OPTION_A", "2")
parseAndCompare(t, `export OPTION_B='\n'`, "OPTION_B", "\n") parseAndCompare(t, `export OPTION_B='\n'`, "OPTION_B", "\\n")
parseAndCompare(t, "export exportFoo=2", "exportFoo", "2")
parseAndCompare(t, "exportFOO=2", "exportFOO", "2")
parseAndCompare(t, "export_FOO =2", "export_FOO", "2")
parseAndCompare(t, "export.FOO= 2", "export.FOO", "2")
parseAndCompare(t, "export\tOPTION_A=2", "OPTION_A", "2")
parseAndCompare(t, " export OPTION_A=2", "OPTION_A", "2")
parseAndCompare(t, "\texport OPTION_A=2", "OPTION_A", "2")
// it 'expands newlines in quoted strings' do // it 'expands newlines in quoted strings' do
// expect(env('FOO="bar\nbaz"')).to eql('FOO' => "bar\nbaz") // expect(env('FOO="bar\nbaz"')).to eql('FOO' => "bar\nbaz")
@ -276,10 +362,15 @@ func TestParsing(t *testing.T) {
parseAndCompare(t, `KEY="`, "KEY", "\"") parseAndCompare(t, `KEY="`, "KEY", "\"")
parseAndCompare(t, `KEY="value`, "KEY", "\"value") parseAndCompare(t, `KEY="value`, "KEY", "\"value")
// leading whitespace should be ignored
parseAndCompare(t, " KEY =value", "KEY", "value")
parseAndCompare(t, " KEY=value", "KEY", "value")
parseAndCompare(t, "\tKEY=value", "KEY", "value")
// it 'throws an error if line format is incorrect' do // it 'throws an error if line format is incorrect' do
// expect{env('lol$wut')}.to raise_error(Dotenv::FormatError) // expect{env('lol$wut')}.to raise_error(Dotenv::FormatError)
badlyFormattedLine := "lol$wut" badlyFormattedLine := "lol$wut"
_, _, err := parseLine(badlyFormattedLine) _, _, err := parseLine(badlyFormattedLine, noopPresets)
if err == nil { if err == nil {
t.Errorf("Expected \"%v\" to return error, but it didn't", badlyFormattedLine) t.Errorf("Expected \"%v\" to return error, but it didn't", badlyFormattedLine)
} }
@ -292,6 +383,10 @@ func TestLinesToIgnore(t *testing.T) {
t.Error("Line with nothing but line break wasn't ignored") t.Error("Line with nothing but line break wasn't ignored")
} }
if !isIgnoredLine("\r\n") {
t.Error("Line with nothing but windows-style line break wasn't ignored")
}
if !isIgnoredLine("\t\t ") { if !isIgnoredLine("\t\t ") {
t.Error("Line full of whitespace wasn't ignored") t.Error("Line full of whitespace wasn't ignored")
} }
@ -347,9 +442,11 @@ func TestWrite(t *testing.T) {
//but single quotes are left alone //but single quotes are left alone
writeAndCompare(`key=va'lu'e`, `key="va'lu'e"`) writeAndCompare(`key=va'lu'e`, `key="va'lu'e"`)
// newlines, backslashes, and some other special chars are escaped // newlines, backslashes, and some other special chars are escaped
writeAndCompare(`foo="$ba\n\r\\r!"`, `foo="\$ba\n\r\\r\!"`) writeAndCompare(`foo="\n\r\\r!"`, `foo="\n\r\\r\!"`)
// lines should be sorted // lines should be sorted
writeAndCompare("foo=bar\nbaz=buzz", "baz=\"buzz\"\nfoo=\"bar\"") writeAndCompare("foo=bar\nbaz=buzz", "baz=\"buzz\"\nfoo=\"bar\"")
// integers should not be quoted
writeAndCompare(`key="10"`, `key=10`)
} }