Skip to content

Commit 0c223d9

Browse files
committed
envsubst: skip on ! prefix
1 parent 6ade1ff commit 0c223d9

5 files changed

Lines changed: 86 additions & 13 deletions

File tree

context_test.go

Lines changed: 20 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -96,21 +96,28 @@ func TestEnv(t *testing.T) {
9696

9797
func TestEnvsubst(t *testing.T) {
9898
c := Context{}
99+
c.Setenv("REPLACE", "me")
100+
for s, expected := range map[string]string{
101+
"BEFORE${REPLACE}AFTER": "BEFOREmeAFTER",
102+
"BEFORE${!REPLACE}AFTER": "BEFORE${REPLACE}AFTER",
99103

100-
if s, err := c.Envsubst("start${example}end"); s != "startend" || err != nil {
101-
t.Fail()
102-
}
104+
"BEFORE${REPLACE:-default}AFTER": "BEFOREmeAFTER",
105+
"BEFORE${!REPLACE:-default}AFTER": "BEFORE${REPLACE:-default}AFTER",
103106

104-
if s, err := c.Envsubst("start${example:-default}end"); s != "startdefaultend" || err != nil {
105-
t.Fail()
106-
}
107+
"BEFORE${REPLACE/me/you}AFTER": "BEFOREyouAFTER",
108+
"BEFORE${!REPLACE/me/you}AFTER": "BEFORE${REPLACE/me/you}AFTER",
107109

108-
c.Setenv("example", "value")
109-
if s, err := c.Envsubst("start${example}end"); s != "startvalueend" || err != nil {
110-
t.Fail()
111-
}
112-
113-
if s, err := c.Envsubst("start${example:-default}end"); s != "startvalueend" || err != nil {
114-
t.Fail()
110+
"BEFORE${UNSET:-default}AFTER": "BEFOREdefaultAFTER",
111+
"BEFORE${!UNSET:-default}AFTER": "BEFORE${UNSET:-default}AFTER",
112+
} {
113+
t.Run(s, func(t *testing.T) {
114+
actual, err := c.Envsubst(s)
115+
if err != nil {
116+
t.Fatal(err)
117+
}
118+
if actual != expected {
119+
t.Fatalf("invalid replacement\nexpected: %#v\nactual : %#v", expected, actual)
120+
}
121+
})
115122
}
116123
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,32 @@
11
# Envsubst
2+
3+
Expands variables in a string using [drone/envsubst].
4+
5+
> Expressions are ignored if the variable name has a `!` prefix (which is stripped).
6+
7+
## Supported Functions
8+
9+
| Expression | Meaning |
10+
| ----------------- | -------------- |
11+
| `${var}` | Value of `$var` |
12+
| `${#var}` | String length of `$var` |
13+
| `${var^}` | Uppercase first character of `$var` |
14+
| `${var^^}` | Uppercase all characters in `$var` |
15+
| `${var,}` | Lowercase first character of `$var` |
16+
| `${var,,}` | Lowercase all characters in `$var` |
17+
| `${var:n}` | Offset `$var` `n` characters from start |
18+
| `${var:n:len}` | Offset `$var` `n` characters with max length of `len` |
19+
| `${var#pattern}` | Strip shortest `pattern` match from start |
20+
| `${var##pattern}` | Strip longest `pattern` match from start |
21+
| `${var%pattern}` | Strip shortest `pattern` match from end |
22+
| `${var%%pattern}` | Strip longest `pattern` match from end |
23+
| `${var-default` | If `$var` is not set, evaluate expression as `$default` |
24+
| `${var:-default` | If `$var` is not set or is empty, evaluate expression as `$default` |
25+
| `${var=default` | If `$var` is not set, evaluate expression as `$default` |
26+
| `${var:=default` | If `$var` is not set or is empty, evaluate expression as `$default` |
27+
| `${var/pattern/replacement}` | Replace as few `pattern` matches as possible with `replacement` |
28+
| `${var//pattern/replacement}` | Replace as many `pattern` matches as possible with `replacement` |
29+
| `${var/#pattern/replacement}` | Replace `pattern` match with `replacement` from `$var` start |
30+
| `${var/%pattern/replacement}` | Replace `pattern` match with `replacement` from `$var` end |
31+
32+
[drone/envsubst]:https://github.com/drone/envsubst
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
Patched to ignore any substitution prefixed with `!`:
2+
3+
```
4+
${HOME:-replacement} # /home/user
5+
${!HOME:-replacement} # ${HOME:-replacement}
6+
```

third_party/github.com/drone/envsubst/parse/parse.go

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,8 @@ func (t *Tree) parseFunc() (Node, error) {
9090
// Turn on all escape characters
9191
t.scanner.escapeChars = escapeAll
9292
switch t.scanner.peek() {
93+
case '!':
94+
return t.parseIgnoredFunc()
9395
case '#':
9496
return t.parseLenFunc()
9597
}
@@ -355,6 +357,29 @@ func (t *Tree) parseCasingFunc(name string) (Node, error) {
355357
return node, t.consumeRbrack()
356358
}
357359

360+
// parses the ignored ${!...} function
361+
func (t *Tree) parseIgnoredFunc() (Node, error) {
362+
node := new(TextNode)
363+
364+
t.scanner.accept = acceptOneExclamationMark
365+
t.scanner.mode = scanIdent
366+
switch t.scanner.scan() {
367+
case tokenIdent:
368+
default:
369+
return nil, ErrBadSubstitution
370+
}
371+
372+
t.scanner.accept = acceptNotClosing
373+
t.scanner.mode = scanIdent
374+
switch t.scanner.scan() {
375+
case tokenIdent:
376+
node.Value = "${" + t.scanner.string() + "}"
377+
default:
378+
return nil, ErrBadSubstitution
379+
}
380+
return node, t.consumeRbrack()
381+
}
382+
358383
// parses the ${#param} string function
359384
func (t *Tree) parseLenFunc() (Node, error) {
360385
node := new(FuncNode)

third_party/github.com/drone/envsubst/parse/scan.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -227,6 +227,10 @@ func acceptColon(r rune, i int) bool {
227227
return r == ':'
228228
}
229229

230+
func acceptOneExclamationMark(r rune, i int) bool {
231+
return r == '!' && i == 1
232+
}
233+
230234
func acceptOneHash(r rune, i int) bool {
231235
return r == '#' && i == 1
232236
}

0 commit comments

Comments
 (0)