-
Notifications
You must be signed in to change notification settings - Fork 24
Follow symlinks when stripping vendor directories #199
Changes from 8 commits
3b518fc
5bf17a1
352abcf
21bb8a5
00bcf14
c281797
b0151a8
8720092
c2a099c
42ce45c
3e1d193
d59bce6
87ee43d
5e19c27
c32885d
6eda647
06bcb96
edee8c8
9a38a0e
e640789
853e768
05ef430
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
// +build !windows | ||
|
||
package gps | ||
|
||
import ( | ||
"os" | ||
"testing" | ||
) | ||
|
||
// setup inflates fs onto the actual host file system | ||
func (fs filesystemState) setup(t *testing.T) { | ||
for _, dir := range fs.dirs { | ||
p := dir.prepend(fs.root) | ||
if err := os.MkdirAll(p.String(), 0777); err != nil { | ||
t.Fatalf("os.MkdirAll(%q, 0777) err=%q", p, 0777) | ||
} | ||
} | ||
for _, file := range fs.files { | ||
p := file.prepend(fs.root) | ||
f, err := os.Create(p.String()) | ||
if err != nil { | ||
t.Fatalf("os.Create(%q) err=%q", p, err) | ||
} | ||
if err := f.Close(); err != nil { | ||
t.Fatalf("file %q Close() err=%q", p, err) | ||
} | ||
} | ||
for _, link := range fs.links { | ||
p := link.path.prepend(fs.root) | ||
if err := os.Symlink(link.to, p.String()); err != nil { | ||
t.Fatalf("os.Symlink(%q, %q) err=%q", link.to, p, err) | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,108 @@ | ||
package gps | ||
|
||
import ( | ||
"os" | ||
"path/filepath" | ||
"testing" | ||
) | ||
|
||
// This file contains utilities for running tests around file system state. | ||
|
||
// fspath represents a file system path in an OS-agnostic way. | ||
type fsPath []string | ||
|
||
func (f fsPath) String() string { return filepath.Join(f...) } | ||
|
||
func (f fsPath) prepend(prefix string) fsPath { | ||
p := fsPath{prefix} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think it's worth wrapping |
||
return append(p, f...) | ||
} | ||
|
||
// filesystemState represents the state of a file system. It has a setup method | ||
// which inflates its state to the actual host file system, and an assert | ||
// method which checks that the actual file system matches the described state. | ||
type filesystemState struct { | ||
root string | ||
dirs []fsPath | ||
files []fsPath | ||
links []fsLink | ||
} | ||
|
||
// assert makes sure that the fs state matches the state of the actual host | ||
// file system | ||
func (fs filesystemState) assert(t *testing.T) { | ||
dirMap := make(map[string]struct{}) | ||
fileMap := make(map[string]struct{}) | ||
linkMap := make(map[string]struct{}) | ||
|
||
for _, d := range fs.dirs { | ||
dirMap[d.prepend(fs.root).String()] = struct{}{} | ||
} | ||
for _, f := range fs.files { | ||
fileMap[f.prepend(fs.root).String()] = struct{}{} | ||
} | ||
for _, l := range fs.links { | ||
linkMap[l.path.prepend(fs.root).String()] = struct{}{} | ||
} | ||
|
||
err := filepath.Walk(fs.root, func(path string, info os.FileInfo, err error) error { | ||
if err != nil { | ||
t.Errorf("filepath.Walk path=%q err=%q", path, err) | ||
return err | ||
} | ||
|
||
if path == fs.root { | ||
return nil | ||
} | ||
|
||
// Careful! Have to check whether the path is a symlink first because, on | ||
// windows, a symlink to a directory will return 'true' for info.IsDir(). | ||
if (info.Mode() & os.ModeSymlink) != 0 { | ||
_, ok := linkMap[path] | ||
if !ok { | ||
t.Errorf("unexpected symlink exists %q", path) | ||
} else { | ||
delete(linkMap, path) | ||
} | ||
return nil | ||
} | ||
|
||
if info.IsDir() { | ||
_, ok := dirMap[path] | ||
if !ok { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Let's change if dirMap[path] {
// ...
else {
...
} instead of having to do the two-value map lookup return style. |
||
t.Errorf("unexpected directory exists %q", path) | ||
} else { | ||
delete(dirMap, path) | ||
} | ||
return nil | ||
} | ||
|
||
_, ok := fileMap[path] | ||
if !ok { | ||
t.Errorf("unexpected file exists %q", path) | ||
} else { | ||
delete(fileMap, path) | ||
} | ||
return nil | ||
}) | ||
|
||
if err != nil { | ||
t.Errorf("filesystem.Walk err=%q", err) | ||
} | ||
|
||
for d := range dirMap { | ||
t.Errorf("could not find expected directory %q", d) | ||
} | ||
for f := range fileMap { | ||
t.Errorf("could not find expected file %q", f) | ||
} | ||
for l := range linkMap { | ||
t.Errorf("could not find expected symlink %q", l) | ||
} | ||
} | ||
|
||
// fsLink represents a symbolic link. | ||
type fsLink struct { | ||
path fsPath | ||
to string | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
// +build windows | ||
|
||
package gps | ||
|
||
import ( | ||
"os" | ||
"path/filepath" | ||
"testing" | ||
) | ||
|
||
// setup inflates fs onto the actual host file system | ||
func (fs filesystemState) setup(t *testing.T) { | ||
for _, dir := range fs.dirs { | ||
p := dir.prepend(fs.root) | ||
if err := os.MkdirAll(p.String(), 0777); err != nil { | ||
t.Fatalf("os.MkdirAll(%q, 0777) err=%q", p, 0777) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ibid above |
||
} | ||
} | ||
for _, file := range fs.files { | ||
p := file.prepend(fs.root) | ||
f, err := os.Create(p.String()) | ||
if err != nil { | ||
t.Fatalf("os.Create(%q) err=%q", p, err) | ||
} | ||
if err := f.Close(); err != nil { | ||
t.Fatalf("file %q Close() err=%q", p, err) | ||
} | ||
} | ||
for _, link := range fs.links { | ||
p := link.path.prepend(fs.root) | ||
|
||
// On Windows, relative symlinks confuse filepath.Walk. This is golang/go | ||
// issue 17540. So, we'll just sigh and do absolute links, assuming they are | ||
// relative to the directory of link.path. | ||
dir := filepath.Dir(p.String()) | ||
to := filepath.Join(dir, link.to) | ||
|
||
if err := os.Symlink(to, p.String()); err != nil { | ||
t.Fatalf("os.Symlink(%q, %q) err=%q", to, p, err) | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -72,3 +72,25 @@ func (r solution) Attempts() int { | |
func (r solution) InputHash() []byte { | ||
return r.hd | ||
} | ||
|
||
func stripVendor(path string, info os.FileInfo, err error) error { | ||
if info.Name() == "vendor" { | ||
if _, err := os.Lstat(path); err == nil { | ||
if info.IsDir() { | ||
return removeAll(path) | ||
} | ||
|
||
if (info.Mode() & os.ModeSymlink) != 0 { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. wait - shouldn't this check be first, because Windows junction dirs? i must be missing something... There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You probably aren't missing anything. I don't know much of anything about Windows and don't know what a junction dir is. Should these be reversed? What's the test that would prove it? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. hah, i scarcely know either. it's just problems discussed in golang/go#17540 - Windows APIs can indicate that a file is both a symlink AND a dir. they do it when it's one of these "junction dirs." Bottom line is, this approach on windows would potentially (?) erase the target of the symlink. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ah, gotcha. I'll try to figure out how to create a junction dir and write a test that exercises this. For what it's worth, if this erases the target of symlinks, then we're already doing that today. |
||
realInfo, err := os.Stat(path) | ||
if err != nil { | ||
return err | ||
} | ||
if realInfo.IsDir() { | ||
return os.Remove(path) | ||
} | ||
} | ||
} | ||
} | ||
|
||
return nil | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
seems like second param should be
err
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice catch! Will do.