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