Parse the Maven license from the pom.xml if not contained in the mani… (#2115)

* Parse the Maven license from the pom.xml if not contained in the manifest

Signed-off-by: Colm O hEigeartaigh <coheigea@apache.org>

* chore: restore 10.0.2 schema

Signed-off-by: Christopher Phillips <christopher.phillips@anchore.com>

* chore: generate new 11.0.1 schema

Signed-off-by: Christopher Phillips <christopher.phillips@anchore.com>

* refactor: remove schema change

Signed-off-by: Christopher Phillips <christopher.phillips@anchore.com>

* test: update unit tests to align with new pattern

Signed-off-by: Christopher Phillips <christopher.phillips@anchore.com>

* chore: pr feedback

Signed-off-by: Christopher Phillips <christopher.phillips@anchore.com>

* chore: remove struct tags

Signed-off-by: Christopher Phillips <christopher.phillips@anchore.com>

* keep license name and url semantics preserved on the pkg object

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>

---------

Signed-off-by: Colm O hEigeartaigh <coheigea@apache.org>
Signed-off-by: Christopher Phillips <christopher.phillips@anchore.com>
Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>
Co-authored-by: Christopher Angelo Phillips <32073428+spiffcs@users.noreply.github.com>
Co-authored-by: Christopher Phillips <christopher.phillips@anchore.com>
Co-authored-by: Alex Goodman <wagoodman@users.noreply.github.com>
This commit is contained in:
Colm O hEigeartaigh 2023-10-10 18:09:44 +01:00 committed by GitHub
parent 185d0d1bfa
commit 0748945c83
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 691 additions and 82 deletions

View file

@ -171,19 +171,14 @@ func (j *archiveParser) discoverMainPackage() (*pkg.Package, error) {
return nil, nil
}
archiveCloser, err := os.Open(j.archivePath)
if err != nil {
return nil, fmt.Errorf("unable to open archive path (%s): %w", j.archivePath, err)
}
defer archiveCloser.Close()
// grab and assign digest for the entire archive
digests, err := intFile.NewDigestsFromFile(archiveCloser, javaArchiveHashes)
digests, err := getDigestsFromArchive(j.archivePath)
if err != nil {
log.Warnf("failed to create digest for file=%q: %+v", j.archivePath, err)
return nil, err
}
// we use j.location because we want to associate the license declaration with where we discovered the contents in the manifest
// TODO: when we support locations of paths within archives we should start passing the specific manifest location object instead of the top jar
licenses := pkg.NewLicensesFromLocation(j.location, selectLicenses(manifest)...)
/*
We should name and version from, in this order:
@ -192,13 +187,20 @@ func (j *archiveParser) discoverMainPackage() (*pkg.Package, error) {
3. manifest
4. filename
*/
name, version := j.guessMainPackageNameAndVersionFromPomInfo()
name, version, pomLicenses := j.guessMainPackageNameAndVersionFromPomInfo()
if name == "" {
name = selectName(manifest, j.fileInfo)
}
if version == "" {
version = selectVersion(manifest, j.fileInfo)
}
if len(licenses) == 0 {
// Today we don't have a way to distinguish between licenses from the manifest and licenses from the pom.xml
// until the file.Location object can support sub-paths (i.e. paths within archives, recursively; issue https://github.com/anchore/syft/issues/2211).
// Until then it's less confusing to use the licenses from the pom.xml only if the manifest did not list any.
licenses = append(licenses, pomLicenses...)
}
return &pkg.Package{
// TODO: maybe select name should just have a pom properties in it?
Name: name,
@ -218,11 +220,16 @@ func (j *archiveParser) discoverMainPackage() (*pkg.Package, error) {
}, nil
}
func (j *archiveParser) guessMainPackageNameAndVersionFromPomInfo() (string, string) {
type parsedPomProject struct {
*pkg.PomProject
Licenses []pkg.License
}
func (j *archiveParser) guessMainPackageNameAndVersionFromPomInfo() (name, version string, licenses []pkg.License) {
pomPropertyMatches := j.fileManifest.GlobMatch(pomPropertiesGlob)
pomMatches := j.fileManifest.GlobMatch(pomXMLGlob)
var pomPropertiesObject pkg.PomProperties
var pomProjectObject pkg.PomProject
var pomProjectObject parsedPomProject
if len(pomPropertyMatches) == 1 || len(pomMatches) == 1 {
// we have exactly 1 pom.properties or pom.xml in the archive; assume it represents the
// package we're scanning if the names seem like a plausible match
@ -238,15 +245,15 @@ func (j *archiveParser) guessMainPackageNameAndVersionFromPomInfo() (string, str
}
}
}
name := pomPropertiesObject.ArtifactID
if name == "" {
name = pomPropertiesObject.ArtifactID
if name == "" && pomProjectObject.PomProject != nil {
name = pomProjectObject.ArtifactID
}
version := pomPropertiesObject.Version
if version == "" {
version = pomPropertiesObject.Version
if version == "" && pomProjectObject.PomProject != nil {
version = pomProjectObject.Version
}
return name, version
return name, version, pomProjectObject.Licenses
}
// discoverPkgsFromAllMavenFiles parses Maven POM properties/xml for a given
@ -273,7 +280,7 @@ func (j *archiveParser) discoverPkgsFromAllMavenFiles(parentPkg *pkg.Package) ([
}
for parentPath, propertiesObj := range properties {
var pomProject *pkg.PomProject
var pomProject *parsedPomProject
if proj, exists := projects[parentPath]; exists {
pomProject = &proj
}
@ -287,6 +294,22 @@ func (j *archiveParser) discoverPkgsFromAllMavenFiles(parentPkg *pkg.Package) ([
return pkgs, nil
}
func getDigestsFromArchive(archivePath string) ([]file.Digest, error) {
archiveCloser, err := os.Open(archivePath)
if err != nil {
return nil, fmt.Errorf("unable to open archive path (%s): %w", archivePath, err)
}
defer archiveCloser.Close()
// grab and assign digest for the entire archive
digests, err := intFile.NewDigestsFromFile(archiveCloser, javaArchiveHashes)
if err != nil {
log.Warnf("failed to create digest for file=%q: %+v", archivePath, err)
}
return digests, nil
}
func (j *archiveParser) discoverPkgsFromNestedArchives(parentPkg *pkg.Package) ([]pkg.Package, []artifact.Relationship, error) {
// we know that all java archives are zip formatted files, so we can use the shared zip helper
return discoverPkgsFromZip(j.location, j.archivePath, j.contentPath, j.fileManifest, parentPkg)
@ -388,15 +411,16 @@ func pomPropertiesByParentPath(archivePath string, location file.Location, extra
return propertiesByParentPath, nil
}
func pomProjectByParentPath(archivePath string, location file.Location, extractPaths []string) (map[string]pkg.PomProject, error) {
func pomProjectByParentPath(archivePath string, location file.Location, extractPaths []string) (map[string]parsedPomProject, error) {
contentsOfMavenProjectFiles, err := intFile.ContentsFromZip(archivePath, extractPaths...)
if err != nil {
return nil, fmt.Errorf("unable to extract maven files: %w", err)
}
projectByParentPath := make(map[string]pkg.PomProject)
projectByParentPath := make(map[string]parsedPomProject)
for filePath, fileContents := range contentsOfMavenProjectFiles {
pomProject, err := parsePomXMLProject(filePath, strings.NewReader(fileContents))
// TODO: when we support locations of paths within archives we should start passing the specific pom.xml location object instead of the top jar
pomProject, err := parsePomXMLProject(filePath, strings.NewReader(fileContents), location)
if err != nil {
log.WithFields("contents-path", filePath, "location", location.AccessPath()).Warnf("failed to parse pom.xml: %+v", err)
continue
@ -418,7 +442,7 @@ func pomProjectByParentPath(archivePath string, location file.Location, extractP
// newPackageFromMavenData processes a single Maven POM properties for a given parent package, returning all listed Java packages found and
// associating each discovered package to the given parent package. Note the pom.xml is optional, the pom.properties is not.
func newPackageFromMavenData(pomProperties pkg.PomProperties, pomProject *pkg.PomProject, parentPkg *pkg.Package, location file.Location) *pkg.Package {
func newPackageFromMavenData(pomProperties pkg.PomProperties, parsedPomProject *parsedPomProject, parentPkg *pkg.Package, location file.Location) *pkg.Package {
// keep the artifact name within the virtual path if this package does not match the parent package
vPathSuffix := ""
groupID := ""
@ -440,20 +464,27 @@ func newPackageFromMavenData(pomProperties pkg.PomProperties, pomProject *pkg.Po
}
virtualPath := location.AccessPath() + vPathSuffix
// discovered props = new package
var pkgPomProject *pkg.PomProject
licenses := make([]pkg.License, 0)
if parsedPomProject != nil {
pkgPomProject = parsedPomProject.PomProject
licenses = append(licenses, parsedPomProject.Licenses...)
}
p := pkg.Package{
Name: pomProperties.ArtifactID,
Version: pomProperties.Version,
Locations: file.NewLocationSet(
location.WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation),
),
Licenses: pkg.NewLicenseSet(licenses...),
Language: pkg.Java,
Type: pomProperties.PkgTypeIndicated(),
MetadataType: pkg.JavaMetadataType,
Metadata: pkg.JavaMetadata{
VirtualPath: virtualPath,
PomProperties: &pomProperties,
PomProject: pomProject,
PomProject: pkgPomProject,
Parent: parentPkg,
},
}

View file

@ -6,7 +6,6 @@ import (
"io"
"os"
"os/exec"
"path"
"path/filepath"
"strings"
"syscall"
@ -17,6 +16,7 @@ import (
"github.com/anchore/syft/internal"
"github.com/anchore/syft/syft/file"
"github.com/anchore/syft/syft/license"
"github.com/anchore/syft/syft/pkg"
"github.com/anchore/syft/syft/pkg/cataloger/internal/pkgtest"
)
@ -83,11 +83,13 @@ func generateJavaBuildFixture(t *testing.T, fixturePath string) {
func TestParseJar(t *testing.T) {
tests := []struct {
name string
fixture string
expected map[string]pkg.Package
ignoreExtras []string
}{
{
name: "example-jenkins-plugin",
fixture: "test-fixtures/java-builds/packages/example-jenkins-plugin.hpi",
ignoreExtras: []string{
"Plugin-Version", // has dynamic date
@ -146,6 +148,7 @@ func TestParseJar(t *testing.T) {
},
},
{
name: "example-java-app-gradle",
fixture: "test-fixtures/java-builds/packages/example-java-app-gradle-0.1.0.jar",
expected: map[string]pkg.Package{
"example-java-app-gradle": {
@ -172,6 +175,16 @@ func TestParseJar(t *testing.T) {
Language: pkg.Java,
Type: pkg.JavaPkg,
MetadataType: pkg.JavaMetadataType,
Licenses: pkg.NewLicenseSet(
pkg.NewLicenseFromFields(
"Apache 2",
"http://www.apache.org/licenses/LICENSE-2.0.txt",
func() *file.Location {
l := file.NewLocation("test-fixtures/java-builds/packages/example-java-app-gradle-0.1.0.jar")
return &l
}(),
),
),
Metadata: pkg.JavaMetadata{
// ensure that nested packages with different names than that of the parent are appended as
// a suffix on the virtual path with a colon separator between group name and artifact name
@ -196,6 +209,7 @@ func TestParseJar(t *testing.T) {
},
},
{
name: "example-java-app-maven",
fixture: "test-fixtures/java-builds/packages/example-java-app-maven-0.1.0.jar",
ignoreExtras: []string{
"Build-Jdk", // can't guarantee the JDK used at build time
@ -231,9 +245,19 @@ func TestParseJar(t *testing.T) {
},
},
"joda-time": {
Name: "joda-time",
Version: "2.9.2",
PURL: "pkg:maven/joda-time/joda-time@2.9.2",
Name: "joda-time",
Version: "2.9.2",
PURL: "pkg:maven/joda-time/joda-time@2.9.2",
Licenses: pkg.NewLicenseSet(
pkg.NewLicenseFromFields(
"Apache 2",
"http://www.apache.org/licenses/LICENSE-2.0.txt",
func() *file.Location {
l := file.NewLocation("test-fixtures/java-builds/packages/example-java-app-maven-0.1.0.jar")
return &l
}(),
),
),
Language: pkg.Java,
Type: pkg.JavaPkg,
MetadataType: pkg.JavaMetadataType,
@ -263,7 +287,7 @@ func TestParseJar(t *testing.T) {
}
for _, test := range tests {
t.Run(path.Base(test.fixture), func(t *testing.T) {
t.Run(test.name, func(t *testing.T) {
generateJavaBuildFixture(t, test.fixture)
@ -618,7 +642,7 @@ func Test_newPackageFromMavenData(t *testing.T) {
tests := []struct {
name string
props pkg.PomProperties
project *pkg.PomProject
project *parsedPomProject
parent *pkg.Package
expectedParent pkg.Package
expectedPackage *pkg.Package
@ -687,18 +711,29 @@ func Test_newPackageFromMavenData(t *testing.T) {
ArtifactID: "some-artifact-id",
Version: "1.0",
},
project: &pkg.PomProject{
Parent: &pkg.PomParent{
GroupID: "some-parent-group-id",
ArtifactID: "some-parent-artifact-id",
Version: "1.0-parent",
project: &parsedPomProject{
PomProject: &pkg.PomProject{
Parent: &pkg.PomParent{
GroupID: "some-parent-group-id",
ArtifactID: "some-parent-artifact-id",
Version: "1.0-parent",
},
Name: "some-name",
GroupID: "some-group-id",
ArtifactID: "some-artifact-id",
Version: "1.0",
Description: "desc",
URL: "aweso.me",
},
Licenses: []pkg.License{
{
Value: "MIT",
SPDXExpression: "MIT",
Type: license.Declared,
URLs: internal.NewStringSet("https://opensource.org/licenses/MIT"),
Locations: file.NewLocationSet(file.NewLocation("some-license-path")),
},
},
Name: "some-name",
GroupID: "some-group-id",
ArtifactID: "some-artifact-id",
Version: "1.0",
Description: "desc",
URL: "aweso.me",
},
parent: &pkg.Package{
Name: "some-parent-name",
@ -727,6 +762,15 @@ func Test_newPackageFromMavenData(t *testing.T) {
Language: pkg.Java,
Type: pkg.JavaPkg,
MetadataType: pkg.JavaMetadataType,
Licenses: pkg.NewLicenseSet(
pkg.License{
Value: "MIT",
SPDXExpression: "MIT",
Type: license.Declared,
URLs: internal.NewStringSet("https://opensource.org/licenses/MIT"),
Locations: file.NewLocationSet(file.NewLocation("some-license-path")),
},
),
Metadata: pkg.JavaMetadata{
VirtualPath: virtualPath + ":" + "some-group-id" + ":" + "some-artifact-id",
PomProperties: &pkg.PomProperties{

View file

@ -49,28 +49,51 @@ func parserPomXML(_ file.Resolver, _ *generic.Environment, reader file.LocationR
return pkgs, nil, nil
}
func parsePomXMLProject(path string, reader io.Reader) (*pkg.PomProject, error) {
func parsePomXMLProject(path string, reader io.Reader, location file.Location) (*parsedPomProject, error) {
project, err := decodePomXML(reader)
if err != nil {
return nil, err
}
return newPomProject(path, project), nil
return newPomProject(path, project, location), nil
}
func newPomProject(path string, p gopom.Project) *pkg.PomProject {
func newPomProject(path string, p gopom.Project, location file.Location) *parsedPomProject {
artifactID := safeString(p.ArtifactID)
name := safeString(p.Name)
projectURL := safeString(p.URL)
var licenses []pkg.License
if p.Licenses != nil {
for _, license := range *p.Licenses {
var licenseName, licenseURL string
if license.Name != nil {
licenseName = *license.Name
}
if license.URL != nil {
licenseURL = *license.URL
}
if licenseName == "" && licenseURL == "" {
continue
}
licenses = append(licenses, pkg.NewLicenseFromFields(licenseName, licenseURL, &location))
}
}
log.WithFields("path", path, "artifactID", artifactID, "name", name, "projectURL", projectURL).Trace("parsing pom.xml")
return &pkg.PomProject{
Path: path,
Parent: pomParent(p, p.Parent),
GroupID: resolveProperty(p, p.GroupID, "groupId"),
ArtifactID: artifactID,
Version: resolveProperty(p, p.Version, "version"),
Name: name,
Description: cleanDescription(p.Description),
URL: projectURL,
return &parsedPomProject{
PomProject: &pkg.PomProject{
Path: path,
Parent: pomParent(p, p.Parent),
GroupID: resolveProperty(p, p.GroupID, "groupId"),
ArtifactID: artifactID,
Version: resolveProperty(p, p.Version, "version"),
Name: name,
Description: cleanDescription(p.Description),
URL: projectURL,
},
Licenses: licenses,
}
}

View file

@ -11,7 +11,9 @@ import (
"github.com/stretchr/testify/require"
"github.com/vifraa/gopom"
"github.com/anchore/syft/internal"
"github.com/anchore/syft/syft/file"
"github.com/anchore/syft/syft/license"
"github.com/anchore/syft/syft/pkg"
"github.com/anchore/syft/syft/pkg/cataloger/internal/pkgtest"
)
@ -293,33 +295,79 @@ func Test_parseCommonsTextPomXMLProject(t *testing.T) {
}
func Test_parsePomXMLProject(t *testing.T) {
// TODO: ideally we would have the path to the contained pom.xml, not the jar
jarLocation := file.NewLocation("path/to/archive.jar")
tests := []struct {
expected pkg.PomProject
name string
expected parsedPomProject
}{
{
expected: pkg.PomProject{
Path: "test-fixtures/pom/commons-codec.pom.xml",
Parent: &pkg.PomParent{
GroupID: "org.apache.commons",
ArtifactID: "commons-parent",
Version: "42",
name: "go case",
expected: parsedPomProject{
PomProject: &pkg.PomProject{
Path: "test-fixtures/pom/commons-codec.pom.xml",
Parent: &pkg.PomParent{
GroupID: "org.apache.commons",
ArtifactID: "commons-parent",
Version: "42",
},
GroupID: "commons-codec",
ArtifactID: "commons-codec",
Version: "1.11",
Name: "Apache Commons Codec",
Description: "The Apache Commons Codec package contains simple encoder and decoders for various formats such as Base64 and Hexadecimal. In addition to these widely used encoders and decoders, the codec package also maintains a collection of phonetic encoding utilities.",
URL: "http://commons.apache.org/proper/commons-codec/",
},
},
},
{
name: "with license data",
expected: parsedPomProject{
PomProject: &pkg.PomProject{
Path: "test-fixtures/pom/neo4j-license-maven-plugin.pom.xml",
Parent: &pkg.PomParent{
GroupID: "org.sonatype.oss",
ArtifactID: "oss-parent",
Version: "7",
},
GroupID: "org.neo4j.build.plugins",
ArtifactID: "license-maven-plugin",
Version: "4-SNAPSHOT",
Name: "${project.artifactId}", // TODO: this is not an ideal answer
Description: "Maven 2 plugin to check and update license headers in source files",
URL: "http://components.neo4j.org/${project.artifactId}/${project.version}", // TODO: this is not an ideal answer
},
Licenses: []pkg.License{
{
Value: "The Apache Software License, Version 2.0",
SPDXExpression: "", // TODO: ideally we would parse this title to get Apache-2.0 (created issue #2210 https://github.com/anchore/syft/issues/2210)
Type: license.Declared,
URLs: internal.NewStringSet("http://www.apache.org/licenses/LICENSE-2.0.txt"),
Locations: file.NewLocationSet(jarLocation),
},
{
Value: "MIT",
SPDXExpression: "MIT",
Type: license.Declared,
URLs: internal.NewStringSet(),
Locations: file.NewLocationSet(jarLocation),
},
{
Type: license.Declared,
URLs: internal.NewStringSet("https://opensource.org/license/unlicense/"),
Locations: file.NewLocationSet(jarLocation),
},
},
GroupID: "commons-codec",
ArtifactID: "commons-codec",
Version: "1.11",
Name: "Apache Commons Codec",
Description: "The Apache Commons Codec package contains simple encoder and decoders for various formats such as Base64 and Hexadecimal. In addition to these widely used encoders and decoders, the codec package also maintains a collection of phonetic encoding utilities.",
URL: "http://commons.apache.org/proper/commons-codec/",
},
},
}
for _, test := range tests {
t.Run(test.expected.Path, func(t *testing.T) {
t.Run(test.name, func(t *testing.T) {
fixture, err := os.Open(test.expected.Path)
assert.NoError(t, err)
actual, err := parsePomXMLProject(fixture.Name(), fixture)
actual, err := parsePomXMLProject(fixture.Name(), fixture, jarLocation)
assert.NoError(t, err)
assert.Equal(t, &test.expected, actual)

View file

@ -0,0 +1,459 @@
<!-- sourced from https://github.com/neo4j/license-maven-plugin/blob/3/pom.xml -->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.sonatype.oss</groupId>
<artifactId>oss-parent</artifactId>
<version>7</version>
<relativePath />
</parent>
<groupId>org.neo4j.build.plugins</groupId>
<artifactId>license-maven-plugin</artifactId>å
<version>4-SNAPSHOT</version>
<packaging>maven-plugin</packaging>
<name>${project.artifactId}</name>
<description>Maven 2 plugin to check and update license headers in source files</description>
<url>http://components.neo4j.org/${project.artifactId}/${project.version}</url>
<inceptionYear>2008</inceptionYear>
<!--
Properties
-->
<properties>
<jdk>1.6</jdk>
<jdk.version>1.6</jdk.version>
</properties>
<!--
Versioning system
-->
<scm>
<connection>scm:git:git://github.com/neo4j/license-maven-plugin.git</connection>
<developerConnection>scm:git:git@github.com:neo4j/license-maven-plugin.git</developerConnection>
<url>https://github.com/neo4j/license-maven-plugin</url>
</scm>
<!--
Project settings
-->
<organization>
<name>Mathieu Carbou</name>
<url>http://mathieu.carbou.free.fr/</url>
</organization>
<licenses>
<!-- this has been modified for the purposes of a syft test -->
<license>
<name>The Apache Software License, Version 2.0</name>
<url>http://www.apache.org/licenses/LICENSE-2.0.txt</url>
<distribution>repo</distribution>
</license>
<license>
<name>MIT</name>
</license>
<license>
<url>https://opensource.org/license/unlicense/</url>
</license>
</licenses>
<developers>
<developer>
<id>mimilowns</id>
<name>Cédric</name>
<email>mimilowns@gmail.com</email>
<timezone>+1</timezone>
<roles>
<role>developer</role>
</roles>
</developer>
<developer>
<id>mathieu.carbou</id>
<name>Mathieu Carbou</name>
<email>mathieu.carbou@gmail.com</email>
<organization>Mycila</organization>
<organizationUrl>http://mathieu.carbou.free.fr/</organizationUrl>
<timezone>-5</timezone>
<roles>
<role>project owner</role>
<role>developer</role>
</roles>
</developer>
</developers>
<issueManagement>
<system>Google Code</system>
<url>http://code.google.com/p/${project.artifactId}/issues/list</url>
</issueManagement>
<ciManagement />
<mailingLists>
<mailingList>
<name>maven-license-plugin-announce</name>
<subscribe>maven-license-plugin-announce-subscribe@googlegroups.com</subscribe>
<unsubscribe>maven-license-plugin-announce-unsubscribe@googlegroups.com</unsubscribe>
<archive>http://groups.google.com/group/maven-license-plugin-announce</archive>
</mailingList>
<mailingList>
<name>maven-license-plugin-codesite</name>
<subscribe>maven-license-plugin-codesite-subscribe@googlegroups.com</subscribe>
<unsubscribe>maven-license-plugin-codesite-unsubscribe@googlegroups.com</unsubscribe>
<archive>http://groups.google.com/group/maven-license-plugin-codesite</archive>
</mailingList>
</mailingLists>
<!--
Distributions
-->
<distributionManagement>
<repository>
<id>sonatype-nexus-staging</id>
<name>Nexus Release Repository</name>
<url>https://oss.sonatype.org/service/local/staging/deploy/maven2/</url>
</repository>
<snapshotRepository>
<id>snapshots@m2.neo4j.org</id>
<name>snapshots@m2.neo4j.org</name>
<url>http://m2.neo4j.org/content/repositories/snapshots</url>
</snapshotRepository>
<site>
<id>neo4j-site</id>
<url>scpexe://components.neo4j.org/home/neo/components/${project.artifactId}/${project.version}</url>
</site>
</distributionManagement>
<!--
BUILD
-->
<build>
<extensions>
<extension>
<groupId>org.apache.maven.wagon</groupId>
<artifactId>wagon-webdav</artifactId>
<version>1.0-beta-2</version>
</extension>
</extensions>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-bundle-plugin</artifactId>
<version>2.1.0</version>
</plugin>
<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-ipojo-plugin</artifactId>
<version>1.6.0</version>
</plugin>
<plugin>
<groupId>com.mycila.maven-license-plugin</groupId>
<artifactId>maven-license-plugin</artifactId>
<version>1.8.0</version>
</plugin>
<plugin>
<artifactId>maven-antrun-plugin</artifactId>
<version>1.4</version>
</plugin>
<plugin>
<artifactId>maven-source-plugin</artifactId>
<version>2.1.2</version>
</plugin>
<plugin>
<artifactId>maven-jar-plugin</artifactId>
<version>2.3.1</version>
</plugin>
<plugin>
<artifactId>maven-javadoc-plugin</artifactId>
<version>2.7</version>
</plugin>
<plugin>
<artifactId>maven-deploy-plugin</artifactId>
<version>2.5</version>
</plugin>
<plugin>
<artifactId>maven-clean-plugin</artifactId>
<version>2.4.1</version>
</plugin>
<plugin>
<artifactId>maven-invoker-plugin</artifactId>
<version>1.5</version>
</plugin>
<plugin>
<artifactId>maven-shade-plugin</artifactId>
<version>1.3.3</version>
</plugin>
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<version>2.2-beta-5</version>
</plugin>
<plugin>
<artifactId>maven-dependency-plugin</artifactId>
<version>2.1</version>
</plugin>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>2.3.1</version>
</plugin>
<plugin>
<artifactId>maven-release-plugin</artifactId>
<version>2.0</version>
</plugin>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.6</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-gpg-plugin</artifactId>
<version>1.4</version>
<configuration>
<keyname>Neo Technology Build Server</keyname>
<useAgent>true</useAgent>
</configuration>
</plugin>
</plugins>
</pluginManagement>
<plugins>
<!-- for maven plugins -->
<plugin>
<artifactId>maven-plugin-plugin</artifactId>
<version>2.6</version>
</plugin>
<plugin>
<artifactId>maven-clean-plugin</artifactId>
<configuration>
<filesets>
<fileset>
<directory>.clover</directory>
</fileset>
<fileset>
<directory>test-output</directory>
</fileset>
</filesets>
</configuration>
</plugin>
<!-- compilation -->
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>${jdk}</source>
<target>${jdk}</target>
</configuration>
</plugin>
<!-- testing -->
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
</plugin>
<!-- packaging -->
<plugin>
<artifactId>maven-jar-plugin</artifactId>
<configuration>
<archive>
<manifest>
<addDefaultImplementationEntries>true</addDefaultImplementationEntries>
<addDefaultSpecificationEntries>true</addDefaultSpecificationEntries>
</manifest>
</archive>
</configuration>
</plugin>
<plugin>
<artifactId>maven-source-plugin</artifactId>
<executions>
<execution>
<id>attach-sources</id>
<goals>
<goal>jar</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<artifactId>maven-remote-resources-plugin</artifactId>
<version>1.1</version>
<executions>
<execution>
<goals>
<goal>process</goal>
</goals>
<configuration>
<resourceBundles>
<resourceBundle>org.apache:apache-jar-resource-bundle:1.3</resourceBundle>
</resourceBundles>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<configuration>
<descriptorRefs>
<descriptorRef>project</descriptorRef>
</descriptorRefs>
</configuration>
</plugin>
<!-- releasing -->
<plugin>
<artifactId>maven-deploy-plugin</artifactId>
<configuration>
<updateReleaseInfo>true</updateReleaseInfo>
</configuration>
</plugin>
<plugin>
<artifactId>maven-release-plugin</artifactId>
</plugin>
<!-- documentation -->
<plugin>
<artifactId>maven-dependency-plugin</artifactId>
</plugin>
<plugin>
<groupId>com.mycila.maven-license-plugin</groupId>
<artifactId>maven-license-plugin</artifactId>
<version>1.9.0</version>
<configuration>
<header>${basedir}/src/etc/header.txt</header>
<failIfMissing>true</failIfMissing>
<excludes>
<exclude>.gitignore</exclude>
<exclude>LICENSE.txt</exclude>
<exclude>NOTICE.txt</exclude>
<exclude>src/test/data/**</exclude>
<exclude>src/test/integration/**</exclude>
<exclude>src/test/resources/**</exclude>
</excludes>
</configuration>
<executions>
<execution>
<goals>
<goal>check</goal>
</goals>
</execution>
</executions>
</plugin>
<!-- automatically handle it tests -->
<plugin>
<artifactId>maven-clean-plugin</artifactId>
<configuration>
<filesets>
<fileset>
<directory>it</directory>
<includes>
<include>target/**</include>
<include>*/target/**</include>
</includes>
</fileset>
<fileset>
<directory>target</directory>
</fileset>
</filesets>
</configuration>
</plugin>
<plugin>
<inherited>true</inherited>
<artifactId>maven-invoker-plugin</artifactId>
<configuration>
<projectsDirectory>it</projectsDirectory>
<showErrors>true</showErrors>
<streamLogs>true</streamLogs>
<skipInstallation>${ittest.skip}</skipInstallation>
<skipInvocation>${ittest.skip}</skipInvocation>
<properties>
<target.version>${target.version}</target.version>
</properties>
<goals>
<goal>test</goal>
</goals>
<pomIncludes>
<pomInclude>**/pom.xml</pomInclude>
</pomIncludes>
</configuration>
<executions>
<execution>
<id>integration-test</id>
<goals>
<goal>install</goal>
<goal>run</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
<!--
LIBS
-->
<dependencies>
<dependency>
<groupId>org.apache.maven</groupId>
<artifactId>maven-plugin-api</artifactId>
<version>3.0.1</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>com.mycila.xmltool</groupId>
<artifactId>xmltool</artifactId>
<version>3.3</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.apache.maven</groupId>
<artifactId>maven-project</artifactId>
<version>3.0-alpha-2</version>
<scope>compile</scope>
<exclusions>
<exclusion>
<artifactId>junit</artifactId>
<groupId>junit</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.codehaus.plexus</groupId>
<artifactId>plexus-utils</artifactId>
<version>2.0.5</version>
<scope>compile</scope>
</dependency>
<!-- testing -->
<dependency>
<groupId>org.testng</groupId>
<artifactId>testng</artifactId>
<version>5.7</version>
<classifier>jdk15</classifier>
<scope>test</scope>
<exclusions>
<exclusion>
<artifactId>junit</artifactId>
<groupId>junit</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.apache.maven</groupId>
<artifactId>maven-embedder</artifactId>
<version>3.0.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.maven.plugin-testing</groupId>
<artifactId>maven-plugin-testing-harness</artifactId>
<version>2.0-alpha-1</version>
<scope>test</scope>
<exclusions>
<exclusion>
<artifactId>junit</artifactId>
<groupId>junit</groupId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
</project>

View file

@ -60,24 +60,17 @@ func (l Licenses) Swap(i, j int) {
}
func NewLicense(value string) License {
spdxExpression, err := license.ParseExpression(value)
if err != nil {
log.Trace("unable to parse license expression for %q: %w", value, err)
}
return License{
Value: value,
SPDXExpression: spdxExpression,
Type: license.Declared,
URLs: internal.NewStringSet(),
Locations: file.NewLocationSet(),
}
return NewLicenseFromType(value, license.Declared)
}
func NewLicenseFromType(value string, t license.Type) License {
spdxExpression, err := license.ParseExpression(value)
if err != nil {
log.Trace("unable to parse license expression: %w", err)
var spdxExpression string
if value != "" {
var err error
spdxExpression, err = license.ParseExpression(value)
if err != nil {
log.Trace("unable to parse license expression: %w", err)
}
}
return License{
@ -124,6 +117,17 @@ func NewLicenseFromURLs(value string, urls ...string) License {
return l
}
func NewLicenseFromFields(value, url string, location *file.Location) License {
l := NewLicense(value)
if location != nil {
l.Locations.Add(*location)
}
if url != "" {
l.URLs.Add(url)
}
return l
}
// this is a bit of a hack to not infinitely recurse when hashing a license
func (s License) Merge(l License) (*License, error) {
sHash, err := artifact.IDByHash(s)