Skip to content

Commit

Permalink
Handle * when resolving paths in watch path
Browse files Browse the repository at this point in the history
Signed-off-by: Joana Hrotkó <[email protected]>
  • Loading branch information
jhrotko committed Feb 17, 2025
1 parent 4aacd02 commit b0bb892
Show file tree
Hide file tree
Showing 8 changed files with 131 additions and 51 deletions.
125 changes: 80 additions & 45 deletions loader/loader_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3097,7 +3097,8 @@ services:
}

func TestLoadDevelopConfig(t *testing.T) {
project, err := Load(buildConfigDetails(`
t.Run("successfully load watch config", func(t *testing.T) {
project, err := Load(buildConfigDetails(`
name: load-develop
services:
frontend:
Expand All @@ -3112,7 +3113,6 @@ services:
target: /var/www
ignore:
- node_modules/
backend:
image: example/backend
build: ./backend
Expand All @@ -3123,7 +3123,6 @@ services:
path:
- ./backend/src
- ./backend
proxy:
image: example/proxy
build: ./proxy
Expand All @@ -3134,67 +3133,103 @@ services:
action: sync+restart
target: /etc/nginx/proxy.conf
`, nil), func(options *Options) {
options.ResolvePaths = false
options.SkipValidation = true
})
assert.NilError(t, err)
frontend, err := project.GetService("frontend")
assert.NilError(t, err)
assert.DeepEqual(t, *frontend.Develop, types.DevelopConfig{
Watch: []types.Trigger{
{
Path: []string{"./webapp/html"},
Action: types.WatchActionSync,
Target: "/var/www",
Ignore: []string{"node_modules/"},
Extensions: types.Extensions{
"x-initialSync": true,
options.ResolvePaths = false
options.SkipValidation = true
})
assert.NilError(t, err)
frontend, err := project.GetService("frontend")
assert.NilError(t, err)
assert.DeepEqual(t, *frontend.Develop, types.DevelopConfig{
Watch: []types.Trigger{
{
Path: []string{"./webapp/html"},
Action: types.WatchActionSync,
Target: "/var/www",
Ignore: []string{"node_modules/"},
Extensions: types.Extensions{
"x-initialSync": true,
},
},
},
},
})
backend, err := project.GetService("backend")
assert.NilError(t, err)
assert.DeepEqual(t, *backend.Develop, types.DevelopConfig{
Watch: []types.Trigger{
{
Path: []string{"./backend/src", "./backend"},
Action: types.WatchActionRebuild,
})
backend, err := project.GetService("backend")
assert.NilError(t, err)
assert.DeepEqual(t, *backend.Develop, types.DevelopConfig{
Watch: []types.Trigger{
{
Path: []string{"./backend/src", "./backend"},
Action: types.WatchActionRebuild,
},
},
},
})
proxy, err := project.GetService("proxy")
assert.NilError(t, err)
assert.DeepEqual(t, *proxy.Develop, types.DevelopConfig{
Watch: []types.Trigger{
{
Path: []string{"./proxy/proxy.conf"},
Action: types.WatchActionSyncRestart,
Target: "/etc/nginx/proxy.conf",
})
proxy, err := project.GetService("proxy")
assert.NilError(t, err)
assert.DeepEqual(t, *proxy.Develop, types.DevelopConfig{
Watch: []types.Trigger{
{
Path: []string{"./proxy/proxy.conf"},
Action: types.WatchActionSyncRestart,
Target: "/etc/nginx/proxy.conf",
},
},
},
})
})
}

func TestBadDevelopConfig(t *testing.T) {
_, err := LoadWithContext(context.TODO(), buildConfigDetails(`
t.Run("should not load successfully bad watch config", func(t *testing.T) {
_, err := LoadWithContext(context.TODO(), buildConfigDetails(`
name: load-develop
services:
frontend:
image: example/webapp
build: ./webapp
develop:
watch:
# sync static content
# sync static content
- path: ./webapp/html
target: /var/www
ignore:
- node_modules/
`, nil), func(options *Options) {
options.ResolvePaths = false
options.ResolvePaths = false
})
assert.ErrorContains(t, err, "validating filename0.yml: services.frontend.develop.watch.0 action is required")
})

t.Run("should return an error when cannot resolve path", func(t *testing.T) {
b, err := os.ReadFile("testdata/watch/compose-test-watch-star.yaml")
assert.NilError(t, err)

configDetails := types.ConfigDetails{
WorkingDir: "testdata",
ConfigFiles: []types.ConfigFile{
{Filename: "watch/compose-test-watch-star.yaml", Content: b},
},
Environment: map[string]string{},
}
expServices := types.Services{
"app": {
Name: "app",
Image: "example/app",
Environment: types.MappingWithEquals{},
Networks: map[string]*types.ServiceNetworkConfig{"default": nil},
Develop: &types.DevelopConfig{
Watch: []types.Trigger{
{
Path: []string{
filepath.FromSlash("testdata/watch/other.txt"),
filepath.FromSlash("testdata/watch/some-text.txt"),
},
Action: types.WatchActionRebuild,
},
},
},
},
}

actual, err := Load(configDetails)
assert.NilError(t, err)
assert.DeepEqual(t, actual.Services, expServices)
})
assert.ErrorContains(t, err, "validating filename0.yml: services.frontend.develop.watch.0 action is required")
}

func TestBadServiceConfig(t *testing.T) {
Expand Down
11 changes: 11 additions & 0 deletions loader/testdata/watch/compose-test-watch-star.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
name: compose-test-watch-star
services:
app:
image: example/app
develop:
watch:
- path: ./watch/*.txt
action: rebuild
# - path: ./watch/*
# target: ./app
# action: sync
Empty file added loader/testdata/watch/other.txt
Empty file.
Empty file.
2 changes: 1 addition & 1 deletion loader/validate.go
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,7 @@ func checkConsistency(project *types.Project) error {

}
if len(watch.Path) > 1 {
return fmt.Errorf("services.%s.develop.watch: can only use more than one path for actions %s and %s: %w", s.Name, types.WatchActionRebuild, types.WatchActionRestart, errdefs.ErrInvalid)
return fmt.Errorf("services.%s.develop.watch: detected multiple paths %s for action %s. Multiple files are only valid for %s and %s actions: %w", s.Name, watch.Path, watch.Action, types.WatchActionRebuild, types.WatchActionRestart, errdefs.ErrInvalid)
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion loader/validate_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -396,7 +396,7 @@ func TestValidateWatch(t *testing.T) {
},
}
err := checkConsistency(&project)
assert.Error(t, err, "services.myservice.develop.watch: can only use more than one path for actions rebuild and restart: invalid compose project")
assert.ErrorContains(t, err, "services.myservice.develop.watch: detected multiple paths")
})
}
tests = []WatchActionTest{
Expand Down
41 changes: 37 additions & 4 deletions paths/unix.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
package paths

import (
"fmt"
"path"
"path/filepath"

Expand Down Expand Up @@ -49,9 +50,41 @@ func (r *relativePathsResolver) absSymbolicLink(value any) (any, error) {
if err != nil {
return nil, err
}
str, ok := abs.(string)
if !ok {
return abs, nil
switch t := abs.(type) {
case string:
// this can return []string if * matches more than one file
return resolveAbsStarPath(t)
case []any:
var res []any
for _, tt := range t {
s, _ := tt.(string)
r, err := resolveAbsStarPath(s)
if err != nil {
return nil, err
}
res = append(res, r...)
}
return res, nil
}

return abs, nil
}

func resolveAbsStarPath(t string) ([]any, error) {
matches, err := filepath.Glob(t)
if err != nil {
return nil, err
}
if len(matches) == 0 {
return nil, fmt.Errorf("could not resolve %s. Please make sure it exists?", t)
}
res := make([]any, len(matches))
for i, m := range matches {
symb, err := utils.ResolveSymbolicLink(m)
if err != nil {
return nil, err
}
res[i] = symb
}
return utils.ResolveSymbolicLink(str)
return res, nil
}
1 change: 1 addition & 0 deletions transform/canonical.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ func init() {
transformers["services.*.networks"] = transformServiceNetworks
transformers["services.*.volumes.*"] = transformVolumeMount
transformers["services.*.dns"] = transformStringOrList
transformers["services.*.develop.watch.*.path"] = transformStringOrList
transformers["services.*.devices.*"] = transformDeviceMapping
transformers["services.*.secrets.*"] = transformFileMount
transformers["services.*.configs.*"] = transformFileMount
Expand Down

0 comments on commit b0bb892

Please sign in to comment.