11/*
2-
32 Copyright 2020 Docker Compose CLI authors
3+
44 Licensed under the Apache License, Version 2.0 (the "License");
55 you may not use this file except in compliance with the License.
66 You may obtain a copy of the License at
@@ -17,13 +17,13 @@ package compose
1717import (
1818 "context"
1919 "fmt"
20- "io/fs"
21- "os"
2220 "path"
2321 "path/filepath"
2422 "strings"
2523 "time"
2624
25+ "github.com/docker/compose/v2/internal/sync"
26+
2727 "github.com/compose-spec/compose-go/types"
2828 "github.com/jonboulle/clockwork"
2929 "github.com/mitchellh/mapstructure"
@@ -54,11 +54,8 @@ type Trigger struct {
5454
5555const quietPeriod = 2 * time .Second
5656
57- // fileMapping contains the Compose service and modified host system path.
58- //
59- // For file sync, the container path is also included.
60- // For rebuild, there is no container path, so it is always empty.
61- type fileMapping struct {
57+ // fileEvent contains the Compose service and modified host system path.
58+ type fileEvent struct {
6259 // Service that the file event is for.
6360 Service string
6461 // HostPath that was created/modified/deleted outside the container.
@@ -67,17 +64,11 @@ type fileMapping struct {
6764 // - C:\Users\moby\Documents\hello-world\main.go
6865 // - /Users/moby/Documents/hello-world/main.go
6966 HostPath string
70- // ContainerPath for the target file inside the container (only populated
71- // for sync events, not rebuild).
72- //
73- // This is the path as used in Docker CLI commands, e.g.
74- // - /workdir/main.go
75- ContainerPath string
7667}
7768
7869func (s * composeService ) Watch (ctx context.Context , project * types.Project , services []string , _ api.WatchOptions ) error { //nolint: gocyclo
79- needRebuild := make (chan fileMapping )
80- needSync := make (chan fileMapping )
70+ needRebuild := make (chan fileEvent )
71+ needSync := make (chan sync. PathMapping )
8172
8273 _ , err := s .prepareProjectForBuild (project , nil )
8374 if err != nil {
@@ -175,7 +166,7 @@ func (s *composeService) Watch(ctx context.Context, project *types.Project, serv
175166 return eg .Wait ()
176167}
177168
178- func (s * composeService ) watch (ctx context.Context , name string , watcher watch.Notify , triggers []Trigger , needSync chan fileMapping , needRebuild chan fileMapping ) error {
169+ func (s * composeService ) watch (ctx context.Context , name string , watcher watch.Notify , triggers []Trigger , needSync chan sync. PathMapping , needRebuild chan fileEvent ) error {
179170 ignores := make ([]watch.PathMatcher , len (triggers ))
180171 for i , trigger := range triggers {
181172 ignore , err := watch .NewDockerPatternMatcher (trigger .Path , trigger .Ignore )
@@ -209,24 +200,25 @@ WATCH:
209200
210201 fmt .Fprintf (s .stdinfo (), "change detected on %s\n " , hostPath )
211202
212- f := fileMapping {
213- HostPath : hostPath ,
214- Service : name ,
215- }
216-
217203 switch trigger .Action {
218204 case WatchActionSync :
219205 logrus .Debugf ("modified file %s triggered sync" , hostPath )
220206 rel , err := filepath .Rel (trigger .Path , hostPath )
221207 if err != nil {
222208 return err
223209 }
224- // always use Unix-style paths for inside the container
225- f .ContainerPath = path .Join (trigger .Target , rel )
226- needSync <- f
210+ needSync <- sync.PathMapping {
211+ Service : name ,
212+ HostPath : hostPath ,
213+ // always use Unix-style paths for inside the container
214+ ContainerPath : path .Join (trigger .Target , rel ),
215+ }
227216 case WatchActionRebuild :
228217 logrus .Debugf ("modified file %s requires image to be rebuilt" , hostPath )
229- needRebuild <- f
218+ needRebuild <- fileEvent {
219+ HostPath : hostPath ,
220+ Service : name ,
221+ }
230222 default :
231223 return fmt .Errorf ("watch action %q is not supported" , trigger )
232224 }
@@ -304,57 +296,25 @@ func (s *composeService) makeRebuildFn(ctx context.Context, project *types.Proje
304296 }
305297}
306298
307- func (s * composeService ) makeSyncFn (ctx context.Context , project * types.Project , needSync <- chan fileMapping ) func () error {
299+ func (s * composeService ) makeSyncFn (
300+ ctx context.Context ,
301+ project * types.Project ,
302+ needSync <- chan sync.PathMapping ,
303+ ) func () error {
304+ syncer := sync .NewDockerCopy (project .Name , s , s .stdinfo ())
305+
308306 return func () error {
309307 for {
310308 select {
311309 case <- ctx .Done ():
312310 return nil
313- case opt := <- needSync :
314- service , err := project .GetService (opt .Service )
311+ case pathMapping := <- needSync :
312+ service , err := project .GetService (pathMapping .Service )
315313 if err != nil {
316314 return err
317315 }
318- scale := 1
319- if service .Deploy != nil && service .Deploy .Replicas != nil {
320- scale = int (* service .Deploy .Replicas )
321- }
322-
323- if fi , statErr := os .Stat (opt .HostPath ); statErr == nil {
324- if fi .IsDir () {
325- for i := 1 ; i <= scale ; i ++ {
326- _ , err := s .Exec (ctx , project .Name , api.RunOptions {
327- Service : opt .Service ,
328- Command : []string {"mkdir" , "-p" , opt .ContainerPath },
329- Index : i ,
330- })
331- if err != nil {
332- logrus .Warnf ("failed to create %q from %s: %v" , opt .ContainerPath , opt .Service , err )
333- }
334- }
335- fmt .Fprintf (s .stdinfo (), "%s created\n " , opt .ContainerPath )
336- } else {
337- err := s .Copy (ctx , project .Name , api.CopyOptions {
338- Source : opt .HostPath ,
339- Destination : fmt .Sprintf ("%s:%s" , opt .Service , opt .ContainerPath ),
340- })
341- if err != nil {
342- return err
343- }
344- fmt .Fprintf (s .stdinfo (), "%s updated\n " , opt .ContainerPath )
345- }
346- } else if errors .Is (statErr , fs .ErrNotExist ) {
347- for i := 1 ; i <= scale ; i ++ {
348- _ , err := s .Exec (ctx , project .Name , api.RunOptions {
349- Service : opt .Service ,
350- Command : []string {"rm" , "-rf" , opt .ContainerPath },
351- Index : i ,
352- })
353- if err != nil {
354- logrus .Warnf ("failed to delete %q from %s: %v" , opt .ContainerPath , opt .Service , err )
355- }
356- }
357- fmt .Fprintf (s .stdinfo (), "%s deleted from service\n " , opt .ContainerPath )
316+ if err := syncer .Sync (ctx , service , []sync.PathMapping {pathMapping }); err != nil {
317+ return err
358318 }
359319 }
360320 }
@@ -363,7 +323,7 @@ func (s *composeService) makeSyncFn(ctx context.Context, project *types.Project,
363323
364324type rebuildServices map [string ]utils.Set [string ]
365325
366- func debounce (ctx context.Context , clock clockwork.Clock , delay time.Duration , input <- chan fileMapping , fn func (services rebuildServices )) {
326+ func debounce (ctx context.Context , clock clockwork.Clock , delay time.Duration , input <- chan fileEvent , fn func (services rebuildServices )) {
367327 services := make (rebuildServices )
368328 t := clock .NewTimer (delay )
369329 defer t .Stop ()
0 commit comments