internal/stdlib: add ContentDir function
Add a function that returns the content directory of the stdlib
module.
Remove the part of the test that checks go.mod; it was never
executing, because there is no go.mod file in any of the repos being
tested.
For golang/go#47834
Change-Id: Idc982620f6736ec60fe9a295f0372fd272745457
Reviewed-on: https://go-review.googlesource.com/c/pkgsite/+/343965
Trust: Jonathan Amsterdam <jba@google.com>
Run-TryBot: Jonathan Amsterdam <jba@google.com>
TryBot-Result: kokoro <noreply+kokoro@google.com>
Reviewed-by: Jamal Carvalho <jamal@golang.org>
diff --git a/internal/stdlib/stdlib.go b/internal/stdlib/stdlib.go
index 96ae3c5..c1f7ffe 100644
--- a/internal/stdlib/stdlib.go
+++ b/internal/stdlib/stdlib.go
@@ -13,6 +13,7 @@
"bytes"
"fmt"
"io"
+ "io/fs"
"os"
"path"
"path/filepath"
@@ -331,6 +332,11 @@
// https://github.com/shurcooL/play/blob/master/256/moduleproxy/std/std.go.
defer derrors.Wrap(&err, "stdlib.Zip(%q)", requestedVersion)
+ zr, resolvedVersion, commitTime, _, err := zipInternal(requestedVersion)
+ return zr, resolvedVersion, commitTime, err
+}
+
+func zipInternal(requestedVersion string) (_ *zip.Reader, resolvedVersion string, commitTime time.Time, prefix string, err error) {
var repo *git.Repository
if UseTestData {
repo, err = getTestGoRepo(requestedVersion)
@@ -338,23 +344,23 @@
if requestedVersion == version.Latest {
requestedVersion, err = semanticVersion(requestedVersion)
if err != nil {
- return nil, "", time.Time{}, err
+ return nil, "", time.Time{}, "", err
}
}
repo, err = getGoRepo(requestedVersion)
}
if err != nil {
- return nil, "", time.Time{}, err
+ return nil, "", time.Time{}, "", err
}
var buf bytes.Buffer
z := zip.NewWriter(&buf)
head, err := repo.Head()
if err != nil {
- return nil, "", time.Time{}, err
+ return nil, "", time.Time{}, "", err
}
commit, err := repo.CommitObject(head.Hash())
if err != nil {
- return nil, "", time.Time{}, err
+ return nil, "", time.Time{}, "", err
}
resolvedVersion = requestedVersion
if SupportedBranches[requestedVersion] {
@@ -362,33 +368,60 @@
}
root, err := repo.TreeObject(commit.TreeHash)
if err != nil {
- return nil, "", time.Time{}, err
+ return nil, "", time.Time{}, "", err
}
prefixPath := ModulePath + "@" + requestedVersion
// Add top-level files.
if err := addFiles(z, repo, root, prefixPath, false); err != nil {
- return nil, "", time.Time{}, err
+ return nil, "", time.Time{}, "", err
}
// Add files from the stdlib directory.
libdir := root
for _, d := range strings.Split(Directory(resolvedVersion), "/") {
libdir, err = subTree(repo, libdir, d)
if err != nil {
- return nil, "", time.Time{}, err
+ return nil, "", time.Time{}, "", err
}
}
if err := addFiles(z, repo, libdir, prefixPath, true); err != nil {
- return nil, "", time.Time{}, err
+ return nil, "", time.Time{}, "", err
}
if err := z.Close(); err != nil {
- return nil, "", time.Time{}, err
+ return nil, "", time.Time{}, "", err
}
br := bytes.NewReader(buf.Bytes())
zr, err := zip.NewReader(br, int64(br.Len()))
if err != nil {
+ return nil, "", time.Time{}, "", err
+ }
+ return zr, resolvedVersion, commit.Committer.When, prefixPath, nil
+}
+
+// ContentDir creates an fs.FS representing the entire Go standard library at the
+// given version (which must have been resolved with ZipInfo) and returns a
+// reader to it. It also returns the time of the commit for that version.
+//
+// Normally, ContentDir returns the resolved version it was passed. If the
+// resolved version is a supported branch like "master", ContentDir returns a
+// semantic version for the branch.
+//
+// ContentDir reads the standard library at the Go repository tag corresponding
+// to to the given semantic version.
+//
+// ContentDir ignores go.mod files in the standard library, treating it as if it
+// were a single module named "std" at the given version.
+func ContentDir(requestedVersion string) (_ fs.FS, resolvedVersion string, commitTime time.Time, err error) {
+ defer derrors.Wrap(&err, "stdlib.ContentDir(%q)", requestedVersion)
+
+ zr, resolvedVersion, commitTime, prefix, err := zipInternal(requestedVersion)
+ if err != nil {
return nil, "", time.Time{}, err
}
- return zr, resolvedVersion, commit.Committer.When, nil
+ cdir, err := fs.Sub(zr, prefix)
+ if err != nil {
+ return nil, "", time.Time{}, err
+ }
+ return cdir, resolvedVersion, commitTime, nil
}
func newPseudoVersion(version string, commitTime time.Time, hash plumbing.Hash) string {
@@ -452,7 +485,7 @@
continue
}
if e.Name == "go.mod" {
- // ignore; we'll synthesize our own
+ // Ignore; we don't need it.
continue
}
if strings.HasPrefix(e.Name, "README") && !strings.Contains(dirpath, "/") {
diff --git a/internal/stdlib/stdlib_test.go b/internal/stdlib/stdlib_test.go
index 812e29a..3555b2f 100644
--- a/internal/stdlib/stdlib_test.go
+++ b/internal/stdlib/stdlib_test.go
@@ -5,9 +5,9 @@
package stdlib
import (
- "io/ioutil"
+ "errors"
+ "io/fs"
"reflect"
- "strings"
"testing"
"golang.org/x/mod/semver"
@@ -117,7 +117,7 @@
}
}
-func TestZip(t *testing.T) {
+func TestContentDir(t *testing.T) {
UseTestData = true
defer func() { UseTestData = false }()
for _, resolvedVersion := range []string{
@@ -128,7 +128,7 @@
version.Master,
} {
t.Run(resolvedVersion, func(t *testing.T) {
- zr, gotResolvedVersion, gotTime, err := Zip(resolvedVersion)
+ cdir, gotResolvedVersion, gotTime, err := ContentDir(resolvedVersion)
if err != nil {
t.Fatal(err)
}
@@ -151,38 +151,26 @@
wantFiles["cmd/README.vendor"] = true
}
- wantPrefix := "std@" + resolvedVersion + "/"
- readmeVendorFile := wantPrefix + "README.vendor"
- for _, f := range zr.File {
- if f.Name == readmeVendorFile {
- t.Fatalf("got %q; want file to be removed", readmeVendorFile)
+ const readmeVendorFile = "README.vendor"
+ if _, err := fs.Stat(cdir, readmeVendorFile); !errors.Is(err, fs.ErrNotExist) {
+ t.Fatalf("fs.Stat returned %v; want %q to be removed", err, readmeVendorFile)
+ }
+ err = fs.WalkDir(cdir, ".", func(path string, d fs.DirEntry, err error) error {
+ if err != nil {
+ return err
}
- if !strings.HasPrefix(f.Name, wantPrefix) {
- t.Errorf("filename %q missing prefix %q", f.Name, wantPrefix)
- continue
+ if d.IsDir() {
+ return nil
}
- delete(wantFiles, f.Name[len(wantPrefix):])
+ delete(wantFiles, path)
+ return nil
+ })
+ if err != nil {
+ t.Fatal(err)
}
if len(wantFiles) > 0 {
t.Errorf("zip missing files: %v", reflect.ValueOf(wantFiles).MapKeys())
}
- for _, f := range zr.File {
- if f.Name == wantPrefix+"go.mod" {
- r, err := f.Open()
- if err != nil {
- t.Fatal(err)
- }
- defer r.Close()
- b, err := ioutil.ReadAll(r)
- if err != nil {
- t.Fatal(err)
- }
- if got, want := string(b), "module std\n"; got != want {
- t.Errorf("go.mod: got %q, want %q", got, want)
- }
- break
- }
- }
})
}
}