@@ -94,6 +94,12 @@ func NewComposeService(dockerCli command.Cli, options ...Option) (api.Compose, e
9494 return defaultValue , nil
9595 }
9696 }
97+
98+ // If custom streams were provided, wrap the Docker CLI to use them
99+ if s .outStream != nil || s .errStream != nil || s .inStream != nil {
100+ s .dockerCli = s .wrapDockerCliWithStreams (dockerCli )
101+ }
102+
97103 return s , nil
98104}
99105
@@ -141,7 +147,7 @@ func WithContextInfo(info api.ContextInfo) Option {
141147
142148// WithProxyConfig sets custom HTTP proxy configuration for builds
143149func WithProxyConfig (config map [string ]string ) Option {
144- return func (s * composeService ) error {
150+ return func (s * composeService ) error {
145151 s .proxyConfig = config
146152 return nil
147153 }
@@ -238,28 +244,15 @@ func (s *composeService) getProxyConfig() map[string]string {
238244 return storeutil .GetProxyConfig (s .dockerCli )
239245}
240246
241-
242247func (s * composeService ) stdout () * streams.Out {
243- // If stream overrides are provided, use them
244- if s .outStream != nil {
245- return streams .NewOut (s .outStream )
246- }
247248 return s .dockerCli .Out ()
248249}
249250
250251func (s * composeService ) stdin () * streams.In {
251- // If stream overrides are provided, use them
252- if s .inStream != nil {
253- return streams .NewIn (& readCloserAdapter {r : s .inStream })
254- }
255252 return s .dockerCli .In ()
256253}
257254
258255func (s * composeService ) stderr () * streams.Out {
259- // If stream overrides are provided, use them
260- if s .errStream != nil {
261- return streams .NewOut (s .errStream )
262- }
263256 return s .dockerCli .Err ()
264257}
265258
@@ -270,6 +263,11 @@ func (s *composeService) stdinfo() *streams.Out {
270263 return s .stderr ()
271264}
272265
266+ // GetConfiguredStreams returns the configured I/O streams (implements api.Compose interface)
267+ func (s * composeService ) GetConfiguredStreams () (io.Writer , io.Writer , io.Reader ) {
268+ return s .stdout (), s .stderr (), s .stdin ()
269+ }
270+
273271// readCloserAdapter adapts io.Reader to io.ReadCloser
274272type readCloserAdapter struct {
275273 r io.Reader
@@ -283,6 +281,55 @@ func (r *readCloserAdapter) Close() error {
283281 return nil
284282}
285283
284+ // wrapDockerCliWithStreams wraps the Docker CLI to intercept and override stream methods
285+ func (s * composeService ) wrapDockerCliWithStreams (baseCli command.Cli ) command.Cli {
286+ wrapper := & streamOverrideWrapper {
287+ Cli : baseCli ,
288+ }
289+
290+ // Wrap custom streams in Docker CLI's stream types
291+ if s .outStream != nil {
292+ wrapper .outStream = streams .NewOut (s .outStream )
293+ }
294+ if s .errStream != nil {
295+ wrapper .errStream = streams .NewOut (s .errStream )
296+ }
297+ if s .inStream != nil {
298+ wrapper .inStream = streams .NewIn (& readCloserAdapter {r : s .inStream })
299+ }
300+
301+ return wrapper
302+ }
303+
304+ // streamOverrideWrapper wraps command.Cli to override streams with custom implementations
305+ type streamOverrideWrapper struct {
306+ command.Cli
307+ outStream * streams.Out
308+ errStream * streams.Out
309+ inStream * streams.In
310+ }
311+
312+ func (w * streamOverrideWrapper ) Out () * streams.Out {
313+ if w .outStream != nil {
314+ return w .outStream
315+ }
316+ return w .Cli .Out ()
317+ }
318+
319+ func (w * streamOverrideWrapper ) Err () * streams.Out {
320+ if w .errStream != nil {
321+ return w .errStream
322+ }
323+ return w .Cli .Err ()
324+ }
325+
326+ func (w * streamOverrideWrapper ) In () * streams.In {
327+ if w .inStream != nil {
328+ return w .inStream
329+ }
330+ return w .Cli .In ()
331+ }
332+
286333func getCanonicalContainerName (c container.Summary ) string {
287334 if len (c .Names ) == 0 {
288335 // corner case, sometime happens on removal. return short ID as a safeguard value
0 commit comments