@@ -163,7 +163,10 @@ func (s *composeService) watch(ctx context.Context, syncChannel chan bool, proje
163163 success , err := trigger .Extensions .Get ("x-initialSync" , & initialSync )
164164 if err == nil && success && initialSync && (trigger .Action == types .WatchActionSync || trigger .Action == types .WatchActionSyncRestart ) {
165165 // 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 )
166+ err := s .initialSync (ctx , project , service , trigger , ignore , syncer )
167+ if err != nil {
168+ return err
169+ }
167170 }
168171 }
169172 paths = append (paths , trigger .Path )
@@ -583,29 +586,43 @@ func (s *composeService) pruneDanglingImagesOnRebuild(ctx context.Context, proje
583586 }
584587}
585588
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+ // Walks develop.watch.path and checks which files should be copied inside the container
590+ // ignores develop.watch.ignore, Dockerfile, compose files, bind mounted paths and .git
591+ func (s * composeService ) initialSync (ctx context.Context , project * types.Project , service types.ServiceConfig , trigger types.Trigger , ignore watch.PathMatcher , syncer sync.Syncer ) error {
592+ dockerFileIgnore , err := watch .NewDockerPatternMatcher ("/" , []string {"Dockerfile" , "*compose*.y*ml" })
593+ if err != nil {
594+ return err
595+ }
596+ triggerIgnore , err := watch .NewDockerPatternMatcher ("/" , trigger .Ignore )
597+ if err != nil {
598+ return err
599+ }
589600 ignoreInitialSync := watch .NewCompositeMatcher (ignore , dockerFileIgnore , triggerIgnore )
590601
591- pathsToCopy , err := initialSyncFiles (service , trigger , ignoreInitialSync )
602+ pathsToCopy , err := s . initialSyncFiles (ctx , project , service , trigger , ignoreInitialSync )
592603 if err != nil {
593604 return err
594605 }
595606
596607 return syncer .Sync (ctx , service , pathsToCopy )
597608}
598609
599- func initialSyncFiles (service types.ServiceConfig , trigger types.Trigger , ignore watch.PathMatcher ) ([]sync.PathMapping , error ) {
610+ // Syncs files from develop.watch.path if thy have been modified after the image has been created
611+ //
612+ //nolint:gocyclo
613+ func (s * composeService ) initialSyncFiles (ctx context.Context , project * types.Project , service types.ServiceConfig , trigger types.Trigger , ignore watch.PathMatcher ) ([]sync.PathMapping , error ) {
600614 fi , err := os .Stat (trigger .Path )
601615 if err != nil {
602616 return nil , err
603617 }
604-
618+ timeImageCreated , err := s .imageCreatedTime (ctx , project , service .Name )
619+ if err != nil {
620+ return nil , err
621+ }
605622 var pathsToCopy []sync.PathMapping
606623 switch mode := fi .Mode (); {
607624 case mode .IsDir ():
608- // do directory stuff
625+ // process directory
609626 err = filepath .WalkDir (trigger .Path , func (path string , d fs.DirEntry , err error ) error {
610627 if err != nil {
611628 // handle possible path err, just in case...
@@ -615,34 +632,76 @@ func initialSyncFiles(service types.ServiceConfig, trigger types.Trigger, ignore
615632 // walk starts at the root directory
616633 return nil
617634 }
618- rel , _ := filepath .Rel (trigger .Path , path )
619- if shouldIgnoreOrSkip (filepath .Base (path ), ignore ) || checkIfPathAlreadyBindMounted (path , service .Volumes ) {
635+ if shouldIgnore (filepath .Base (path ), ignore ) || checkIfPathAlreadyBindMounted (path , service .Volumes ) {
620636 // By definition sync ignores bind mounted paths
621637 if d .IsDir () {
622- return fs .SkipDir // ignore or skip folder
638+ // skip folder
639+ return fs .SkipDir
623640 }
624- return nil // ignore or skip file
641+ return nil // skip file
642+ }
643+ info , err := d .Info ()
644+ if err != nil {
645+ return err
646+ }
647+ if ! d .IsDir () {
648+ if info .ModTime ().Before (timeImageCreated ) {
649+ // skip file if it was modified before image creation
650+ return nil
651+ }
652+ rel , err := filepath .Rel (trigger .Path , path )
653+ if err != nil {
654+ return err
655+ }
656+ // only copy files (and not full directories)
657+ pathsToCopy = append (pathsToCopy , sync.PathMapping {
658+ HostPath : path ,
659+ ContainerPath : filepath .Join (trigger .Target , rel ),
660+ })
625661 }
626- pathsToCopy = append (pathsToCopy , sync.PathMapping {
627- HostPath : path ,
628- ContainerPath : filepath .Join (trigger .Target , rel ),
629- })
630662 return nil
631663 })
632664 case mode .IsRegular ():
633- // do file stuff
634- if ! shouldIgnoreOrSkip (filepath .Base (trigger .Path ), ignore ) && ! checkIfPathAlreadyBindMounted (trigger .Path , service .Volumes ) {
665+ // process file
666+ if fi . ModTime (). After ( timeImageCreated ) && ! shouldIgnore (filepath .Base (trigger .Path ), ignore ) && ! checkIfPathAlreadyBindMounted (trigger .Path , service .Volumes ) {
635667 pathsToCopy = append (pathsToCopy , sync.PathMapping {
636668 HostPath : trigger .Path ,
637669 ContainerPath : trigger .Target ,
638670 })
639671 }
640672 }
641- return pathsToCopy , nil
673+ return pathsToCopy , err
642674}
643675
644- func shouldIgnoreOrSkip ( rel string , ignore watch.PathMatcher ) bool {
645- shouldIgnore , _ := ignore .Matches (rel )
676+ func shouldIgnore ( name string , ignore watch.PathMatcher ) bool {
677+ shouldIgnore , _ := ignore .Matches (name )
646678 // ignore files that match any ignore pattern
647679 return shouldIgnore
648680}
681+
682+ // gets the image creation time for a service
683+ func (s * composeService ) imageCreatedTime (ctx context.Context , project * types.Project , serviceName string ) (time.Time , error ) {
684+ containers , err := s .apiClient ().ContainerList (ctx , container.ListOptions {
685+ All : true ,
686+ Filters : filters .NewArgs (
687+ filters .Arg ("label" , fmt .Sprintf ("%s=%s" , api .ProjectLabel , project .Name )),
688+ filters .Arg ("label" , fmt .Sprintf ("%s=%s" , api .ServiceLabel , serviceName ))),
689+ })
690+ if err != nil {
691+ return time .Now (), err
692+ }
693+ if len (containers ) == 0 {
694+ return time .Now (), fmt .Errorf ("Could not get created time for service's image" )
695+ }
696+
697+ img , _ , err := s .apiClient ().ImageInspectWithRaw (ctx , containers [0 ].ImageID )
698+ if err != nil {
699+ return time .Now (), err
700+ }
701+ // Need to get oldest one?
702+ timeCreated , err := time .Parse (time .RFC3339Nano , img .Created )
703+ if err != nil {
704+ return time .Now (), err
705+ }
706+ return timeCreated , nil
707+ }
0 commit comments