This repository was archived by the owner on Sep 9, 2020. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 1k
Handling symlinks as project root #641
Merged
sdboyer
merged 24 commits into
golang:master
from
ibrasho-forks:symlink-project-roots-changes
Jun 17, 2017
Merged
Changes from 4 commits
Commits
Show all changes
24 commits
Select commit
Hold shift + click to select a range
3b21f6e
update FAQ about symlinks
ibrasho 9730410
internal/fs: add ResolvePath function
ibrasho 91ac4d2
dep: update NewContext(), rename ResolveProjectRoot() to ResolveProje…
ibrasho 3be59e3
dep: minor fixes & tweaks in context.go
ibrasho 6a464ac
dep: update letter casing to detectGOPATH() and ResolveProjectRootAnd…
ibrasho 9675932
Minor text edits
ibrasho b7048cc
Merge remote-tracking branch 'upstream/master' into symlink-project-r…
ibrasho 28b6f86
internal/fs: Remove fs.ResolvePath()
ibrasho 22cd768
fix tests
ibrasho 5bf9205
dep: Add project.ResolvedAbsRoot()
ibrasho 71a2607
dep: Add dep.NewProject() and add ResolveAbsRoot to dep.Project
ibrasho 929ef28
dep/cmd/dep: Update init.go to use dep.Project
ibrasho cd9bea6
dep: add Ctx.SetPaths() ctx.DetectProjectGOPATH()
ibrasho 6595001
Merge branch 'master' into symlink-project-roots-changes
ibrasho 9166034
dep: Add *Project.SetRoot() instead of NewProject()
ibrasho 5a3291a
dep: small nits in context.go
ibrasho 0b8edb1
Ensure dep reads GOPATH from Config.Env
ibrasho 026038a
Update docs for dep.Ctx. Ctx.SetPaths and Ctx.DetectProjectGOPATH
ibrasho b98d713
Tweak dep.Project.SetRoot to ensure consistent internal state
ibrasho 138cebb
dep: Update godoc to use // instead of /* ... */
ibrasho 3826bb0
dep: Simplify dep.*Project.SetRoot
ibrasho f4b9f49
dep: Fix godoc for dep.*Ctx.DetectProjectGOPATH
ibrasho a69d6ae
dep: update tests for dep.*Ctx.DetectProjectGOPATH
ibrasho 13f512f
dep: cleanup TestLoadProject and TestDetectProjectGOPATH
ibrasho File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -19,9 +19,9 @@ import ( | |
|
||
// Ctx defines the supporting context of the tool. | ||
type Ctx struct { | ||
GOPATH string // Selected Go path | ||
GOPATHS []string // Other Go paths | ||
WorkingDir string | ||
GOPATHS []string // Other Go paths | ||
GOPATH string // Selected Go path | ||
*Loggers | ||
} | ||
|
||
|
@@ -32,30 +32,21 @@ type Loggers struct { | |
Verbose bool | ||
} | ||
|
||
// NewContext creates a struct with the project's GOPATH. It assumes | ||
// that of your "GOPATH"'s we want the one we are currently in. | ||
func NewContext(wd string, env []string, loggers *Loggers) (*Ctx, error) { | ||
// NewContext creates a struct with all the environment's GOPATHs. | ||
func NewContext(wd string, env []string, loggers *Loggers) *Ctx { | ||
ctx := &Ctx{WorkingDir: wd, Loggers: loggers} | ||
|
||
GOPATH := getEnv(env, "GOPATH") | ||
|
||
if GOPATH == "" { | ||
GOPATH = defaultGOPATH() | ||
} | ||
for _, gp := range filepath.SplitList(GOPATH) { | ||
gp = filepath.FromSlash(gp) | ||
|
||
if fs.HasFilepathPrefix(filepath.FromSlash(wd), gp) { | ||
ctx.GOPATH = gp | ||
} | ||
|
||
ctx.GOPATHS = append(ctx.GOPATHS, gp) | ||
} | ||
|
||
if ctx.GOPATH == "" { | ||
return nil, errors.New("project not in a GOPATH") | ||
for _, gp := range filepath.SplitList(GOPATH) { | ||
ctx.GOPATHS = append(ctx.GOPATHS, filepath.FromSlash(gp)) | ||
} | ||
|
||
return ctx, nil | ||
return ctx | ||
} | ||
|
||
// getEnv returns the last instance of an environment variable. | ||
|
@@ -116,9 +107,11 @@ func (c *Ctx) LoadProject() (*Project, error) { | |
|
||
// The path may lie within a symlinked directory, resolve the path | ||
// before moving forward | ||
p.AbsRoot, err = c.resolveProjectRoot(p.AbsRoot) | ||
p.AbsRoot, c.GOPATH, err = c.ResolveProjectRootAndGoPath(p.AbsRoot) | ||
if err != nil { | ||
return nil, errors.Wrapf(err, "resolve project root") | ||
} else if c.GOPATH == "" { | ||
return nil, errors.New("project not within a GOPATH") | ||
} | ||
|
||
ip, err := c.SplitAbsoluteProjectRoot(p.AbsRoot) | ||
|
@@ -168,41 +161,72 @@ func (c *Ctx) LoadProject() (*Project, error) { | |
return p, nil | ||
} | ||
|
||
// resolveProjectRoot evaluates the root directory and does the following: | ||
// ResolveProjectRootAndGoPath evaluates the project root and the containing GOPATH | ||
// by doing the following: | ||
// | ||
// If the passed path is a symlink outside GOPATH to a directory within a | ||
// GOPATH, the resolved full real path is returned. | ||
// If path isn't a symlink and is within a GOPATH, path and its GOPATH are returned. | ||
// | ||
// If the passed path is a symlink within a GOPATH, we return an error. | ||
// If path is a symlink not within any GOPATH and resolves to a directory within a | ||
// GOPATH, the resolved path and its GOPATH are returned. | ||
// | ||
// If the passed path isn't a symlink at all, we just pass through. | ||
func (c *Ctx) resolveProjectRoot(path string) (string, error) { | ||
// Determine if this path is a Symlink | ||
l, err := os.Lstat(path) | ||
// ResolveProjectRootAndGoPath will return an error in the following cases: | ||
// | ||
// If path is not a symlink and it's not within any GOPATH. | ||
// If both path and the directory it resolves to are not within any GOPATH. | ||
// If path is a symlink within a GOPATH, an error is returned. | ||
// If both path and the directory it resolves to are within the same GOPATH. | ||
// If path and the directory it resolves to are each within a different GOPATH. | ||
func (c *Ctx) ResolveProjectRootAndGoPath(path string) (string, string, error) { | ||
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. s/GoPath/GOPATH/ |
||
pgp, pgperr := c.detectGoPath(path) | ||
|
||
if sym, err := fs.IsSymlink(path); err != nil { | ||
return "", "", errors.Wrap(err, "IsSymlink") | ||
} else if !sym { | ||
// If path is not a symlink and detectGoPath failed, then we assume that path is not | ||
// within a known GOPATH. | ||
if pgperr != nil { | ||
return "", "", errors.Errorf("project root %v not within a GOPATH", path) | ||
} | ||
return path, pgp, nil | ||
} | ||
|
||
resolved, err := fs.ResolvePath(path) | ||
if err != nil { | ||
return "", errors.Wrap(err, "resolveProjectRoot") | ||
return "", "", errors.Wrap(err, "resolveProjectRoot") | ||
} | ||
|
||
// Pass through if not | ||
if l.Mode()&os.ModeSymlink == 0 { | ||
return path, nil | ||
rgp, rgperr := c.detectGoPath(resolved) | ||
if pgperr != nil && rgperr != nil { | ||
return "", "", errors.Errorf("path %s resolved to %s, both are not within any GOPATH", path, resolved) | ||
} | ||
|
||
// Resolve path | ||
resolved, err := filepath.EvalSymlinks(path) | ||
if err != nil { | ||
return "", errors.Wrap(err, "resolveProjectRoot") | ||
// If pgp equals rgp, then both are within the same GOPATH. | ||
if pgp == rgp { | ||
return "", "", errors.Errorf("path %s resolved to %s, both in the same GOPATH %s", path, resolved, pgp) | ||
} | ||
|
||
// Determine if the symlink is within any of the GOPATHs, in which case we're not | ||
// sure how to resolve it. | ||
// path and resolved are within different GOPATHs | ||
if pgp != "" && rgp != "" && pgp == rgp { | ||
return "", "", errors.Errorf("path %s resolved to %s, each is in a different GOPATH", path, resolved) | ||
} | ||
|
||
// Otherwise, either the symlink or the resolved path is within a GOPATH. | ||
if pgp == "" { | ||
return resolved, rgp, nil | ||
} | ||
|
||
return path, pgp, nil | ||
} | ||
|
||
// detectGoPath detects the GOPATH for a given path from ctx.GOPATHS. | ||
func (c *Ctx) detectGoPath(path string) (string, error) { | ||
for _, gp := range c.GOPATHS { | ||
if fs.HasFilepathPrefix(path, gp) { | ||
return "", errors.Errorf("'%s' is linked to another path within a GOPATH (%s)", path, gp) | ||
if fs.HasFilepathPrefix(filepath.FromSlash(path), gp) { | ||
return gp, nil | ||
} | ||
} | ||
|
||
return resolved, nil | ||
return "", errors.Errorf("Unable to detect GOPATH for %s", path) | ||
} | ||
|
||
// SplitAbsoluteProjectRoot takes an absolute path and compares it against declared | ||
|
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
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.
Sentence construction thing:
"If neither the symlink nor the resolved path are in a
GOPATH
, then an error is thrown".