package commands import ( "fmt" "os" "os/exec" "path/filepath" "datasmith/config" "github.com/fsnotify/fsnotify" "gopkg.in/yaml.v2" ) // Load the datasmith.yaml file func loadConfig(projectDir string) (config.DatasmithConfig, error) { configFilePath := filepath.Join(projectDir, "datasmith.yaml") file, err := os.Open(configFilePath) if err != nil { return config.DatasmithConfig{}, fmt.Errorf("error opening config file: %v", err) } defer file.Close() var cfg config.DatasmithConfig decoder := yaml.NewDecoder(file) if err := decoder.Decode(&cfg); err != nil { return config.DatasmithConfig{}, fmt.Errorf("error decoding config file: %v", err) } return cfg, nil } func Serve() { projectDir := "." // Assuming current directory is the project directory config, err := loadConfig(projectDir) if err != nil { fmt.Printf("Error loading config: %v\n", err) return } fmt.Println("Config loaded successfully.") // Determine which container tool to use (Docker or Podman) containerTool := "docker" if _, err := exec.LookPath("podman"); err == nil { containerTool = "podman" } else if _, err := exec.LookPath("docker"); err != nil { fmt.Println("Error: neither Docker nor Podman is installed.") return } // Build the Docker image fmt.Println("Building the Docker image...") if err := buildDockerImage(containerTool, config); err != nil { fmt.Printf("Error building Docker image: %v\n", err) return } fmt.Println("Docker image built successfully.") // Start the database container fmt.Println("Starting the database container...") if err := startDatabaseContainer(containerTool, config); err != nil { fmt.Printf("Error starting database container: %v\n", err) return } fmt.Println("Database container started successfully.") // Print connection information printConnectionInfo(config) fmt.Println("Watching for changes in the SQL files...") // Watch for changes in the SQL files watcher, err := fsnotify.NewWatcher() if err != nil { fmt.Printf("Error creating file watcher: %v\n", err) return } defer watcher.Close() done := make(chan bool) go func() { for { select { case event, ok := <-watcher.Events: if !ok { return } fmt.Println("Event:", event) if event.Op&fsnotify.Write == fsnotify.Write { fmt.Println("Modified file:", event.Name) if err := rebuildAndRestartContainer(containerTool, config); err != nil { fmt.Printf("Error rebuilding and restarting container: %v\n", err) } else { fmt.Println("Container rebuilt and restarted successfully.") } } case err, ok := <-watcher.Errors: if !ok { return } fmt.Println("Error:", err) } } }() err = watcher.Add("sql") if err != nil { fmt.Printf("Error adding watcher: %v\n", err) return } <-done } func buildDockerImage(containerTool string, config config.DatasmithConfig) error { imageName := fmt.Sprintf("ds_%s:%s", config.Name, config.Version) fmt.Printf("Building Docker image: %s\n", imageName) var buildCmd *exec.Cmd switch config.DbType { case "mysql": buildCmd = exec.Command(containerTool, "build", "-t", imageName, "--build-arg", "DB_USER=user", "--build-arg", "DB_PASSWORD=password", "--build-arg", fmt.Sprintf("DB_DATABASE=%s", config.Name), "--build-arg", "MARIADB_ROOT_PASSWORD=example", ".") case "postgres": buildCmd = exec.Command(containerTool, "build", "-t", imageName, "--build-arg", "DB_USER=user", "--build-arg", "DB_PASSWORD=password", "--build-arg", fmt.Sprintf("DB_DATABASE=%s", config.Name), "--build-arg", "POSTGRES_PASSWORD=example", ".") default: return fmt.Errorf("unsupported database type: %s", config.DbType) } buildCmd.Stdout = os.Stdout buildCmd.Stderr = os.Stderr return buildCmd.Run() } func startDatabaseContainer(containerTool string, config config.DatasmithConfig) error { var runCmd *exec.Cmd switch config.DbType { case "mysql": runCmd = exec.Command(containerTool, "run", "--rm", "-d", "-e", "MYSQL_ROOT_PASSWORD=example", "-e", fmt.Sprintf("DB_DATABASE=%s", config.Name), "-e", "DB_USER=user", "-e", "DB_PASSWORD=password", "-p", "3306:3306", "--name", "datasmith_db_container", "datasmith_db_image") case "postgres": runCmd = exec.Command(containerTool, "run", "--rm", "-d", "-e", "POSTGRES_PASSWORD=example", "-e", fmt.Sprintf("POSTGRES_DB=%s", config.Name), "-e", "POSTGRES_USER=user", "-p", "5432:5432", "--name", "datasmith_db_container", "datasmith_db_image") default: return fmt.Errorf("unsupported database type: %s", config.DbType) } runCmd.Stdout = os.Stdout runCmd.Stderr = os.Stderr return runCmd.Run() } func rebuildAndRestartContainer(containerTool string, config config.DatasmithConfig) error { fmt.Println("Stopping the current database container...") stopCmd := exec.Command(containerTool, "stop", "datasmith_db_container") stopCmd.Stdout = os.Stdout stopCmd.Stderr = os.Stderr if err := stopCmd.Run(); err != nil { return fmt.Errorf("error stopping database container: %v", err) } fmt.Println("Rebuilding the Docker image...") if err := buildDockerImage(containerTool, config); err != nil { return fmt.Errorf("error rebuilding Docker image: %v", err) } fmt.Println("Restarting the database container...") if err := startDatabaseContainer(containerTool, config); err != nil { return fmt.Errorf("error restarting database container: %v", err) } return nil } func printConnectionInfo(config config.DatasmithConfig) { fmt.Println("Connection Information:") switch config.DbType { case "mysql": fmt.Println("MySQL connection string:") fmt.Printf("Host: localhost\nPort: 3306\nDatabase: %s\nUser: user\nPassword: password\n", config.Name) case "postgres": fmt.Println("PostgreSQL connection string:") fmt.Printf("Host: localhost\nPort: 5432\nDatabase: %s\nUser: user\nPassword: password\n", config.Name) default: fmt.Printf("Unsupported database type: %s\n", config.DbType) } }