зеркало из https://github.com/golang/pkgsite.git
internal: add GetLicenses by path
This change introduces a new method on the DataSource interface, GetLicenses, which returns the licenses that apply for a given path, module path, and resolved version combination. A license in the current or any parent directory of the specified path applies to it. Fixes golang/go#40027 Change-Id: If91429ac12880ac3b6254bf0acd7d3ac983c93e7 Reviewed-on: https://go-review.googlesource.com/c/pkgsite/+/241718 Reviewed-by: Julie Qiu <julie@golang.org>
This commit is contained in:
Родитель
130a4e343b
Коммит
f7303dcc15
|
@ -21,6 +21,8 @@ type DataSource interface {
|
|||
// GetImports returns a slice of import paths imported by the package
|
||||
// specified by path and version.
|
||||
GetImports(ctx context.Context, pkgPath, modulePath, version string) ([]string, error)
|
||||
// GetLicenses returns licenses at the given path for given modulePath and version.
|
||||
GetLicenses(ctx context.Context, fullPath, modulePath, resolvedVersion string) ([]*licenses.License, error)
|
||||
// GetModuleInfo returns the ModuleInfo corresponding to modulePath and
|
||||
// version.
|
||||
GetModuleInfo(ctx context.Context, modulePath, version string) (*ModuleInfo, error)
|
||||
|
|
|
@ -8,6 +8,7 @@ import (
|
|||
"context"
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"path"
|
||||
"reflect"
|
||||
"sort"
|
||||
"strings"
|
||||
|
@ -17,6 +18,61 @@ import (
|
|||
"golang.org/x/pkgsite/internal/licenses"
|
||||
)
|
||||
|
||||
// GetLicenses returns the licenses that applies to the fullPath for the given module version.
|
||||
// It returns an InvalidArgument error if the module path or version is invalid.
|
||||
func (db *DB) GetLicenses(ctx context.Context, fullPath, modulePath, resolvedVersion string) (_ []*licenses.License, err error) {
|
||||
defer derrors.Wrap(&err, "GetLicenses(ctx, %q, %q, %q)", fullPath, modulePath, resolvedVersion)
|
||||
|
||||
if fullPath == "" || resolvedVersion == "" {
|
||||
return nil, fmt.Errorf("neither fullpath nor resolvedVersion can be empty: %w", derrors.InvalidArgument)
|
||||
}
|
||||
query := `
|
||||
SELECT
|
||||
l.types,
|
||||
l.file_path,
|
||||
l.contents,
|
||||
l.coverage
|
||||
FROM
|
||||
licenses l
|
||||
INNER JOIN
|
||||
paths p
|
||||
ON
|
||||
p.module_id=l.module_id
|
||||
INNER JOIN
|
||||
modules m
|
||||
ON
|
||||
p.module_id=m.id
|
||||
WHERE
|
||||
p.path = $1
|
||||
AND m.module_path = $2
|
||||
AND m.version = $3;`
|
||||
|
||||
rows, err := db.db.Query(ctx, query, fullPath, modulePath, resolvedVersion)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
moduleLicenses, err := collectLicenses(rows)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var lics []*licenses.License
|
||||
|
||||
// The `query` returns all licenses for the module version. We need to
|
||||
// filter the licenses that applies to the specified fullPath, i.e.
|
||||
// A license in the current or any parent directory of the specified
|
||||
// fullPath applies to it.
|
||||
for _, license := range moduleLicenses {
|
||||
licensePath := path.Join(modulePath, path.Dir(license.FilePath))
|
||||
if strings.HasPrefix(fullPath, licensePath) {
|
||||
lics = append(lics, license)
|
||||
}
|
||||
}
|
||||
return lics, nil
|
||||
}
|
||||
|
||||
// LegacyGetModuleLicenses returns all licenses associated with the given module path and
|
||||
// version. These are the top-level licenses in the module zip file.
|
||||
// It returns an InvalidArgument error if the module path or version is invalid.
|
||||
|
|
|
@ -6,13 +6,83 @@ package postgres
|
|||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"sort"
|
||||
"testing"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"github.com/google/go-cmp/cmp/cmpopts"
|
||||
"golang.org/x/pkgsite/internal/derrors"
|
||||
"golang.org/x/pkgsite/internal/licenses"
|
||||
"golang.org/x/pkgsite/internal/testing/sample"
|
||||
)
|
||||
|
||||
func TestGetLicenses(t *testing.T) {
|
||||
testModule := sample.Module(sample.ModulePath, "v1.2.3", "A/B")
|
||||
mit := &licenses.Metadata{Types: []string{"MIT"}, FilePath: "LICENSE"}
|
||||
bsd := &licenses.Metadata{Types: []string{"BSD-3-Clause"}, FilePath: "A/B/LICENSE"}
|
||||
|
||||
mitLicense := &licenses.License{Metadata: mit}
|
||||
bsdLicense := &licenses.License{Metadata: bsd}
|
||||
testModule.Licenses = []*licenses.License{bsdLicense, mitLicense}
|
||||
sort.Slice(testModule.Directories, func(i, j int) bool {
|
||||
return testModule.Directories[i].Path < testModule.Directories[j].Path
|
||||
})
|
||||
|
||||
// github.com/valid/module_name
|
||||
testModule.Directories[0].Licenses = []*licenses.Metadata{mit}
|
||||
// github.com/valid/module_name/A
|
||||
testModule.Directories[1].Licenses = []*licenses.Metadata{mit}
|
||||
// github.com/valid/module_name/A/B
|
||||
testModule.Directories[2].Licenses = []*licenses.Metadata{mit, bsd}
|
||||
|
||||
tests := []struct {
|
||||
err error
|
||||
name string
|
||||
fullPath string
|
||||
want []*licenses.License
|
||||
}{
|
||||
{name: "empty path", err: derrors.InvalidArgument},
|
||||
{name: "module root", fullPath: sample.ModulePath, want: []*licenses.License{testModule.Licenses[1]}},
|
||||
{name: "package without license", fullPath: sample.ModulePath + "/A", want: []*licenses.License{testModule.Licenses[1]}},
|
||||
{name: "package with additional license", fullPath: sample.ModulePath + "/A/B", want: testModule.Licenses},
|
||||
}
|
||||
|
||||
defer ResetTestDB(testDB, t)
|
||||
ctx, cancel := context.WithTimeout(context.Background(), testTimeout*5)
|
||||
defer cancel()
|
||||
if err := testDB.InsertModule(ctx, testModule); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
got, err := testDB.GetLicenses(ctx, test.fullPath, sample.ModulePath, testModule.Version)
|
||||
if !errors.Is(err, test.err) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
sort.Slice(got, func(i, j int) bool {
|
||||
return got[i].FilePath < got[j].FilePath
|
||||
})
|
||||
sort.Slice(test.want, func(i, j int) bool {
|
||||
return test.want[i].FilePath < test.want[j].FilePath
|
||||
})
|
||||
for i := range got {
|
||||
sort.Strings(got[i].Types)
|
||||
}
|
||||
for i := range test.want {
|
||||
sort.Strings(test.want[i].Types)
|
||||
}
|
||||
|
||||
cmpopt := cmpopts.IgnoreFields(licenses.License{}, "Contents")
|
||||
if diff := cmp.Diff(test.want, got, cmpopt); diff != "" {
|
||||
t.Errorf("mismatch (-want +got):\n%s", diff)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestLegacyGetModuleLicenses(t *testing.T) {
|
||||
modulePath := "test.module"
|
||||
testModule := sample.Module(modulePath, "v1.2.3", "", "foo", "bar")
|
||||
|
|
|
@ -117,6 +117,33 @@ func (ds *DataSource) GetImports(ctx context.Context, pkgPath, modulePath, versi
|
|||
return vp.Imports, nil
|
||||
}
|
||||
|
||||
// GetLicenses return licenses at path for the given module path and version.
|
||||
func (ds *DataSource) GetLicenses(ctx context.Context, fullPath, modulePath, resolvedVersion string) (_ []*licenses.License, err error) {
|
||||
defer derrors.Wrap(&err, "GetLicenses(%q, %q, %q)", fullPath, modulePath, resolvedVersion)
|
||||
v, err := ds.getModule(ctx, modulePath, resolvedVersion)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var lics []*licenses.License
|
||||
|
||||
// ds.getModule() returns all licenses for the module version. We need to
|
||||
// filter the licenses that applies to the specified fullPath, i.e.
|
||||
// A license in the current or any parent directory of the specified
|
||||
// fullPath applies to it.
|
||||
for _, license := range v.Licenses {
|
||||
licensePath := path.Join(modulePath, path.Dir(license.FilePath))
|
||||
if strings.HasPrefix(fullPath, licensePath) {
|
||||
lics = append(lics, license)
|
||||
}
|
||||
}
|
||||
|
||||
if len(lics) == 0 {
|
||||
return nil, fmt.Errorf("path %s is missing from module %s: %w", fullPath, modulePath, derrors.NotFound)
|
||||
}
|
||||
return lics, nil
|
||||
}
|
||||
|
||||
// LegacyGetModuleLicenses returns root-level licenses detected within the module zip
|
||||
// for modulePath and version.
|
||||
func (ds *DataSource) LegacyGetModuleLicenses(ctx context.Context, modulePath, version string) (_ []*licenses.License, err error) {
|
||||
|
|
|
@ -6,12 +6,16 @@ package proxydatasource
|
|||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"sort"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"github.com/google/go-cmp/cmp/cmpopts"
|
||||
"github.com/google/licensecheck"
|
||||
"golang.org/x/pkgsite/internal"
|
||||
"golang.org/x/pkgsite/internal/derrors"
|
||||
"golang.org/x/pkgsite/internal/licenses"
|
||||
"golang.org/x/pkgsite/internal/proxy"
|
||||
"golang.org/x/pkgsite/internal/testing/sample"
|
||||
|
@ -49,6 +53,28 @@ func setup(t *testing.T) (context.Context, *DataSource, func()) {
|
|||
var (
|
||||
wantLicenseMD = sample.LicenseMetadata[0]
|
||||
wantLicense = &licenses.License{Metadata: wantLicenseMD}
|
||||
wantLicenseMIT = &licenses.License{
|
||||
Metadata: &licenses.Metadata{
|
||||
Types: []string{"MIT"},
|
||||
FilePath: "LICENSE",
|
||||
Coverage: licensecheck.Coverage{
|
||||
Percent: 100,
|
||||
Match: []licensecheck.Match{{Name: "MIT", Type: licensecheck.MIT, Percent: 100, End: 1049}},
|
||||
},
|
||||
},
|
||||
Contents: []byte(testhelper.MITLicense),
|
||||
}
|
||||
wantLicenseBSD = &licenses.License{
|
||||
Metadata: &licenses.Metadata{
|
||||
Types: []string{"BSD-0-Clause"},
|
||||
FilePath: "qux/LICENSE",
|
||||
Coverage: licensecheck.Coverage{
|
||||
Percent: 100,
|
||||
Match: []licensecheck.Match{{Name: "BSD-0-Clause", Type: licensecheck.BSD, Percent: 100, End: 633}},
|
||||
},
|
||||
},
|
||||
Contents: []byte(testhelper.BSD0License),
|
||||
}
|
||||
wantPackage = internal.LegacyPackage{
|
||||
Path: "foo.com/bar/baz",
|
||||
Name: "baz",
|
||||
|
@ -144,6 +170,68 @@ func TestDataSource_GetModuleInfo(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestDataSource_GetLicenses(t *testing.T) {
|
||||
t.Helper()
|
||||
testModules := []*proxy.TestModule{
|
||||
{
|
||||
ModulePath: "foo.com/bar",
|
||||
Version: "v1.1.0",
|
||||
Files: map[string]string{
|
||||
"go.mod": "module foo.com/bar",
|
||||
"LICENSE": testhelper.MITLicense,
|
||||
"bar.go": "//Package bar provides a helpful constant.\npackage bar\nimport \"net/http\"\nconst OK = http.StatusOK",
|
||||
|
||||
"baz/baz.go": "//Package baz provides a helpful constant.\npackage baz\nimport \"net/http\"\nconst OK = http.StatusOK",
|
||||
|
||||
"qux/LICENSE": testhelper.BSD0License,
|
||||
"qux/qux.go": "//Package qux provides a helpful constant.\npackage qux\nimport \"net/http\"\nconst OK = http.StatusOK",
|
||||
},
|
||||
},
|
||||
}
|
||||
client, teardownProxy := proxy.SetupTestProxy(t, testModules)
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||||
ds := New(client)
|
||||
teardown := func() {
|
||||
teardownProxy()
|
||||
cancel()
|
||||
}
|
||||
defer teardown()
|
||||
|
||||
tests := []struct {
|
||||
err error
|
||||
name string
|
||||
fullPath string
|
||||
modulePath string
|
||||
want []*licenses.License
|
||||
}{
|
||||
{name: "no license dir", fullPath: "foo.com", modulePath: "foo.com/bar", err: derrors.NotFound},
|
||||
{name: "invalid dir", fullPath: "foo.com/invalid", modulePath: "foo.com/invalid", err: derrors.NotFound},
|
||||
{name: "root dir", fullPath: "foo.com/bar", modulePath: "foo.com/bar", want: []*licenses.License{wantLicenseMIT}},
|
||||
{name: "package with no extra license", fullPath: "foo.com/bar/baz", modulePath: "foo.com/bar", want: []*licenses.License{wantLicenseMIT}},
|
||||
{name: "package with additional license", fullPath: "foo.com/bar/qux", modulePath: "foo.com/bar", want: []*licenses.License{wantLicenseMIT, wantLicenseBSD}},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
got, err := ds.GetLicenses(ctx, test.fullPath, test.modulePath, "v1.1.0")
|
||||
if !errors.Is(err, test.err) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
sort.Slice(got, func(i, j int) bool {
|
||||
return got[i].FilePath < got[j].FilePath
|
||||
})
|
||||
sort.Slice(test.want, func(i, j int) bool {
|
||||
return test.want[i].FilePath < test.want[j].FilePath
|
||||
})
|
||||
|
||||
if diff := cmp.Diff(test.want, got); diff != "" {
|
||||
t.Errorf("GetLicenses diff (-want +got):\n%s", diff)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestDataSource_LegacyGetModuleLicenses(t *testing.T) {
|
||||
ctx, ds, teardown := setup(t)
|
||||
defer teardown()
|
||||
|
|
Загрузка…
Ссылка в новой задаче