浏览代码

introduce experimental watch command (skeletton)

Signed-off-by: Nicolas De Loof <[email protected]>
Nicolas De Loof 2 年之前
父节点
当前提交
bb9cf32245

+ 31 - 0
cmd/compose/alpha.go

@@ -0,0 +1,31 @@
+/*
+
+   Copyright 2020 Docker Compose CLI authors
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+       http://www.apache.org/licenses/LICENSE-2.0
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+*/
+
+package compose
+
+import (
+	"github.com/docker/compose/v2/pkg/api"
+	"github.com/spf13/cobra"
+)
+
+// alphaCommand groups all experimental subcommands
+func alphaCommand(p *ProjectOptions, backend api.Service) *cobra.Command {
+	cmd := &cobra.Command{
+		Short:  "Experimental commands",
+		Use:    "alpha [COMMAND]",
+		Hidden: true,
+	}
+	cmd.AddCommand(watchCommand(p, backend))
+	return cmd
+}

+ 2 - 0
cmd/compose/compose.go

@@ -365,7 +365,9 @@ func RootCommand(streams api.Streams, backend api.Service) *cobra.Command { //no
 		pullCommand(&opts, backend),
 		pullCommand(&opts, backend),
 		createCommand(&opts, backend),
 		createCommand(&opts, backend),
 		copyCommand(&opts, backend),
 		copyCommand(&opts, backend),
+		alphaCommand(&opts, backend),
 	)
 	)
