Skip to content

Commit

Permalink
language isolation
Browse files Browse the repository at this point in the history
# Conflicts:
#	internal/buildengine/build.go
#	internal/buildengine/engine.go
  • Loading branch information
matt2e committed Sep 25, 2024
1 parent 61ccd16 commit 97737ab
Show file tree
Hide file tree
Showing 25 changed files with 1,292 additions and 736 deletions.
5 changes: 3 additions & 2 deletions backend/controller/admin/local_client.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,9 +47,10 @@ func (s *diskSchemaRetriever) GetActiveSchema(ctx context.Context) (*schema.Sche

sch := &schema.Schema{}
for _, m := range modules {
schemaPath := m.Config.Abs().Schema
config := m.Abs()
schemaPath := config.Schema()
if r, ok := s.deployRoot.Get(); ok {
schemaPath = filepath.Join(r, m.Config.Module, m.Config.DeployDir, m.Config.Schema)
schemaPath = filepath.Join(r, config.Module, config.DeployDir, config.Schema())
}

module, err := schema.ModuleFromProtoFile(schemaPath)
Expand Down
2 changes: 1 addition & 1 deletion frontend/cli/cmd_box.go
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,7 @@ func (b *boxCmd) Run(ctx context.Context, client ftlv1connect.ControllerServiceC
return err
}
files = append(files, filepath.Join(config.Dir, "ftl.toml"))
files = append(files, config.Schema)
files = append(files, config.Schema())
for _, file := range files {
relFile, err := filepath.Rel(config.Dir, file)
if err != nil {
Expand Down
2 changes: 1 addition & 1 deletion frontend/cli/cmd_box_run.go
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ func (b *boxRunCmd) Run(ctx context.Context, projConfig projectconfig.Config) er
// Manually import the schema for each module to get the dependency graph.
err = engine.Each(func(m buildengine.Module) error {
logger.Debugf("Loading schema for module %q", m.Config.Module)
mod, err := schema.ModuleFromProtoFile(m.Config.Abs().Schema)
mod, err := schema.ModuleFromProtoFile(m.Config.Abs().Schema())
if err != nil {
return fmt.Errorf("failed to read schema for module %q: %w", m.Config.Module, err)
}
Expand Down
34 changes: 34 additions & 0 deletions frontend/cli/cmd_init.go
Original file line number Diff line number Diff line change
@@ -1,14 +1,20 @@
package main

import (
"archive/zip"
"bufio"
"context"
"fmt"
"os"
"path"
"strings"
"text/template"

"github.com/TBD54566975/scaffolder"

"github.com/TBD54566975/ftl"
"github.com/TBD54566975/ftl/backend/schema"
"github.com/TBD54566975/ftl/backend/schema/strcase"
"github.com/TBD54566975/ftl/internal"
"github.com/TBD54566975/ftl/internal/exec"
"github.com/TBD54566975/ftl/internal/log"
Expand Down Expand Up @@ -103,3 +109,31 @@ func updateGitIgnore(ctx context.Context, gitRoot string) error {
// Add .gitignore to git
return maybeGitAdd(ctx, gitRoot, ".gitignore")
}

func scaffold(ctx context.Context, includeBinDir bool, source *zip.Reader, destination string, sctx any, options ...scaffolder.Option) error {
logger := log.FromContext(ctx)
opts := []scaffolder.Option{scaffolder.Functions(scaffoldFuncs), scaffolder.Exclude("^go.mod$")}
if !includeBinDir {
logger.Debugf("Excluding bin directory")
opts = append(opts, scaffolder.Exclude("^bin"))
}
opts = append(opts, options...)
if err := internal.ScaffoldZip(source, destination, sctx, opts...); err != nil {
return fmt.Errorf("failed to scaffold: %w", err)
}
return nil
}

var scaffoldFuncs = template.FuncMap{
"snake": strcase.ToLowerSnake,
"screamingSnake": strcase.ToUpperSnake,
"camel": strcase.ToUpperCamel,
"lowerCamel": strcase.ToLowerCamel,
"strippedCamel": strcase.ToUpperStrippedCamel,
"kebab": strcase.ToLowerKebab,
"screamingKebab": strcase.ToUpperKebab,
"upper": strings.ToUpper,
"lower": strings.ToLower,
"title": strings.Title,
"typename": schema.TypeName,
}
243 changes: 109 additions & 134 deletions frontend/cli/cmd_new.go
Original file line number Diff line number Diff line change
@@ -1,143 +1,142 @@
package main

import (
"archive/zip"
"context"
"fmt"
"go/token"
"html/template"
"os"
"path/filepath"
"regexp"
"runtime"
"strings"

"github.com/TBD54566975/scaffolder"

"github.com/TBD54566975/ftl/backend/schema"
"github.com/TBD54566975/ftl/backend/schema/strcase"
goruntime "github.com/TBD54566975/ftl/go-runtime"
"github.com/TBD54566975/ftl/internal"
"github.com/TBD54566975/ftl/internal/exec"
"github.com/TBD54566975/ftl/internal/buildengine"
"github.com/TBD54566975/ftl/internal/log"
"github.com/TBD54566975/ftl/internal/moduleconfig"
"github.com/TBD54566975/ftl/internal/projectconfig"
"github.com/TBD54566975/ftl/jvm-runtime/java"
"github.com/TBD54566975/ftl/jvm-runtime/kotlin"
)

type newCmd struct {
Go newGoCmd `cmd:"" help:"Initialize a new FTL Go module."`
Java newJavaCmd `cmd:"" help:"Initialize a new FTL Java module."`
Kotlin newKotlinCmd `cmd:"" help:"Initialize a new FTL Kotlin module."`
}

type newGoCmd struct {
Replace map[string]string `short:"r" help:"Replace a module import path with a local path in the initialised FTL module." placeholder:"OLD=NEW,..." env:"FTL_INIT_GO_REPLACE"`
Dir string `arg:"" help:"Directory to initialize the module in."`
Name string `arg:"" help:"Name of the FTL module to create underneath the base directory."`
GoVersion string
}

type newJavaCmd struct {
Dir string `arg:"" help:"Directory to initialize the module in."`
Name string `arg:"" help:"Name of the FTL module to create underneath the base directory."`
Group string `help:"The Maven groupId of the project." default:"com.example"`
}
type newKotlinCmd struct {
Dir string `arg:"" help:"Directory to initialize the module in."`
Name string `arg:"" help:"Name of the FTL module to create underneath the base directory."`
Group string `help:"The Maven groupId of the project." default:"com.example"`
// Go newGoCmd `cmd:"" help:"Initialize a new FTL Go module."`
// Java newJavaCmd `cmd:"" help:"Initialize a new FTL Java module."`
// Kotlin newKotlinCmd `cmd:"" help:"Initialize a new FTL Kotlin module."`

// TODO: add a way for language plugins to get extra configuration
// TODO: a way to get a list of languages and/or extra configuration?
Language string `arg:"" help:"Language of the module to create."`
Dir string `arg:"" help:"Directory to initialize the module in."`
Name string `arg:"" help:"Name of the FTL module to create underneath the base directory."`
}

func (i newGoCmd) Run(ctx context.Context, config projectconfig.Config) error {
// type newGoCmd struct {
// Replace map[string]string `short:"r" help:"Replace a module import path with a local path in the initialised FTL module." placeholder:"OLD=NEW,..." env:"FTL_INIT_GO_REPLACE"`
// Dir string `arg:"" help:"Directory to initialize the module in."`
// Name string `arg:"" help:"Name of the FTL module to create underneath the base directory."`
// GoVersion string
// }

// type newJavaCmd struct {
// Dir string `arg:"" help:"Directory to initialize the module in."`
// Name string `arg:"" help:"Name of the FTL module to create underneath the base directory."`
// Group string `help:"The Maven groupId of the project." default:"com.example"`
// }
// type newKotlinCmd struct {
// Dir string `arg:"" help:"Directory to initialize the module in."`
// Name string `arg:"" help:"Name of the FTL module to create underneath the base directory."`
// Group string `help:"The Maven groupId of the project." default:"com.example"`
// }

func (i newCmd) Run(ctx context.Context, config projectconfig.Config) error {
name, path, err := validateModule(i.Dir, i.Name)
if err != nil {
return err
}

// Validate the module name with custom validation
// TODO: rewrite this to be less go specific
if !isValidGoModuleName(name) {
return fmt.Errorf("module name %q must be a valid Go module name and not a reserved keyword", name)
}

logger := log.FromContext(ctx)
logger.Debugf("Creating FTL Go module %q in %s", name, path)

i.GoVersion = runtime.Version()[2:]
if err := scaffold(ctx, config.Hermit, goruntime.Files(), i.Dir, i, scaffolder.Exclude("^go.mod$")); err != nil {
return err
}

logger.Debugf("Running go mod tidy")
if err := exec.Command(ctx, log.Debug, path, "go", "mod", "tidy").RunBuffered(ctx); err != nil {
return err
}

_, ok := internal.GitRoot(i.Dir).Get()
if !config.NoGit && ok {
logger.Debugf("Adding files to git")
if config.Hermit {
if err := maybeGitAdd(ctx, i.Dir, "bin/*"); err != nil {
return err
}
}
if err := maybeGitAdd(ctx, i.Dir, filepath.Join(i.Name, "*")); err != nil {
return err
}
}
return nil
}

func (i newJavaCmd) Run(ctx context.Context, config projectconfig.Config) error {
return RunJvmScaffolding(ctx, config, i.Dir, i.Name, i.Group, java.Files())
}

func (i newKotlinCmd) Run(ctx context.Context, config projectconfig.Config) error {
return RunJvmScaffolding(ctx, config, i.Dir, i.Name, i.Group, kotlin.Files())
}

func RunJvmScaffolding(ctx context.Context, config projectconfig.Config, dir string, name string, group string, source *zip.Reader) error {
name, path, err := validateModule(dir, name)
logger.Debugf("Creating FTL %s module %q in %s", i.Language, name, path)

moduleConfig := moduleconfig.ModuleConfig{
Module: name,
Language: i.Language,
}.Abs()
plugin, err := buildengine.PluginFromConfig(ctx, moduleConfig, config.Root())
plugin.CreateModule(ctx, moduleConfig)
if err != nil {
return err
}

logger := log.FromContext(ctx)
logger.Debugf("Creating FTL module %q in %s", name, path)

packageDir := strings.ReplaceAll(group, ".", "/")

javaContext := struct {
Dir string
Name string
Group string
PackageDir string
}{
Dir: dir,
Name: name,
Group: group,
PackageDir: packageDir,
}

if err := scaffold(ctx, config.Hermit, source, dir, javaContext); err != nil {
return err
}

_, ok := internal.GitRoot(dir).Get()
if !config.NoGit && ok {
logger.Debugf("Adding files to git")
if config.Hermit {
if err := maybeGitAdd(ctx, dir, "bin/*"); err != nil {
return err
}
}
if err := maybeGitAdd(ctx, dir, filepath.Join(name, "*")); err != nil {
return err
}
}
// TODO: re-add git logic

// _, ok := internal.GitRoot(i.Dir).Get()
// if !config.NoGit && ok {
// logger.Debugf("Adding files to git")
// if config.Hermit {
// if err := maybeGitAdd(ctx, i.Dir, "bin/*"); err != nil {
// return err
// }
// }
// if err := maybeGitAdd(ctx, i.Dir, filepath.Join(i.Name, "*")); err != nil {
// return err
// }
// }
return nil
}

// func (i newJavaCmd) Run(ctx context.Context, config projectconfig.Config) error {
// return RunJvmScaffolding(ctx, config, i.Dir, i.Name, i.Group, java.Files())
// }

// func (i newKotlinCmd) Run(ctx context.Context, config projectconfig.Config) error {
// return RunJvmScaffolding(ctx, config, i.Dir, i.Name, i.Group, kotlin.Files())
// }

// func RunJvmScaffolding(ctx context.Context, config projectconfig.Config, dir string, name string, group string, source *zip.Reader) error {
// name, path, err := validateModule(dir, name)
// if err != nil {
// return err
// }

// logger := log.FromContext(ctx)
// logger.Debugf("Creating FTL module %q in %s", name, path)

// packageDir := strings.ReplaceAll(group, ".", "/")

// javaContext := struct {
// Dir string
// Name string
// Group string
// PackageDir string
// }{
// Dir: dir,
// Name: name,
// Group: group,
// PackageDir: packageDir,
// }

// if err := scaffold(ctx, config.Hermit, source, dir, javaContext); err != nil {
// return err
// }

// _, ok := internal.GitRoot(dir).Get()
// if !config.NoGit && ok {
// logger.Debugf("Adding files to git")
// if config.Hermit {
// if err := maybeGitAdd(ctx, dir, "bin/*"); err != nil {
// return err
// }
// }
// if err := maybeGitAdd(ctx, dir, filepath.Join(name, "*")); err != nil {
// return err
// }
// }
// return nil
// }

func validateModule(dir string, name string) (string, string, error) {
if dir == "" {
return "", "", fmt.Errorf("directory is required")
Expand All @@ -149,10 +148,14 @@ func validateModule(dir string, name string) (string, string, error) {
return "", "", fmt.Errorf("module name %q is invalid", name)
}
path := filepath.Join(dir, name)
if _, err := os.Stat(path); err == nil {
absPath, err := filepath.Abs(path)
if err != nil {
return "", "", fmt.Errorf("could not make %q an absolute path: %w", path, err)
}
if _, err := os.Stat(absPath); err == nil {
return "", "", fmt.Errorf("module directory %s already exists", path)
}
return name, path, nil
return name, absPath, nil
}

func isValidGoModuleName(name string) bool {
Expand All @@ -165,31 +168,3 @@ func isValidGoModuleName(name string) bool {
}
return true
}

func scaffold(ctx context.Context, includeBinDir bool, source *zip.Reader, destination string, sctx any, options ...scaffolder.Option) error {
logger := log.FromContext(ctx)
opts := []scaffolder.Option{scaffolder.Functions(scaffoldFuncs), scaffolder.Exclude("^go.mod$")}
if !includeBinDir {
logger.Debugf("Excluding bin directory")
opts = append(opts, scaffolder.Exclude("^bin"))
}
opts = append(opts, options...)
if err := internal.ScaffoldZip(source, destination, sctx, opts...); err != nil {
return fmt.Errorf("failed to scaffold: %w", err)
}
return nil
}

var scaffoldFuncs = template.FuncMap{
"snake": strcase.ToLowerSnake,
"screamingSnake": strcase.ToUpperSnake,
"camel": strcase.ToUpperCamel,
"lowerCamel": strcase.ToLowerCamel,
"strippedCamel": strcase.ToUpperStrippedCamel,
"kebab": strcase.ToLowerKebab,
"screamingKebab": strcase.ToUpperKebab,
"upper": strings.ToUpper,
"lower": strings.ToLower,
"title": strings.Title,
"typename": schema.TypeName,
}
4 changes: 2 additions & 2 deletions frontend/cli/cmd_schema_diff.go
Original file line number Diff line number Diff line change
Expand Up @@ -93,9 +93,9 @@ func localSchema(ctx context.Context, projectConfig projectconfig.Config) (*sche
return nil, fmt.Errorf("failed to discover modules %w", err)
}
for _, moduleSettings := range modules {
mod, err := schema.ModuleFromProtoFile(moduleSettings.Config.Abs().Schema)
mod, err := schema.ModuleFromProtoFile(moduleSettings.Abs().Schema())
if err != nil {
tried += fmt.Sprintf(" failed to read schema file %s; did you run ftl build?", moduleSettings.Config.Abs().Schema)
tried += fmt.Sprintf(" failed to read schema file %s; did you run ftl build?", moduleSettings.Abs().Schema())
} else {
found = true
pb.Modules = append(pb.Modules, mod)
Expand Down
Loading

0 comments on commit 97737ab

Please sign in to comment.