feat: implement basic functions

This commit is contained in:
Suzuki Shunsuke
2019-06-11 09:48:49 +09:00
parent 9c6d97c8d4
commit 0f0061d80d
10 changed files with 439 additions and 0 deletions

1
.envrc Normal file
View File

@@ -0,0 +1 @@
export GO111MODULE=on

View File

@@ -0,0 +1,22 @@
package main
import (
"os"
"github.com/urfave/cli"
"github.com/suzuki-shunsuke/drone-jsonnet-generator/generator/domain"
"github.com/suzuki-shunsuke/drone-jsonnet-generator/generator/handler"
)
func main() {
app := cli.NewApp()
app.Name = "drone-jsonnet-generator"
app.Version = domain.Version
app.Author = "suzuki-shunsuke https://github.com/suzuki-shunsuke"
app.Usage = "convert Drone v0.8x format .drone.yml to v1 format .drone.jsonnet"
app.Commands = []cli.Command{
handler.GenCommand,
}
app.Run(os.Args)
}

View File

@@ -0,0 +1,21 @@
package domain
type (
ConvertArg struct {
Source string
Target string
Stdout bool
}
LegacyYAML struct {
Matrix map[string][]string
}
Include struct {
Include []map[string]string
}
LegacyIncludedYAML struct {
Matrix Include
}
)

View File

@@ -0,0 +1,22 @@
package domain
const (
MatrixTemplate = `local pipeline({{ .ArgName }}) = {{ .Pipeline }};
{{range $k, $v := .ArrayArgs -}}
local array_{{ $k }} = {{ $v }};
{{end}}
[
pipeline({{ .ArgName }}) {{range $k, $v := .ArrayArgs }}for {{ $k }} in array_{{ $k }} {{end}}
]
`
IncludeTemplate = `local pipeline({{ .ArgName }}) = {{ .Pipeline }};
local args = {{ .Args }};
[
pipeline({{ .ArgNameWithArg }}) for arg in args
]
`
)

View File

@@ -0,0 +1,4 @@
package domain
// Version is the drone-jsonnet-generator's version.
const Version = "0.1.0"

42
generator/handler/main.go Normal file
View File

@@ -0,0 +1,42 @@
package handler
import (
"github.com/urfave/cli"
"github.com/suzuki-shunsuke/go-cliutil"
"github.com/suzuki-shunsuke/drone-jsonnet-generator/generator/domain"
"github.com/suzuki-shunsuke/drone-jsonnet-generator/generator/usecase"
)
// GenCommand is the sub command "gen".
var GenCommand = cli.Command{
Name: "gen",
Usage: "convert Drone v0.8x format .drone.yml to v1 format .drone.jsonnet",
Action: Gen,
Flags: []cli.Flag{
cli.StringFlag{
Name: "source, s",
Usage: "source .drone.yml path",
Value: ".drone.yml",
},
cli.StringFlag{
Name: "target, t",
Usage: "target .drone.jsonnet path",
Value: ".drone.jsonnet",
},
cli.BoolFlag{
Name: "stdout",
Usage: "output generated jsonnet to stdout",
},
},
}
// Gen is the sub command "gen".
func Gen(c *cli.Context) error {
return cliutil.ConvErrToExitError(usecase.Convert(&domain.ConvertArg{
Source: c.String("source"),
Target: c.String("target"),
Stdout: c.Bool("stdout"),
}))
}

View File

