mirror of
https://github.com/docker/compose.git
synced 2025-04-08 17:05:13 +02:00
feat(watch): Add --prune option to docker-compose watch command
Signed-off-by: Suleiman Dibirov <idsulik@gmail.com>
This commit is contained in:
parent
da434013e3
commit
9549a213ba
@ -32,7 +32,8 @@ import (
|
||||
|
||||
type watchOptions struct {
|
||||
*ProjectOptions
|
||||
noUp bool
|
||||
prune bool
|
||||
noUp bool
|
||||
}
|
||||
|
||||
func watchCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Service) *cobra.Command {
|
||||
@ -58,6 +59,7 @@ func watchCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Service)
|
||||
}
|
||||
|
||||
cmd.Flags().BoolVar(&buildOpts.quiet, "quiet", false, "hide build output")
|
||||
cmd.Flags().BoolVar(&watchOpts.prune, "prune", false, "Prune dangling images on rebuild")
|
||||
cmd.Flags().BoolVar(&watchOpts.noUp, "no-up", false, "Do not build & start services before watching")
|
||||
return cmd
|
||||
}
|
||||
@ -118,5 +120,6 @@ func runWatch(ctx context.Context, dockerCli command.Cli, backend api.Service, w
|
||||
return backend.Watch(ctx, project, services, api.WatchOptions{
|
||||
Build: &build,
|
||||
LogTo: consumer,
|
||||
Prune: watchOpts.prune,
|
||||
})
|
||||
}
|
||||
|
@ -121,6 +121,7 @@ const WatchLogger = "#watch"
|
||||
type WatchOptions struct {
|
||||
Build *BuildOptions
|
||||
LogTo LogConsumer
|
||||
Prune bool
|
||||
}
|
||||
|
||||
// BuildOptions group options of the Build API
|
||||
|
@ -34,6 +34,8 @@ import (
|
||||
"github.com/docker/compose/v2/pkg/watch"
|
||||
moby "github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/api/types/container"
|
||||
"github.com/docker/docker/api/types/filters"
|
||||
"github.com/docker/docker/api/types/image"
|
||||
"github.com/jonboulle/clockwork"
|
||||
"github.com/mitchellh/mapstructure"
|
||||
"github.com/sirupsen/logrus"
|
||||
@ -175,7 +177,11 @@ func (s *composeService) watch(ctx context.Context, syncChannel chan bool, proje
|
||||
}
|
||||
watching = true
|
||||
eg.Go(func() error {
|
||||
defer watcher.Close() //nolint:errcheck
|
||||
defer func() {
|
||||
if err := watcher.Close(); err != nil {
|
||||
logrus.Debugf("Error closing watcher for service %s: %v", service.Name, err)
|
||||
}
|
||||
}()
|
||||
return s.watchEvents(ctx, project, service.Name, options, watcher, syncer, config.Watch)
|
||||
})
|
||||
}
|
||||
@ -471,11 +477,17 @@ func (s *composeService) handleWatchBatch(ctx context.Context, project *types.Pr
|
||||
options.LogTo.Log(api.WatchLogger, fmt.Sprintf("Rebuilding service %q after changes were detected...", serviceName))
|
||||
// restrict the build to ONLY this service, not any of its dependencies
|
||||
options.Build.Services = []string{serviceName}
|
||||
_, err := s.build(ctx, project, *options.Build, nil)
|
||||
imageNameToIdMap, err := s.build(ctx, project, *options.Build, nil)
|
||||
|
||||
if err != nil {
|
||||
options.LogTo.Log(api.WatchLogger, fmt.Sprintf("Build failed. Error: %v", err))
|
||||
return err
|
||||
}
|
||||
|
||||
if options.Prune {
|
||||
s.pruneDanglingImagesOnRebuild(ctx, project.Name, imageNameToIdMap)
|
||||
}
|
||||
|
||||
options.LogTo.Log(api.WatchLogger, fmt.Sprintf("service %q successfully built", serviceName))
|
||||
|
||||
err = s.create(ctx, project, api.CreateOptions{
|
||||
@ -539,3 +551,26 @@ func writeWatchSyncMessage(log api.LogConsumer, serviceName string, pathMappings
|
||||
log.Log(api.WatchLogger, fmt.Sprintf("Syncing service %q after %d changes were detected", serviceName, len(pathMappings)))
|
||||
}
|
||||
}
|
||||
|
||||
func (s *composeService) pruneDanglingImagesOnRebuild(ctx context.Context, projectName string, imageNameToIdMap map[string]string) {
|
||||
images, err := s.apiClient().ImageList(ctx, image.ListOptions{
|
||||
Filters: filters.NewArgs(
|
||||
filters.Arg("dangling", "true"),
|
||||
filters.Arg("label", api.ProjectLabel+"="+projectName),
|
||||
),
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
logrus.Debugf("Failed to list images: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
for _, img := range images {
|
||||
if _, ok := imageNameToIdMap[img.ID]; !ok {
|
||||
_, err := s.apiClient().ImageRemove(ctx, img.ID, image.RemoveOptions{})
|
||||
if err != nil {
|
||||
logrus.Debugf("Failed to remove image %s: %v", img.ID, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -28,6 +28,8 @@ import (
|
||||
"github.com/docker/compose/v2/pkg/mocks"
|
||||
"github.com/docker/compose/v2/pkg/watch"
|
||||
moby "github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/api/types/filters"
|
||||
"github.com/docker/docker/api/types/image"
|
||||
"github.com/jonboulle/clockwork"
|
||||
"github.com/stretchr/testify/require"
|
||||
"go.uber.org/mock/gomock"
|
||||
@ -120,12 +122,26 @@ func TestWatch_Sync(t *testing.T) {
|
||||
apiClient.EXPECT().ContainerList(gomock.Any(), gomock.Any()).Return([]moby.Container{
|
||||
testContainer("test", "123", false),
|
||||
}, nil).AnyTimes()
|
||||
// we expect the image to be pruned
|
||||
apiClient.EXPECT().ImageList(gomock.Any(), image.ListOptions{
|
||||
Filters: filters.NewArgs(
|
||||
filters.Arg("dangling", "true"),
|
||||
filters.Arg("label", api.ProjectLabel+"=myProjectName"),
|
||||
),
|
||||
}).Return([]image.Summary{
|
||||
{ID: "123"},
|
||||
{ID: "456"},
|
||||
}, nil).Times(1)
|
||||
apiClient.EXPECT().ImageRemove(gomock.Any(), "123", image.RemoveOptions{}).Times(1)
|
||||
apiClient.EXPECT().ImageRemove(gomock.Any(), "456", image.RemoveOptions{}).Times(1)
|
||||
//
|
||||
cli.EXPECT().Client().Return(apiClient).AnyTimes()
|
||||
|
||||
ctx, cancelFunc := context.WithCancel(context.Background())
|
||||
t.Cleanup(cancelFunc)
|
||||
|
||||
proj := types.Project{
|
||||
Name: "myProjectName",
|
||||
Services: types.Services{
|
||||
"test": {
|
||||
Name: "test",
|
||||
@ -148,6 +164,7 @@ func TestWatch_Sync(t *testing.T) {
|
||||
err := service.watchEvents(ctx, &proj, "test", api.WatchOptions{
|
||||
Build: &api.BuildOptions{},
|
||||
LogTo: stdLogger{},
|
||||
Prune: true,
|
||||
}, watcher, syncer, []types.Trigger{
|
||||
{
|
||||
Path: "/sync",
|
||||
|
Loading…
x
Reference in New Issue
Block a user