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
@ -234,6 +248,16 @@ func TestParseJar(t *testing.T) {
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,7 +711,8 @@ func Test_newPackageFromMavenData(t *testing.T) {
ArtifactID: "some-artifact-id",
Version: "1.0",
},
project: &pkg.PomProject{
project: &parsedPomProject{
PomProject: &pkg.PomProject{
Parent: &pkg.PomParent{
GroupID: "some-parent-group-id",
ArtifactID: "some-parent-artifact-id",
@ -700,6 +725,16 @@ func Test_newPackageFromMavenData(t *testing.T) {
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")),
},
},
},
parent: &pkg.Package{
Name: "some-parent-name",
Version: "2.0",
@ -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,20 +49,41 @@ 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{
return &parsedPomProject{
PomProject: &pkg.PomProject{
Path: path,
Parent: pomParent(p, p.Parent),
GroupID: resolveProperty(p, p.GroupID, "groupId"),
@ -71,6 +92,8 @@ func newPomProject(path string, p gopom.Project) *pkg.PomProject {
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,11 +295,16 @@ 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{
name: "go case",
expected: parsedPomProject{
PomProject: &pkg.PomProject{
Path: "test-fixtures/pom/commons-codec.pom.xml",
Parent: &pkg.PomParent{
GroupID: "org.apache.commons",
@ -312,14 +319,55 @@ func Test_parsePomXMLProject(t *testing.T) {
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),
},
},
},
},
}
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,25 +60,18 @@ 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)
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{
Value: value,
@ -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)