+
 	c.Flags().SetInterspersed(false)
 	c.Flags().SetInterspersed(false)
 	opts.addProjectFlags(c.Flags())
 	opts.addProjectFlags(c.Flags())
 	c.RegisterFlagCompletionFunc( //nolint:errcheck
 	c.RegisterFlagCompletionFunc( //nolint:errcheck

+ 61 - 0
cmd/compose/watch.go

@@ -0,0 +1,61 @@
+/*
+   Copyright 2020 Docker Compose CLI authors
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+*/
+
+package compose
+
+import (
+	"context"
+	"fmt"
+	"os"
+
+	"github.com/docker/compose/v2/pkg/api"
+	"github.com/spf13/cobra"
+)
+
+type watchOptions struct {
+	*ProjectOptions
+	quiet bool
+}
+
+func watchCommand(p *ProjectOptions, backend api.Service) *cobra.Command {
+	opts := watchOptions{
+		ProjectOptions: p,
+	}
+	cmd := &cobra.Command{
+		Use:   "watch [SERVICE...]",
+		Short: "EXPERIMENTAL - Watch build context for service and rebuild/refresh containers when files are updated",
+		PreRunE: Adapt(func(ctx context.Context, args []string) error {
+			return nil
+		}),
+		RunE: Adapt(func(ctx context.Context, args []string) error {
+			return runWatch(ctx, backend, opts, args)
+		}),
+		ValidArgsFunction: completeServiceNames(p),
+	}
+
+	cmd.Flags().BoolVar(&opts.quiet, "quiet", false, "hide build output")
+	return cmd
+}
+
+func runWatch(ctx context.Context, backend api.Service, opts watchOptions, services []string) error {
+	fmt.Fprintln(os.Stderr, "watch command is EXPERIMENTAL")
+	project, err := opts.ToProject(nil)
+	if err != nil {
+		return err
+	}
+
+	return backend.Watch(ctx, project, services, api.WatchOptions{})
+}

+ 1 - 0
docs/reference/compose.md

@@ -7,6 +7,7 @@ Docker Compose
 
 
 | Name                            | Description                                                             |
 | Name                            | Description                                                             |
 |:--------------------------------|:------------------------------------------------------------------------|
 |:--------------------------------|:------------------------------------------------------------------------|
+| [`alpha`](compose_alpha.md)     | Experimental commands                                                   |
 | [`build`](compose_build.md)     | Build or rebuild services                                               |
 | [`build`](compose_build.md)     | Build or rebuild services                                               |
 | [`convert`](compose_convert.md) | Converts the compose file to platform's canonical format                |
 | [`convert`](compose_convert.md) | Converts the compose file to platform's canonical format                |
 | [`cp`](compose_cp.md)           | Copy files/folders between a service container and the local filesystem |
 | [`cp`](compose_cp.md)           | Copy files/folders between a service container and the local filesystem |

+ 15 - 0
docs/reference/compose_alpha.md

@@ -0,0 +1,15 @@
+# docker compose alpha
+
+<!---MARKER_GEN_START-->
+Experimental commands
+
+### Subcommands
+
+| Name                              | Description                                                                                          |
+|:----------------------------------|:-----------------------------------------------------------------------------------------------------|
+| [`watch`](compose_alpha_watch.md) | EXPERIMENTAL - Watch build context for service and rebuild/refresh containers when files are updated |
+
+
+
+<!---MARKER_GEN_END-->
+

+ 14 - 0
docs/reference/compose_alpha_watch.md

@@ -0,0 +1,14 @@
+# docker compose alpha watch
+
+<!---MARKER_GEN_START-->
+EXPERIMENTAL - Watch build context for service and rebuild/refresh containers when files are updated
+
+### Options
+
+| Name      | Type | Default | Description       |
+|:----------|:-----|:--------|:------------------|
+| `--quiet` |      |         | hide build output |
+
+
+<!---MARKER_GEN_END-->
+

+ 15 - 0
docs/reference/docker_compose_alpha.yaml

@@ -0,0 +1,15 @@
+command: docker compose alpha
+short: Experimental commands
+long: Experimental commands
+pname: docker compose
+plink: docker_compose.yaml
+cname:
+    - docker compose alpha watch
+clink:
+    - docker_compose_alpha_watch.yaml
+deprecated: false
+experimental: false
+experimentalcli: false
+kubernetes: false
+swarm: false
+

+ 25 - 0
docs/reference/docker_compose_alpha_watch.yaml

@@ -0,0 +1,25 @@
+command: docker compose alpha watch
+short: |
+    EXPERIMENTAL - Watch build context for service and rebuild/refresh containers when files are updated
+long: |
+    EXPERIMENTAL - Watch build context for service and rebuild/refresh containers when files are updated
+usage: docker compose alpha watch [SERVICE...]
+pname: docker compose alpha
+plink: docker_compose_alpha.yaml
+options:
+    - option: quiet
+      value_type: bool
+      default_value: "false"
+      description: hide build output
+      deprecated: false
+      hidden: false
+      experimental: false
+      experimentalcli: false
+      kubernetes: false
+      swarm: false
+deprecated: false
+experimental: false
+experimentalcli: false
+kubernetes: false
+swarm: false
+

+ 15 - 18
go.mod

@@ -8,6 +8,7 @@ require (
 	github.com/compose-spec/compose-go v1.8.2
 	github.com/compose-spec/compose-go v1.8.2
 	github.com/containerd/console v1.0.3
 	github.com/containerd/console v1.0.3
 	github.com/containerd/containerd v1.6.15
 	github.com/containerd/containerd v1.6.15
+	github.com/cucumber/godog v0.0.0-00010101000000-000000000000
 	github.com/distribution/distribution/v3 v3.0.0-20221201083218-92d136e113cf
 	github.com/distribution/distribution/v3 v3.0.0-20221201083218-92d136e113cf
 	github.com/docker/buildx v0.9.1 // when updating, also update the replace rules accordingly
 	github.com/docker/buildx v0.9.1 // when updating, also update the replace rules accordingly
 	github.com/docker/cli v20.10.20+incompatible // replaced; see replace rule for actual version
 	github.com/docker/cli v20.10.20+incompatible // replaced; see replace rule for actual version
@@ -15,11 +16,12 @@ require (
 	github.com/docker/docker v20.10.20+incompatible // replaced; see replace rule for actual version
 	github.com/docker/docker v20.10.20+incompatible // replaced; see replace rule for actual version
 	github.com/docker/go-connections v0.4.0
 	github.com/docker/go-connections v0.4.0
 	github.com/docker/go-units v0.5.0
 	github.com/docker/go-units v0.5.0
+	github.com/fsnotify/fsnotify v1.6.0
 	github.com/golang/mock v1.6.0
 	github.com/golang/mock v1.6.0
 	github.com/hashicorp/go-multierror v1.1.1
 	github.com/hashicorp/go-multierror v1.1.1
 	github.com/hashicorp/go-version v1.6.0
 	github.com/hashicorp/go-version v1.6.0
-	github.com/mattn/go-isatty v0.0.16 // indirect
 	github.com/mattn/go-shellwords v1.0.12
 	github.com/mattn/go-shellwords v1.0.12
+	github.com/mitchellh/mapstructure v1.5.0
 	github.com/moby/buildkit v0.10.4 // replaced; see replace rule for actual version
 	github.com/moby/buildkit v0.10.4 // replaced; see replace rule for actual version
 	github.com/moby/term v0.0.0-20221128092401-c43b287e0e0f
 	github.com/moby/term v0.0.0-20221128092401-c43b287e0e0f
 	github.com/morikuni/aec v1.0.0
 	github.com/morikuni/aec v1.0.0
@@ -42,11 +44,15 @@ require (
 	github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect
 	github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect
 	github.com/Microsoft/go-winio v0.5.2 // indirect
 	github.com/Microsoft/go-winio v0.5.2 // indirect
 	github.com/beorn7/perks v1.0.1 // indirect
 	github.com/beorn7/perks v1.0.1 // indirect
+	github.com/bugsnag/bugsnag-go v1.5.0 // indirect
 	github.com/cenkalti/backoff/v4 v4.1.2 // indirect
 	github.com/cenkalti/backoff/v4 v4.1.2 // indirect
 	github.com/cespare/xxhash/v2 v2.1.2 // indirect
 	github.com/cespare/xxhash/v2 v2.1.2 // indirect
+	github.com/cloudflare/cfssl v1.4.1 // indirect
 	github.com/containerd/continuity v0.3.0 // indirect
 	github.com/containerd/continuity v0.3.0 // indirect
 	github.com/containerd/ttrpc v1.1.0 // indirect
 	github.com/containerd/ttrpc v1.1.0 // indirect
 	github.com/containerd/typeurl v1.0.2 // indirect
 	github.com/containerd/typeurl v1.0.2 // indirect
+	github.com/cucumber/gherkin-go/v19 v19.0.3 // indirect
+	github.com/cucumber/messages-go/v16 v16.0.1 // indirect
 	github.com/davecgh/go-spew v1.1.1 // indirect
 	github.com/davecgh/go-spew v1.1.1 // indirect
 	github.com/docker/distribution v2.8.1+incompatible // indirect
 	github.com/docker/distribution v2.8.1+incompatible // indirect
 	github.com/docker/docker-credential-helpers v0.7.0 // indirect
 	github.com/docker/docker-credential-helpers v0.7.0 // indirect
@@ -57,6 +63,7 @@ require (
 	github.com/go-logr/logr v1.2.3 // indirect
 	github.com/go-logr/logr v1.2.3 // indirect
 	github.com/go-logr/stdr v1.2.2 // indirect
 	github.com/go-logr/stdr v1.2.2 // indirect
 	github.com/gofrs/flock v0.8.0 // indirect
 	github.com/gofrs/flock v0.8.0 // indirect
+	github.com/gofrs/uuid v4.2.0+incompatible // indirect
 	github.com/gogo/googleapis v1.4.1 // indirect
 	github.com/gogo/googleapis v1.4.1 // indirect
 	github.com/gogo/protobuf v1.3.2 // indirect
 	github.com/gogo/protobuf v1.3.2 // indirect
 	github.com/golang/protobuf v1.5.2 // indirect
 	github.com/golang/protobuf v1.5.2 // indirect
@@ -68,17 +75,21 @@ require (
 	github.com/grpc-ecosystem/go-grpc-middleware v1.3.0 // indirect
 	github.com/grpc-ecosystem/go-grpc-middleware v1.3.0 // indirect
 	github.com/grpc-ecosystem/grpc-gateway v1.16.0 // indirect
 	github.com/grpc-ecosystem/grpc-gateway v1.16.0 // indirect
 	github.com/hashicorp/errwrap v1.1.0 // indirect
 	github.com/hashicorp/errwrap v1.1.0 // indirect
+	github.com/hashicorp/go-immutable-radix v1.3.1 // indirect
+	github.com/hashicorp/go-memdb v1.3.2 // indirect
+	github.com/hashicorp/golang-lru v0.5.4 // indirect
 	github.com/imdario/mergo v0.3.13 // indirect
 	github.com/imdario/mergo v0.3.13 // indirect
 	github.com/inconshreveable/mousetrap v1.0.1 // indirect
 	github.com/inconshreveable/mousetrap v1.0.1 // indirect
+	github.com/jinzhu/gorm v1.9.11 // indirect
 	github.com/json-iterator/go v1.1.12 // indirect
 	github.com/json-iterator/go v1.1.12 // indirect
 	github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect
 	github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect
 	github.com/klauspost/compress v1.15.9 // indirect
 	github.com/klauspost/compress v1.15.9 // indirect
 	github.com/mattn/go-colorable v0.1.12 // indirect
 	github.com/mattn/go-colorable v0.1.12 // indirect
+	github.com/mattn/go-isatty v0.0.16 // indirect
 	github.com/mattn/go-runewidth v0.0.14 // indirect
 	github.com/mattn/go-runewidth v0.0.14 // indirect
 	github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect
 	github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect
 	github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b // indirect
 	github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b // indirect
 	github.com/miekg/pkcs11 v1.1.1 // indirect
 	github.com/miekg/pkcs11 v1.1.1 // indirect
-	github.com/mitchellh/mapstructure v1.5.0 // indirect
 	github.com/moby/locker v1.0.1 // indirect
 	github.com/moby/locker v1.0.1 // indirect
 	github.com/moby/patternmatcher v0.5.0 // indirect
 	github.com/moby/patternmatcher v0.5.0 // indirect
 	github.com/moby/spdystream v0.2.0 // indirect
 	github.com/moby/spdystream v0.2.0 // indirect
@@ -96,6 +107,7 @@ require (
 	github.com/prometheus/procfs v0.7.3 // indirect
 	github.com/prometheus/procfs v0.7.3 // indirect
 	github.com/rivo/uniseg v0.2.0 // indirect
 	github.com/rivo/uniseg v0.2.0 // indirect
 	github.com/serialx/hashring v0.0.0-20190422032157-8b2912629002 // indirect
 	github.com/serialx/hashring v0.0.0-20190422032157-8b2912629002 // indirect
+	github.com/spf13/viper v1.4.0 // indirect
 	github.com/tonistiigi/fsutil v0.0.0-20220930225714-4638ad635be5 // indirect
 	github.com/tonistiigi/fsutil v0.0.0-20220930225714-4638ad635be5 // indirect
 	github.com/tonistiigi/units v0.0.0-20180711220420-6950e57a87ea // indirect
 	github.com/tonistiigi/units v0.0.0-20180711220420-6950e57a87ea // indirect
 	github.com/tonistiigi/vt100 v0.0.0-20210615222946-8066bb97264f // indirect
 	github.com/tonistiigi/vt100 v0.0.0-20210615222946-8066bb97264f // indirect
@@ -119,7 +131,7 @@ require (
 	golang.org/x/crypto v0.0.0-20220511200225-c6db032c6c88 // indirect
 	golang.org/x/crypto v0.0.0-20220511200225-c6db032c6c88 // indirect
 	golang.org/x/net v0.4.0 // indirect
 	golang.org/x/net v0.4.0 // indirect
 	golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8 // indirect
 	golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8 // indirect
-	golang.org/x/sys v0.3.0 // indirect
+	golang.org/x/sys v0.4.0 // indirect
 	golang.org/x/term v0.3.0 // indirect
 	golang.org/x/term v0.3.0 // indirect
 	golang.org/x/text v0.6.0 // indirect
 	golang.org/x/text v0.6.0 // indirect
 	golang.org/x/time v0.0.0-20220210224613-90d013bbcef8 // indirect
 	golang.org/x/time v0.0.0-20220210224613-90d013bbcef8 // indirect
@@ -137,21 +149,6 @@ require (
 	sigs.k8s.io/yaml v1.2.0 // indirect
 	sigs.k8s.io/yaml v1.2.0 // indirect
 )
 )
 
 
-require github.com/cucumber/godog v0.0.0-00010101000000-000000000000
-
-require (
-	github.com/bugsnag/bugsnag-go v1.5.0 // indirect
-	github.com/cloudflare/cfssl v1.4.1 // indirect
-	github.com/cucumber/gherkin-go/v19 v19.0.3 // indirect
-	github.com/cucumber/messages-go/v16 v16.0.1 // indirect
-	github.com/gofrs/uuid v4.2.0+incompatible // indirect
-	github.com/hashicorp/go-immutable-radix v1.3.1 // indirect
-	github.com/hashicorp/go-memdb v1.3.2 // indirect
-	github.com/hashicorp/golang-lru v0.5.4 // indirect
-	github.com/jinzhu/gorm v1.9.11 // indirect
-	github.com/spf13/viper v1.4.0 // indirect
-)
-
 replace (
 replace (
 	// Override for e2e tests
 	// Override for e2e tests
 	github.com/cucumber/godog => github.com/laurazard/godog v0.0.0-20220922095256-4c4b17abdae7
 	github.com/cucumber/godog => github.com/laurazard/godog v0.0.0-20220922095256-4c4b17abdae7

+ 5 - 3
go.sum

@@ -206,8 +206,9 @@ github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoD
 github.com/form3tech-oss/jwt-go v3.2.3+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k=
 github.com/form3tech-oss/jwt-go v3.2.3+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k=
 github.com/frankban/quicktest v1.11.3/go.mod h1:wRf/ReqHper53s+kmmSZizM8NamnL3IM0I9ntUbOk+k=
 github.com/frankban/quicktest v1.11.3/go.mod h1:wRf/ReqHper53s+kmmSZizM8NamnL3IM0I9ntUbOk+k=
 github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
 github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
-github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
 github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
 github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
+github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY=
+github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw=
 github.com/fvbommel/sortorder v1.0.2 h1:mV4o8B2hKboCdkJm+a7uX/SIpZob4JzUpc5GGnM45eo=
 github.com/fvbommel/sortorder v1.0.2 h1:mV4o8B2hKboCdkJm+a7uX/SIpZob4JzUpc5GGnM45eo=
 github.com/fvbommel/sortorder v1.0.2/go.mod h1:uk88iVf1ovNn1iLfgUVU2F9o5eO30ui720w+kxuqRs0=
 github.com/fvbommel/sortorder v1.0.2/go.mod h1:uk88iVf1ovNn1iLfgUVU2F9o5eO30ui720w+kxuqRs0=
 github.com/getsentry/raven-go v0.0.0-20180121060056-563b81fc02b7/go.mod h1:KungGk8q33+aIAZUIVWZDr2OfAEBsO49PX4NzFV5kcQ=
 github.com/getsentry/raven-go v0.0.0-20180121060056-563b81fc02b7/go.mod h1:KungGk8q33+aIAZUIVWZDr2OfAEBsO49PX4NzFV5kcQ=
@@ -903,8 +904,9 @@ golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBc
 golang.org/x/sys v0.0.0-20220422013727-9388b58f7150/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20220422013727-9388b58f7150/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.3.0 h1:w8ZOecv6NaNa/zC8944JTU3vz4u6Lagfk4RPQxv92NQ=
-golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.4.0 h1:Zr2JFtRQNX3BCZ8YtxRE9hNJYC8J6I1MVbMg6owUp18=
+golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
 golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
 golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
 golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
 golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
 golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=

+ 6 - 0
pkg/api/api.go

@@ -77,6 +77,12 @@ type Service interface {
 	Images(ctx context.Context, projectName string, options ImagesOptions) ([]ImageSummary, error)
 	Images(ctx context.Context, projectName string, options ImagesOptions) ([]ImageSummary, error)
 	// MaxConcurrency defines upper limit for concurrent operations against engine API
 	// MaxConcurrency defines upper limit for concurrent operations against engine API
 	MaxConcurrency(parallel int)
 	MaxConcurrency(parallel int)
+	// Watch services' development context and sync/notify/rebuild/restart on changes
+	Watch(ctx context.Context, project *types.Project, services []string, options WatchOptions) error
+}
+
+// WatchOptions group options of the Watch API
+type WatchOptions struct {
 }
 }
 
 
 // BuildOptions group options of the Build API
 // BuildOptions group options of the Build API

+ 10 - 0
pkg/api/proxy.go

@@ -50,6 +50,7 @@ type ServiceProxy struct {
 	EventsFn             func(ctx context.Context, project string, options EventsOptions) error
 	EventsFn             func(ctx context.Context, project string, options EventsOptions) error
 	PortFn               func(ctx context.Context, project string, service string, port uint16, options PortOptions) (string, int, error)
 	PortFn               func(ctx context.Context, project string, service string, port uint16, options PortOptions) (string, int, error)
 	ImagesFn             func(ctx context.Context, projectName string, options ImagesOptions) ([]ImageSummary, error)
 	ImagesFn             func(ctx context.Context, projectName string, options ImagesOptions) ([]ImageSummary, error)
+	WatchFn              func(ctx context.Context, project *types.Project, services []string, options WatchOptions) error
 	MaxConcurrencyFn     func(parallel int)
 	MaxConcurrencyFn     func(parallel int)
 	interceptors         []Interceptor
 	interceptors         []Interceptor
 }
 }
@@ -88,6 +89,7 @@ func (s *ServiceProxy) WithService(service Service) *ServiceProxy {
 	s.EventsFn = service.Events
 	s.EventsFn = service.Events
 	s.PortFn = service.Port
 	s.PortFn = service.Port
 	s.ImagesFn = service.Images
 	s.ImagesFn = service.Images
+	s.WatchFn = service.Watch
 	s.MaxConcurrencyFn = service.MaxConcurrency
 	s.MaxConcurrencyFn = service.MaxConcurrency
 	return s
 	return s
 }
 }
@@ -311,6 +313,14 @@ func (s *ServiceProxy) Images(ctx context.Context, project string, options Image
 	return s.ImagesFn(ctx, project, options)
 	return s.ImagesFn(ctx, project, options)
 }
 }
 
 
+// Watch implements Service interface
+func (s *ServiceProxy) Watch(ctx context.Context, project *types.Project, services []string, options WatchOptions) error {
+	if s.WatchFn == nil {
+		return ErrNotImplemented
+	}
+	return s.WatchFn(ctx, project, services, options)
+}
+
 func (s *ServiceProxy) MaxConcurrency(i int) {
 func (s *ServiceProxy) MaxConcurrency(i int) {
 	s.MaxConcurrencyFn(i)
 	s.MaxConcurrencyFn(i)
 }
 }

+ 79 - 0
pkg/compose/watch.go

@@ -0,0 +1,79 @@
+/*
+
+   Copyright 2020 Docker Compose CLI authors
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+       http://www.apache.org/licenses/LICENSE-2.0
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+*/
+
+package compose
+
+import (
+	"context"
+	"fmt"
+	"log"
+
+	"github.com/compose-spec/compose-go/types"
+	"github.com/docker/compose/v2/pkg/api"
+	"github.com/fsnotify/fsnotify"
+	"github.com/mitchellh/mapstructure"
+	"github.com/pkg/errors"
+	"golang.org/x/sync/errgroup"
+)
+
+type DevelopmentConfig struct {
+}
+
+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)
+	err := project.WithServices(services, func(service types.ServiceConfig) error {
+		var config DevelopmentConfig
+		if y, ok := service.Extensions["x-develop"]; ok {
+			err := mapstructure.Decode(y, &config)
+			if err != nil {
+				return err
+			}
+		}
+		if service.Build == nil {
+			return errors.New("can't watch a service without a build section")
+		}
+		context := service.Build.Context
+
+		watcher, err := fsnotify.NewWatcher()
+		if err != nil {
+			return err
+		}
+		fmt.Println("watching " + context)
+		err = watcher.Add(context)
+		if err != nil {
+			return err
+		}
+		eg.Go(func() error {
+			defer watcher.Close() //nolint:errcheck
+			for {
+				select {
+				case <-ctx.Done():
+					return nil
+				case event := <-watcher.Events:
+					log.Println("fs event :", event.String())
+				case err := <-watcher.Errors:
+					return err
+				}
+			}
+		})
+		return nil
+	})
+	if err != nil {
+		return err
+	}
+
+	return eg.Wait()
+}

+ 14 - 0
pkg/mocks/mock_docker_compose_api.go

@@ -393,6 +393,20 @@ func (mr *MockServiceMockRecorder) Up(ctx, project, options interface{}) *gomock
 	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Up", reflect.TypeOf((*MockService)(nil).Up), ctx, project, options)
 	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Up", reflect.TypeOf((*MockService)(nil).Up), ctx, project, options)
 }
 }
 
 
+// Watch mocks base method.
+func (m *MockService) Watch(ctx context.Context, project *types.Project, services []string, options api.WatchOptions) error {
+	m.ctrl.T.Helper()
+	ret := m.ctrl.Call(m, "Watch", ctx, project, services, options)
+	ret0, _ := ret[0].(error)
+	return ret0
+}
+
+// Watch indicates an expected call of Watch.
+func (mr *MockServiceMockRecorder) Watch(ctx, project, services, options interface{}) *gomock.Call {
+	mr.mock.ctrl.T.Helper()
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Watch", reflect.TypeOf((*MockService)(nil).Watch), ctx, project, services, options)
+}
+
 // MockLogConsumer is a mock of LogConsumer interface.
 // MockLogConsumer is a mock of LogConsumer interface.
 type MockLogConsumer struct {
 type MockLogConsumer struct {
 	ctrl     *gomock.Controller
 	ctrl     *gomock.Controller