diff --git a/lib/galaxy/tool_util/linters/general.py b/lib/galaxy/tool_util/linters/general.py
index f395e335090..35642210f06 100644
--- a/lib/galaxy/tool_util/linters/general.py
+++ b/lib/galaxy/tool_util/linters/general.py
@@ -8,6 +8,7 @@ from typing import (
from packaging.version import Version
+from galaxy.tool_util.biotools.source import ApiBiotoolsMetadataSource
from galaxy.tool_util.lint import Linter
from galaxy.tool_util.version import (
LegacyVersion,
@@ -227,3 +228,16 @@ class ResourceRequirementExpression(Linter):
lint_ctx.warn(
"Expressions in resource requirement not supported yet", linter=cls.name(), node=tool_node
)
+
+
+class BioToolsValid(Linter):
+ @classmethod
+ def lint(cls, tool_source: "ToolSource", lint_ctx: "LintContext"):
+ _, tool_node = _tool_xml_and_root(tool_source)
+ xrefs = tool_source.parse_xrefs()
+ for xref in xrefs:
+ if xref["reftype"] != "bio.tools":
+ continue
+ metadata_source = ApiBiotoolsMetadataSource()
+ if not metadata_source.get_biotools_metadata(xref["value"]):
+ lint_ctx.error(f'No entry {xref["value"]} in bio.tools.', linter=cls.name(), node=tool_node)
diff --git a/test/unit/tool_util/test_tool_linters.py b/test/unit/tool_util/test_tool_linters.py
index e553d2c1688..324825c44e2 100644
--- a/test/unit/tool_util/test_tool_linters.py
+++ b/test/unit/tool_util/test_tool_linters.py
@@ -30,6 +30,7 @@ from galaxy.util import (
ElementTree,
parse_xml,
)
+from galaxy.util.unittest_utils import skip_if_site_down
from galaxy.util.xml_macros import load_with_references
# TODO tests tool xml for general linter
@@ -118,6 +119,15 @@ GENERAL_VALID_NEW_PROFILE_FMT = """
"""
+GENERAL_VALID_BIOTOOLS = """
+
+
+ bwa
+ johnscoolbowtie
+
+
+"""
+
# test tool xml for help linter
HELP_MULTIPLE = """
@@ -1081,6 +1091,17 @@ def test_general_valid_new_profile_fmt(lint_ctx):
assert not lint_ctx.error_messages
+@skip_if_site_down("https://bio.tools/")
+def test_general_valid_biotools(lint_ctx):
+ tool_source = get_xml_tool_source(GENERAL_VALID_BIOTOOLS)
+ run_lint_module(lint_ctx, general, tool_source)
+ assert "No entry johnscoolbowtie in bio.tools." in lint_ctx.error_messages
+ assert not lint_ctx.info_messages
+ assert len(lint_ctx.valid_messages) == 4
+ assert not lint_ctx.warn_messages
+ assert len(lint_ctx.error_messages) == 1
+
+
def test_help_multiple(lint_ctx):
tool_source = get_xml_tool_source(HELP_MULTIPLE)
run_lint_module(lint_ctx, help, tool_source)
@@ -2078,7 +2099,7 @@ def test_xml_comments_are_ignored(lint_ctx: LintContext):
def test_list_linters():
linter_names = Linter.list_listers()
# make sure to add/remove a test for new/removed linters if this number changes
- assert len(linter_names) == 130
+ assert len(linter_names) == 131
assert "Linter" not in linter_names
# make sure that linters from all modules are available
for prefix in [