diff --git a/atlasaction/action.go b/atlasaction/action.go index 873eff9..ed0ed84 100644 --- a/atlasaction/action.go +++ b/atlasaction/action.go @@ -1240,23 +1240,28 @@ func RenderTemplate(name string, data any) (string, error) { return buf.String(), nil } -// toEnvName converts the given string to an environment variable name. -func toEnvName(s string) string { +// ToEnvName converts the given string to an environment variable name. +func ToEnvName(s string) string { return strings.ToUpper(strings.NewReplacer( " ", "_", "-", "_", "/", "_", ).Replace(s)) } -// toInputVarName converts the given string to an input variable name. -func toInputVarName(input string) string { - return fmt.Sprintf("ATLAS_INPUT_%s", toEnvName(input)) +// ToInputVarName converts the given string to an input variable name. +func ToInputVarName(input string) string { + return "ATLAS_INPUT_" + ToEnvName(input) +} + +// ToInputVarName converts the given string to an input variable name. +func ToOutputVarName(action, output string) string { + return "ATLAS_OUTPUT_" + ToEnvName(action+"_"+output) } // toOutputVar converts the given values to an output variable. // The action and output are used to create the output variable name with the format: // ATLAS_OUTPUT__="" func toOutputVar(action, output, value string) string { - return fmt.Sprintf("ATLAS_OUTPUT_%s=%q", toEnvName(action+"_"+output), value) + return fmt.Sprintf("%s=%q", ToOutputVarName(action, output), value) } // fprintln writes the given values to the file using fmt.Fprintln. diff --git a/atlasaction/bitbucket.go b/atlasaction/bitbucket.go index d2add49..3fe7618 100644 --- a/atlasaction/bitbucket.go +++ b/atlasaction/bitbucket.go @@ -80,7 +80,7 @@ func (a *bbPipe) GetTriggerContext(context.Context) (*TriggerContext, error) { // GetInput implements the Action interface. func (a *bbPipe) GetInput(name string) string { - return strings.TrimSpace(a.getenv(toInputVarName(name))) + return strings.TrimSpace(a.getenv(ToInputVarName(name))) } // SetOutput implements Action. diff --git a/atlasaction/circleci_action.go b/atlasaction/circleci_action.go index 5184474..f18afe6 100644 --- a/atlasaction/circleci_action.go +++ b/atlasaction/circleci_action.go @@ -38,10 +38,10 @@ func (a *circleCIOrb) Getenv(key string) string { // GetInput implements the Action interface. func (a *circleCIOrb) GetInput(name string) string { - v := a.getenv(toInputVarName(name)) + v := a.getenv(ToInputVarName(name)) if v == "" { // TODO: Remove this fallback once all the actions are updated. - v = a.getenv(toEnvName("INPUT_" + name)) + v = a.getenv(ToEnvName("INPUT_" + name)) } return strings.TrimSpace(v) } diff --git a/atlasaction/gen/templates.go b/atlasaction/gen/templates.go new file mode 100644 index 0000000..3851508 --- /dev/null +++ b/atlasaction/gen/templates.go @@ -0,0 +1,125 @@ +package gen + +import ( + "bytes" + "io/fs" + "strings" + "text/template" + + "ariga.io/atlas-action/atlasaction" +) + +var ( + // Templates holds the Go Templates for the code generation. + Templates *Template + // Funcs are the predefined template + // functions used by the codegen. + Funcs = template.FuncMap{ + "xtemplate": xtemplate, + "hasTemplate": hasTemplate, + "trimnl": func(s string) string { + return strings.Trim(s, "\n") + }, + "nl2sp": func(s string) string { + return strings.ReplaceAll(s, "\n", " ") + }, + "env": atlasaction.ToEnvName, + "inputvar": atlasaction.ToInputVarName, + "outputvar": atlasaction.ToOutputVarName, + "dockers": func() []DockerURL { + return []DockerURL{ + {Label: "MySQL", Driver: "mysql"}, + {Label: "Postgres", Driver: "postgres"}, + {Label: "MariaDB", Driver: "mariadb"}, + {Label: "SQL Server", Driver: "sqlserver"}, + {Label: "ClickHouse", Driver: "clickhouse"}, + {Label: "SQLite", Driver: "sqlite"}, + } + }, + } +) + +type DockerURL struct { + Label string + Driver string +} + +func (d DockerURL) DevURL() string { + switch d.Driver { + case "mysql": + return "docker://mysql/8/dev" + case "postgres": + return "docker://postgres/15/dev?search_path=public" + case "mariadb": + return "docker://maria/latest/schema" + case "sqlserver": + return "docker://sqlserver/2022-latest?mode=schema" + case "clickhouse": + return "docker://clickhouse/23.11/dev" + case "sqlite": + return "sqlite://db?mode-memory" + } + return "" +} + +type Template struct { + *template.Template + FuncMap template.FuncMap +} + +// MustParse is a helper that wraps a call to a function returning (*Template, error) +// and panics if the error is non-nil. +func MustParse(t *Template, err error) *Template { + if err != nil { + panic(err) + } + return t +} + +// NewTemplate creates an empty template with the standard codegen functions. +func NewTemplate(name string) *Template { + t := &Template{Template: template.New(name)} + return t.Funcs(Funcs) +} + +// ParseFS is like ParseFiles or ParseGlob but reads from the file system fsys +// instead of the host operating system's file system. +func (t *Template) ParseFS(fsys fs.FS, patterns ...string) (*Template, error) { + if _, err := t.Template.ParseFS(fsys, patterns...); err != nil { + return nil, err + } + return t, nil +} + +// Funcs merges the given funcMap with the template functions. +func (t *Template) Funcs(funcMap template.FuncMap) *Template { + t.Template.Funcs(funcMap) + if t.FuncMap == nil { + t.FuncMap = template.FuncMap{} + } + for name, f := range funcMap { + if _, ok := t.FuncMap[name]; !ok { + t.FuncMap[name] = f + } + } + return t +} + +// xtemplate dynamically executes templates by their names. +func xtemplate(name string, v any) (string, error) { + buf := bytes.NewBuffer(nil) + if err := Templates.ExecuteTemplate(buf, name, v); err != nil { + return "", err + } + return buf.String(), nil +} + +// hasTemplate checks whether a template exists in the loaded templates. +func hasTemplate(name string) bool { + for _, t := range Templates.Templates() { + if t.Name() == name { + return true + } + } + return false +} diff --git a/atlasaction/gitlab_ci.go b/atlasaction/gitlab_ci.go index d490cd9..62d22c5 100644 --- a/atlasaction/gitlab_ci.go +++ b/atlasaction/gitlab_ci.go @@ -39,7 +39,7 @@ func (a *gitlabCI) Getenv(key string) string { // GetInput implements the Action interface. func (a *gitlabCI) GetInput(name string) string { - return strings.TrimSpace(a.getenv(toInputVarName(name))) + return strings.TrimSpace(a.getenv(ToInputVarName(name))) } // SetOutput implements the Action interface. diff --git a/tools/gen/go.mod b/tools/gen/go.mod new file mode 100644 index 0000000..b4bc788 --- /dev/null +++ b/tools/gen/go.mod @@ -0,0 +1,33 @@ +module atlas-action-gen + +go 1.24rc1 + +replace ariga.io/atlas-action => ../../ + +require ( + ariga.io/atlas-action v0.0.0-00010101000000-000000000000 + gopkg.in/yaml.v3 v3.0.1 +) + +require ( + ariga.io/atlas v0.21.2-0.20240418081819-02b3f6239b04 // indirect + ariga.io/atlas-go-sdk v0.6.5 // indirect + github.com/agext/levenshtein v1.2.3 // indirect + github.com/apparentlymart/go-textseg/v15 v15.0.0 // indirect + github.com/fatih/color v1.17.0 // indirect + github.com/go-openapi/inflect v0.19.0 // indirect + github.com/google/go-cmp v0.6.0 // indirect + github.com/hashicorp/go-cleanhttp v0.5.2 // indirect + github.com/hashicorp/go-retryablehttp v0.7.7 // indirect + github.com/hashicorp/hcl/v2 v2.18.1 // indirect + github.com/mattn/go-colorable v0.1.13 // indirect + github.com/mattn/go-isatty v0.0.20 // indirect + github.com/mitchellh/go-wordwrap v1.0.1 // indirect + github.com/mitchellh/mapstructure v1.1.2 // indirect + github.com/sethvargo/go-githubactions v1.3.0 // indirect + github.com/vektah/gqlparser v1.3.1 // indirect + github.com/zclconf/go-cty v1.14.1 // indirect + golang.org/x/oauth2 v0.22.0 // indirect + golang.org/x/sys v0.21.0 // indirect + golang.org/x/text v0.16.0 // indirect +) diff --git a/tools/gen/go.sum b/tools/gen/go.sum new file mode 100644 index 0000000..d72110e --- /dev/null +++ b/tools/gen/go.sum @@ -0,0 +1,89 @@ +ariga.io/atlas v0.21.2-0.20240418081819-02b3f6239b04 h1:YF3qiqtnhn+y4tfhZKTfZKfizpjqHYt7rWPUb+eA4ZA= +ariga.io/atlas v0.21.2-0.20240418081819-02b3f6239b04/go.mod h1:VPlcXdd4w2KqKnH54yEZcry79UAhpaWaxEsmn5JRNoE= +ariga.io/atlas-go-sdk v0.6.5 h1:tl0L3ObGtHjitP9N/56njjDHUrj5jJTQBjftMNwJBcM= +ariga.io/atlas-go-sdk v0.6.5/go.mod h1:9Q+/04PVyJHUse1lEE9Kp6E18xj/6mIzaUTcWYSjSnQ= +github.com/DATA-DOG/go-sqlmock v1.5.0 h1:Shsta01QNfFxHCfpW6YH2STWB0MudeXXEWMr20OEh60= +github.com/DATA-DOG/go-sqlmock v1.5.0/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM= +github.com/agext/levenshtein v1.2.3 h1:YB2fHEn0UJagG8T1rrWknE3ZQzWM06O8AMAatNn7lmo= +github.com/agext/levenshtein v1.2.3/go.mod h1:JEDfjyjHDjOF/1e4FlBE/PkbqA9OfWu2ki2W0IB5558= +github.com/agnivade/levenshtein v1.0.1/go.mod h1:CURSv5d9Uaml+FovSIICkLbAUZ9S4RqaHDIsdSBg7lM= +github.com/alecthomas/kong v0.8.0 h1:ryDCzutfIqJPnNn0omnrgHLbAggDQM2VWHikE1xqK7s= +github.com/alecthomas/kong v0.8.0/go.mod h1:n1iCIO2xS46oE8ZfYCNDqdR0b0wZNrXAIAqro/2132U= +github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883 h1:bvNMNQO63//z+xNgfBlViaCIJKLlCJ6/fmUseuG0wVQ= +github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8= +github.com/apparentlymart/go-textseg/v15 v15.0.0 h1:uYvfpb3DyLSCGWnctWKGj857c6ew1u1fNQOlOtuGxQY= +github.com/apparentlymart/go-textseg/v15 v15.0.0/go.mod h1:K8XmNZdhEBkdlyDdvbmmsvpAG721bKi0joRfFdHIWJ4= +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/fatih/color v1.17.0 h1:GlRw1BRJxkpqUCBKzKOw098ed57fEsKeNjpTe3cSjK4= +github.com/fatih/color v1.17.0/go.mod h1:YZ7TlrGPkiz6ku9fK3TLD/pl3CpsiFyu8N92HLgmosI= +github.com/go-openapi/inflect v0.19.0 h1:9jCH9scKIbHeV9m12SmPilScz6krDxKRasNNSNPXu/4= +github.com/go-openapi/inflect v0.19.0/go.mod h1:lHpZVlpIQqLyKwJ4N+YSc9hchQy/i12fJykb83CRBH4= +github.com/go-test/deep v1.0.3 h1:ZrJSEWsXzPOxaZnFteGEfooLba+ju3FYIbOrS+rQd68= +github.com/go-test/deep v1.0.3/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA= +github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY= +github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ= +github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ= +github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48= +github.com/hashicorp/go-hclog v1.6.3 h1:Qr2kF+eVWjTiYmU7Y31tYlP1h0q/X3Nl3tPGdaB11/k= +github.com/hashicorp/go-hclog v1.6.3/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M= +github.com/hashicorp/go-retryablehttp v0.7.7 h1:C8hUCYzor8PIfXHa4UrZkU4VvK8o9ISHxT2Q8+VepXU= +github.com/hashicorp/go-retryablehttp v0.7.7/go.mod h1:pkQpWZeYWskR+D1tR2O5OcBFOxfA7DoAO6xtkuQnHTk= +github.com/hashicorp/hcl/v2 v2.18.1 h1:6nxnOJFku1EuSawSD81fuviYUV8DxFr3fp2dUi3ZYSo= +github.com/hashicorp/hcl/v2 v2.18.1/go.mod h1:ThLC89FV4p9MPW804KVbe/cEXoQ8NZEh+JtMeeGErHE= +github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= +github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= +github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= +github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= +github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= +github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= +github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/mattn/go-sqlite3 v1.14.17 h1:mCRHCLDUBXgpKAqIKsaAaAsrAlbkeomtRFKXh2L6YIM= +github.com/mattn/go-sqlite3 v1.14.17/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg= +github.com/mitchellh/go-wordwrap v1.0.1 h1:TLuKupo69TCn6TQSyGxwI1EblZZEsQ0vMlAFQflz0v0= +github.com/mitchellh/go-wordwrap v1.0.1/go.mod h1:R62XHJLzvMFRBbcrT7m7WgmE1eOyTSsCt+hzestvNj0= +github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE= +github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= +github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= +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/rogpeppe/go-internal v1.12.1-0.20240709150035-ccf4b4329d21 h1:igWZJluD8KtEtAgRyF4x6lqcxDry1ULztksMJh2mnQE= +github.com/rogpeppe/go-internal v1.12.1-0.20240709150035-ccf4b4329d21/go.mod h1:RMRJLmBOqWacUkmJHRMiPKh1S1m3PA7Zh4W80/kWPpg= +github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= +github.com/sergi/go-diff v1.3.1 h1:xkr+Oxo4BOQKmkn/B9eMK0g5Kg/983T9DqqPHwYqD+8= +github.com/sergi/go-diff v1.3.1/go.mod h1:aMJSSKb2lpPvRNec0+w3fl7LP9IOFzdc9Pa4NFbPK1I= +github.com/sethvargo/go-githubactions v1.3.0 h1:Kg633LIUV2IrJsqy2MfveiED/Ouo+H2P0itWS0eLh8A= +github.com/sethvargo/go-githubactions v1.3.0/go.mod h1:7/4WeHgYfSz9U5vwuToCK9KPnELVHAhGtRwLREOQV80= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/vektah/gqlparser v1.3.1 h1:8b0IcD3qZKWJQHSzynbDlrtP3IxVydZ2DZepCGofqfU= +github.com/vektah/gqlparser v1.3.1/go.mod h1:bkVf0FX+Stjg/MHnm8mEyubuaArhNEqfQhF+OTiAL74= +github.com/zclconf/go-cty v1.14.1 h1:t9fyA35fwjjUMcmL5hLER+e/rEPqrbCK1/OSE4SI9KA= +github.com/zclconf/go-cty v1.14.1/go.mod h1:VvMs5i0vgZdhYawQNq5kePSpLAoz8u1xvZgrPIxfnZE= +golang.org/x/oauth2 v0.22.0 h1:BzDx2FehcG7jJwgWLELCdmLuxk2i+x9UDpSiss2u0ZA= +golang.org/x/oauth2 v0.22.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= +golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws= +golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4= +golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI= +golang.org/x/tools v0.0.0-20190125232054-d66bd3c5d5a6/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.22.0 h1:gqSGLZqv+AI9lIQzniJ0nZDRG5GBPsSi+DRNHWNz6yA= +golang.org/x/tools v0.22.0/go.mod h1:aCwcsjqvq7Yqt6TNyX7QMU2enbQ/Gt0bo6krSeEri+c= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU= +gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/tools/gen/main.go b/tools/gen/main.go new file mode 100644 index 0000000..39576af --- /dev/null +++ b/tools/gen/main.go @@ -0,0 +1,128 @@ +package main + +import ( + "cmp" + "embed" + "errors" + "io" + "io/fs" + "iter" + "maps" + "os" + "slices" + "strings" + + "ariga.io/atlas-action/atlasaction/gen" + "gopkg.in/yaml.v3" +) + +//go:embed template/* +var templateDir embed.FS + +func main() { + if len(os.Args) != 2 { + panic("gen: invalid arguments") + } + wd := os.Args[1] + if err := process(wd); err != nil { + panic(err) + } +} + +func process(path string) error { + fsys := os.DirFS(path) + files, err := fs.Glob(fsys, "**/*/action.yml") + if err != nil { + return err + } + actions := make([]actionSpec, len(files)) + for i, f := range files { + if err := actions[i].fromPath(fsys, f); err != nil { + return err + } + } + slices.SortFunc(actions, func(i, j actionSpec) int { + return cmp.Compare(i.ID, j.ID) + }) + return readmeMarkdown(os.Stdout, actions) +} + +func readmeMarkdown(w io.Writer, actions []actionSpec) error { + gen.Templates = gen.MustParse(gen.NewTemplate("templates"). + ParseFS(templateDir, + "template/*.md", + )) + return gen.Templates.ExecuteTemplate(w, "bitbucket.md", actions) +} + +type ( + actionSpec struct { + ID string `yaml:"-"` + Description string `yaml:"description"` + Name string `yaml:"name"` + Inputs map[string]actionInput `yaml:"inputs"` + Outputs map[string]actionOutput `yaml:"outputs"` + } + actionInput struct { + Description string `yaml:"description"` + Default string `yaml:"default"` + Required bool `yaml:"required"` + } + actionOutput struct { + Description string `yaml:"description"` + } +) + +func (a *actionSpec) fromPath(fsys fs.FS, path string) error { + f, err := fsys.Open(path) + if err != nil { + return err + } + defer f.Close() + if err = yaml.NewDecoder(f).Decode(a); err != nil { + return err + } + if id, ok := strings.CutSuffix(path, "/action.yml"); ok { + a.ID = strings.Trim(id, "/") + return nil + } + return errors.New("gen: invalid action.yml path") +} + +func (a *actionSpec) SortedInputs() iter.Seq2[string, actionInput] { + orders := []string{"working-directory", "config", "env", "vars", "dev-url"} + return func(yield func(string, actionInput) bool) { + keys := slices.SortedFunc(maps.Keys(a.Inputs), ComparePriority(orders)) + for _, k := range keys { + if !yield(k, a.Inputs[k]) { + return + } + } + } +} + +func (a *actionSpec) SortedOutputs() iter.Seq2[string, actionOutput] { + return func(yield func(string, actionOutput) bool) { + keys := slices.Sorted(maps.Keys(a.Outputs)) + for _, k := range keys { + if !yield(k, a.Outputs[k]) { + return + } + } + } +} + +func ComparePriority[T cmp.Ordered](ordered []T) func(T, T) int { + return func(x, y T) int { + switch xi, yi := slices.Index(ordered, x), slices.Index(ordered, y); { + case xi != -1 && yi != -1: + return cmp.Compare(xi, yi) + case yi != -1: + return -1 + case xi != -1: + return +1 + } + // fallback to default comparison + return cmp.Compare(x, y) + } +} diff --git a/tools/gen/template/bitbucket.md b/tools/gen/template/bitbucket.md new file mode 100644 index 0000000..96cd982 --- /dev/null +++ b/tools/gen/template/bitbucket.md @@ -0,0 +1,81 @@ +--- +title: Bitbucket Pipes +id: bitbucket-pipes +slug: /integrations/bitbucket-pipes +--- + +import Tabs from '@theme/Tabs' +import TabItem from '@theme/TabItem' + +// TODO: Write introduce + +{{ range . }} + +## `{{ .ID }}` + +{{ .Description }} + +{{ $tmpl := printf "usage/bitbucket/%s" .ID -}} +{{- if hasTemplate $tmpl -}} +### Usage + +{{ xtemplate $tmpl . -}} +{{- end -}} + +{{ if .Inputs }} +### Inputs + +* `ATLAS_ACTION` - (Required) always is `{{ .ID }}`. +* `ATLAS_TOKEN` - (Optional) to authenticate with Atlas Cloud. +* `BITBUCKET_ACTION_TOKEN` - (Optional) Bitbucket access token to post comment on the PR. +{{- range $name, $item := .SortedInputs }} +* `{{ $name | inputvar }}` - {{ if not $item.Required }}(Optional) {{end}}{{ $item.Description | trimnl | nl2sp }} +{{- end }} +{{ end -}} +{{ if .Outputs }} +### Outputs + +The outputs are written into the `.atlas-action/outputs.sh`, we can load it for the next step using the `source` command. +{{ $action := . }} +{{ range $name, $item := .SortedOutputs }} +* `{{ $name | outputvar $action.ID }}` - {{ $item.Description | trimnl | nl2sp }} +{{- end }} +{{- end }} +{{ end }} +{{- define "usage/bitbucket/schema/plan" -}} +Add `bitbucket-pipelines.yml` to your repo with the following contents: + + + +{{- $action := . -}} +{{- range $item := dockers }} + + +```yaml +image: atlassian/default-image:3 +pipelines: + branches: + master: + - step: + name: "{{ $action.Description }}" + script: + - name: "{{ $action.Name }}" + pipe: docker://arigaio/atlas-action:master + variables: + ATLAS_ACTION: "{{ $action.ID }}" # Required + ATLAS_TOKEN: ${ATLAS_TOKEN} + BITBUCKET_ACCESS_TOKEN: ${BITBUCKET_ACCESS_TOKEN} + ATLAS_INPUT_DEV_URL: "{{ $item.DevURL }}" + - source .atlas-action/outputs.sh +``` + + +{{- end }} + +{{- end -}} \ No newline at end of file diff --git a/tools/gen/template/github.md b/tools/gen/template/github.md new file mode 100644 index 0000000..e21b882 --- /dev/null +++ b/tools/gen/template/github.md @@ -0,0 +1,19 @@ +{{ range . }} +### `ariga/atlas-action/{{ .ID }}` + +{{ .Description }} +{{ if .Inputs }} +#### Inputs + +All inputs are optional as they may be specified in the Atlas configuration file. +{{ range $name, $item := .SortedInputs }} +* `{{ $name }}` - {{ if not $item.Required }}(Optional) {{end}}{{ $item.Description | trimnl | nl2sp }} +{{- end }} +{{ end -}} +{{ if .Outputs }} +#### Outputs +{{ range $name, $item := .SortedOutputs }} +* `{{ $name }}` - {{ $item.Description | trimnl | nl2sp }} +{{- end }} +{{ end }} +{{ end }}