@@ -18,6 +18,7 @@ package cmd
1818import (
1919 "context"
2020 "fmt"
21+ "io"
2122 "net/url"
2223 "os"
2324 "os/exec"
@@ -68,8 +69,6 @@ func configureDriver(driver string) error {
6869 }
6970}
7071
71- // shouldUsePodman auto-detects podman only when it's clearly the intended
72- // runtime: no explicit DOCKER_HOST, podman on PATH, and docker absent.
7372func shouldUsePodman () bool {
7473 if os .Getenv ("DOCKER_HOST" ) != "" {
7574 return false // respect an explicitly configured endpoint
@@ -142,14 +141,18 @@ func rewriteLocalEndpoint(testEndpoint string) (string, int, bool) {
142141}
143142
144143func runDryRunTest (opts dryRunOptions ) bool {
144+ // Progress/diagnostics go to stderr for machine output formats so stdout
145+ // carries only the formatted result.
146+ progress := progressWriter (opts .params .outputFormat )
147+
145148 if err := validateDryRunOptions (opts ); err != nil {
146- fmt .Println ( err )
149+ fmt .Fprintln ( os . Stderr , err )
147150 return false
148151 }
149152
150153 // Select the container runtime (docker default, podman wired via DOCKER_HOST).
151154 if err := configureDriver (opts .driver ); err != nil {
152- fmt .Println ( err )
155+ fmt .Fprintln ( os . Stderr , err )
153156 return false
154157 }
155158
@@ -164,32 +167,32 @@ func runDryRunTest(opts dryRunOptions) bool {
164167 // A localhost test endpoint refers to the user's machine, not the
165168 // container: expose the port and point Microcks at the host gateway.
166169 if rewritten , hostPort , ok := rewriteLocalEndpoint (opts .params .testEndpoint ); ok {
167- fmt .Printf ( "Test endpoint %s is local: reaching it from the container as %s\n " , opts .params .testEndpoint , rewritten )
170+ fmt .Fprintf ( progress , "Test endpoint %s is local: reaching it from the container as %s\n " , opts .params .testEndpoint , rewritten )
168171 opts .params .testEndpoint = rewritten
169172 containerOpts = append (containerOpts , testcontainers .WithHostPortAccess (hostPort ))
170173 }
171174
172- fmt .Printf ( "Starting ephemeral Microcks container (%s)...\n " , opts .image )
175+ fmt .Fprintf ( progress , "Starting ephemeral Microcks container (%s)...\n " , opts .image )
173176 startCtx , startCancel := context .WithTimeout (ctx , opts .readyTimeout )
174177 defer startCancel ()
175178
176179 container , err := microcks .Run (startCtx , opts .image , containerOpts ... )
177180 if err != nil {
178- fmt .Printf ( "Failed to start ephemeral Microcks container: %s\n " , err )
179- fmt .Println ( "Check that the container runtime is running, the port is free and the image is reachable (or raise --ready-timeout)." )
181+ fmt .Fprintf ( os . Stderr , "Failed to start ephemeral Microcks container: %s\n " , err )
182+ fmt .Fprintln ( os . Stderr , "Check that the container runtime is running, the port is free and the image is reachable (or raise --ready-timeout)." )
180183 if container != nil {
181- terminateContainer (container )
184+ terminateContainer (container , progress )
182185 }
183186 return false
184187 }
185- defer terminateContainer (container )
188+ defer terminateContainer (container , progress )
186189
187190 endpoint , err := container .HttpEndpoint (ctx )
188191 if err != nil {
189- fmt .Printf ( "Failed to resolve ephemeral Microcks endpoint: %s\n " , err )
192+ fmt .Fprintf ( os . Stderr , "Failed to resolve ephemeral Microcks endpoint: %s\n " , err )
190193 return false
191194 }
192- fmt .Printf ( "Ephemeral Microcks is ready at %s\n " , endpoint )
195+ fmt .Fprintf ( progress , "Ephemeral Microcks is ready at %s\n " , endpoint )
193196
194197 // The uber-native image runs without Keycloak: a headless client with
195198 // the unauthenticated token is enough.
@@ -198,30 +201,32 @@ func runDryRunTest(opts dryRunOptions) bool {
198201
199202 success , testResultID , err := runTestAndWait (mc , opts .params )
200203 if err != nil {
201- fmt .Println ( err )
204+ fmt .Fprintln ( os . Stderr , err )
202205 return false
203206 }
204207
205208 if ! opts .watch {
206209 return success
207210 }
208- printDetailsLink (endpoint , testResultID )
211+ printDetailsLink (progress , endpoint , testResultID )
209212 return watchAndRerun (ctx , mc , endpoint , opts )
210213}
211214
212- func terminateContainer (container * microcks.MicrocksContainer ) {
215+ func terminateContainer (container * microcks.MicrocksContainer , progress io. Writer ) {
213216 ctx , cancel := context .WithTimeout (context .Background (), 30 * time .Second )
214217 defer cancel ()
215- fmt .Println ( "Tearing down ephemeral Microcks container..." )
218+ fmt .Fprintln ( progress , "Tearing down ephemeral Microcks container..." )
216219 if err := container .Terminate (ctx ); err != nil {
217- fmt .Printf ( "Failed to terminate container %s: %s\n " , container .GetContainerID (), err )
220+ fmt .Fprintf ( os . Stderr , "Failed to terminate container %s: %s\n " , container .GetContainerID (), err )
218221 }
219222}
220223
221224func watchAndRerun (ctx context.Context , mc connectors.MicrocksClient , serverAddr string , opts dryRunOptions ) bool {
225+ progress := progressWriter (opts .params .outputFormat )
226+
222227 watcher , err := fsnotify .NewWatcher ()
223228 if err != nil {
224- fmt .Printf ( "Failed to create file watcher: %s\n " , err )
229+ fmt .Fprintf ( os . Stderr , "Failed to create file watcher: %s\n " , err )
225230 return false
226231 }
227232 defer watcher .Close ()
@@ -230,23 +235,23 @@ func watchAndRerun(ctx context.Context, mc connectors.MicrocksClient, serverAddr
230235 // (rename + create), which silently drops a watch set on the file itself.
231236 artifactPath , err := filepath .Abs (opts .artifact )
232237 if err != nil {
233- fmt .Printf ( "Failed to resolve artifact path: %s\n " , err )
238+ fmt .Fprintf ( os . Stderr , "Failed to resolve artifact path: %s\n " , err )
234239 return false
235240 }
236241 if err := watcher .Add (filepath .Dir (artifactPath )); err != nil {
237- fmt .Printf ( "Failed to watch %s: %s\n " , filepath .Dir (artifactPath ), err )
242+ fmt .Fprintf ( os . Stderr , "Failed to watch %s: %s\n " , filepath .Dir (artifactPath ), err )
238243 return false
239244 }
240245
241- fmt .Printf ( "\n Watching %s for changes — press Ctrl+C to stop.\n " , opts .artifact )
246+ fmt .Fprintf ( progress , "\n Watching %s for changes — press Ctrl+C to stop.\n " , opts .artifact )
242247
243248 rerun := make (chan struct {}, 1 )
244249 var debounce * time.Timer
245250
246251 for {
247252 select {
248253 case <- ctx .Done ():
249- fmt .Println ( "\n Stopping watch mode." )
254+ fmt .Fprintln ( progress , "\n Stopping watch mode." )
250255 return true
251256
252257 case event , ok := <- watcher .Events :
@@ -275,32 +280,32 @@ func watchAndRerun(ctx context.Context, mc connectors.MicrocksClient, serverAddr
275280 if ! ok {
276281 return true
277282 }
278- fmt .Printf ( "Watch error: %s\n " , err )
283+ fmt .Fprintf ( os . Stderr , "Watch error: %s\n " , err )
279284
280285 case <- rerun :
281- fmt .Println ( strings .Repeat ("-" , 60 ))
282- fmt .Printf ( "Artifact changed, re-importing %s ...\n " , opts .artifact )
286+ fmt .Fprintln ( progress , strings .Repeat ("-" , 60 ))
287+ fmt .Fprintf ( progress , "Artifact changed, re-importing %s ...\n " , opts .artifact )
283288 if _ , err := mc .UploadArtifact (opts .artifact , true ); err != nil {
284289 // Invalid spec mid-edit is normal in a TDD loop: report and
285290 // keep watching, the next valid save recovers.
286- fmt .Printf ( "Re-import failed, waiting for next change: %s\n " , err )
291+ fmt .Fprintf ( os . Stderr , "Re-import failed, waiting for next change: %s\n " , err )
287292 continue
288293 }
289294 success , testResultID , err := runTestAndWait (mc , opts .params )
290295 if err != nil {
291- fmt .Printf ( "Test run failed, waiting for next change: %s\n " , err )
296+ fmt .Fprintf ( os . Stderr , "Test run failed, waiting for next change: %s\n " , err )
292297 continue
293298 }
294- printDetailsLink (serverAddr , testResultID )
299+ printDetailsLink (progress , serverAddr , testResultID )
295300 if success {
296- fmt .Println ( "Contract test PASSED — waiting for next change." )
301+ fmt .Fprintln ( progress , "Contract test PASSED — waiting for next change." )
297302 } else {
298- fmt .Println ( "Contract test FAILED — waiting for next change." )
303+ fmt .Fprintln ( progress , "Contract test FAILED — waiting for next change." )
299304 }
300305 }
301306 }
302307}
303308
304- func printDetailsLink (serverAddr , testResultID string ) {
305- fmt .Printf ( "Test details (live while watching): %s/#/tests/%s\n " , serverAddr , testResultID )
309+ func printDetailsLink (progress io. Writer , serverAddr , testResultID string ) {
310+ fmt .Fprintf ( progress , "Test details (live while watching): %s/#/tests/%s\n " , serverAddr , testResultID )
306311}
0 commit comments