grim/convey

Bump the version for release
v0.14.0-alpha3
2018-02-20, Gary Kramlich
166a6d1979fa
Bump the version for release
// Convey
// Copyright 2016-2018 Gary Kramlich <grim@reaperworld.com>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
package environment
import (
"fmt"
"os"
"strings"
)
type envMapper struct {
env []string
}
const (
maxExpandWidth = 100
)
// mapVariable replaces instances of the given name.
func (e *envMapper) mapVariable(name string) string {
// We might have something like $(pwd), in which case we don't want
// to wrap the name in `{}`, which would cause it to be ${}(pwd) and
// cannot be evaluated by a run task.
if name == "" {
return "$"
}
for _, item := range e.env {
if parts := strings.SplitN(item, "=", 2); parts[0] == name {
if len(parts) == 2 {
return parts[1]
}
return os.Getenv(name)
}
}
return fmt.Sprintf("${%s}", name)
}
// Map will return the value matching a KEY=VAL pair in the given environment.
func Map(key string, env []string) string {
mapper := envMapper{env}
return mapper.mapVariable(key)
}
// Mapper will replace ${TEMPLATE} patterns in the string with the KEY=VAL pairs
// in the given environment. This function will replace patterns recursively, so
// if VAL has the form ${TEMPLATE}, it will be replaced again.
func Mapper(str string, env []string) (string, error) {
mapper := envMapper{env}
orig := str
last := str
next := os.Expand(last, mapper.mapVariable)
prev := map[string]struct{}{}
for i := 0; i < maxExpandWidth; i++ {
if last == next {
return next, nil
}
if _, ok := prev[next]; ok {
break
}
last = next
next = os.Expand(last, mapper.mapVariable)
prev[last] = struct{}{}
}
return "", fmt.Errorf("infinite environment mapping loop while expanding '%s'", orig)
}
// SliceMapper calls Mapper for each item in a slice and returns a new slice.
func SliceMapper(slice []string, env []string) ([]string, error) {
mapped := []string{}
for _, template := range slice {
expanded, err := Mapper(template, env)
if err != nil {
return nil, err
}
mapped = append(mapped, expanded)
}
return mapped, nil
}