Skip to content

Commit

Permalink
feat: generate ftl-project.toml in the Git root if it DNE (#1648)
Browse files Browse the repository at this point in the history
Fixes #1639

The current implementation ignores any `--config` flag values, but let
me know if we should default to creating the last _specified_ config
whenever one's provided. For that, I'd just move this code to the
resolver and add an `r.Config` check. I decided to minimize it purely to
operating on the default path because the specified paths may not
actually be at the Git root, which could make the new behavior a bit
more complex to comprehend. That said, there could be a good reason to
use the flag value that I didn't think of.
  • Loading branch information
deniseli authored Jun 5, 2024
1 parent c24620c commit 71fde02
Show file tree
Hide file tree
Showing 10 changed files with 81 additions and 20 deletions.
11 changes: 9 additions & 2 deletions backend/controller/scaling/localscaling/devel.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,19 @@ import (
var templateDirOnce sync.Once

func templateDir(ctx context.Context) string {
gitRoot, ok := internal.GitRoot("").Get()
if !ok {
// If GitRoot encounters an error, it will fail to find the correct dir.
// This line preserves the original behavior to prevent a regression, but
// it is still not the desired outcome. More thinking needed.
gitRoot = ""
}
templateDirOnce.Do(func() {
// TODO: Figure out how to make maven build offline
err := exec.Command(ctx, log.Debug, internal.GitRoot(""), "just", "build-kt-runtime").RunBuffered(ctx)
err := exec.Command(ctx, log.Debug, gitRoot, "just", "build-kt-runtime").RunBuffered(ctx)
if err != nil {
panic(err)
}
})
return filepath.Join(internal.GitRoot(""), "build/template")
return filepath.Join(gitRoot, "build/template")
}
4 changes: 2 additions & 2 deletions buildengine/walk.go
Original file line number Diff line number Diff line change
Expand Up @@ -102,8 +102,8 @@ func initGitIgnore(dir string) []string {
if err == nil {
ignore = append(ignore, loadGitIgnore(home)...)
}
gitRoot := internal.GitRoot(dir)
if gitRoot != "" {
gitRoot, ok := internal.GitRoot(dir).Get()
if ok {
for current := dir; strings.HasPrefix(current, gitRoot); current = path.Dir(current) {
ignore = append(ignore, loadGitIgnore(current)...)
}
Expand Down
10 changes: 8 additions & 2 deletions cmd/ftl/cmd_init.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import (
"github.com/TBD54566975/ftl/backend/schema"
"github.com/TBD54566975/ftl/backend/schema/strcase"
"github.com/TBD54566975/ftl/buildengine"
"github.com/TBD54566975/ftl/common/projectconfig"
goruntime "github.com/TBD54566975/ftl/go-runtime"
"github.com/TBD54566975/ftl/internal"
"github.com/TBD54566975/ftl/internal/exec"
Expand Down Expand Up @@ -50,8 +51,10 @@ func (i initGoCmd) Run(ctx context.Context, parent *initCmd) error {
if err := updateGitIgnore(i.Dir); err != nil {
return err
}
if err := projectconfig.CreateDefaultFileIfNonexistent(ctx); err != nil {
return err
}
logger.Debugf("Running go mod tidy")

return exec.Command(ctx, log.Debug, filepath.Join(i.Dir, i.Name), "go", "mod", "tidy").RunBuffered(ctx)
}

Expand Down Expand Up @@ -121,7 +124,10 @@ var scaffoldFuncs = template.FuncMap{
}

func updateGitIgnore(dir string) error {
gitRoot := internal.GitRoot(dir)
gitRoot, ok := internal.GitRoot(dir).Get()
if !ok {
return nil
}
f, err := os.OpenFile(path.Join(gitRoot, ".gitignore"), os.O_RDWR|os.O_CREATE, 0644) //nolint:gosec
if err != nil {
return err
Expand Down
3 changes: 2 additions & 1 deletion common/configuration/projectconfig_resolver_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@ import (
)

func TestSet(t *testing.T) {
defaultPath := projectconfig.GetDefaultConfigPath()
defaultPath, ok := projectconfig.DefaultConfigPath().Get()
assert.True(t, ok)
origConfigBytes, err := os.ReadFile(defaultPath)
assert.NoError(t, err)

Expand Down
40 changes: 34 additions & 6 deletions common/projectconfig/projectconfig.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"strings"

"github.com/BurntSushi/toml"
"github.com/alecthomas/types/optional"

"github.com/TBD54566975/ftl"
"github.com/TBD54566975/ftl/internal"
Expand Down Expand Up @@ -43,16 +44,43 @@ func ConfigPaths(input []string) []string {
if len(input) > 0 {
return input
}
path := GetDefaultConfigPath()
path, ok := DefaultConfigPath().Get()
if !ok {
return []string{}
}
_, err := os.Stat(path)
if err == nil {
return []string{path}
if err != nil {
return []string{}
}
return []string{}
return []string{path}
}

func GetDefaultConfigPath() string {
return filepath.Join(internal.GitRoot(""), "ftl-project.toml")
func DefaultConfigPath() optional.Option[string] {
gitRoot, ok := internal.GitRoot("").Get()
if !ok {
return optional.None[string]()
}
return optional.Some(filepath.Join(gitRoot, "ftl-project.toml"))
}

// CreateDefaultFileIfNonexistent creates the ftl-project.toml file in the Git root if it
// does not already exist.
func CreateDefaultFileIfNonexistent(ctx context.Context) error {
logger := log.FromContext(ctx)
path, ok := DefaultConfigPath().Get()
if !ok {
logger.Warnf("Failed to find Git root, so cannot verify whether an ftl-project.toml file exists there")
return nil
}
_, err := os.Stat(path)
if err == nil {
return nil
}
if !errors.Is(err, os.ErrNotExist) {
return err
}
logger.Warnf("Creating a new project config file at %q because the file does not already exist", path)
return Save(path, Config{})
}

func LoadConfig(ctx context.Context, input []string) (Config, error) {
Expand Down
8 changes: 7 additions & 1 deletion frontend/local.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ package frontend

import (
"context"
"fmt"
"net/http"
"net/http/httputil"
"net/url"
Expand All @@ -22,7 +23,12 @@ func Server(ctx context.Context, timestamp time.Time, publicURL *url.URL, allowO
logger := log.FromContext(ctx)
logger.Debugf("Building console...")

err := exec.Command(ctx, log.Debug, internal.GitRoot(""), "just", "build-frontend").RunBuffered(ctx)
gitRoot, ok := internal.GitRoot("").Get()
if !ok {
return nil, fmt.Errorf("failed to find Git root")
}

err := exec.Command(ctx, log.Debug, gitRoot, "just", "build-frontend").RunBuffered(ctx)
if err != nil {
return nil, err
}
Expand Down
3 changes: 2 additions & 1 deletion go-runtime/ftl/ftltest/ftltest_integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ import (
)

func TestModuleUnitTests(t *testing.T) {
in.Run(t, "",
in.Run(t, "wrapped/ftl-project.toml",
in.GitInit(),
in.CopyModule("time"),
in.CopyModule("wrapped"),
in.CopyModule("verbtypes"),
Expand Down
9 changes: 9 additions & 0 deletions integration/actions.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,15 @@ func Scaffold(src, dest string, tmplCtx any) Action {
}
}

// GitInit calls git init on the working directory.
func GitInit() Action {
return func(t testing.TB, ic TestContext) {
Infof("Running `git init` on the working directory: %s", ic.workDir)
err := ftlexec.Command(ic, log.Debug, ic.workDir, "git", "init", ic.workDir).RunBuffered(ic)
assert.NoError(t, err)
}
}

// Copy a module from the testdata directory to the working directory.
//
// Ensures that replace directives are correctly handled.
Expand Down
3 changes: 2 additions & 1 deletion integration/harness.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,8 @@ func Run(t *testing.T, ftlConfigPath string, actions ...Action) {
cwd, err := os.Getwd()
assert.NoError(t, err)

rootDir := internal.GitRoot("")
rootDir, ok := internal.GitRoot("").Get()
assert.True(t, ok)

if ftlConfigPath != "" {
// Use a path into the testdata directory instead of one relative to
Expand Down
10 changes: 6 additions & 4 deletions internal/source_root.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,24 +4,26 @@ import (
"os"
"os/exec" //nolint:depguard
"strings"

"github.com/alecthomas/types/optional"
)

// GitRoot returns the root of the git repository containing dir, or empty string if dir is not in a git repository.
//
// If dir is empty, the current working directory is used.
func GitRoot(dir string) string {
func GitRoot(dir string) optional.Option[string] {
if dir == "" {
var err error
dir, err = os.Getwd()
if err != nil {
return ""
return optional.None[string]()
}
}
cmd := exec.Command("git", "rev-parse", "--show-toplevel")
cmd.Dir = dir
output, err := cmd.CombinedOutput()
if err != nil {
return ""
return optional.None[string]()
}
return strings.TrimSpace(string(output))
return optional.Some(strings.TrimSpace(string(output)))
}

0 comments on commit 71fde02

Please sign in to comment.