-
Notifications
You must be signed in to change notification settings - Fork 6
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
11 changed files
with
688 additions
and
10 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,112 @@ | ||
package gen | ||
|
||
import ( | ||
"cmp" | ||
"errors" | ||
"io/fs" | ||
"iter" | ||
"maps" | ||
"slices" | ||
"strings" | ||
|
||
"gopkg.in/yaml.v3" | ||
) | ||
|
||
type ( | ||
// Actions represents a list of actions. | ||
Actions struct { | ||
Actions []ActionSpec | ||
} | ||
ActionSpec struct { | ||
ID string `yaml:"id"` | ||
Description string `yaml:"description"` | ||
Name string `yaml:"name"` | ||
Inputs map[string]ActionInput `yaml:"inputs,omitempty"` | ||
Outputs map[string]ActionOutput `yaml:"outputs,omitempty"` | ||
} | ||
ActionInput struct { | ||
Description string `yaml:"description,omitempty"` | ||
Default string `yaml:"default,omitempty"` | ||
Required bool `yaml:"required"` | ||
} | ||
ActionOutput struct { | ||
Description string `yaml:"description"` | ||
} | ||
) | ||
|
||
// ParseFS parses the actions from the given file system. | ||
func ParseFS(fsys fs.FS) (*Actions, error) { | ||
level1, err := fs.Glob(fsys, "**/*/action.yml") | ||
if err != nil { | ||
return nil, err | ||
} | ||
level2, err := fs.Glob(fsys, "**/*/*/action.yml") | ||
if err != nil { | ||
return nil, err | ||
} | ||
files := append(level1, level2...) | ||
actions := make([]ActionSpec, len(files)) | ||
for i, f := range files { | ||
if err := actions[i].fromPath(fsys, f); err != nil { | ||
return nil, err | ||
} | ||
} | ||
slices.SortFunc(actions, func(i, j ActionSpec) int { | ||
return cmp.Compare(i.ID, j.ID) | ||
}) | ||
return &Actions{Actions: actions}, nil | ||
} | ||
|
||
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) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,132 @@ | ||
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"}, | ||
} | ||
}, | ||
} | ||
) | ||
|
||
// LoadTemplates loads the templates from the given file system. | ||
func LoadTemplates(fsys fs.FS, patterns ...string) *Template { | ||
templates = MustParse(NewTemplate("templates"). | ||
ParseFS(fsys, patterns...)) | ||
return templates | ||
} | ||
|
||
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 | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 | ||
) |
Oops, something went wrong.