internal/fetch: a directoryModuleGetter knows its module path
A correctly configured directoryModuleGetter knows the module path it
is serving, and returns NotFound for any others.
This will enable trying multiple ModuleGetters when looking for a
path.
For golang/go#47780
Change-Id: Ib33bd3d7e222ff048ae8a35843df43219e9d844b
Reviewed-on: https://go-review.googlesource.com/c/pkgsite/+/343590
Trust: Jonathan Amsterdam <jba@google.com>
Run-TryBot: Jonathan Amsterdam <jba@google.com>
TryBot-Result: kokoro <noreply+kokoro@google.com>
Reviewed-by: Julie Qiu <julie@golang.org>
diff --git a/internal/fetch/fetchlocal.go b/internal/fetch/fetchlocal.go
index be9cb9e..640b2df 100644
--- a/internal/fetch/fetchlocal.go
+++ b/internal/fetch/fetchlocal.go
@@ -17,6 +17,7 @@
"strings"
"time"
+ "golang.org/x/mod/modfile"
"golang.org/x/pkgsite/internal/derrors"
"golang.org/x/pkgsite/internal/proxy"
"golang.org/x/pkgsite/internal/source"
@@ -32,16 +33,41 @@
// A directoryModuleGetter is a ModuleGetter whose source is a directory in the file system that contains
// a module's files.
type directoryModuleGetter struct {
- dir string // the directory containing the module's files
+ modulePath string
+ dir string
}
// NewDirectoryModuleGetter returns a ModuleGetter for reading a module from a directory.
-func NewDirectoryModuleGetter(dir string) ModuleGetter {
- return &directoryModuleGetter{dir: dir}
+func NewDirectoryModuleGetter(modulePath, dir string) (*directoryModuleGetter, error) {
+ if modulePath == "" {
+ goModBytes, err := ioutil.ReadFile(filepath.Join(dir, "go.mod"))
+ if err != nil {
+ return nil, fmt.Errorf("cannot obtain module path for %q (%v): %w", dir, err, derrors.BadModule)
+ }
+ modulePath = modfile.ModulePath(goModBytes)
+ if modulePath == "" {
+ return nil, fmt.Errorf("go.mod in %q has no module path: %w", dir, derrors.BadModule)
+ }
+ }
+ return &directoryModuleGetter{
+ dir: dir,
+ modulePath: modulePath,
+ }, nil
+}
+
+func (g *directoryModuleGetter) checkPath(path string) error {
+ if path != g.modulePath {
+ return fmt.Errorf("given module path %q does not match %q for directory %q: %w",
+ path, g.modulePath, g.dir, derrors.NotFound)
+ }
+ return nil
}
// Info returns basic information about the module.
func (g *directoryModuleGetter) Info(ctx context.Context, path, version string) (*proxy.VersionInfo, error) {
+ if err := g.checkPath(path); err != nil {
+ return nil, err
+ }
return &proxy.VersionInfo{
Version: LocalVersion,
Time: LocalCommitTime,
@@ -51,18 +77,21 @@
// Mod returns the contents of the module's go.mod file.
// If the file does not exist, it returns a synthesized one.
func (g *directoryModuleGetter) Mod(ctx context.Context, path, version string) ([]byte, error) {
+ if err := g.checkPath(path); err != nil {
+ return nil, err
+ }
data, err := ioutil.ReadFile(filepath.Join(g.dir, "go.mod"))
if errors.Is(err, os.ErrNotExist) {
- if path == "" {
- return nil, fmt.Errorf("no module path: %w", derrors.BadModule)
- }
- return []byte(fmt.Sprintf("module %s\n", path)), nil
+ return []byte(fmt.Sprintf("module %s\n", g.modulePath)), nil
}
return data, err
}
// Zip returns a reader for the module's zip file.
func (g *directoryModuleGetter) Zip(ctx context.Context, path, version string) (*zip.Reader, error) {
+ if err := g.checkPath(path); err != nil {
+ return nil, err
+ }
return createZipReader(g.dir, path, LocalVersion)
}
@@ -77,7 +106,16 @@
// FetchResult.Error should be checked to verify that the fetch succeeded. Even if the
// error is non-nil the result may contain useful data.
func FetchLocalModule(ctx context.Context, modulePath, localPath string, sourceClient *source.Client) *FetchResult {
- g := NewDirectoryModuleGetter(localPath)
+ g, err := NewDirectoryModuleGetter(modulePath, localPath)
+ if err != nil {
+ return &FetchResult{
+ ModulePath: modulePath,
+ Error: err,
+ }
+ }
+ if modulePath == "" {
+ modulePath = g.modulePath
+ }
fr := FetchModule(ctx, modulePath, LocalVersion, g, sourceClient)
if fr.Error != nil {
fr.Error = fmt.Errorf("FetchLocalModule(%q, %q): %w", modulePath, localPath, fr.Error)