2016-04-11 18:15:53 +00:00
|
|
|
package expander
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
"regexp"
|
|
|
|
"strconv"
|
|
|
|
"strings"
|
|
|
|
)
|
|
|
|
|
|
|
|
// these are helper functions that bring bash-substitution to the drone yaml file.
|
|
|
|
// see http://tldp.org/LDP/abs/html/parameter-substitution.html
|
|
|
|
|
|
|
|
type substituteFunc func(str, key, val string) string
|
|
|
|
|
|
|
|
var substitutors = []substituteFunc{
|
|
|
|
substituteQ,
|
|
|
|
substitute,
|
|
|
|
substitutePrefix,
|
|
|
|
substituteSuffix,
|
|
|
|
substituteDefault,
|
|
|
|
substituteReplace,
|
|
|
|
substituteLeft,
|
|
|
|
substituteSubstr,
|
|
|
|
}
|
|
|
|
|
|
|
|
// substitute is a helper function that substitutes a simple parameter using
|
|
|
|
// ${parameter} notation.
|
|
|
|
func substitute(str, key, val string) string {
|
|
|
|
key = fmt.Sprintf("${%s}", key)
|
|
|
|
return strings.Replace(str, key, val, -1)
|
|
|
|
}
|
|
|
|
|
|
|
|
// substituteQ is a helper function that substitutes a simple parameter using
|
|
|
|
// "${parameter}" notation with the escaped value, using %q.
|
|
|
|
func substituteQ(str, key, val string) string {
|
|
|
|
key = fmt.Sprintf(`"${%s}"`, key)
|
|
|
|
val = fmt.Sprintf("%q", val)
|
|
|
|
return strings.Replace(str, key, val, -1)
|
|
|
|
}
|
|
|
|
|
|
|
|
// substitutePrefix is a helper function that substitutes paramters using
|
|
|
|
// ${parameter##prefix} notation with the parameter value minus the trimmed prefix.
|
|
|
|
func substitutePrefix(str, key, val string) string {
|
|
|
|
key = fmt.Sprintf("\\${%s##(.+)}", key)
|
|
|
|
reg, err := regexp.Compile(key)
|
|
|
|
if err != nil {
|
|
|
|
return str
|
|
|
|
}
|
|
|
|
for _, match := range reg.FindAllStringSubmatch(str, -1) {
|
|
|
|
if len(match) != 2 {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
val_ := strings.TrimPrefix(val, match[1])
|
|
|
|
str = strings.Replace(str, match[0], val_, -1)
|
|
|
|
}
|
|
|
|
return str
|
|
|
|
}
|
|
|
|
|
|
|
|
// substituteSuffix is a helper function that substitutes paramters using
|
|
|
|
// ${parameter%%suffix} notation with the parameter value minus the trimmed suffix.
|
|
|
|
func substituteSuffix(str, key, val string) string {
|
|
|
|
key = fmt.Sprintf("\\${%s%%%%(.+)}", key)
|
|
|
|
reg, err := regexp.Compile(key)
|
|
|
|
if err != nil {
|
|
|
|
return str
|
|
|
|
}
|
|
|
|
for _, match := range reg.FindAllStringSubmatch(str, -1) {
|
|
|
|
if len(match) != 2 {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
val_ := strings.TrimSuffix(val, match[1])
|
|
|
|
str = strings.Replace(str, match[0], val_, -1)
|
|
|
|
}
|
|
|
|
return str
|
|
|
|
}
|
|
|
|
|
|
|
|
// substituteDefault is a helper function that substitutes paramters using
|
|
|
|
// ${parameter=default} notation with the parameter value. When empty the
|
|
|
|
// default value is used.
|
|
|
|
func substituteDefault(str, key, val string) string {
|
|
|
|
key = fmt.Sprintf("\\${%s=(.+)}", key)
|
|
|
|
reg, err := regexp.Compile(key)
|
|
|
|
if err != nil {
|
|
|
|
return str
|
|
|
|
}
|
|
|
|
for _, match := range reg.FindAllStringSubmatch(str, -1) {
|
|
|
|
if len(match) != 2 {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
if len(val) == 0 {
|
|
|
|
str = strings.Replace(str, match[0], match[1], -1)
|
|
|
|
} else {
|
|
|
|
str = strings.Replace(str, match[0], val, -1)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return str
|
|
|
|
}
|
|
|
|
|
2016-07-09 17:37:13 +00:00
|
|
|
// unescapeBackslash is a helper function to unescape any backslashes in str.
|
|
|
|
// Note that no actual literal conversions are done.
|
|
|
|
func unescapeBackslash(str string) string {
|
|
|
|
re := regexp.MustCompile(`\\(.)`)
|
|
|
|
|
|
|
|
return string(re.ReplaceAll([]byte(str), []byte("$1")))
|
|
|
|
}
|
|
|
|
|
2016-04-11 18:15:53 +00:00
|
|
|
// substituteReplace is a helper function that substitutes paramters using
|
|
|
|
// ${parameter/old/new} notation with the parameter value. A find and replace
|
|
|
|
// is performed before injecting the strings, replacing the old pattern with
|
|
|
|
// the new value.
|
|
|
|
func substituteReplace(str, key, val string) string {
|
2016-07-09 17:37:13 +00:00
|
|
|
key = fmt.Sprintf(`\${%s/((?:\\.|[^\\])+)/(.+)}`, key)
|
2016-04-11 18:15:53 +00:00
|
|
|
reg, err := regexp.Compile(key)
|
|
|
|
if err != nil {
|
|
|
|
return str
|
|
|
|
}
|
2016-07-09 17:37:13 +00:00
|
|
|
match := reg.FindStringSubmatch(str)
|
|
|
|
if match == nil {
|
|
|
|
return str
|
2016-04-11 18:15:53 +00:00
|
|
|
}
|
2016-07-09 17:37:13 +00:00
|
|
|
|
|
|
|
old := unescapeBackslash(match[1])
|
|
|
|
new := unescapeBackslash(match[2])
|
|
|
|
|
|
|
|
with := strings.Replace(val, old, new, -1)
|
|
|
|
return strings.Replace(str, match[0], with, -1)
|
2016-04-11 18:15:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// substituteLeft is a helper function that substitutes paramters using
|
|
|
|
// ${parameter:pos} notation with the parameter value, sliced up to the
|
|
|
|
// specified position.
|
|
|
|
func substituteLeft(str, key, val string) string {
|
|
|
|
key = fmt.Sprintf("\\${%s:([0-9]*)}", key)
|
|
|
|
reg, err := regexp.Compile(key)
|
|
|
|
if err != nil {
|
|
|
|
return str
|
|
|
|
}
|
|
|
|
for _, match := range reg.FindAllStringSubmatch(str, -1) {
|
|
|
|
if len(match) != 2 {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
index, err := strconv.Atoi(match[1])
|
|
|
|
if err != nil {
|
|
|
|
continue // skip
|
|
|
|
}
|
|
|
|
if index > len(val)-1 {
|
|
|
|
continue // skip
|
|
|
|
}
|
|
|
|
|
|
|
|
str = strings.Replace(str, match[0], val[:index], -1)
|
|
|
|
}
|
|
|
|
return str
|
|
|
|
}
|
|
|
|
|
|
|
|
// substituteLeft is a helper function that substitutes paramters using
|
|
|
|
// ${parameter:pos:len} notation with the parameter value as a substring,
|
|
|
|
// starting at the specified position for the specified length.
|
|
|
|
func substituteSubstr(str, key, val string) string {
|
|
|
|
key = fmt.Sprintf("\\${%s:([0-9]*):([0-9]*)}", key)
|
|
|
|
reg, err := regexp.Compile(key)
|
|
|
|
if err != nil {
|
|
|
|
return str
|
|
|
|
}
|
|
|
|
for _, match := range reg.FindAllStringSubmatch(str, -1) {
|
|
|
|
if len(match) != 3 {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
pos, err := strconv.Atoi(match[1])
|
|
|
|
if err != nil {
|
|
|
|
continue // skip
|
|
|
|
}
|
|
|
|
length, err := strconv.Atoi(match[2])
|
|
|
|
if err != nil {
|
|
|
|
continue // skip
|
|
|
|
}
|
|
|
|
if pos+length > len(val)-1 {
|
|
|
|
continue // skip
|
|
|
|
}
|
|
|
|
str = strings.Replace(str, match[0], val[pos:pos+length], -1)
|
|
|
|
}
|
|
|
|
return str
|
|
|
|
}
|