From 231448ee856980784c2fb9ed64f6090e419e98f8 Mon Sep 17 00:00:00 2001 From: walnuts1018 Date: Sun, 27 Oct 2024 00:19:49 +0900 Subject: [PATCH 1/4] add windows symlink & junction support Signed-off-by: walnuts1018 --- .github/workflows/test.yaml | 2 +- go.mod | 3 +- go.sum | 6 +- helpers_unix.go | 6 ++ helpers_windows.go | 46 ++++++++++++++- helpers_windows_test.go | 115 ++++++++++++++++++++++++++++++++++++ local_repository.go | 2 +- 7 files changed, 174 insertions(+), 6 deletions(-) create mode 100644 helpers_windows_test.go diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index 52bac86..3f31096 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -26,6 +26,6 @@ jobs: with: go-version-file: go.mod - name: test - run: go test -coverprofile coverage.out -covermode atomic ./... + run: sudo go test -coverprofile coverage.out -covermode atomic ./... - name: Send coverage uses: codecov/codecov-action@v1 diff --git a/go.mod b/go.mod index a52ec97..6a7961b 100644 --- a/go.mod +++ b/go.mod @@ -9,7 +9,7 @@ require ( github.com/saracen/walker v0.1.4 github.com/urfave/cli/v2 v2.27.2 golang.org/x/net v0.27.0 - golang.org/x/sync v0.7.0 + golang.org/x/sync v0.8.0 ) require ( @@ -23,6 +23,7 @@ require ( github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 // indirect golang.org/x/sys v0.22.0 // indirect + golang.org/x/text v0.19.0 golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index c2b2566..0a562c7 100644 --- a/go.sum +++ b/go.sum @@ -84,8 +84,8 @@ golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.27.0 h1:5K3Njcw06/l2y9vpGCSdcxWOYHOUk3dVNGDXN+FvAys= golang.org/x/net v0.27.0/go.mod h1:dDi0PyhWNoiUOrAS8uXv/vnScO4wnHQO4mj9fn/RytE= -golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M= -golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ= +golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -104,6 +104,8 @@ golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9sn golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.19.0 h1:kTxAhCbGbxhK0IwgSKiMO5awPoDQ0RpfiVYBfK860YM= +golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20220609144429-65e65417b02f/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= diff --git a/helpers_unix.go b/helpers_unix.go index 23391b7..22db2f3 100644 --- a/helpers_unix.go +++ b/helpers_unix.go @@ -2,6 +2,12 @@ package main +import "path/filepath" + func toFullPath(s string) (string, error) { return s, nil } + +func evalSymlinks(path string) (string, error) { + return filepath.EvalSymlinks(path) +} diff --git a/helpers_windows.go b/helpers_windows.go index ba056cc..a92ffd3 100644 --- a/helpers_windows.go +++ b/helpers_windows.go @@ -2,7 +2,12 @@ package main -import "syscall" +import ( + "os" + "path/filepath" + "strings" + "syscall" +) func toFullPath(s string) (string, error) { p := syscall.StringToUTF16(s) @@ -21,3 +26,42 @@ func toFullPath(s string) (string, error) { b = b[:n] return syscall.UTF16ToString(b), nil } + +func evalSymlinks(path string) (string, error) { + _, err := os.Stat(path) + if err != nil { + return "", err + } + + list := filepathSplitAll(path) + evaled := list[0] + for i := 1; i < len(list); i++ { + evaled = filepath.Join(evaled, list[i]) + + linkSrc, err := os.Readlink(evaled) + if err != nil { + // not symlink + continue + } else { + if filepath.IsAbs(linkSrc) { + evaled = linkSrc + } else { + evaled = filepath.Join(filepath.Dir(evaled), linkSrc) + } + } + } + + return evaled, nil +} + +func filepathSplitAll(path string) []string { + path = filepath.Clean(path) + path = filepath.ToSlash(path) + + vol := filepath.VolumeName(path) + + path = path[len(vol):] + list := strings.Split(path, "/") + list[0] = vol + string(filepath.Separator) + list[0] + return list +} diff --git a/helpers_windows_test.go b/helpers_windows_test.go new file mode 100644 index 0000000..c9a619e --- /dev/null +++ b/helpers_windows_test.go @@ -0,0 +1,115 @@ +//go:build windows + +package main + +import ( + "bytes" + "fmt" + "io" + "os" + "os/exec" + "path/filepath" + "testing" + + "golang.org/x/text/encoding/japanese" + "golang.org/x/text/transform" +) + +type testEvalSymlinksMode int + +const ( + testEvalSymlinksNotLink testEvalSymlinksMode = iota + testEvalSymlinksSymbolicLink + testEvalSymlinksJunction +) + +func Test_evalSymlinks(t *testing.T) { + type args struct { + path string + } + tests := []struct { + name string + mode testEvalSymlinksMode + linkBasePath string + args args + want string + wantErr bool + }{ + { + name: "not link", + mode: testEvalSymlinksNotLink, + args: args{ + path: filepath.Join(os.TempDir(), "not_link"), + }, + want: filepath.Join(os.TempDir(), "not_link"), + wantErr: false, + }, + { + name: "symbolic link", + mode: testEvalSymlinksSymbolicLink, + linkBasePath: filepath.Join(os.TempDir(), "link_base"), + args: args{ + path: filepath.Join(os.TempDir(), "symbolic_link"), + }, + want: filepath.Join(os.TempDir(), "link_base"), + wantErr: false, + }, + { + name: "junction", + mode: testEvalSymlinksJunction, + linkBasePath: filepath.Join(os.TempDir(), "link_base"), + args: args{ + path: filepath.Join(os.TempDir(), "junction"), + }, + want: filepath.Join(os.TempDir(), "link_base"), + wantErr: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if err := createLink(tt.linkBasePath, tt.args.path, tt.mode); err != nil { + t.Errorf("failed to create link: %v", err) + return + } + + got, err := evalSymlinks(tt.args.path) + if (err != nil) != tt.wantErr { + t.Errorf("evalSymlinks() error = %v, wantErr %v", err, tt.wantErr) + return + } + if got != tt.want { + t.Errorf("evalSymlinks() = %v, want %v", got, tt.want) + } + }) + } +} + +func createLink(linkBasePath, path string, mode testEvalSymlinksMode) error { + if err := os.RemoveAll(path); err != nil { + return err + } + + if mode == testEvalSymlinksNotLink { + return os.MkdirAll(path, 0755) + } + + if err := os.MkdirAll(linkBasePath, 0755); err != nil { + return err + } + + switch mode { + case testEvalSymlinksSymbolicLink: + return os.Symlink(linkBasePath, path) + case testEvalSymlinksJunction: + output, err := exec.Command("cmd", "/c", "mklink", "/J", path, linkBasePath).CombinedOutput() + if err != nil { + output, err := io.ReadAll(transform.NewReader(bytes.NewBuffer(output), japanese.ShiftJIS.NewDecoder())) + if err != nil { + return fmt.Errorf("failed to transform output: %w", err) + } + return fmt.Errorf("failed to create junction: %s, %w", string(output), err) + } + return nil + } + return nil +} diff --git a/local_repository.go b/local_repository.go index 67b7d49..8444824 100644 --- a/local_repository.go +++ b/local_repository.go @@ -399,7 +399,7 @@ func localRepositoryRoots(all bool) ([]string, error) { for _, v := range roots { path := filepath.Clean(v) if _, err := os.Stat(path); err == nil { - if path, err = filepath.EvalSymlinks(path); err != nil { + if path, err = evalSymlinks(path); err != nil { _localRepoErr = err return } From f78e2d4467fda031e3073e5d408e6b3a89545e76 Mon Sep 17 00:00:00 2001 From: walnuts1018 Date: Sun, 27 Oct 2024 00:21:43 +0900 Subject: [PATCH 2/4] tmp Signed-off-by: walnuts1018 --- .github/workflows/test.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index 3f31096..930efa0 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -3,6 +3,7 @@ on: push: branches: - "**" + workflow_dispatch: jobs: test: runs-on: ${{ matrix.os }} From 16c02bc032dd3fc194d072d220e6fe6c44ccfe05 Mon Sep 17 00:00:00 2001 From: walnuts1018 Date: Sun, 27 Oct 2024 00:23:47 +0900 Subject: [PATCH 3/4] rm sudo Signed-off-by: walnuts1018 --- .github/workflows/test.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index 930efa0..b5287db 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -27,6 +27,6 @@ jobs: with: go-version-file: go.mod - name: test - run: sudo go test -coverprofile coverage.out -covermode atomic ./... + run: go test -coverprofile coverage.out -covermode atomic ./... - name: Send coverage uses: codecov/codecov-action@v1 From 83eab518c159f2fb275c0902d9de72b5f4ff1486 Mon Sep 17 00:00:00 2001 From: walnuts1018 Date: Sun, 27 Oct 2024 00:26:20 +0900 Subject: [PATCH 4/4] rm workflow_dispatch Signed-off-by: walnuts1018 --- .github/workflows/test.yaml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index b5287db..52bac86 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -3,7 +3,6 @@ on: push: branches: - "**" - workflow_dispatch: jobs: test: runs-on: ${{ matrix.os }}