1+ package test
2+
3+ import (
4+ "bufio"
5+ "bytes"
6+ "fmt"
7+ "io"
8+ "os/exec"
9+ "strings"
10+ "sync"
11+ )
12+
13+ // CommandResult holds the result of a command execution
14+ type CommandResult struct {
15+ Stdout string
16+ Stderr string
17+ ExitCode int
18+ Err error
19+ }
20+
21+ // String returns a formatted string of the command result
22+ func (r CommandResult ) String () string {
23+ return fmt .Sprintf ("ExitCode: %d\n Stdout:\n %s\n Stderr:\n %s\n Error: %v" ,
24+ r .ExitCode , r .Stdout , r .Stderr , r .Err )
25+ }
26+
27+ // CommandRunner provides command execution with optional colored output
28+ type CommandRunner struct {
29+ ColorOutput bool
30+ }
31+
32+ // NewCommandRunner creates a new CommandRunner
33+ func NewCommandRunner (colorOutput bool ) * CommandRunner {
34+ return & CommandRunner {ColorOutput : colorOutput }
35+ }
36+
37+ // RunCommand executes a command and returns the result
38+ func (c * CommandRunner ) RunCommand (name string , args ... string ) CommandResult {
39+ if c .ColorOutput {
40+ fmt .Printf ("%s%s>>> Executing: %s %s%s\n " , colorBlue , colorBold , name , strings .Join (args , " " ), colorReset )
41+ }
42+
43+ cmd := exec .Command (name , args ... )
44+
45+ // Create pipes for stdout and stderr
46+ stdoutPipe , err := cmd .StdoutPipe ()
47+ if err != nil {
48+ return CommandResult {
49+ Err : fmt .Errorf ("failed to create stdout pipe: %w" , err ),
50+ ExitCode : - 1 ,
51+ }
52+ }
53+
54+ stderrPipe , err := cmd .StderrPipe ()
55+ if err != nil {
56+ return CommandResult {
57+ Err : fmt .Errorf ("failed to create stderr pipe: %w" , err ),
58+ ExitCode : - 1 ,
59+ }
60+ }
61+
62+ // Buffers to capture output
63+ var stdout , stderr bytes.Buffer
64+
65+ // Start the command
66+ if err := cmd .Start (); err != nil {
67+ return CommandResult {
68+ Err : fmt .Errorf ("failed to start command: %w" , err ),
69+ ExitCode : - 1 ,
70+ }
71+ }
72+
73+ // Stream output in real-time with colors
74+ var wg sync.WaitGroup
75+ wg .Add (2 )
76+
77+ go c .streamOutput (stdoutPipe , "stdout" , colorGray , & stdout , & wg )
78+ go c .streamOutput (stderrPipe , "stderr" , colorRed , & stderr , & wg )
79+
80+ // Wait for output streaming to complete
81+ wg .Wait ()
82+
83+ // Wait for command to complete
84+ err = cmd .Wait ()
85+ exitCode := 0
86+ if exitErr , ok := err .(* exec.ExitError ); ok {
87+ exitCode = exitErr .ExitCode ()
88+ }
89+
90+ result := CommandResult {
91+ Stdout : stdout .String (),
92+ Stderr : stderr .String (),
93+ ExitCode : exitCode ,
94+ Err : err ,
95+ }
96+
97+ // Print exit status
98+ if c .ColorOutput {
99+ if result .Err != nil {
100+ fmt .Printf ("%s%s<<< Command failed with exit code %d%s\n " , colorRed , colorBold , result .ExitCode , colorReset )
101+ } else {
102+ fmt .Printf ("%s<<< Command completed successfully%s\n " , colorGray , colorReset )
103+ }
104+ fmt .Println () // Add blank line for readability
105+ }
106+
107+ return result
108+ }
109+
110+ // RunCommandQuiet executes a command without output streaming
111+ func (c * CommandRunner ) RunCommandQuiet (name string , args ... string ) CommandResult {
112+ cmd := exec .Command (name , args ... )
113+ var stdout , stderr bytes.Buffer
114+ cmd .Stdout = & stdout
115+ cmd .Stderr = & stderr
116+
117+ err := cmd .Run ()
118+ exitCode := 0
119+ if exitErr , ok := err .(* exec.ExitError ); ok {
120+ exitCode = exitErr .ExitCode ()
121+ }
122+
123+ return CommandResult {
124+ Stdout : stdout .String (),
125+ Stderr : stderr .String (),
126+ ExitCode : exitCode ,
127+ Err : err ,
128+ }
129+ }
130+
131+ func (c * CommandRunner ) streamOutput (reader io.Reader , prefix string , color string , buffer * bytes.Buffer , wg * sync.WaitGroup ) {
132+ defer wg .Done ()
133+ scanner := bufio .NewScanner (reader )
134+ for scanner .Scan () {
135+ line := scanner .Text ()
136+ buffer .WriteString (line + "\n " )
137+ if c .ColorOutput {
138+ fmt .Printf ("%s%s%s: %s%s\n " , color , prefix , colorReset , color , line + colorReset )
139+ }
140+ }
141+ }
142+
143+ // Printf prints a formatted colored message
144+ func (c * CommandRunner ) Printf (color , style , format string , args ... interface {}) {
145+ if c .ColorOutput {
146+ fmt .Printf ("%s%s%s%s\n " , color , style , fmt .Sprintf (format , args ... ), colorReset )
147+ } else {
148+ fmt .Printf (format + "\n " , args ... )
149+ }
150+ }
0 commit comments