|
|
@@ -18,10 +18,14 @@ import (
|
|
|
"context"
|
|
|
"fmt"
|
|
|
"log"
|
|
|
+ "strings"
|
|
|
+ "time"
|
|
|
|
|
|
"github.com/compose-spec/compose-go/types"
|
|
|
"github.com/docker/compose/v2/pkg/api"
|
|
|
+ "github.com/docker/compose/v2/pkg/utils"
|
|
|
"github.com/fsnotify/fsnotify"
|
|
|
+ "github.com/jonboulle/clockwork"
|
|
|
"github.com/mitchellh/mapstructure"
|
|
|
"github.com/pkg/errors"
|
|
|
"golang.org/x/sync/errgroup"
|
|
|
@@ -30,10 +34,47 @@ import (
|
|
|
type DevelopmentConfig struct {
|
|
|
}
|
|
|
|
|
|
+const quietPeriod = 2 * time.Second
|
|
|
+
|
|
|
func (s *composeService) Watch(ctx context.Context, project *types.Project, services []string, options api.WatchOptions) error {
|
|
|
fmt.Fprintln(s.stderr(), "not implemented yet")
|
|
|
|
|
|
eg, ctx := errgroup.WithContext(ctx)
|
|
|
+ needRefresh := make(chan string)
|
|
|
+ eg.Go(func() error {
|
|
|
+ clock := clockwork.NewRealClock()
|
|
|
+ debounce(ctx, clock, quietPeriod, needRefresh, func(services []string) {
|
|
|
+ fmt.Fprintf(s.stderr(), "Updating %s after changes were detected\n", strings.Join(services, ", "))
|
|
|
+ imageIds, err := s.build(ctx, project, api.BuildOptions{
|
|
|
+ Services: services,
|
|
|
+ })
|
|
|
+ if err != nil {
|
|
|
+ fmt.Fprintf(s.stderr(), "Build failed")
|
|
|
+ }
|
|
|
+ for i, service := range project.Services {
|
|
|
+ if id, ok := imageIds[service.Name]; ok {
|
|
|
+ service.Image = id
|
|
|
+ }
|
|
|
+ project.Services[i] = service
|
|
|
+ }
|
|
|
+
|
|
|
+ err = s.Up(ctx, project, api.UpOptions{
|
|
|
+ Create: api.CreateOptions{
|
|
|
+ Services: services,
|
|
|
+ Inherit: true,
|
|
|
+ },
|
|
|
+ Start: api.StartOptions{
|
|
|
+ Services: services,
|
|
|
+ Project: project,
|
|
|
+ },
|
|
|
+ })
|
|
|
+ if err != nil {
|
|
|
+ fmt.Fprintf(s.stderr(), "Application failed to start after update")
|
|
|
+ }
|
|
|
+ })
|
|
|
+ return nil
|
|
|
+ })
|
|
|
+
|
|
|
err := project.WithServices(services, func(service types.ServiceConfig) error {
|
|
|
var config DevelopmentConfig
|
|
|
if y, ok := service.Extensions["x-develop"]; ok {
|
|
|
@@ -64,6 +105,7 @@ func (s *composeService) Watch(ctx context.Context, project *types.Project, serv
|
|
|
return nil
|
|
|
case event := <-watcher.Events:
|
|
|
log.Println("fs event :", event.String())
|
|
|
+ needRefresh <- service.Name
|
|
|
case err := <-watcher.Errors:
|
|
|
return err
|
|
|
}
|
|
|
@@ -77,3 +119,23 @@ func (s *composeService) Watch(ctx context.Context, project *types.Project, serv
|
|
|
|
|
|
return eg.Wait()
|
|
|
}
|
|
|
+
|
|
|
+func debounce(ctx context.Context, clock clockwork.Clock, delay time.Duration, input chan string, fn func(services []string)) {
|
|
|
+ services := utils.Set[string]{}
|
|
|
+ t := clock.AfterFunc(delay, func() {
|
|
|
+ if len(services) > 0 {
|
|
|
+ refresh := services.Elements()
|
|
|
+ services.Clear()
|
|
|
+ fn(refresh)
|
|
|
+ }
|
|
|
+ })
|
|
|
+ for {
|
|
|
+ select {
|
|
|
+ case <-ctx.Done():
|
|
|
+ return
|
|
|
+ case service := <-input:
|
|
|
+ t.Reset(delay)
|
|
|
+ services.Add(service)
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|