From f0179b17571b95b309481514d073d8b72c32b96a Mon Sep 17 00:00:00 2001 From: Maarten <48209028+maartenmasschelein@users.noreply.github.com> Date: Tue, 7 Apr 2026 19:10:45 +0200 Subject: [PATCH] fix: init command now actually creates files and directories The init command previously only printed success messages without performing any file I/O. This adds the actual os.WriteFile and os.MkdirAll calls to create soda.yml, contracts/, and configs/. Also adds a guard against running init in a directory that already has a soda.yml, and integration tests to verify the fix. Fixes #6 Co-Authored-By: Claude Opus 4.6 (1M context) --- go/cmd/init.go | 30 +++++++++++++++-- go/tests/integration/helpers_test.go | 28 ++++++++++++++++ go/tests/integration/init_test.go | 49 ++++++++++++++++++++++++++++ 3 files changed, 105 insertions(+), 2 deletions(-) create mode 100644 go/tests/integration/init_test.go diff --git a/go/cmd/init.go b/go/cmd/init.go index 627c058..2f1341f 100644 --- a/go/cmd/init.go +++ b/go/cmd/init.go @@ -2,12 +2,23 @@ package cmd import ( "fmt" + "os" "github.com/spf13/cobra" "github.com/soda-data-inc/soda-cli/internal/output" ) +var defaultSodaYml = `# soda.yml — project configuration +# Docs: https://github.com/sodadata/soda-cli + +# datasource: my_datasource +# type: postgres +# host: localhost +# port: "5432" +# database: my_db +` + var initCmd = &cobra.Command{ Use: "init", Short: "Scaffold soda.yml and project structure", @@ -17,9 +28,24 @@ var initCmd = &cobra.Command{ fmt.Println("Scaffolding soda project structure...") fmt.Println() } + + // Create soda.yml if it doesn't already exist + if _, err := os.Stat("soda.yml"); err == nil { + return fmt.Errorf("soda.yml already exists in this directory") + } + if err := os.WriteFile("soda.yml", []byte(defaultSodaYml), 0644); err != nil { + return fmt.Errorf("failed to create soda.yml: %w", err) + } fmt.Println(" Created soda.yml") - fmt.Println(" Created contracts/") - fmt.Println(" Created configs/") + + // Create contracts/ and configs/ directories + for _, dir := range []string{"contracts", "configs"} { + if err := os.MkdirAll(dir, 0755); err != nil { + return fmt.Errorf("failed to create %s/: %w", dir, err) + } + fmt.Printf(" Created %s/\n", dir) + } + fmt.Println() output.PrintSuccess("Project initialized. Edit soda.yml to configure your datasources.", GCtx) return nil diff --git a/go/tests/integration/helpers_test.go b/go/tests/integration/helpers_test.go index 0304fe8..f523998 100644 --- a/go/tests/integration/helpers_test.go +++ b/go/tests/integration/helpers_test.go @@ -136,6 +136,34 @@ func run(t *testing.T, args ...string) Result { } } +// runInDir executes sodacli with the given args in the specified directory. +func runInDir(t *testing.T, dir string, args ...string) Result { + t.Helper() + bin := ensureBinary(t) + + cmd := exec.Command(bin, args...) + cmd.Dir = dir + var stdout, stderr bytes.Buffer + cmd.Stdout = &stdout + cmd.Stderr = &stderr + + err := cmd.Run() + exitCode := 0 + if err != nil { + if exitErr, ok := err.(*exec.ExitError); ok { + exitCode = exitErr.ExitCode() + } else { + t.Fatalf("failed to run command: %v", err) + } + } + + return Result{ + Stdout: stdout.String(), + Stderr: stderr.String(), + ExitCode: exitCode, + } +} + // Output returns combined stdout+stderr for simple checks. func (r Result) Output() string { return r.Stdout + r.Stderr diff --git a/go/tests/integration/init_test.go b/go/tests/integration/init_test.go new file mode 100644 index 0000000..4a370d7 --- /dev/null +++ b/go/tests/integration/init_test.go @@ -0,0 +1,49 @@ +//go:build integration + +package integration + +import ( + "os" + "path/filepath" + "testing" +) + +func TestInit(t *testing.T) { + t.Run("creates_files_and_dirs", func(t *testing.T) { + dir := t.TempDir() + r := runInDir(t, dir, "init", "--no-interactive") + assertExitCode(t, r, 0) + assertOutputContains(t, r, "Created soda.yml") + assertOutputContains(t, r, "Created contracts/") + assertOutputContains(t, r, "Created configs/") + + // Verify soda.yml was actually created + info, err := os.Stat(filepath.Join(dir, "soda.yml")) + if err != nil { + t.Fatalf("soda.yml not created: %v", err) + } + if info.Size() == 0 { + t.Error("soda.yml is empty") + } + + // Verify directories were created + for _, d := range []string{"contracts", "configs"} { + info, err := os.Stat(filepath.Join(dir, d)) + if err != nil { + t.Fatalf("%s/ not created: %v", d, err) + } + if !info.IsDir() { + t.Errorf("%s is not a directory", d) + } + } + }) + + t.Run("fails_if_soda_yml_exists", func(t *testing.T) { + dir := t.TempDir() + os.WriteFile(filepath.Join(dir, "soda.yml"), []byte("existing"), 0644) + + r := runInDir(t, dir, "init", "--no-interactive") + assertExitCode(t, r, 1) + assertOutputContains(t, r, "already exists") + }) +}