@@ -19,6 +19,7 @@ import (
1919 "errors"
2020 "fmt"
2121 "io"
22+ "io/fs"
2223 "os"
2324 "path"
2425 "path/filepath"
@@ -157,6 +158,13 @@ func (s *composeService) watch(ctx context.Context, syncChannel chan bool, proje
157158 if checkIfPathAlreadyBindMounted (trigger .Path , service .Volumes ) {
158159 logrus .Warnf ("path '%s' also declared by a bind mount volume, this path won't be monitored!\n " , trigger .Path )
159160 continue
161+ } else {
162+ var initialSync bool
163+ success , err := trigger .Extensions .Get ("x-initialSync" , & initialSync )
164+ if err == nil && success && initialSync && (trigger .Action == types .WatchActionSync || trigger .Action == types .WatchActionSyncRestart ) {
165+ // Need to check initial files are in container that are meant to be synched from watch action
166+ s .initialSync (ctx , service , trigger , ignore , syncer )
167+ }
160168 }
161169 paths = append (paths , trigger .Path )
162170 pathLogs = append (pathLogs , fmt .Sprintf ("Action %s for path %q" , trigger .Action , trigger .Path ))
@@ -574,3 +582,67 @@ func (s *composeService) pruneDanglingImagesOnRebuild(ctx context.Context, proje
574582 }
575583 }
576584}
585+
586+ func (s * composeService ) initialSync (ctx context.Context , service types.ServiceConfig , trigger types.Trigger , ignore watch.PathMatcher , syncer sync.Syncer ) error {
587+ dockerFileIgnore , _ := watch .NewDockerPatternMatcher ("/" , []string {"Dockerfile" , "*compose*.y*ml" })
588+ triggerIgnore , _ := watch .NewDockerPatternMatcher ("/" , trigger .Ignore )
589+ ignoreInitialSync := watch .NewCompositeMatcher (ignore , dockerFileIgnore , triggerIgnore )
590+
591+ pathsToCopy , err := initialSyncFiles (service , trigger , ignoreInitialSync )
592+ if err != nil {
593+ return err
594+ }
595+
596+ return syncer .Sync (ctx , service , pathsToCopy )
597+ }
598+
599+ func initialSyncFiles (service types.ServiceConfig , trigger types.Trigger , ignore watch.PathMatcher ) ([]sync.PathMapping , error ) {
600+ fi , err := os .Stat (trigger .Path )
601+ if err != nil {
602+ return nil , err
603+ }
604+
605+ var pathsToCopy []sync.PathMapping
606+ switch mode := fi .Mode (); {
607+ case mode .IsDir ():
608+ // do directory stuff
609+ err = filepath .WalkDir (trigger .Path , func (path string , d fs.DirEntry , err error ) error {
610+ if err != nil {
611+ // handle possible path err, just in case...
612+ return err
613+ }
614+ if trigger .Path == path {
615+ // walk starts at the root directory
616+ return nil
617+ }
618+ rel , _ := filepath .Rel (trigger .Path , path )
619+ if shouldIgnoreOrSkip (filepath .Base (path ), ignore ) || checkIfPathAlreadyBindMounted (path , service .Volumes ) {
620+ // By definition sync ignores bind mounted paths
621+ if d .IsDir () {
622+ return fs .SkipDir // ignore or skip folder
623+ }
624+ return nil // ignore or skip file
625+ }
626+ pathsToCopy = append (pathsToCopy , sync.PathMapping {
627+ HostPath : path ,
628+ ContainerPath : filepath .Join (trigger .Target , rel ),
629+ })
630+ return nil
631+ })
632+ case mode .IsRegular ():
633+ // do file stuff
634+ if ! shouldIgnoreOrSkip (filepath .Base (trigger .Path ), ignore ) && ! checkIfPathAlreadyBindMounted (trigger .Path , service .Volumes ) {
635+ pathsToCopy = append (pathsToCopy , sync.PathMapping {
636+ HostPath : trigger .Path ,
637+ ContainerPath : trigger .Target ,
638+ })
639+ }
640+ }
641+ return pathsToCopy , nil
642+ }
643+
644+ func shouldIgnoreOrSkip (rel string , ignore watch.PathMatcher ) bool {
645+ shouldIgnore , _ := ignore .Matches (rel )
646+ // ignore files that match any ignore pattern
647+ return shouldIgnore
648+ }
0 commit comments