@@ -0,0 +1,158 @@
package usecase
import (
"bytes"
"encoding/json"
"fmt"
"io"
"io/ioutil"
"os"
"strings"
"text/template"
"gopkg.in/yaml.v2"
"github.com/drone/drone-yaml/yaml/converter/legacy"
gyaml "github.com/ghodss/yaml"
"github.com/pkg/errors"
"github.com/suzuki-shunsuke/drone-jsonnet-generator/generator/domain"
)
func validateArg(arg *domain.ConvertArg) error {
if arg == nil {
return errors.New("arg is nil")
}
if arg.Source == "" {
return errors.New("source is required")
}
if !arg.Stdout && arg.Target == "" {
return errors.New("target or stdout is required")
}
return nil
}
func Convert(arg *domain.ConvertArg) error {
if err := validateArg(arg); err != nil {
return err
}
f, err := os.Open(arg.Source)
if err != nil {
return errors.Wrap(err, "failed to open the source file .drone.yml "+arg.Source)
}
defer f.Close()
b, err := ioutil.ReadAll(f)
if err != nil {
return errors.Wrap(err, "failed to read the source file .drone.yml "+arg.Source)
}
m, matrix, include, err := convertMatrix(b)
if err != nil {
return err
}
if matrix == nil && include == nil {
// matrix isn't used
return errors.New("matrix build isn't used")
}
// remove matrix and convert to byte
delete(m, "matrix")
bWithoutMatrix, err := yaml.Marshal(m)
if err != nil {
return err
}
// convert to v1 format
yamlV1, err := legacy.Convert(bWithoutMatrix)
if err != nil {
return err
}
// convert yaml to json struct
d := map[string]interface{}{}
if err := gyaml.Unmarshal(yamlV1, &d); err != nil {
return err
}
// convert pipeline name
if matrix != nil {
c := make([]string, len(matrix))
i := 0
for k := range matrix {
c[i] = fmt.Sprintf("' %s:' + %s", k, k)
i++
}
d["name"] = "'" + strings.Join(c, " + ")[2:]
} else {
c := make([]string, len(include[0]))
i := 0
for k := range include[0] {
c[i] = fmt.Sprintf("' %s:' + %s", k, k)
i++
}
d["name"] = "'" + strings.Join(c, " + ")[2:]
}
// convert YAML to JSON
buf := bytes.NewBuffer([]byte{})
encoder := json.NewEncoder(buf)
encoder.SetIndent("", " ")
if err := encoder.Encode(d); err != nil {
return errors.Wrap(err, "failed to convert YAML to JSON")
}
jsonV1 := strings.TrimSpace(buf.String())
if arg.Stdout {
return genJSONNet(os.Stdout, jsonV1, matrix, include)
}
w, err := os.Create(arg.Target)
if err != nil {
return err
}
defer w.Close()
return genJSONNet(w, jsonV1, matrix, include)
}
func genJSONNet(w io.Writer, jsonV1 string, matrix map[string][]string, include []map[string]string) error {
if include == nil {
renderer := &MatrixRenderer{
Pipeline: jsonV1,
Matrix: matrix,
}
tpl, err := template.New("matrix").Parse(domain.MatrixTemplate)
if err != nil {
return err
}
return tpl.Execute(w, renderer)
}
renderer := &IncludeRenderer{
Pipeline: jsonV1,
Include: include,
}
tpl, err := template.New("include").Parse(domain.IncludeTemplate)
if err != nil {
return err
}
return tpl.Execute(w, renderer)
}
func convertMatrix(data []byte) (map[string]interface{}, map[string][]string, []map[string]string, error) {
m := map[string]interface{}{}
if err := yaml.Unmarshal(data, &m); err != nil {
return nil, nil, nil, errors.Wrap(err, "failed to parse the source .drone.yml")
}
if _, ok := m["matrix"]; !ok {
return m, nil, nil, nil
}
legacyYAML := &domain.LegacyYAML{}
err := yaml.Unmarshal(data, legacyYAML)
if err == nil {
return m, legacyYAML.Matrix, nil, nil
}
legacyIncludedYAML := &domain.LegacyIncludedYAML{}
if err := yaml.Unmarshal(data, legacyIncludedYAML); err != nil {
return m, nil, nil, errors.New("matrix format is invalid")
}
return m, nil, legacyIncludedYAML.Matrix.Include, nil
}

View File

@@ -0,0 +1,71 @@
package usecase
import (
"encoding/json"
"strings"
)
type (
MatrixRenderer struct {
Pipeline string
Matrix map[string][]string
}
IncludeRenderer struct {
Pipeline string
Include []map[string]string
}
)
func (pr *MatrixRenderer) ArgName() string {
if pr.Matrix == nil {
return ""
}
a := make([]string, len(pr.Matrix))
i := 0
for k := range pr.Matrix {
a[i] = k
i++
}
return strings.Join(a, ", ")
}
func (pr *IncludeRenderer) ArgName() string {
if len(pr.Include) == 0 {
return ""
}
a := make([]string, len(pr.Include[0]))
i := 0
for k := range pr.Include[0] {
a[i] = k
i++
}
return strings.Join(a, ", ")
}
func (pr *IncludeRenderer) Args() string {
b, _ := json.MarshalIndent(pr.Include, "", " ")
return string(b)
}
func (pr *IncludeRenderer) ArgNameWithArg() string {
if len(pr.Include) == 0 {
return ""
}
a := make([]string, len(pr.Include[0]))
i := 0
for k := range pr.Include[0] {
a[i] = "arg." + k
i++
}
return strings.Join(a, ", ")
}
func (pr *MatrixRenderer) ArrayArgs() map[string]string {
a := make(map[string]string, len(pr.Matrix))
for k, v := range pr.Matrix {
b, _ := json.MarshalIndent(v, "", " ")
a[k] = string(b)
}
return a
}

12
go.mod Normal file
View File

@@ -0,0 +1,12 @@
module github.com/suzuki-shunsuke/drone-jsonnet-generator
go 1.12
require (
github.com/drone/drone-yaml v1.1.3
github.com/ghodss/yaml v1.0.0
github.com/pkg/errors v0.8.1
github.com/suzuki-shunsuke/go-cliutil v0.0.0-20181211154308-176f852d9bca
github.com/urfave/cli v1.20.0
gopkg.in/yaml.v2 v2.2.2
)

86
go.sum Normal file
View File

