Skip to content

Commit

Permalink
Merge pull request #12 from buildkite/fix-literal-dollar-sign-escapes
Browse files Browse the repository at this point in the history
Fix literal dollar sign escapes
  • Loading branch information
moskyb authored Jun 5, 2024
2 parents c7172ad + 5441920 commit 1466cba
Show file tree
Hide file tree
Showing 3 changed files with 26 additions and 9 deletions.
1 change: 1 addition & 0 deletions interpolate_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -290,6 +290,7 @@ func TestEscapingVariables(t *testing.T) {
{`Do this \${SUCH_ESCAPE}`, `Do this ${SUCH_ESCAPE}`},
{`Do this $${SUCH_ESCAPE:-$OTHERWISE}`, `Do this ${SUCH_ESCAPE:-$OTHERWISE}`},
{`Do this \${SUCH_ESCAPE:-$OTHERWISE}`, `Do this ${SUCH_ESCAPE:-$OTHERWISE}`},
{`echo "my favourite mountain is cotopaxi" | grep 'xi$$'`, `echo "my favourite mountain is cotopaxi" | grep 'xi$'`},
} {
result, err := interpolate.Interpolate(nil, tc.Str)
if err != nil {
Expand Down
26 changes: 17 additions & 9 deletions parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -123,20 +123,28 @@ func (p *Parser) parseExpression(stop ...rune) (Expression, error) {
}

func (p *Parser) parseEscapedExpansion() (Expansion, error) {
// if it's an escaped brace expansion, (eg $${MY_COOL_VAR:-5}) consume text until the close brace
if c := p.peekRune(); c == '{' {
next := p.peekRune()
switch {
case next == '{':
// if it's an escaped brace expansion, (eg $${MY_COOL_VAR:-5}) consume text until the close brace
id := p.scanUntil(func(r rune) bool { return r == '}' })
id = id + string(p.nextRune()) // we know that the next rune is a close brace, chuck it on the end
return EscapedExpansion{Identifier: id}, nil
}

// otherwise, it's an escaped identifier (eg $$MY_COOL_VAR)
id, err := p.scanIdentifier()
if err != nil {
return nil, err
}
case unicode.IsLetter(next):
// it's an escaped identifier (eg $$MY_COOL_VAR)
id, err := p.scanIdentifier()
if err != nil {
return nil, err
}

return EscapedExpansion{Identifier: id}, nil
return EscapedExpansion{Identifier: id}, nil

default:
// there's no identifier or brace afterward, so it's probably a literal escaped dollar sign, so return an empty identifier
// that will be expanded to a single dollar sign
return EscapedExpansion{Identifier: ""}, nil
}
}

func (p *Parser) parseExpansion() (Expansion, error) {
Expand Down
8 changes: 8 additions & 0 deletions parser_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,14 @@ func TestParser(t *testing.T) {
String: "$$MOUNTAIN",
Expected: []interpolate.ExpressionItem{{Expansion: interpolate.EscapedExpansion{Identifier: "MOUNTAIN"}}},
},
{
String: "this is a regex! /^start.*end$$/", // the dollar sign at the end of the regex has to be escaped to be treated as a literal dollar sign by this library
Expected: []interpolate.ExpressionItem{
{Text: "this is a regex! /^start.*end"},
{Expansion: interpolate.EscapedExpansion{Identifier: ""}},
{Text: "/"},
},
},
}

for _, tc := range testCases {
Expand Down

0 comments on commit 1466cba

Please sign in to comment.