Skip to content

Commit e9eb3a0

Browse files
authored
Merge pull request #805 from aboutcode-org/630_1_extend_indexing_for_maven
Extend indexing for maven
2 parents c345699 + 12c95d0 commit e9eb3a0

12 files changed

Lines changed: 542 additions & 43 deletions

File tree

minecode/collectors/maven.py

Lines changed: 64 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -476,9 +476,6 @@ def process_request(purl_str, **kwargs):
476476

477477

478478
collect_links = re.compile(r'href="([^"]+)"').findall
479-
collect_links_and_artifact_timestamps = re.compile(
480-
r'<a href="([^"]+)".*</a>\s+(\d{4}-\d{2}-\d{2}\s+\d{2}:\d{2}|-)'
481-
).findall
482479

483480

484481
def check_if_file_name_is_linked_on_page(file_name, links, **kwargs):
@@ -675,6 +672,62 @@ def filter_for_artifacts(timestamps_by_links):
675672
return timestamps_by_links_filtered
676673

677674

675+
def collect_links_and_artifact_timestamps(text):
676+
# Return a list of sets containing all link locations and their
677+
# corresponding timestamps extracted from a given HTML text.
678+
679+
# Pattern that matches with https://repo.maven.apache.org/maven2
680+
maven_apache_pattern = re.compile(
681+
r'<a href="([^"]+)"[^>]*>[^<]*</a>\s+(\d{4}-\d{2}-\d{2}\s+\d{2}:\d{2}|-)'
682+
)
683+
maven_apache_matches = maven_apache_pattern.findall(text)
684+
if maven_apache_matches:
685+
return maven_apache_matches
686+
687+
# Pattern that matces with
688+
# both Apache (UTC) and Nexus (Z) formats
689+
# https://repository.jboss.org/nexus/service/rest/repository/browse/releases/
690+
# https://repository.jboss.org/nexus/service/rest/repository/browse/public/
691+
# https://repository.apache.org/snapshots/
692+
repo_jboss_apache_pattern = re.compile(
693+
r'<a href="([^"]+)"[^>]*>[^<]*</a></td>\s*<td>\s*((?:[A-Z][a-z]{2}\s+[A-Z][a-z]{2}\s+\d{1,2}\s+\d{2}:\d{2}:\d{2}\s+(?:UTC|Z)\s+\d{4})|&nbsp;)\s*</td>'
694+
)
695+
repo_jboss_apache_matches = repo_jboss_apache_pattern.findall(text)
696+
# Convert &nbsp; to empty string for table format
697+
if repo_jboss_apache_matches:
698+
return [
699+
(item, "" if timestamp == "&nbsp;" else timestamp)
700+
for item, timestamp in repo_jboss_apache_matches
701+
]
702+
703+
# Pattern that matches with
704+
# https://repo.spring.io/milestone
705+
repo_spring_pattern = re.compile(
706+
r'<a href="([^"]+)"[^>]*>[^<]*</a>\s+(\d{2}-[A-Z][a-z]{2}-\d{4}\s+\d{2}:\d{2})'
707+
)
708+
repo_spring_matches = repo_spring_pattern.findall(text)
709+
if repo_spring_matches:
710+
return repo_spring_matches
711+
712+
# Simple links in <pre> tags without timestamps (Gradle plugins format)
713+
# https://plugins.gradle.org/m2/
714+
plugins_gradle_pattern = re.compile(r'<pre><a href="([^"]+)"[^>]*>[^<]*</a></pre>')
715+
plugins_gradle_matches = plugins_gradle_pattern.findall(text)
716+
if plugins_gradle_matches:
717+
# Filter out parent directory link if present
718+
filtered_matches = []
719+
for href in plugins_gradle_matches:
720+
# Skip parent directory links
721+
if href != "../" and not href.startswith(".."):
722+
filtered_matches.append((href, ""))
723+
724+
# Only return if we found non-parent links
725+
if filtered_matches:
726+
return filtered_matches
727+
728+
return []
729+
730+
678731
def collect_links_from_text(text, filter):
679732
"""
680733
Return a mapping of link locations and their timestamps, given HTML `text`
@@ -700,7 +753,7 @@ def create_absolute_urls_for_links(text, url, filter):
700753
url = url.rstrip("/")
701754
timestamps_by_links = collect_links_from_text(text, filter)
702755
for link, timestamp in timestamps_by_links.items():
703-
if not link.startswith(url):
756+
if not link.startswith("http:") and not link.startswith("https:"):
704757
link = f"{url}/{link}"
705758
timestamps_by_absolute_links[link] = timestamp
706759
return timestamps_by_absolute_links
@@ -758,23 +811,20 @@ def get_artifact_sha1(artifact_url):
758811
return sha1
759812

760813

761-
def get_classifier_from_artifact_url(
762-
artifact_url, package_version_page_url, package_name, package_version
763-
):
814+
def get_classifier_from_artifact_url(artifact_url, package_name, package_version):
764815
"""
765816
Return the classifier from a Maven artifact URL `artifact_url`, otherwise
766817
return None if a classifier cannot be determined from `artifact_url`
767818
"""
768819
classifier = None
769-
# https://repo1.maven.org/maven2/net/alchim31/livereload-jvm/0.2.0
770-
package_version_page_url = package_version_page_url.rstrip("/")
771-
# https://repo1.maven.org/maven2/net/alchim31/livereload-jvm/0.2.0/livereload-jvm-0.2.0
772-
leading_url_portion = f"{package_version_page_url}/{package_name}-{package_version}"
820+
package_name_version_portion = f"{package_name}-{package_version}"
821+
artifact_url_filename = artifact_url.rsplit("/", 1)[-1]
822+
remaining_url_portion = artifact_url_filename.replace(package_name_version_portion, "")
773823
# artifact_url = 'https://repo1.maven.org/maven2/net/alchim31/livereload-jvm/0.2.0/livereload-jvm-0.2.0-onejar.jar'
774-
# ['', '-onejar.jar']
775-
_, remaining_url_portion = artifact_url.split(leading_url_portion)
776-
# ['-onejar', 'jar']
824+
# artifact_url_filename = 'livereload-jvm-0.2.0-onejar.jar'
825+
# remaining_url_portion = '-onejar.jar'
777826
remaining_url_portions = remaining_url_portion.split(".")
827+
# ['-onejar', 'jar']
778828
if remaining_url_portions and remaining_url_portions[0]:
779829
# '-onejar'
780830
classifier = remaining_url_portions[0]

minecode/management/commands/import_queue.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -132,12 +132,14 @@ def process_request(importable_uri):
132132
timestamps_by_artifact_links = get_artifact_links(version_page_url)
133133
for artifact_link, timestamp in timestamps_by_artifact_links.items():
134134
sha1 = get_artifact_sha1(artifact_link)
135-
classifier = get_classifier_from_artifact_url(
136-
artifact_link, version_page_url, name, version
137-
)
135+
classifier = get_classifier_from_artifact_url(artifact_link, name, version)
138136
qualifiers = None
139137
if classifier:
140138
qualifiers = f"classifier={classifier}"
139+
if timestamp:
140+
release_date = dateutil_parse(timestamp)
141+
else:
142+
release_date = None
141143
release_date = dateutil_parse(timestamp)
142144
package_data = PackageData(
143145
type="maven",

minecode/management/commands/maven_crawler.py

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,5 +26,15 @@ class Command(VerboseCommand):
2626
help = "Run a Package request queue."
2727

2828
def handle(self, *args, **options):
29-
maven_root_url = "https://repo.maven.apache.org/maven2"
30-
crawl_maven_repo_from_root(root_url=maven_root_url)
29+
# Add the maven root URLs
30+
# Ref: https://github.com/aboutcode-org/purldb/issues/630#issuecomment-3599942716
31+
maven_root_urls = [
32+
"https://repo.maven.apache.org/maven2",
33+
"https://repo.spring.io/artifactory/milestone",
34+
"https://plugins.gradle.org/m2",
35+
"https://repository.apache.org/content/groups/snapshots",
36+
"https://repository.jboss.org/nexus/service/rest/repository/browse/releases",
37+
"https://repository.jboss.org/nexus/service/rest/repository/browse/public",
38+
]
39+
for maven_root_url in maven_root_urls:
40+
crawl_maven_repo_from_root(root_url=maven_root_url)

minecode/tests/collectors/test_maven.py

Lines changed: 73 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -201,7 +201,7 @@ def test_get_merged_ancestor_package_from_maven_package(
201201

202202

203203
class MavenCrawlerFunctionsTest(JsonBasedTesting, DjangoTestCase):
204-
test_data_dir = os.path.join(os.path.dirname(__file__), "testfiles")
204+
test_data_dir = os.path.join(os.path.dirname(__file__), "../testfiles")
205205

206206
def test_check_if_file_name_is_linked_on_page(self):
207207
links = ["foo/", "bar/", "baz/"]
@@ -500,12 +500,80 @@ def test_get_artifact_sha1(self, mock_request_get):
500500

501501
def test_get_classifier_from_artifact_url(self):
502502
artifact_url = "https://repo1.maven.org/maven2/net/alchim31/livereload-jvm/0.2.0/livereload-jvm-0.2.0-onejar.jar"
503-
package_version_page_url = (
504-
"https://repo1.maven.org/maven2/net/alchim31/livereload-jvm/0.2.0/"
505-
)
506503
package_name = "livereload-jvm"
507504
package_version = "0.2.0"
508505
classifier = maven.get_classifier_from_artifact_url(
509-
artifact_url, package_version_page_url, package_name, package_version
506+
artifact_url, package_name, package_version
510507
)
511508
self.assertEqual("onejar", classifier)
509+
510+
def test_collect_links_and_artifact_timestamps_repo_maven_apache_org(self):
511+
# https://repo.maven.apache.org/maven2
512+
with open(self.get_test_loc("maven/html/maven.apache.org/abbot.html")) as file:
513+
text = file.read()
514+
expected = [
515+
("1.4.0/", "2015-09-22 16:03"),
516+
("maven-metadata.xml", "2015-09-24 14:18"),
517+
]
518+
519+
self.assertEqual(expected, maven.collect_links_and_artifact_timestamps(text))
520+
521+
def test_collect_links_and_artifact_timestamps_repository_jboss_org(self):
522+
# https://repository.jboss.org/nexus/service/rest/repository/browse/public/
523+
# https://repository.jboss.org/nexus/service/rest/repository/browse/releases/
524+
with open(self.get_test_loc("maven/html/repository.jboss.org/commons-codec.html")) as file:
525+
text = file.read()
526+
expected = [
527+
("1.2/", ""),
528+
(
529+
"https://repository.jboss.org/nexus/repository/public/apache-codec/commons-codec/maven-metadata.xml",
530+
"Fri Sep 05 09:38:07 Z 2025",
531+
),
532+
]
533+
534+
self.assertEqual(expected, maven.collect_links_and_artifact_timestamps(text))
535+
536+
def test_collect_links_and_artifact_timestamps_repository_apache_org(self):
537+
# https://repository.apache.org/snapshots/
538+
with open(self.get_test_loc("maven/html/repository.apache.org/common-chain.html")) as file:
539+
text = file.read()
540+
expected = [
541+
(
542+
"https://repository.apache.org/content/groups/snapshots/commons-chain/commons-chain/1.3-SNAPSHOT/",
543+
"Thu Jul 04 05:45:00 UTC 2013",
544+
),
545+
(
546+
"https://repository.apache.org/content/groups/snapshots/commons-chain/commons-chain/2.0-SNAPSHOT/",
547+
"Tue Aug 21 20:26:48 UTC 2018",
548+
),
549+
(
550+
"https://repository.apache.org/content/groups/snapshots/commons-chain/commons-chain/maven-metadata.xml.md5",
551+
"Tue Aug 21 20:26:47 UTC 2018",
552+
),
553+
(
554+
"https://repository.apache.org/content/groups/snapshots/commons-chain/commons-chain/maven-metadata.xml.sha1",
555+
"Tue Aug 21 20:26:47 UTC 2018",
556+
),
557+
]
558+
559+
self.assertEqual(expected, maven.collect_links_and_artifact_timestamps(text))
560+
561+
def test_collect_links_and_artifact_timestamps_repo_spring_io(self):
562+
# https://repo.spring.io/release
563+
with open(self.get_test_loc("maven/html/repo.spring.io/scstest.html")) as file:
564+
text = file.read()
565+
expected = [
566+
("0.0.11.M2/", "07-Aug-2019 08:40"),
567+
("0.0.11.RC2/", "07-Aug-2019 08:36"),
568+
("maven-metadata.xml", "07-Aug-2019 09:07"),
569+
]
570+
571+
self.assertEqual(expected, maven.collect_links_and_artifact_timestamps(text))
572+
573+
def test_collect_links_and_artifact_timestamps_plugin_gradle_org(self):
574+
# https://plugins.gradle.org/m2/
575+
with open(self.get_test_loc("maven/html/plugins.gradle.org/test.html")) as file:
576+
text = file.read()
577+
expected = [("0.0.10/", ""), ("1.0.1/", ""), ("1.1.0/", ""), ("maven-metadata.xml", "")]
578+
579+
self.assertEqual(expected, maven.collect_links_and_artifact_timestamps(text))
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
<!DOCTYPE html>
2+
<html>
3+
4+
<head>
5+
<title>Central Repository: abbot/abbot</title>
6+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
7+
<style>
8+
body {
9+
background: #fff;
10+
}
11+
</style>
12+
</head>
13+
14+
<body>
15+
<header>
16+
<h1>abbot/abbot</h1>
17+
</header>
18+
<hr/>
19+
<main>
20+
<pre id="contents">
21+
<a href="../">../</a>
22+
<a href="1.4.0/" title="1.4.0/">1.4.0/</a> 2015-09-22 16:03 -
23+
<a href="maven-metadata.xml" title="maven-metadata.xml">maven-metadata.xml</a> 2015-09-24 14:18 402
24+
</pre>
25+
</main>
26+
<hr/>
27+
</body>
28+
29+
</html>
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<html>
2+
<head><script type='text/javascript' src='https://plugins.gradle.org/dozHnTTSNrd5c_IjGSXETRWLhbS7W7Sl-H-qWMlAJ-nnJaXDMcobTtriNFDE_NxL6mMWRjdcd0usaDqblFsN2Apks_z6IZCWIaCbLqGNPxgytmr6wYHb8SFa8Vogcg7u9QgA3Me1ndlareEd1AF6UF-iHMCznbe9q8_RnrT36M8='></script>
3+
</head>
4+
<body>
5+
<pre><a href="0.0.10/">0.0.10/</a></pre>
6+
<pre><a href="1.0.1/">1.0.1/</a></pre>
7+
<pre><a href="1.1.0/">1.1.0/</a></pre>
8+
<pre><a href="maven-metadata.xml">maven-metadata.xml</a></pre>
9+
</body>
10+
</html>
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<!DOCTYPE html>
2+
<html>
3+
<head><meta name="robots" content="noindex" />
4+
<title>Index of milestone/com/albertoimpl/test/scstest/releasetest</title>
5+
</head>
6+
<body>
7+
<h1>Index of milestone/com/albertoimpl/test/scstest/releasetest</h1>
8+
<pre>Name Last modified Size</pre><hr/>
9+
<pre><a href="../">../</a>
10+
<a href="0.0.11.M2/">0.0.11.M2/</a> 07-Aug-2019 08:40 -
11+
<a href="0.0.11.RC2/">0.0.11.RC2/</a> 07-Aug-2019 08:36 -
12+
<a href="maven-metadata.xml">maven-metadata.xml</a> 07-Aug-2019 09:07 449 bytes
13+
</pre>
14+
<hr/><address style="font-size:small;">Artifactory Online Server</address></body></html>
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
<html>
2+
<head><script type='text/javascript' src='https://repository.apache.org/A0HnoQhqkwFlCnYdGIUSeC6QxiXycDJQit71DQvwBneOTWUYqDfnR_rRZFOArTJIFfR1XaDdweihXOZeZY0IVNNCr8eYelM995osm88CBpWrw07LyaggpNRPkoPQFO9dPSmatFhFWILy9VivvOWnrB5M2ymOQX0LCcQpRa7ItTQ='></script>
3+
<title>Index of /groups/snapshots/commons-chain/commons-chain</title>
4+
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
5+
6+
<link rel="icon" type="image/png" href="https://repository.apache.org/favicon.png">
7+
<!--[if IE]>
8+
<link rel="SHORTCUT ICON" href="https://repository.apache.org/favicon.ico"/>
9+
<![endif]-->
10+
11+
<link rel="stylesheet" href="https://repository.apache.org/static/css/Sonatype-content.css?2.15.2-03" type="text/css" media="screen" title="no title" charset="utf-8">
12+
</head>
13+
<body>
14+
<h1>Index of /groups/snapshots/commons-chain/commons-chain</h1>
15+
<table cellspacing="10">
16+
<tr>
17+
<th align="left">Name</th>
18+
<th>Last Modified</th>
19+
<th>Size</th>
20+
<th>Description</th>
21+
</tr>
22+
<tr>
23+
<td><a href="../">Parent Directory</a></td>
24+
</tr>
25+
<tr>
26+
<td><a href="https://repository.apache.org/content/groups/snapshots/commons-chain/commons-chain/1.3-SNAPSHOT/">1.3-SNAPSHOT/</a></td>
27+
<td>Thu Jul 04 05:45:00 UTC 2013</td>
28+
<td align="right">
29+
&nbsp;
30+
</td>
31+
<td></td>
32+
</tr>
33+
<tr>
34+
<td><a href="https://repository.apache.org/content/groups/snapshots/commons-chain/commons-chain/2.0-SNAPSHOT/">2.0-SNAPSHOT/</a></td>
35+
<td>Tue Aug 21 20:26:48 UTC 2018</td>
36+
<td align="right">
37+
&nbsp;
38+
</td>
39+
<td></td>
40+
</tr>
41+
<tr>
42+
<td><a href="https://repository.apache.org/content/groups/snapshots/commons-chain/commons-chain/maven-metadata.xml.md5">maven-metadata.xml.md5</a></td>
43+
<td>Tue Aug 21 20:26:47 UTC 2018</td>
44+
<td align="right">
45+
33
46+
</td>
47+
<td></td>
48+
</tr>
49+
<tr>
50+
<td><a href="https://repository.apache.org/content/groups/snapshots/commons-chain/commons-chain/maven-metadata.xml.sha1">maven-metadata.xml.sha1</a></td>
51+
<td>Tue Aug 21 20:26:47 UTC 2018</td>
52+
<td align="right">
53+
41
54+
</td>
55+
<td></td>
56+
</tr>
57+
</table>
58+
</body>
59+
</html>

0 commit comments

Comments
 (0)