4
4
//
5
5
// The TL;DR is that you make a .env file that looks something like
6
6
//
7
- // SOME_ENV_VAR=somevalue
7
+ // SOME_ENV_VAR=somevalue
8
8
//
9
9
// and then in your go code you can call
10
10
//
11
- // godotenv.Load()
11
+ // godotenv.Load()
12
12
//
13
13
// and all the env vars declared in .env will be available through os.Getenv("SOME_ENV_VAR")
14
14
package dotenv
15
15
16
16
import (
17
17
"bytes"
18
- "errors"
19
18
"fmt"
20
19
"io"
21
20
"os"
@@ -28,10 +27,10 @@ import (
28
27
"github.com/compose-spec/compose-go/template"
29
28
)
30
29
31
- const doubleQuoteSpecialChars = "\\ \n \r \" !$`"
32
-
33
30
var utf8BOM = []byte ("\uFEFF " )
34
31
32
+ var startsWithDigitRegex = regexp .MustCompile (`^\s*\d.*` ) // Keys starting with numbers are ignored
33
+
35
34
// LookupFn represents a lookup function to resolve variables from
36
35
type LookupFn func (string ) (string , bool )
37
36
@@ -60,13 +59,13 @@ func ParseWithLookup(r io.Reader, lookupFn LookupFn) (map[string]string, error)
60
59
61
60
// Load will read your env file(s) and load them into ENV for this process.
62
61
//
63
- // Call this function as close as possible to the start of your program (ideally in main)
62
+ // Call this function as close as possible to the start of your program (ideally in main).
64
63
//
65
- // If you call Load without any args it will default to loading .env in the current path
64
+ // If you call Load without any args it will default to loading .env in the current path.
66
65
//
67
- // You can otherwise tell it which files to load (there can be more than one) like
66
+ // You can otherwise tell it which files to load (there can be more than one) like:
68
67
//
69
- // godotenv.Load("fileone", "filetwo")
68
+ // godotenv.Load("fileone", "filetwo")
70
69
//
71
70
// It's important to note that it WILL NOT OVERRIDE an env variable that already exists - consider the .env file to set dev vars or sensible defaults
72
71
func Load (filenames ... string ) error {
@@ -75,13 +74,13 @@ func Load(filenames ...string) error {
75
74
76
75
// Overload will read your env file(s) and load them into ENV for this process.
77
76
//
78
- // Call this function as close as possible to the start of your program (ideally in main)
77
+ // Call this function as close as possible to the start of your program (ideally in main).
79
78
//
80
- // If you call Overload without any args it will default to loading .env in the current path
79
+ // If you call Overload without any args it will default to loading .env in the current path.
81
80
//
82
- // You can otherwise tell it which files to load (there can be more than one) like
81
+ // You can otherwise tell it which files to load (there can be more than one) like:
83
82
//
84
- // godotenv.Overload("fileone", "filetwo")
83
+ // godotenv.Overload("fileone", "filetwo")
85
84
//
86
85
// It's important to note this WILL OVERRIDE an env variable that already exists - consider the .env file to forcefilly set all vars.
87
86
func Overload (filenames ... string ) error {
@@ -99,8 +98,6 @@ func load(overload bool, filenames ...string) error {
99
98
return nil
100
99
}
101
100
102
- var startsWithDigitRegex = regexp .MustCompile (`^\s*\d.*` ) // Keys starting with numbers are ignored
103
-
104
101
// ReadWithLookup gets all env vars from the files and/or lookup function and return values as
105
102
// a map rather than automatically writing values into env
106
103
func ReadWithLookup (lookupFn LookupFn , filenames ... string ) (map [string ]string , error ) {
@@ -155,6 +152,8 @@ func UnmarshalBytesWithLookup(src []byte, lookupFn LookupFn) (map[string]string,
155
152
//
156
153
// If you want more fine grained control over your command it's recommended
157
154
// that you use `Load()` or `Read()` and the `os/exec` package yourself.
155
+ //
156
+ // Deprecated: Use the `os/exec` package directly.
158
157
func Exec (filenames []string , cmd string , cmdArgs []string ) error {
159
158
if err := Load (filenames ... ); err != nil {
160
159
return err
@@ -168,7 +167,10 @@ func Exec(filenames []string, cmd string, cmdArgs []string) error {
168
167
}
169
168
170
169
// Write serializes the given environment and writes it to a file
170
+ //
171
+ // Deprecated: The serialization functions are untested and unmaintained.
171
172
func Write (envMap map [string ]string , filename string ) error {
173
+ //goland:noinspection GoDeprecation
172
174
content , err := Marshal (envMap )
173
175
if err != nil {
174
176
return err
@@ -187,6 +189,8 @@ func Write(envMap map[string]string, filename string) error {
187
189
188
190
// Marshal outputs the given environment as a dotenv-formatted environment file.
189
191
// Each line is in the format: KEY="VALUE" where VALUE is backslash-escaped.
192
+ //
193
+ // Deprecated: The serialization functions are untested and unmaintained.
190
194
func Marshal (envMap map [string ]string ) (string , error ) {
191
195
lines := make ([]string , 0 , len (envMap ))
192
196
for k , v := range envMap {
@@ -239,104 +243,6 @@ func readFile(filename string, lookupFn LookupFn) (map[string]string, error) {
239
243
return ParseWithLookup (file , lookupFn )
240
244
}
241
245
242
- var exportRegex = regexp .MustCompile (`^\s*(?:export\s+)?(.*?)\s*$` )
243
-
244
- func parseLine (line string , envMap map [string ]string ) (string , string , error ) {
245
- return parseLineWithLookup (line , envMap , nil )
246
- }
247
- func parseLineWithLookup (line string , envMap map [string ]string , lookupFn LookupFn ) (string , string , error ) {
248
- if line == "" {
249
- return "" , "" , errors .New ("zero length string" )
250
- }
251
-
252
- // ditch the comments (but keep quoted hashes)
253
- if strings .HasPrefix (strings .TrimSpace (line ), "#" ) || strings .Contains (line , " #" ) {
254
- segmentsBetweenHashes := strings .Split (line , "#" )
255
- quotesAreOpen := false
256
- var segmentsToKeep []string
257
- for _ , segment := range segmentsBetweenHashes {
258
- if strings .Count (segment , "\" " ) == 1 || strings .Count (segment , "'" ) == 1 {
259
- if quotesAreOpen {
260
- segmentsToKeep = append (segmentsToKeep , segment )
261
- }
262
- quotesAreOpen = ! quotesAreOpen
263
- }
264
-
265
- if len (segmentsToKeep ) == 0 || quotesAreOpen {
266
- segmentsToKeep = append (segmentsToKeep , segment )
267
- }
268
- }
269
-
270
- line = strings .Join (segmentsToKeep , "#" )
271
- }
272
-
273
- firstEquals := strings .Index (line , "=" )
274
- firstColon := strings .Index (line , ":" )
275
- splitString := strings .SplitN (line , "=" , 2 )
276
- if firstColon != - 1 && (firstColon < firstEquals || firstEquals == - 1 ) {
277
- // This is a yaml-style line
278
- splitString = strings .SplitN (line , ":" , 2 )
279
- }
280
-
281
- if len (splitString ) != 2 {
282
- return "" , "" , errors .New ("can't separate key from value" )
283
- }
284
- key := exportRegex .ReplaceAllString (splitString [0 ], "$1" )
285
-
286
- // Parse the value
287
- value := parseValue (splitString [1 ], envMap , lookupFn )
288
-
289
- return key , value , nil
290
- }
291
-
292
- var (
293
- singleQuotesRegex = regexp .MustCompile (`\A'(.*)'\z` )
294
- doubleQuotesRegex = regexp .MustCompile (`\A"(.*)"\z` )
295
- escapeRegex = regexp .MustCompile (`\\.` )
296
- unescapeCharsRegex = regexp .MustCompile (`\\([^$])` )
297
- )
298
-
299
- func parseValue (value string , envMap map [string ]string , lookupFn LookupFn ) string {
300
-
301
- // trim
302
- value = strings .Trim (value , " " )
303
-
304
- // check if we've got quoted values or possible escapes
305
- if len (value ) > 1 {
306
- singleQuotes := singleQuotesRegex .FindStringSubmatch (value )
307
-
308
- doubleQuotes := doubleQuotesRegex .FindStringSubmatch (value )
309
-
310
- if singleQuotes != nil || doubleQuotes != nil {
311
- // pull the quotes off the edges
312
- value = value [1 : len (value )- 1 ]
313
- }
314
-
315
- if doubleQuotes != nil {
316
- // expand newlines
317
- value = escapeRegex .ReplaceAllStringFunc (value , func (match string ) string {
318
- c := strings .TrimPrefix (match , `\` )
319
- switch c {
320
- case "n" :
321
- return "\n "
322
- case "r" :
323
- return "\r "
324
- default :
325
- return match
326
- }
327
- })
328
- // unescape characters
329
- value = unescapeCharsRegex .ReplaceAllString (value , "$1" )
330
- }
331
-
332
- if singleQuotes == nil {
333
- value , _ = expandVariables (value , envMap , lookupFn )
334
- }
335
- }
336
-
337
- return value
338
- }
339
-
340
246
func expandVariables (value string , envMap map [string ]string , lookupFn LookupFn ) (string , error ) {
341
247
retVal , err := template .Substitute (value , func (k string ) (string , bool ) {
342
248
if v , ok := envMap [k ]; ok {
@@ -350,7 +256,9 @@ func expandVariables(value string, envMap map[string]string, lookupFn LookupFn)
350
256
return retVal , nil
351
257
}
352
258
259
+ // Deprecated: only used by unsupported/untested code for Marshal/Write.
353
260
func doubleQuoteEscape (line string ) string {
261
+ const doubleQuoteSpecialChars = "\\ \n \r \" !$`"
354
262
for _ , c := range doubleQuoteSpecialChars {
355
263
toReplace := "\\ " + string (c )
356
264
if c == '\n' {
0 commit comments