mirror of
https://github.com/drone/drone-cli.git
synced 2024-11-26 14:13:47 +01:00
55e0addb31
* Add support for jpath in jsonnet Co-authored-by: TP Honey <tp@harness.io>
177 lines
3.7 KiB
Go
177 lines
3.7 KiB
Go
package jsonnet
|
|
|
|
import (
|
|
"bytes"
|
|
"fmt"
|
|
"io"
|
|
"io/ioutil"
|
|
"log"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
|
|
"github.com/fatih/color"
|
|
"github.com/ghodss/yaml"
|
|
"github.com/google/go-jsonnet"
|
|
"github.com/urfave/cli"
|
|
)
|
|
// Command exports the jsonnet command.
|
|
var Command = cli.Command{
|
|
Name: "jsonnet",
|
|
Usage: "generate .drone.yml from jsonnet",
|
|
ArgsUsage: "[path/to/.drone.jsonnet]",
|
|
Action: func(c *cli.Context) {
|
|
if err := generate(c); err != nil {
|
|
log.Fatalln(err)
|
|
}
|
|
},
|
|
Flags: []cli.Flag{
|
|
cli.StringFlag{
|
|
Name: "source",
|
|
Usage: "Source file",
|
|
Value: ".drone.jsonnet",
|
|
},
|
|
cli.StringFlag{
|
|
Name: "target",
|
|
Usage: "target file",
|
|
Value: ".drone.yml",
|
|
},
|
|
cli.BoolFlag{
|
|
Name: "stream",
|
|
Usage: "Write output as a YAML stream.",
|
|
},
|
|
cli.BoolFlag{
|
|
Name: "format",
|
|
Usage: "Write output as formatted YAML",
|
|
},
|
|
cli.BoolFlag{
|
|
Name: "stdout",
|
|
Usage: "Write output to stdout",
|
|
},
|
|
cli.BoolFlag{
|
|
Name: "string",
|
|
Usage: "Expect a string, manifest as plain text",
|
|
},
|
|
cli.StringSliceFlag{
|
|
Name: "extVar, V",
|
|
Usage: "Pass extVars to Jsonnet (can be specified multiple times)",
|
|
},
|
|
cli.StringSliceFlag{
|
|
Name: "jpath, J",
|
|
Usage: "Specify an additional library search dir (right-most wins)",
|
|
},
|
|
},
|
|
}
|
|
|
|
func generate(c *cli.Context) error {
|
|
result, err := convert(
|
|
c.String("source"),
|
|
c.Bool("string"),
|
|
c.Bool("format"),
|
|
c.Bool("stream"),
|
|
c.StringSlice("extVar"),
|
|
c.StringSlice("jpath"),
|
|
)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// the user can optionally write the yaml to stdout. This is useful for debugging purposes without mutating an existing file.
|
|
if c.Bool("stdout") {
|
|
io.WriteString(os.Stdout, result)
|
|
return nil
|
|
}
|
|
|
|
target := c.String("target")
|
|
return ioutil.WriteFile(target, []byte(result), 0644)
|
|
}
|
|
|
|
func convert(source string, stringOutput bool, format bool, stream bool, vars []string, jpath []string) (string, error) {
|
|
data, err := ioutil.ReadFile(source)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
vm := jsonnet.MakeVM()
|
|
vm.MaxStack = 500
|
|
vm.StringOutput = stringOutput
|
|
vm.ErrorFormatter.SetMaxStackTraceSize(20)
|
|
vm.ErrorFormatter.SetColorFormatter(
|
|
color.New(color.FgRed).Fprintf,
|
|
)
|
|
|
|
// register native functions
|
|
RegisterNativeFuncs(vm)
|
|
|
|
jsonnetPath := filepath.SplitList(os.Getenv("JSONNET_PATH"))
|
|
jsonnetPath = append(jsonnetPath, jpath...)
|
|
vm.Importer(&jsonnet.FileImporter{
|
|
JPaths: jsonnetPath,
|
|
})
|
|
|
|
// extVars
|
|
for _, v := range vars {
|
|
name, value, err := getVarVal(v)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
vm.ExtVar(name, value)
|
|
}
|
|
|
|
formatDoc := func(doc []byte) ([]byte, error) {
|
|
// enable yaml output
|
|
if format {
|
|
formatted, yErr := yaml.JSONToYAML(doc)
|
|
if yErr != nil {
|
|
return nil, fmt.Errorf("failed to convert to YAML: %v", yErr)
|
|
}
|
|
return formatted, nil
|
|
}
|
|
return doc, nil
|
|
}
|
|
|
|
buf := new(bytes.Buffer)
|
|
if stream {
|
|
docs, err := vm.EvaluateSnippetStream(source, string(data))
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
for _, doc := range docs {
|
|
formatted, err := formatDoc([]byte(doc))
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
buf.WriteString("---")
|
|
buf.WriteString("\n")
|
|
buf.Write(formatted)
|
|
}
|
|
} else {
|
|
result, err := vm.EvaluateSnippet(source, string(data))
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
formatted, err := formatDoc([]byte(result))
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
buf.Write(formatted)
|
|
}
|
|
|
|
return buf.String(), nil
|
|
}
|
|
|
|
// https://github.com/google/go-jsonnet/blob/master/cmd/jsonnet/cmd.go#L149
|
|
func getVarVal(s string) (string, string, error) {
|
|
parts := strings.SplitN(s, "=", 2)
|
|
name := parts[0]
|
|
if len(parts) == 1 {
|
|
content, exists := os.LookupEnv(name)
|
|
if exists {
|
|
return name, content, nil
|
|
}
|
|
return "", "", fmt.Errorf("environment variable %v was undefined", name)
|
|
}
|
|
return name, parts[1], nil
|
|
}
|