@@ -0,0 +1,86 @@
docker.io/go-docker v1.0.0/go.mod h1:7tiAn5a0LFmjbPDbyTPOaTTOuG1ZRNXdPA6RvKY+fpY=
github.com/Microsoft/go-winio v0.4.11/go.mod h1:VhR8bwka0BXejwEJY73c50VrPtXAaKcyvVC4A4RozmA=
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/bmatcuk/doublestar v1.1.1 h1:YroD6BJCZBYx06yYFEWvUuKVWQn3vLLQAVmDmvTSaiQ=
github.com/bmatcuk/doublestar v1.1.1/go.mod h1:UD6OnuiIn0yFxxA2le/rnRU1G4RaI4UvFv1sNto9p6w=
github.com/buildkite/yaml v2.1.0+incompatible h1:xirI+ql5GzfikVNDmt+yeiXpf/v1Gt03qXTtT5WXdr8=
github.com/buildkite/yaml v2.1.0+incompatible/go.mod h1:UoU8vbcwu1+vjZq01+KrpSeLBgQQIjL/H7Y6KwikUrI=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/docker/distribution v0.0.0-20170726174610-edc3ab29cdff/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
github.com/docker/go-connections v0.3.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec=
github.com/docker/go-units v0.3.3 h1:Xk8S3Xj5sLGlG5g67hJmYMmUgXv5N4PhkjJHHqrwnTk=
github.com/docker/go-units v0.3.3/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
github.com/drone/drone-runtime v1.0.5/go.mod h1:+osgwGADc/nyl40J0fdsf8Z09bgcBZXvXXnLOY48zYs=
github.com/drone/drone-yaml v1.1.3 h1:fCtt8RcCQDyqHK5vbbMnTpwYoIspeDErhw4HNWN9I1I=
github.com/drone/drone-yaml v1.1.3/go.mod h1:l/ehbHx9TGs4jgzhRnP5d+M9tmRsAmWyBHWAFEOXrk4=
github.com/drone/signal v1.0.0/go.mod h1:S8t92eFT0g4WUgEc/LxG+LCuiskpMNsG0ajAMGnyZpc=
github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/gogo/protobuf v0.0.0-20170307180453-100ba4e88506/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/go-cmp v0.2.0 h1:+dTQ8DZQJz0Mb/HjFlkptS1FeQ4cWSnN941F8aEG4SQ=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/google/gofuzz v0.0.0-20170612174753-24818f796faf/go.mod h1:HP5RmnzzSNb993RKQDq4+1A4ia9nllfqcQFTQJedwGI=
github.com/googleapis/gnostic v0.2.0/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY=
github.com/gregjones/httpcache v0.0.0-20181110185634-c63ab54fda8f/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/imdario/mergo v0.3.6/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
github.com/json-iterator/go v1.1.5/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/natessilva/dag v0.0.0-20180124060714-7194b8dcc5c4/go.mod h1:cojhOHk1gbMeklOyDP2oKKLftefXoJreOQGOrXk+Z38=
github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s=
github.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0=
github.com/petar/GoLLRB v0.0.0-20130427215148-53be0d36a84c/go.mod h1:HUpKUBZnpzkdx0kD/+Yfuft+uD3zHGtXF/XJB14TUr4=
github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/sergi/go-diff v1.0.0 h1:Kpca3qRNrduNnOQeazBd0ysaKrUJiIuISHxogkT9RPQ=
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/suzuki-shunsuke/go-cliutil v0.0.0-20181211154308-176f852d9bca h1:sWmTDahfBZizD2nPK04+zI1pnykXfRyT1Bu5qPY/t9M=
github.com/suzuki-shunsuke/go-cliutil v0.0.0-20181211154308-176f852d9bca/go.mod h1:Vq3NkhgmA9DT/2UZ08x/3A34xxvzQ/vTMABnTWKoMbY=
github.com/urfave/cli v1.20.0 h1:fDqGv3UG/4jbVl/QkFwEdddtEDjh/5Ov6X+0B/3bPaw=
github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
github.com/vinzenz/yaml v0.0.0-20170920082545-91409cdd725d h1:3wDi6J5APMqaHBVPuVd7RmHD2gRTfqbdcVSpCNoUWtk=
github.com/vinzenz/yaml v0.0.0-20170920082545-91409cdd725d/go.mod h1:mb5taDqMnJiZNRQ3+02W2IFG+oEz1+dTuCXkp4jpkfo=
golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181005035420-146acd28ed58/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/oauth2 v0.0.0-20181203162652-d668ce993890/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20181005133103-4497e2df6f9e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
google.golang.org/appengine v1.3.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
k8s.io/api v0.0.0-20181130031204-d04500c8c3dd/go.mod h1:iuAfoD4hCxJ8Onx9kaTIt30j7jUFS00AXQi6QMi99vA=
k8s.io/apimachinery v0.0.0-20181201231028-18a5ff3097b4/go.mod h1:ccL7Eh7zubPUSh9A3USN90/OzHNSVN6zxzde07TDCL0=
k8s.io/client-go v9.0.0+incompatible/go.mod h1:7vJpHMYJwNQCWgzmNV+VYUl1zCObLyodBc8nIyt8L5s=
k8s.io/klog v0.1.0/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk=
sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o=