From 0108103d10a7cd902a039907a46a3136102ae74d Mon Sep 17 00:00:00 2001 From: Keegan Carruthers-Smith Date: Wed, 3 Feb 2021 13:40:02 +0200 Subject: [PATCH] servegit: eval symlinks on root This is a useful use case which allows changing your root by updating a symlink. Additionally it is convenient. We have to explicitly resolve the symlink since filepath.Walk does not support resolving symlinks. We resolve the symlink each time we crawl so the user can update the value of the symlink at runtime. Note: we still ignore symlinks with the repos directory. Supporting that would be more complex due to symlink cycles. --- internal/servegit/serve.go | 14 ++++++++++---- internal/servegit/serve_test.go | 33 +++++++++++++++++++++++++++++++++ 2 files changed, 43 insertions(+), 4 deletions(-) diff --git a/internal/servegit/serve.go b/internal/servegit/serve.go index f5a6597bc7..ea8d022dcf 100644 --- a/internal/servegit/serve.go +++ b/internal/servegit/serve.go @@ -125,7 +125,13 @@ func (s *Serve) Repos() ([]Repo, error) { var repos []Repo var reposRootIsRepo bool - err := filepath.Walk(s.Root, func(path string, fi os.FileInfo, fileErr error) error { + root, err := filepath.EvalSymlinks(s.Root) + if err != nil { + s.Info.Printf("WARN: ignoring error searching %s: %v", root, err) + return nil, nil + } + + err = filepath.Walk(root, func(path string, fi os.FileInfo, fileErr error) error { if fileErr != nil { s.Info.Printf("WARN: ignoring error searching %s: %v", path, fileErr) return nil @@ -150,11 +156,11 @@ func (s *Serve) Repos() ([]Repo, error) { return nil } - subpath, err := filepath.Rel(s.Root, path) + subpath, err := filepath.Rel(root, path) if err != nil { // According to WalkFunc docs, path is always filepath.Join(root, // subpath). So Rel should always work. - s.Info.Fatalf("filepath.Walk returned %s which is not relative to %s: %v", path, s.Root, err) + s.Info.Fatalf("filepath.Walk returned %s which is not relative to %s: %v", path, root, err) } name := filepath.ToSlash(subpath) @@ -190,7 +196,7 @@ func (s *Serve) Repos() ([]Repo, error) { // Update all names to be relative to the parent of reposRoot. This is to // give a better name than "." for repos root - abs, err := filepath.Abs(s.Root) + abs, err := filepath.Abs(root) if err != nil { return nil, fmt.Errorf("failed to get the absolute path of reposRoot: %w", err) } diff --git a/internal/servegit/serve_test.go b/internal/servegit/serve_test.go index ab7a169beb..ce1c2e063c 100644 --- a/internal/servegit/serve_test.go +++ b/internal/servegit/serve_test.go @@ -80,6 +80,39 @@ func TestReposHandler(t *testing.T) { } testReposHandler(t, h, want) }) + + // Ensure everything still works if root is a symlink + t.Run("rooted-"+tc.name, func(t *testing.T) { + root := gitInitRepos(t, tc.repos...) + + // This is the difference, we create a symlink for root + { + tmp, err := ioutil.TempDir("", "") + if err != nil { + t.Fatal(err) + } + t.Cleanup(func() { os.RemoveAll(tmp) }) + + symlink := filepath.Join(tmp, "symlink-root") + if err := os.Symlink(root, symlink); err != nil { + t.Fatal(err) + } + root = symlink + } + + h := (&Serve{ + Info: testLogger(t), + Debug: discardLogger, + Addr: testAddress, + Root: root, + }).handler() + + var want []Repo + for _, name := range tc.repos { + want = append(want, Repo{Name: name, URI: path.Join("/repos", name)}) + } + testReposHandler(t, h, want) + }) } }