diff --git a/pkg/compose/watch.go b/pkg/compose/watch.go index 71af227f3..1861ed44d 100644 --- a/pkg/compose/watch.go +++ b/pkg/compose/watch.go @@ -784,11 +784,19 @@ func (s *composeService) initialSync(ctx context.Context, service types.ServiceC if err != nil { return err } - // FIXME .dockerignore + + // the Dockerfile and compose files drive the build/orchestration, not the + // application: never copy them into the container on initial sync + dockerFileIgnore, err := watch.NewDockerPatternMatcher("/", []string{"Dockerfile", "*compose*.y*ml"}) + if err != nil { + return err + } + ignoreInitialSync := watch.NewCompositeMatcher( dockerIgnores, watch.EphemeralPathMatcher(), dotGitIgnore, + dockerFileIgnore, triggerIgnore) pathsToCopy, err := s.initialSyncFiles(service, trigger, ignoreInitialSync) diff --git a/pkg/compose/watch_test.go b/pkg/compose/watch_test.go index 04f8bf5a4..8fabbf9c1 100644 --- a/pkg/compose/watch_test.go +++ b/pkg/compose/watch_test.go @@ -241,6 +241,33 @@ func TestInitialSyncFilesRegularFile(t *testing.T) { }}) } +// initialSync's doc comment promises the Dockerfile and compose files are +// never copied into the container, but a refactor (ed10804e0) dropped the +// matcher enforcing it without replacement — neither the .dockerignore-derived +// matcher nor EphemeralPathMatcher cover this. +func TestInitialSync_ExcludesDockerfileAndComposeFiles(t *testing.T) { + hostDir := t.TempDir() + for _, name := range []string{"Dockerfile", "compose.yaml", "compose.override.yml", "app.go"} { + assert.NilError(t, os.WriteFile(filepath.Join(hostDir, name), []byte("content"), 0o600)) + } + + syncer := &fakeSyncer{synced: make(chan []*sync.PathMapping, 1)} + err := (&composeService{}).initialSync(t.Context(), types.ServiceConfig{ + Name: "svc", + Build: &types.BuildConfig{Context: hostDir}, + }, types.Trigger{ + Path: hostDir, + Target: "/app", + }, syncer) + assert.NilError(t, err) + + paths := <-syncer.synced + assert.DeepEqual(t, paths, []*sync.PathMapping{{ + HostPath: filepath.Join(hostDir, "app.go"), + ContainerPath: "/app/app.go", + }}) +} + // TestPruneDanglingImagesOnRebuild verifies the post-rebuild prune only // removes superseded dangling images: a dangling image whose ID matches one // of the freshly built images must be spared. The lookup used to probe the