Skip to content

Commit

Permalink
chore: codegen for doc
Browse files Browse the repository at this point in the history
  • Loading branch information
giautm committed Jan 2, 2025
1 parent 58a63b6 commit 841f915
Show file tree
Hide file tree
Showing 11 changed files with 688 additions and 10 deletions.
17 changes: 11 additions & 6 deletions atlasaction/action.go
Original file line number Diff line number Diff line change
Expand Up @@ -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_<ACTION>_<OUTPUT>="<value>"
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.
Expand Down
2 changes: 1 addition & 1 deletion atlasaction/bitbucket.go
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
4 changes: 2 additions & 2 deletions atlasaction/circleci_action.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
Expand Down
112 changes: 112 additions & 0 deletions atlasaction/gen/actions.go
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)
}
}
132 changes: 132 additions & 0 deletions atlasaction/gen/templates.go
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
}
2 changes: 1 addition & 1 deletion atlasaction/gitlab_ci.go
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
33 changes: 33 additions & 0 deletions tools/gen/go.mod
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
)
Loading

0 comments on commit 841f915

Please sign in to comment.