Skip to content
This repository was archived by the owner on Sep 9, 2020. It is now read-only.

Commit 07a486b

Browse files
committed
wip
Signed-off-by: Ibrahim AshShohail <[email protected]>
1 parent fb9ac8c commit 07a486b

File tree

8 files changed

+365
-391
lines changed

8 files changed

+365
-391
lines changed

gps/filesystem.go

Lines changed: 75 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,25 @@ package gps
77
import (
88
"os"
99
"path/filepath"
10-
"runtime"
10+
"strings"
11+
12+
"github.com/pkg/errors"
1113
)
1214

15+
// fsLink represents a symbolic link.
16+
type fsLink struct {
17+
path string
18+
to string
19+
20+
// circular denotes if evaluating the symlink fails with "too many links" error.
21+
// This errors means that it's very likely that the symlink has circual refernce.
22+
circular bool
23+
24+
// broken denotes that attempting to resolve the link fails, most likely because
25+
// the destaination doesn't exist.
26+
broken bool
27+
}
28+
1329
// filesystemState represents the state of a file system.
1430
type filesystemState struct {
1531
root string
@@ -18,10 +34,51 @@ type filesystemState struct {
1834
links []fsLink
1935
}
2036

21-
// fsLink represents a symbolic link.
22-
type fsLink struct {
23-
path string
24-
to string
37+
func (s filesystemState) setup() error {
38+
for _, dir := range s.dirs {
39+
p := filepath.Join(s.root, dir)
40+
41+
if err := os.MkdirAll(p, 0777); err != nil {
42+
return errors.Errorf("os.MkdirAll(%q, 0777) err=%q", p, err)
43+
}
44+
}
45+
46+
for _, file := range s.files {
47+
p := filepath.Join(s.root, file)
48+
49+
f, err := os.Create(p)
50+
if err != nil {
51+
return errors.Errorf("os.Create(%q) err=%q", p, err)
52+
}
53+
54+
if err := f.Close(); err != nil {
55+
return errors.Errorf("file %q Close() err=%q", p, err)
56+
}
57+
}
58+
59+
for _, link := range s.links {
60+
p := filepath.Join(s.root, link.path)
61+
62+
// On Windows, relative symlinks confuse filepath.Walk. So, we'll just sigh
63+
// and do absolute links, assuming they are relative to the directory of
64+
// link.path.
65+
//
66+
// Reference: https://github.com/golang/go/issues/17540
67+
//
68+
// TODO(ibrasho): This was fixed in Go 1.9. Remove this when support for
69+
// 1.8 is dropped.
70+
dir := filepath.Dir(p)
71+
to := ""
72+
if link.to != "" {
73+
to = filepath.Join(dir, link.to)
74+
}
75+
76+
if err := os.Symlink(to, p); err != nil {
77+
return errors.Errorf("os.Symlink(%q, %q) err=%q", to, p, err)
78+
}
79+
}
80+
81+
return nil
2582
}
2683

2784
// deriveFilesystemState returns a filesystemState based on the state of
@@ -43,36 +100,28 @@ func deriveFilesystemState(root string) (filesystemState, error) {
43100
return err
44101
}
45102

46-
symlink := (info.Mode() & os.ModeSymlink) == os.ModeSymlink
47-
dir := info.IsDir()
48-
49-
if runtime.GOOS == "windows" && symlink && dir {
50-
// This could be a Windows junction directory. Support for these in the
51-
// standard library is spotty, and we could easily delete an important
52-
// folder if we called os.Remove or os.RemoveAll. Just skip these.
53-
//
54-
// TODO: If we could distinguish between junctions and Windows symlinks,
55-
// we might be able to safely delete symlinks, even though junctions are
56-
// dangerous.
103+
if (info.Mode() & os.ModeSymlink) != 0 {
104+
l := fsLink{path: relPath}
57105

58-
return nil
59-
}
106+
l.to, err = filepath.EvalSymlinks(path)
107+
if strings.HasSuffix(err.Error(), "too many links") {
108+
l.circular = true
109+
} else if err != nil {
110+
return err
111+
}
60112

61-
if symlink {
62-
eval, err := filepath.EvalSymlinks(path)
63-
if err != nil {
113+
if _, err := os.Stat(l.to); os.IsNotExist(err) {
114+
l.broken = true
115+
} else if err != nil {
64116
return err
65117
}
66118

67-
fs.links = append(fs.links, fsLink{
68-
path: relPath,
69-
to: eval,
70-
})
119+
fs.links = append(fs.links, l)
71120

72121
return nil
73122
}
74123

75-
if dir {
124+
if info.IsDir() {
76125
fs.dirs = append(fs.dirs, relPath)
77126

78127
return nil

gps/filesystem_test.go

Lines changed: 25 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,10 @@ package gps
77
import (
88
"os"
99
"path/filepath"
10+
"reflect"
1011
"testing"
12+
13+
"github.com/golang/dep/internal/test"
1114
)
1215

1316
// This file contains utilities for running tests around file system state.
@@ -89,45 +92,35 @@ func (tc fsTestCase) assert(t *testing.T) {
8992
// setup inflates fs onto the actual host file system at tc.before.root.
9093
// It doesn't delete existing files and should be used on empty roots only.
9194
func (tc fsTestCase) setup(t *testing.T) {
92-
tc.setupDirs(t)
93-
tc.setupFiles(t)
94-
tc.setupLinks(t)
95-
}
96-
97-
func (tc fsTestCase) setupDirs(t *testing.T) {
98-
for _, dir := range tc.before.dirs {
99-
p := filepath.Join(tc.before.root, dir)
100-
if err := os.MkdirAll(p, 0777); err != nil {
101-
t.Fatalf("os.MkdirAll(%q, 0777) err=%q", p, err)
102-
}
95+
if err := tc.before.setup(); err != nil {
96+
t.Fatal(err)
10397
}
10498
}
10599

106-
func (tc fsTestCase) setupFiles(t *testing.T) {
107-
for _, file := range tc.before.files {
108-
p := filepath.Join(tc.before.root, file)
109-
f, err := os.Create(p)
110-
if err != nil {
111-
t.Fatalf("os.Create(%q) err=%q", p, err)
112-
}
113-
if err := f.Close(); err != nil {
114-
t.Fatalf("file %q Close() err=%q", p, err)
115-
}
100+
func TestDeriveFilesystemState(t *testing.T) {
101+
testcases := []struct {
102+
name string
103+
state filesystemState
104+
}{
105+
{
106+
name: "simple-case",
107+
state: filesystemState{},
108+
},
116109
}
117-
}
118110

119-
func (tc fsTestCase) setupLinks(t *testing.T) {
120-
for _, link := range tc.before.links {
121-
p := filepath.Join(tc.before.root, link.path)
111+
for _, tc := range testcases {
112+
h := test.NewHelper(t)
113+
defer h.Cleanup()
114+
115+
h.TempDir(tc.name)
122116

123-
// On Windows, relative symlinks confuse filepath.Walk. This is golang/go
124-
// issue 17540. So, we'll just sigh and do absolute links, assuming they are
125-
// relative to the directory of link.path.
126-
dir := filepath.Dir(p)
127-
to := filepath.Join(dir, link.to)
117+
state, err := deriveFilesystemState(h.Path(tc.name))
118+
if err != nil {
119+
t.Fatal(err)
120+
}
128121

129-
if err := os.Symlink(to, p); err != nil {
130-
t.Fatalf("os.Symlink(%q, %q) err=%q", to, p, err)
122+
if !reflect.DeepEqual(tc.state, state) {
123+
t.Fatal("filesystem state mismatch")
131124
}
132125
}
133126
}

gps/prune.go

Lines changed: 25 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import (
88
"log"
99
"os"
1010
"path/filepath"
11+
"sort"
1112
"strings"
1213

1314
"github.com/golang/dep/internal/fs"
@@ -126,11 +127,21 @@ func PruneProject(baseDir string, lp LockedProject, options PruneOptions, logger
126127

127128
// pruneVendorDirs deletes all nested vendor directories within baseDir.
128129
func pruneVendorDirs(fsState filesystemState) error {
129-
toDelete := collectNestedVendorDirs(fsState)
130+
for _, dir := range fsState.dirs {
131+
if filepath.Base(dir) == "vendor" {
132+
err := os.RemoveAll(filepath.Join(fsState.root, dir))
133+
if err != nil && !os.IsNotExist(err) {
134+
return err
135+
}
136+
}
137+
}
130138

131-
for _, path := range toDelete {
132-
if err := os.RemoveAll(path); err != nil && !os.IsNotExist(err) {
133-
return err
139+
for _, link := range fsState.links {
140+
if filepath.Base(link.path) == "vendor" {
141+
err := os.Remove(filepath.Join(fsState.root, link.path))
142+
if err != nil && !os.IsNotExist(err) {
143+
return err
144+
}
134145
}
135146
}
136147

@@ -291,6 +302,8 @@ func pruneGoTestFiles(fsState filesystemState) error {
291302
}
292303

293304
func deleteEmptyDirs(fsState filesystemState) error {
305+
toDelete := make(sort.StringSlice, 0)
306+
294307
for _, dir := range fsState.dirs {
295308
path := filepath.Join(fsState.root, dir)
296309

@@ -300,9 +313,14 @@ func deleteEmptyDirs(fsState filesystemState) error {
300313
}
301314

302315
if !notEmpty {
303-
if err := os.Remove(path); err != nil && !os.IsNotExist(err) {
304-
return err
305-
}
316+
toDelete = append(toDelete, path)
317+
}
318+
}
319+
320+
sort.Sort(sort.Reverse(sort.StringSlice(toDelete)))
321+
for _, path := range toDelete {
322+
if err := os.Remove(path); err != nil && !os.IsNotExist(err) {
323+
return err
306324
}
307325
}
308326

0 commit comments

Comments
 (0)