Skip to content

Commit

Permalink
chore: define interfaces for file and project generation (#1568)
Browse files Browse the repository at this point in the history
* chore: rename Example to TestcontainersModule

* chore: convert dependabot into a FileGenerator

* chore: convert makefile into a FileGenerator

* chore: convert go.mod into a FileGenerator

* chore: convert workflow into a ProjectGenerator

* chore: convert mkdocs into a FileGenerator

* chore: convert vscode into a ProjectGenerator

* chore: do not use pointer semantics for the context

It won't be modified

* chore: remove pointer semantics for command variable

* chore: make variable name more explicit
  • Loading branch information
mdelapenya authored Sep 1, 2023
1 parent f874453 commit 69a5d38
Show file tree
Hide file tree
Showing 17 changed files with 321 additions and 284 deletions.
8 changes: 4 additions & 4 deletions modulegen/cmd/modules/example.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,14 @@ var newExampleCmd = &cobra.Command{
Short: "Create a new Example",
Long: "Create a new Example",
RunE: func(cmd *cobra.Command, args []string) error {
return internal.Generate(*exampleVar, false)
return internal.Generate(tcModuleVar, false)
},
}

func init() {
newExampleCmd.Flags().StringVarP(&exampleVar.Name, nameFlag, "n", "", "Name of the example. Only alphabetical characters are allowed.")
newExampleCmd.Flags().StringVarP(&exampleVar.NameTitle, titleFlag, "t", "", "(Optional) Title of the example name, used to override the name in the case of mixed casing (Mongodb -> MongoDB). Use camel-case when needed. Only alphabetical characters are allowed.")
newExampleCmd.Flags().StringVarP(&exampleVar.Image, imageFlag, "i", "", "Fully-qualified name of the Docker image to be used by the example")
newExampleCmd.Flags().StringVarP(&tcModuleVar.Name, nameFlag, "n", "", "Name of the example. Only alphabetical characters are allowed.")
newExampleCmd.Flags().StringVarP(&tcModuleVar.NameTitle, titleFlag, "t", "", "(Optional) Title of the example name, used to override the name in the case of mixed casing (Mongodb -> MongoDB). Use camel-case when needed. Only alphabetical characters are allowed.")
newExampleCmd.Flags().StringVarP(&tcModuleVar.Image, imageFlag, "i", "", "Fully-qualified name of the Docker image to be used by the example")

_ = newExampleCmd.MarkFlagRequired(imageFlag)
_ = newExampleCmd.MarkFlagRequired(nameFlag)
Expand Down
2 changes: 1 addition & 1 deletion modulegen/cmd/modules/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import (
"github.com/testcontainers/testcontainers-go/modulegen/internal/context"
)

var exampleVar = &context.ExampleVar{}
var tcModuleVar = context.TestcontainersModuleVar{}

var NewCmd = &cobra.Command{
Use: "new",
Expand Down
8 changes: 4 additions & 4 deletions modulegen/cmd/modules/module.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,14 @@ var newModuleCmd = &cobra.Command{
Short: "Create a new Module",
Long: "Create a new Module",
RunE: func(cmd *cobra.Command, args []string) error {
return internal.Generate(*exampleVar, true)
return internal.Generate(tcModuleVar, true)
},
}

func init() {
newModuleCmd.Flags().StringVarP(&exampleVar.Name, nameFlag, "n", "", "Name of the module. Only alphabetical characters are allowed.")
newModuleCmd.Flags().StringVarP(&exampleVar.NameTitle, titleFlag, "t", "", "(Optional) Title of the module name, used to override the name in the case of mixed casing (Mongodb -> MongoDB). Use camel-case when needed. Only alphabetical characters are allowed.")
newModuleCmd.Flags().StringVarP(&exampleVar.Image, imageFlag, "i", "", "Fully-qualified name of the Docker image to be used by the module")
newModuleCmd.Flags().StringVarP(&tcModuleVar.Name, nameFlag, "n", "", "Name of the module. Only alphabetical characters are allowed.")
newModuleCmd.Flags().StringVarP(&tcModuleVar.NameTitle, titleFlag, "t", "", "(Optional) Title of the module name, used to override the name in the case of mixed casing (Mongodb -> MongoDB). Use camel-case when needed. Only alphabetical characters are allowed.")
newModuleCmd.Flags().StringVarP(&tcModuleVar.Image, imageFlag, "i", "", "Fully-qualified name of the Docker image to be used by the module")

_ = newModuleCmd.MarkFlagRequired(imageFlag)
_ = newModuleCmd.MarkFlagRequired(nameFlag)
Expand Down
2 changes: 1 addition & 1 deletion modulegen/context_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ func TestModulesHasDependabotEntry(t *testing.T) {
}
}

func getTestRootContext(t *testing.T) *context.Context {
func getTestRootContext(t *testing.T) context.Context {
current, err := os.Getwd()
require.NoError(t, err)
return context.New(filepath.Dir(current))
Expand Down
2 changes: 1 addition & 1 deletion modulegen/internal/context/cmd.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package context

type ExampleVar struct {
type TestcontainersModuleVar struct {
Name string
NameTitle string
Image string
Expand Down
34 changes: 17 additions & 17 deletions modulegen/internal/context/context.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,27 +10,27 @@ type Context struct {
RootDir string
}

func (ctx *Context) DependabotConfigFile() string {
func (ctx Context) DependabotConfigFile() string {
return filepath.Join(ctx.GithubDir(), "dependabot.yml")
}

func (ctx *Context) DocsDir() string {
func (ctx Context) DocsDir() string {
return filepath.Join(ctx.RootDir, "docs")
}

func (ctx *Context) GithubDir() string {
func (ctx Context) GithubDir() string {
return filepath.Join(ctx.RootDir, ".github")
}

func (ctx *Context) GithubWorkflowsDir() string {
func (ctx Context) GithubWorkflowsDir() string {
return filepath.Join(ctx.GithubDir(), "workflows")
}

func (ctx *Context) GoModFile() string {
func (ctx Context) GoModFile() string {
return filepath.Join(ctx.RootDir, "go.mod")
}

func (ctx *Context) getModulesByBaseDir(baseDir string) ([]string, error) {
func (ctx Context) getModulesByBaseDir(baseDir string) ([]string, error) {
dir := filepath.Join(ctx.RootDir, baseDir)

allFiles, err := os.ReadDir(dir)
Expand All @@ -49,7 +49,7 @@ func (ctx *Context) getModulesByBaseDir(baseDir string) ([]string, error) {
return dirs, nil
}

func (ctx *Context) getMarkdownsFromDir(baseDir string) ([]string, error) {
func (ctx Context) getMarkdownsFromDir(baseDir string) ([]string, error) {
dir := filepath.Join(ctx.DocsDir(), baseDir)

allFiles, err := os.ReadDir(dir)
Expand All @@ -68,38 +68,38 @@ func (ctx *Context) getMarkdownsFromDir(baseDir string) ([]string, error) {
return dirs, nil
}

func (ctx *Context) GetExamples() ([]string, error) {
func (ctx Context) GetExamples() ([]string, error) {
return ctx.getModulesByBaseDir("examples")
}

func (ctx *Context) GetModules() ([]string, error) {
func (ctx Context) GetModules() ([]string, error) {
return ctx.getModulesByBaseDir("modules")
}

func (ctx *Context) GetExamplesDocs() ([]string, error) {
func (ctx Context) GetExamplesDocs() ([]string, error) {
return ctx.getMarkdownsFromDir("examples")
}

func (ctx *Context) GetModulesDocs() ([]string, error) {
func (ctx Context) GetModulesDocs() ([]string, error) {
return ctx.getMarkdownsFromDir("modules")
}

func (ctx *Context) MkdocsConfigFile() string {
func (ctx Context) MkdocsConfigFile() string {
return filepath.Join(ctx.RootDir, "mkdocs.yml")
}

func (ctx *Context) VSCodeWorkspaceFile() string {
func (ctx Context) VSCodeWorkspaceFile() string {
return filepath.Join(ctx.RootDir, ".vscode", ".testcontainers-go.code-workspace")
}

func New(dir string) *Context {
return &Context{RootDir: dir}
func New(dir string) Context {
return Context{RootDir: dir}
}

func GetRootContext() (*Context, error) {
func GetRootContext() (Context, error) {
current, err := os.Getwd()
if err != nil {
return nil, err
return Context{}, err
}
return New(filepath.Dir(current)), nil
}
54 changes: 27 additions & 27 deletions modulegen/internal/context/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,25 +11,25 @@ import (
"golang.org/x/text/language"
)

type Example struct {
type TestcontainersModule struct {
Image string // fully qualified name of the Docker image
IsModule bool // if true, the example will be generated as a Go module
IsModule bool // if true, the module will be generated as a Go module, otherwise an example
Name string
TitleName string // title of the name: e.g. "mongodb" -> "MongoDB"
TitleName string // title of the name: m.g. "mongodb" -> "MongoDB"
TCVersion string // Testcontainers for Go version
}

// ContainerName returns the name of the container, which is the lower-cased title of the example
// If the title is set, it will be used instead of the name
func (e *Example) ContainerName() string {
name := e.Lower()
func (m *TestcontainersModule) ContainerName() string {
name := m.Lower()

if e.IsModule {
name = e.Title()
if m.IsModule {
name = m.Title()
} else {
if e.TitleName != "" {
r, n := utf8.DecodeRuneInString(e.TitleName)
name = string(unicode.ToLower(r)) + e.TitleName[n:]
if m.TitleName != "" {
r, n := utf8.DecodeRuneInString(m.TitleName)
name = string(unicode.ToLower(r)) + m.TitleName[n:]
}
}

Expand All @@ -38,48 +38,48 @@ func (e *Example) ContainerName() string {

// Entrypoint returns the name of the entrypoint function, which is the lower-cased title of the example
// If the example is a module, the entrypoint will be "RunContainer"
func (e *Example) Entrypoint() string {
if e.IsModule {
func (m *TestcontainersModule) Entrypoint() string {
if m.IsModule {
return "RunContainer"
}

return "runContainer"
}

func (e *Example) Lower() string {
return strings.ToLower(e.Name)
func (m *TestcontainersModule) Lower() string {
return strings.ToLower(m.Name)
}

func (e *Example) ParentDir() string {
if e.IsModule {
func (m *TestcontainersModule) ParentDir() string {
if m.IsModule {
return "modules"
}

return "examples"
}

func (e *Example) Title() string {
if e.TitleName != "" {
return e.TitleName
func (m *TestcontainersModule) Title() string {
if m.TitleName != "" {
return m.TitleName
}

return cases.Title(language.Und, cases.NoLower).String(e.Lower())
return cases.Title(language.Und, cases.NoLower).String(m.Lower())
}

func (e *Example) Type() string {
if e.IsModule {
func (m *TestcontainersModule) Type() string {
if m.IsModule {
return "module"
}
return "example"
}

func (e *Example) Validate() error {
if !regexp.MustCompile(`^[A-Za-z][A-Za-z0-9]*$`).MatchString(e.Name) {
return fmt.Errorf("invalid name: %s. Only alphanumerical characters are allowed (leading character must be a letter)", e.Name)
func (m *TestcontainersModule) Validate() error {
if !regexp.MustCompile(`^[A-Za-z][A-Za-z0-9]*$`).MatchString(m.Name) {
return fmt.Errorf("invalid name: %s. Only alphanumerical characters are allowed (leading character must be a letter)", m.Name)
}

if !regexp.MustCompile(`^[A-Za-z][A-Za-z0-9]*$`).MatchString(e.TitleName) {
return fmt.Errorf("invalid title: %s. Only alphanumerical characters are allowed (leading character must be a letter)", e.TitleName)
if !regexp.MustCompile(`^[A-Za-z][A-Za-z0-9]*$`).MatchString(m.TitleName) {
return fmt.Errorf("invalid title: %s. Only alphanumerical characters are allowed (leading character must be a letter)", m.TitleName)
}

return nil
Expand Down
34 changes: 24 additions & 10 deletions modulegen/internal/dependabot/main.go
Original file line number Diff line number Diff line change
@@ -1,21 +1,35 @@
package dependabot

import (
"github.com/testcontainers/testcontainers-go/modulegen/internal/context"
)

// update examples in dependabot
func GenerateDependabotUpdates(ctx *context.Context, example context.Example) error {
directory := "/" + example.ParentDir() + "/" + example.Lower()
return UpdateConfig(ctx.DependabotConfigFile(), directory, "gomod")
}
import "github.com/testcontainers/testcontainers-go/modulegen/internal/context"

type Generator struct{}

// AddModule update dependabot with the new module
func (g Generator) AddModule(ctx context.Context, tcModule context.TestcontainersModule) error {
configFile := ctx.DependabotConfigFile()

func UpdateConfig(configFile string, directory string, packageEcosystem string) error {
config, err := readConfig(configFile)
if err != nil {
return err
}

packageEcosystem := "gomod"
directory := "/" + tcModule.ParentDir() + "/" + tcModule.Lower()

config.addUpdate(newUpdate(directory, packageEcosystem))

return writeConfig(configFile, config)
}

// Generate generates dependabot config file from source
func (g Generator) Generate(ctx context.Context) error {
configFile := ctx.DependabotConfigFile()

config, err := readConfig(configFile)
if err != nil {
return err
}

return writeConfig(configFile, config)
}

Expand Down
Loading

0 comments on commit 69a5d38

Please sign in to comment.