Skip to content

Commit 769147e

Browse files
committed
resolve symbolic links when loading develop.watch.path
Signed-off-by: Joana Hrotko <[email protected]>
1 parent 10cb5c6 commit 769147e

File tree

3 files changed

+107
-1
lines changed

3 files changed

+107
-1
lines changed

paths/resolve.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ func ResolveRelativePaths(project map[string]any, base string, remotes []RemoteR
3838
"services.*.build.additional_contexts.*": r.absContextPath,
3939
"services.*.env_file.*.path": r.absPath,
4040
"services.*.extends.file": r.absExtendsPath,
41-
"services.*.develop.watch.*.path": r.absPath,
41+
"services.*.develop.watch.*.path": r.absSymbolicLink,
4242
"services.*.volumes.*": r.absVolumeMount,
4343
"configs.*.file": r.maybeUnixPath,
4444
"secrets.*.file": r.maybeUnixPath,

paths/unix.go

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ package paths
1919
import (
2020
"path"
2121
"path/filepath"
22+
23+
"github.com/compose-spec/compose-go/v2/utils"
2224
)
2325

2426
func (r *relativePathsResolver) maybeUnixPath(a any) (any, error) {
@@ -38,3 +40,15 @@ func (r *relativePathsResolver) maybeUnixPath(a any) (any, error) {
3840
}
3941
return p, nil
4042
}
43+
44+
func (r *relativePathsResolver) absSymbolicLink(value any) (any, error) {
45+
abs, err := r.absPath(value)
46+
if err != nil {
47+
return nil, err
48+
}
49+
str, ok := abs.(string)
50+
if !ok {
51+
return abs, nil
52+
}
53+
return utils.ResolveSymbolicLink(str)
54+
}

utils/pathutils.go

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
/*
2+
Copyright 2020 The Compose Specification Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package utils
18+
19+
import (
20+
"os"
21+
"path/filepath"
22+
"strings"
23+
)
24+
25+
// ResolveSymbolicLink converts the section of an absolute path if it is a
26+
// symbolic link
27+
//
28+
// Parameters:
29+
// - path: an absolute path
30+
//
31+
// Returns:
32+
// - converted path if it has a symbolic link or the same path if there is
33+
// no symbolic link
34+
func ResolveSymbolicLink(path string) (string, error) {
35+
sym, part, err := getSymbolinkLink(path)
36+
if err != nil {
37+
return "", err
38+
}
39+
if sym == "" && part == "" {
40+
// no symbolic link detected
41+
return path, nil
42+
}
43+
return strings.Replace(path, part, sym, 1), nil
44+
45+
}
46+
47+
// getSymbolinkLink parses all parts of the path and returns the
48+
// the symbolic link part as well as the correspondent original part
49+
// Parameters:
50+
// - path: an absolute path
51+
//
52+
// Returns:
53+
// - string section of the path that is a symbolic link
54+
// - string correspondent path section of the symbolic link
55+
// - An error
56+
func getSymbolinkLink(path string) (string, string, error) {
57+
parts := strings.Split(path, string(os.PathSeparator))
58+
59+
// Reconstruct the path step by step, checking each component
60+
var currentPath string
61+
if filepath.IsAbs(path) {
62+
currentPath = string(os.PathSeparator)
63+
}
64+
65+
for _, part := range parts {
66+
if part == "" {
67+
continue
68+
}
69+
currentPath = filepath.Join(currentPath, part)
70+
71+
if isSymLink := isSymbolicLink(currentPath); isSymLink {
72+
// return symbolic link, and correspondent part
73+
target, err := filepath.EvalSymlinks(currentPath)
74+
if err != nil {
75+
return "", "", err
76+
}
77+
return target, currentPath, nil
78+
}
79+
}
80+
return "", "", nil // no symbolic link
81+
}
82+
83+
// isSymbolicLink validates if the path is a symbolic link
84+
func isSymbolicLink(path string) bool {
85+
info, err := os.Lstat(path)
86+
if err != nil {
87+
return false
88+
}
89+
90+
// Check if the file mode indicates a symbolic link
91+
return info.Mode()&os.ModeSymlink != 0
92+
}

0 commit comments

Comments
 (0)