From 5f448d7fc733d15e544a04efe6edb75afcefb852 Mon Sep 17 00:00:00 2001 From: jon brookes Date: Sat, 16 Aug 2025 11:45:47 +0100 Subject: [PATCH] update: enhance command execution with real-time logging and add shorthand flag for deployment file --- app/k8s.go | 46 ++++++++++++++++++++++++++++++++++++++++------ config/base.go | 6 +++++- main.go | 46 +++++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 90 insertions(+), 8 deletions(-) diff --git a/app/k8s.go b/app/k8s.go index 49390e9..86f7a70 100644 --- a/app/k8s.go +++ b/app/k8s.go @@ -1,6 +1,7 @@ package app import ( + "bufio" "bytes" "fmt" "log/slog" @@ -67,19 +68,52 @@ func k8sCreateNamespace(project string) error { func RunCommand(command string) error { slog.Debug(fmt.Sprintf("🐞 Running command: %s", command)) cmd := exec.Command("sh", "-c", command) + var stdout, stderr bytes.Buffer - cmd.Stdout = &stdout - cmd.Stderr = &stderr - err := cmd.Run() + + // Get pipes for real-time reading + stdoutPipe, err := cmd.StdoutPipe() + if err != nil { + return fmt.Errorf("failed to create stdout pipe: %w", err) + } + stderrPipe, err := cmd.StderrPipe() + if err != nil { + return fmt.Errorf("failed to create stderr pipe: %w", err) + } + + // Start the command + if err := cmd.Start(); err != nil { + return fmt.Errorf("failed to start command: %w", err) + } + + // Read stdout line by line and log through slog + go func() { + scanner := bufio.NewScanner(stdoutPipe) + for scanner.Scan() { + line := scanner.Text() + stdout.WriteString(line + "\n") + slog.Info(line) + } + }() + + // Read stderr line by line and log through slog + go func() { + scanner := bufio.NewScanner(stderrPipe) + for scanner.Scan() { + line := scanner.Text() + stderr.WriteString(line + "\n") + slog.Error(line) + } + }() + + // Wait for command to complete + err = cmd.Wait() if err != nil { slog.Error(fmt.Sprintf("❌ Command failed with error: %v\n", err)) slog.Debug(fmt.Sprintf("🐞 Stdout: %s\n", stdout.String())) slog.Debug(fmt.Sprintf("🐞 Stderr: %s\n", stderr.String())) return fmt.Errorf("failed to run script command: %w", err) } - output := stdout.String() - slog.Debug(fmt.Sprintf("RunCommand executed successfully: %s", command)) - slog.Debug(fmt.Sprintf("RunCommand output: %s", output)) return nil } diff --git a/config/base.go b/config/base.go index abb6831..acbd4d2 100644 --- a/config/base.go +++ b/config/base.go @@ -35,6 +35,8 @@ func ReadBaseConfig(path string) (BaseConfig, error) { deploymentType := os.Getenv("DEPLOYMENT_TYPE") deploymentFile := flag.String("deployment-file", "", "path to config file") + deploymentFileShorthand := flag.String("f", "", "shorthand for -deployment-file") + helpFlag := flag.Bool("help", false, "show help") flag.Parse() @@ -45,7 +47,9 @@ func ReadBaseConfig(path string) (BaseConfig, error) { } var config BaseConfig - if *deploymentFile != "" { + if *deploymentFileShorthand != "" { + config.DeploymentFile = *deploymentFileShorthand + } else if *deploymentFile != "" { config.DeploymentFile = *deploymentFile } diff --git a/main.go b/main.go index 059dd29..f8602b6 100644 --- a/main.go +++ b/main.go @@ -14,6 +14,7 @@ package main import ( + "context" "fmt" "log" "log/slog" @@ -23,11 +24,54 @@ import ( "headshed/infctl-cli/config" ) +type customMessageOnlyHandler struct { + output *os.File +} + +func (h *customMessageOnlyHandler) Enabled(_ context.Context, _ slog.Level) bool { + return true +} + +func (h *customMessageOnlyHandler) Handle(_ context.Context, r slog.Record) error { + // Directly retrieve the message from the record + msg := r.Message + if msg != "" { + _, err := fmt.Fprintln(h.output, msg) + return err + } + return nil +} + +func (h *customMessageOnlyHandler) WithAttrs(_ []slog.Attr) slog.Handler { + return h +} + +func (h *customMessageOnlyHandler) WithGroup(_ string) slog.Handler { + return h +} + func main() { var levelVar slog.LevelVar levelVar.Set(slog.LevelDebug) - logger := slog.New(slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{Level: &levelVar})) + + var logger *slog.Logger + if os.Getenv("LOG_FORMAT") == "basic" { + logger = slog.New(slog.NewTextHandler(os.Stdout, &slog.HandlerOptions{ + ReplaceAttr: func(groups []string, a slog.Attr) slog.Attr { + if a.Key == slog.TimeKey { + return slog.Attr{} + } + return a + }, + })) + + } else if os.Getenv("LOG_FORMAT") == "none" { + logger = slog.New(&customMessageOnlyHandler{output: os.Stdout}) + + } else { + logger = slog.New(slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{Level: &levelVar})) + } slog.SetDefault(logger)