Skip to content

Commit f523966

Browse files
committed
Fix initial watch sync for existing files
Signed-off-by: trinhchien <trinhdacchien1598@gmail.com>
1 parent d518da2 commit f523966

2 files changed

Lines changed: 59 additions & 40 deletions

File tree

pkg/compose/watch.go

Lines changed: 4 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -752,26 +752,22 @@ func (s *composeService) initialSync(ctx context.Context, project *types.Project
752752
dotGitIgnore,
753753
triggerIgnore)
754754

755-
pathsToCopy, err := s.initialSyncFiles(ctx, project, service, trigger, ignoreInitialSync)
755+
pathsToCopy, err := s.initialSyncFiles(service, trigger, ignoreInitialSync)
756756
if err != nil {
757757
return err
758758
}
759759

760760
return syncer.Sync(ctx, service.Name, pathsToCopy)
761761
}
762762

763-
// Syncs files from develop.watch.path if thy have been modified after the image has been created
763+
// Syncs files from develop.watch.path, ignoring bind-mounted and excluded paths.
764764
//
765765
//nolint:gocyclo
766-
func (s *composeService) initialSyncFiles(ctx context.Context, project *types.Project, service types.ServiceConfig, trigger types.Trigger, ignore watch.PathMatcher) ([]*sync.PathMapping, error) {
766+
func (s *composeService) initialSyncFiles(service types.ServiceConfig, trigger types.Trigger, ignore watch.PathMatcher) ([]*sync.PathMapping, error) {
767767
fi, err := os.Stat(trigger.Path)
768768
if err != nil {
769769
return nil, err
770770
}
771-
timeImageCreated, err := s.imageCreatedTime(ctx, project, service.Name)
772-
if err != nil {
773-
return nil, err
774-
}
775771
var pathsToCopy []*sync.PathMapping
776772
switch mode := fi.Mode(); {
777773
case mode.IsDir():
@@ -793,15 +789,7 @@ func (s *composeService) initialSyncFiles(ctx context.Context, project *types.Pr
793789
}
794790
return nil // skip file
795791
}
796-
info, err := d.Info()
797-
if err != nil {
798-
return err
799-
}
800792
if !d.IsDir() {
801-
if info.ModTime().Before(timeImageCreated) {
802-
// skip file if it was modified before image creation
803-
return nil
804-
}
805793
rel, err := filepath.Rel(trigger.Path, path)
806794
if err != nil {
807795
return err
@@ -816,7 +804,7 @@ func (s *composeService) initialSyncFiles(ctx context.Context, project *types.Pr
816804
})
817805
case mode.IsRegular():
818806
// process file
819-
if fi.ModTime().After(timeImageCreated) && !shouldIgnore(filepath.Base(trigger.Path), ignore) && !checkIfPathAlreadyBindMounted(trigger.Path, service.Volumes) {
807+
if !shouldIgnore(filepath.Base(trigger.Path), ignore) && !checkIfPathAlreadyBindMounted(trigger.Path, service.Volumes) {
820808
pathsToCopy = append(pathsToCopy, &sync.PathMapping{
821809
HostPath: trigger.Path,
822810
ContainerPath: trigger.Target,
@@ -832,27 +820,3 @@ func shouldIgnore(name string, ignore watch.PathMatcher) bool {
832820
return shouldIgnore
833821
}
834822

835-
// gets the image creation time for a service
836-
func (s *composeService) imageCreatedTime(ctx context.Context, project *types.Project, serviceName string) (time.Time, error) {
837-
res, err := s.apiClient().ContainerList(ctx, client.ContainerListOptions{
838-
All: true,
839-
Filters: projectFilter(project.Name).Add("label", serviceFilter(serviceName)),
840-
})
841-
if err != nil {
842-
return time.Now(), err
843-
}
844-
if len(res.Items) == 0 {
845-
return time.Now(), fmt.Errorf("could not get created time for service's image")
846-
}
847-
848-
img, err := s.apiClient().ImageInspect(ctx, res.Items[0].ImageID)
849-
if err != nil {
850-
return time.Now(), err
851-
}
852-
// Need to get the oldest one?
853-
timeCreated, err := time.Parse(time.RFC3339Nano, img.Created)
854-
if err != nil {
855-
return time.Now(), err
856-
}
857-
return timeCreated, nil
858-
}

pkg/compose/watch_test.go

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import (
1919
"context"
2020
"fmt"
2121
"os"
22+
"path/filepath"
2223
"slices"
2324
"testing"
2425
"time"
@@ -194,3 +195,57 @@ func (f *fakeSyncer) Sync(ctx context.Context, service string, paths []*sync.Pat
194195
f.synced <- paths
195196
return nil
196197
}
198+
199+
func TestInitialSyncFiles_DirectoryIncludesExistingFilesEvenIfOlderThanImage(t *testing.T) {
200+
dir := t.TempDir()
201+
hostDir := filepath.Join(dir, "src")
202+
assert.NilError(t, os.MkdirAll(hostDir, 0o755))
203+
hostFile := filepath.Join(hostDir, "test.txt")
204+
assert.NilError(t, os.WriteFile(hostFile, []byte("hello"), 0o644))
205+
206+
fileTime := time.Now().Add(-24 * time.Hour)
207+
assert.NilError(t, os.Chtimes(hostFile, fileTime, fileTime))
208+
209+
service := composeService{}
210+
trigger := types.Trigger{
211+
Path: hostDir,
212+
Action: types.WatchActionSync,
213+
Target: "/app/src",
214+
InitialSync: true,
215+
}
216+
217+
got, err := service.initialSyncFiles(types.ServiceConfig{Name: "test"}, trigger, watch.EmptyMatcher{})
218+
assert.NilError(t, err)
219+
220+
expected := []*sync.PathMapping{{
221+
HostPath: hostFile,
222+
ContainerPath: "/app/src/test.txt",
223+
}}
224+
assert.DeepEqual(t, expected, got)
225+
}
226+
227+
func TestInitialSyncFiles_FileIncludesExistingFileEvenIfOlderThanImage(t *testing.T) {
228+
dir := t.TempDir()
229+
hostFile := filepath.Join(dir, "test.txt")
230+
assert.NilError(t, os.WriteFile(hostFile, []byte("hello"), 0o644))
231+
232+
fileTime := time.Now().Add(-24 * time.Hour)
233+
assert.NilError(t, os.Chtimes(hostFile, fileTime, fileTime))
234+
235+
service := composeService{}
236+
trigger := types.Trigger{
237+
Path: hostFile,
238+
Action: types.WatchActionSync,
239+
Target: "/app/test.txt",
240+
InitialSync: true,
241+
}
242+
243+
got, err := service.initialSyncFiles(types.ServiceConfig{Name: "test"}, trigger, watch.EmptyMatcher{})
244+
assert.NilError(t, err)
245+
246+
expected := []*sync.PathMapping{{
247+
HostPath: hostFile,
248+
ContainerPath: "/app/test.txt",
249+
}}
250+
assert.DeepEqual(t, expected, got)
251+
}

0 commit comments

Comments
 (0)