diff --git a/compose_files/pulp/assets/maven-fixture/foo/bar/1.2.3.rhlw-00001/foo.bar.1.2.3.rhlw-00001.jar b/compose_files/pulp/assets/maven-fixture/foo/bar/1.2.3.rhlw-00001/foo.bar.1.2.3.rhlw-00001.jar new file mode 100644 index 0000000..518a38a Binary files /dev/null and b/compose_files/pulp/assets/maven-fixture/foo/bar/1.2.3.rhlw-00001/foo.bar.1.2.3.rhlw-00001.jar differ diff --git a/compose_files/pulp/assets/maven-fixture/foo/bar/1.2.3/foo.bar.1.2.3.pom b/compose_files/pulp/assets/maven-fixture/foo/bar/1.2.3.rhlw-00001/foo.bar.1.2.3.rhlw-00001.pom similarity index 83% rename from compose_files/pulp/assets/maven-fixture/foo/bar/1.2.3/foo.bar.1.2.3.pom rename to compose_files/pulp/assets/maven-fixture/foo/bar/1.2.3.rhlw-00001/foo.bar.1.2.3.rhlw-00001.pom index 3745968..6130be7 100644 --- a/compose_files/pulp/assets/maven-fixture/foo/bar/1.2.3/foo.bar.1.2.3.pom +++ b/compose_files/pulp/assets/maven-fixture/foo/bar/1.2.3.rhlw-00001/foo.bar.1.2.3.rhlw-00001.pom @@ -3,5 +3,5 @@ 4.0.0 foo bar - 1.2.3 + 1.2.3.rhlw-00001 diff --git a/compose_files/pulp/assets/maven-fixture/foo/bar/1.2.3.rhlw-00002/foo.bar.1.2.3.rhlw-00002.jar b/compose_files/pulp/assets/maven-fixture/foo/bar/1.2.3.rhlw-00002/foo.bar.1.2.3.rhlw-00002.jar new file mode 100644 index 0000000..518a38a Binary files /dev/null and b/compose_files/pulp/assets/maven-fixture/foo/bar/1.2.3.rhlw-00002/foo.bar.1.2.3.rhlw-00002.jar differ diff --git a/compose_files/pulp/assets/maven-fixture/foo/bar/2.0.1/foo.bar.2.0.1.pom b/compose_files/pulp/assets/maven-fixture/foo/bar/1.2.3.rhlw-00002/foo.bar.1.2.3.rhlw-00002.pom similarity index 83% rename from compose_files/pulp/assets/maven-fixture/foo/bar/2.0.1/foo.bar.2.0.1.pom rename to compose_files/pulp/assets/maven-fixture/foo/bar/1.2.3.rhlw-00002/foo.bar.1.2.3.rhlw-00002.pom index 29f7623..b97e0aa 100644 --- a/compose_files/pulp/assets/maven-fixture/foo/bar/2.0.1/foo.bar.2.0.1.pom +++ b/compose_files/pulp/assets/maven-fixture/foo/bar/1.2.3.rhlw-00002/foo.bar.1.2.3.rhlw-00002.pom @@ -3,5 +3,5 @@ 4.0.0 foo bar - 2.0.1 + 1.2.3.rhlw-00002 diff --git a/compose_files/pulp/assets/maven-fixture/foo/bar/1.2.3/foo.bar.1.2.3.rhlw-00001.jar b/compose_files/pulp/assets/maven-fixture/foo/bar/1.2.3/foo.bar.1.2.3.rhlw-00001.jar deleted file mode 100644 index e979a54..0000000 Binary files a/compose_files/pulp/assets/maven-fixture/foo/bar/1.2.3/foo.bar.1.2.3.rhlw-00001.jar and /dev/null differ diff --git a/compose_files/pulp/assets/maven-fixture/foo/bar/1.2.3/foo.bar.1.2.3.rhlw-00002.jar b/compose_files/pulp/assets/maven-fixture/foo/bar/1.2.3/foo.bar.1.2.3.rhlw-00002.jar deleted file mode 100644 index e979a54..0000000 Binary files a/compose_files/pulp/assets/maven-fixture/foo/bar/1.2.3/foo.bar.1.2.3.rhlw-00002.jar and /dev/null differ diff --git a/compose_files/pulp/assets/maven-fixture/foo/bar/2.0.1.rhlw-00001/foo.bar.2.0.1.rhlw-00001.jar b/compose_files/pulp/assets/maven-fixture/foo/bar/2.0.1.rhlw-00001/foo.bar.2.0.1.rhlw-00001.jar new file mode 100644 index 0000000..97203b3 Binary files /dev/null and b/compose_files/pulp/assets/maven-fixture/foo/bar/2.0.1.rhlw-00001/foo.bar.2.0.1.rhlw-00001.jar differ diff --git a/compose_files/pulp/assets/maven-fixture/foo/bar/2.0.1.rhlw-00001/foo.bar.2.0.1.rhlw-00001.pom b/compose_files/pulp/assets/maven-fixture/foo/bar/2.0.1.rhlw-00001/foo.bar.2.0.1.rhlw-00001.pom new file mode 100644 index 0000000..c82bb4b --- /dev/null +++ b/compose_files/pulp/assets/maven-fixture/foo/bar/2.0.1.rhlw-00001/foo.bar.2.0.1.rhlw-00001.pom @@ -0,0 +1,7 @@ + + + 4.0.0 + foo + bar + 2.0.1.rhlw-00001 + diff --git a/compose_files/pulp/assets/maven-fixture/foo/bar/2.0.1/foo.bar.2.0.1.rhlw-00001.jar b/compose_files/pulp/assets/maven-fixture/foo/bar/2.0.1/foo.bar.2.0.1.rhlw-00001.jar deleted file mode 100644 index e979a54..0000000 Binary files a/compose_files/pulp/assets/maven-fixture/foo/bar/2.0.1/foo.bar.2.0.1.rhlw-00001.jar and /dev/null differ diff --git a/internal/test/integration/maven_test.go b/internal/test/integration/maven_test.go index 99dd24a..8534b79 100644 --- a/internal/test/integration/maven_test.go +++ b/internal/test/integration/maven_test.go @@ -22,16 +22,18 @@ const ( testMavenContentOrigin = "http://localhost:8088" testMavenGroupID = "foo" testMavenArtifactID = "bar" - testMavenVersion123 = "1.2.3" - testMavenVersion201 = "2.0.1" + testMavenBuildVersion123R1 = "1.2.3.rhlw-00001" + testMavenBuildVersion123R2 = "1.2.3.rhlw-00002" + testMavenBuildVersion201R1 = "2.0.1.rhlw-00001" ) var testMavenArtifactPaths = []string{ - "foo/bar/1.2.3/foo.bar.1.2.3.pom", - "foo/bar/1.2.3/foo.bar.1.2.3.rhlw-00001.jar", - "foo/bar/1.2.3/foo.bar.1.2.3.rhlw-00002.jar", - "foo/bar/2.0.1/foo.bar.2.0.1.pom", - "foo/bar/2.0.1/foo.bar.2.0.1.rhlw-00001.jar", + "foo/bar/1.2.3.rhlw-00001/foo.bar.1.2.3.rhlw-00001.pom", + "foo/bar/1.2.3.rhlw-00001/foo.bar.1.2.3.rhlw-00001.jar", + "foo/bar/1.2.3.rhlw-00002/foo.bar.1.2.3.rhlw-00002.pom", + "foo/bar/1.2.3.rhlw-00002/foo.bar.1.2.3.rhlw-00002.jar", + "foo/bar/2.0.1.rhlw-00001/foo.bar.2.0.1.rhlw-00001.pom", + "foo/bar/2.0.1.rhlw-00001/foo.bar.2.0.1.rhlw-00001.jar", } type MavenSuite struct { @@ -105,8 +107,9 @@ func (m *MavenSuite) TestMavenPackageList() { pkg := response.Results[0] assert.Equal(m.T(), testMavenGroupID, pkg.GroupID) assert.Equal(m.T(), testMavenArtifactID, pkg.ArtifactID) - assert.Contains(m.T(), pkg.Versions, testMavenVersion123) - assert.Contains(m.T(), pkg.Versions, testMavenVersion201) + assert.Contains(m.T(), pkg.Versions, testMavenBuildVersion123R1) + assert.Contains(m.T(), pkg.Versions, testMavenBuildVersion123R2) + assert.Contains(m.T(), pkg.Versions, testMavenBuildVersion201R1) require.NotEmpty(m.T(), pkg.LatestReleases) foundVersions := make(map[string]bool) @@ -114,8 +117,9 @@ func (m *MavenSuite) TestMavenPackageList() { foundVersions[latest.Version] = true assert.NotEmpty(m.T(), latest.CreatedAt) } - assert.True(m.T(), foundVersions[testMavenVersion123]) - assert.True(m.T(), foundVersions[testMavenVersion201]) + assert.True(m.T(), foundVersions[testMavenBuildVersion123R1]) + assert.True(m.T(), foundVersions[testMavenBuildVersion123R2]) + assert.True(m.T(), foundVersions[testMavenBuildVersion201R1]) } func (m *MavenSuite) TestMavenPackageListSearchFilter() { @@ -169,7 +173,7 @@ func (m *MavenSuite) TestMavenBuildList() { m.repositoryHref, testMavenGroupID, testMavenArtifactID, - testMavenVersion123, + testMavenBuildVersion123R1, tangy.PageOptions{Offset: 0, Limit: 10}, ) require.NoError(m.T(), err) @@ -180,7 +184,7 @@ func (m *MavenSuite) TestMavenBuildList() { build := response.Results[0] assert.Equal(m.T(), testMavenGroupID, build.GroupID) assert.Equal(m.T(), testMavenArtifactID, build.ArtifactID) - assert.Equal(m.T(), testMavenVersion123, build.Version) + assert.Equal(m.T(), testMavenBuildVersion123R1, build.Version) assert.True(m.T(), strings.HasSuffix(build.Filename, ".pom"), build.Filename) assert.NotEmpty(m.T(), build.CreatedAt) } @@ -191,7 +195,7 @@ func (m *MavenSuite) TestMavenBuildListPagination() { m.repositoryHref, testMavenGroupID, testMavenArtifactID, - testMavenVersion123, + testMavenBuildVersion123R1, tangy.PageOptions{Offset: 0, Limit: 1}, ) require.NoError(m.T(), err) @@ -203,7 +207,7 @@ func (m *MavenSuite) TestMavenBuildListPagination() { m.repositoryHref, testMavenGroupID, testMavenArtifactID, - testMavenVersion123, + testMavenBuildVersion123R1, tangy.PageOptions{Offset: 1, Limit: 10}, ) require.NoError(m.T(), err) @@ -212,7 +216,7 @@ func (m *MavenSuite) TestMavenBuildListPagination() { } func (m *MavenSuite) TestMavenBuildListEmptyHref() { - response, err := m.tangy.MavenBuildList(context.Background(), "", testMavenGroupID, testMavenArtifactID, testMavenVersion123, tangy.PageOptions{Limit: 10}) + response, err := m.tangy.MavenBuildList(context.Background(), "", testMavenGroupID, testMavenArtifactID, testMavenBuildVersion123R1, tangy.PageOptions{Limit: 10}) require.NoError(m.T(), err) assert.Empty(m.T(), response.Results) assert.Zero(m.T(), response.Total) @@ -229,7 +233,7 @@ func (m *MavenSuite) TestMavenBuildListOptionalFilters() { ) require.NoError(m.T(), err) require.NotEmpty(m.T(), response.Results) - assert.Equal(m.T(), 2, response.Total) + assert.Equal(m.T(), 3, response.Total) build := response.Results[0] assert.Equal(m.T(), testMavenGroupID, build.GroupID) @@ -244,8 +248,8 @@ func (m *MavenSuite) TestMavenBuildListOptionalFilters() { tangy.PageOptions{Offset: 0, Limit: 10}, ) require.NoError(m.T(), err) - require.Len(m.T(), filtered.Results, 2) - assert.Equal(m.T(), 2, filtered.Total) + require.Len(m.T(), filtered.Results, 3) + assert.Equal(m.T(), 3, filtered.Total) filtered, err = m.tangy.MavenBuildList( context.Background(), @@ -256,8 +260,8 @@ func (m *MavenSuite) TestMavenBuildListOptionalFilters() { tangy.PageOptions{Offset: 0, Limit: 10}, ) require.NoError(m.T(), err) - require.Len(m.T(), filtered.Results, 2) - assert.Equal(m.T(), 2, filtered.Total) + require.Len(m.T(), filtered.Results, 3) + assert.Equal(m.T(), 3, filtered.Total) } func (m *MavenSuite) TestMavenRepositoryMetrics() { diff --git a/pkg/tangy/maven.go b/pkg/tangy/maven.go index c433170..7a16267 100644 --- a/pkg/tangy/maven.go +++ b/pkg/tangy/maven.go @@ -243,13 +243,26 @@ func (t *tangyImpl) MavenPackageList(ctx context.Context, repositoryHref string, }, nil } +const mavenReleaseQualifierPattern = `[a-zA-Z]+-\d+` + +const mavenReleaseVersionSuffixPattern = `\.` + mavenReleaseQualifierPattern + `$` + +const mavenReleaseFilenamePattern = `\.(` + mavenReleaseQualifierPattern + `)\.pom$` + +var mavenReleaseVersionSuffixRegexp = regexp.MustCompile(mavenReleaseVersionSuffixPattern) + +var mavenReleaseFilenameRegexp = regexp.MustCompile(mavenReleaseFilenamePattern) + +// stripMavenReleaseVersion removes a trailing release qualifier from a Maven version string. +// Example: 5.3.18.rhlw-00003 -> 5.3.18 +func stripMavenReleaseVersion(version string) string { + return mavenReleaseVersionSuffixRegexp.ReplaceAllString(version, "") +} + // extractRelease extracts the release version from a filename // Example: smallrye-mutiny-vertx-core-3.16.0.rhlw-3002.pom -> rhlw-3002 func extractRelease(filename string) string { - // Pattern to match .rhlw-XXXX or similar release patterns before .pom - // Looks for a dot followed by alphanumeric+dash, then .pom - re := regexp.MustCompile(`\.([a-zA-Z]+-\d+)\.pom$`) - matches := re.FindStringSubmatch(filename) + matches := mavenReleaseFilenameRegexp.FindStringSubmatch(filename) if len(matches) > 1 { return matches[1] } @@ -411,7 +424,8 @@ func (t *tangyImpl) MavenBuildList(ctx context.Context, repositoryHref, groupID, } // MavenRepositoryMetrics returns package, build, and version counts for the latest version of a repository. -// Packages are distinct group_id/artifact_id pairs from .pom artifacts; builds and versions are counted from .jar artifacts. +// All counts are based on .jar artifacts. Builds are distinct full versions (e.g. 5.3.18.rhlw-00003); +// versions are distinct base versions with release qualifiers stripped (e.g. 5.3.18). func (t *tangyImpl) MavenRepositoryMetrics(ctx context.Context, repositoryHref string) (MavenRepositoryMetrics, error) { if repositoryHref == "" { return MavenRepositoryMetrics{}, nil @@ -444,11 +458,7 @@ func (t *tangyImpl) MavenRepositoryMetrics(ctx context.Context, repositoryHref s return MavenRepositoryMetrics{}, err } - pomFilter := ` AND rp.filename LIKE '%.pom'` jarFilter := ` AND rp.filename LIKE '%.jar'` - pomFrom := ` - FROM maven_mavenartifact rp - ` + innerUnion + pomFilter jarFrom := ` FROM maven_mavenartifact rp ` + innerUnion + jarFilter @@ -456,10 +466,14 @@ func (t *tangyImpl) MavenRepositoryMetrics(ctx context.Context, repositoryHref s metricsQuery := ` SELECT (SELECT COUNT(DISTINCT (rp.group_id, rp.artifact_id)) - ` + pomFrom + `) AS package_count, - (SELECT COUNT(*) - ` + jarFrom + `) AS build_count, + ` + jarFrom + `) AS package_count, (SELECT COUNT(DISTINCT (rp.group_id, rp.artifact_id, rp.version)) + ` + jarFrom + `) AS build_count, + (SELECT COUNT(DISTINCT ( + rp.group_id, + rp.artifact_id, + regexp_replace(rp.version, '` + mavenReleaseVersionSuffixPattern + `', '') + )) ` + jarFrom + `) AS version_count` var metrics MavenRepositoryMetrics diff --git a/pkg/tangy/maven_test.go b/pkg/tangy/maven_test.go index 40853bc..3bf5be3 100644 --- a/pkg/tangy/maven_test.go +++ b/pkg/tangy/maven_test.go @@ -50,6 +50,115 @@ func TestParseRepositoryHref(t *testing.T) { } } +func TestStripMavenReleaseVersion(t *testing.T) { + t.Parallel() + + tests := []struct { + name string + version string + want string + }{ + { + name: "release suffix in version", + version: "5.3.18.rhlw-00003", + want: "5.3.18", + }, + { + name: "plain version", + version: "1.2.3", + want: "1.2.3", + }, + { + name: "final qualifier version", + version: "4.1.114.Final", + want: "4.1.114.Final", + }, + { + name: "empty version", + version: "", + want: "", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + assert.Equal(t, tt.want, stripMavenReleaseVersion(tt.version)) + }) + } +} + +func TestMavenRepositoryMetricsCounting(t *testing.T) { + t.Parallel() + + type artifact struct { + group string + artifact string + version string + } + + countMetrics := func(artifacts []artifact) (packageCount, buildCount, versionCount int) { + packages := make(map[[2]string]struct{}) + builds := make(map[[3]string]struct{}) + versions := make(map[[3]string]struct{}) + + for _, a := range artifacts { + packages[[2]string{a.group, a.artifact}] = struct{}{} + builds[[3]string{a.group, a.artifact, a.version}] = struct{}{} + baseVersion := stripMavenReleaseVersion(a.version) + versions[[3]string{a.group, a.artifact, baseVersion}] = struct{}{} + } + + return len(packages), len(builds), len(versions) + } + + springCoreVersions := []string{ + "5.3.18.rhlw-00003", + "5.3.18.rhlw-00004", + "5.3.18.rhlw-00005", + "5.3.18.rhlw-00006", + "5.3.18.rhlw-00007", + } + springCoreArtifacts := make([]artifact, 0, len(springCoreVersions)*3) + for _, version := range springCoreVersions { + for range 3 { + springCoreArtifacts = append(springCoreArtifacts, artifact{ + group: "org.springframework", + artifact: "spring-core", + version: version, + }) + } + } + + nettyVersions := []string{ + "4.1.114.Final", + "4.1.127.Final", + "4.1.128.Final", + "4.1.130.Final", + "4.1.133.Final", + } + nettyArtifacts := make([]artifact, 0, len(nettyVersions)*5) + for _, version := range nettyVersions { + for range 5 { + nettyArtifacts = append(nettyArtifacts, artifact{ + group: "io.netty", + artifact: "netty-codec", + version: version, + }) + } + } + + packageCount, buildCount, versionCount := countMetrics(springCoreArtifacts) + assert.Equal(t, 1, packageCount) + assert.Equal(t, 5, buildCount) + assert.Equal(t, 1, versionCount) + + packageCount, buildCount, versionCount = countMetrics(nettyArtifacts) + assert.Equal(t, 1, packageCount) + assert.Equal(t, 5, buildCount) + assert.Equal(t, 5, versionCount) +} + func TestExtractRelease(t *testing.T) { t.Parallel()