Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions tests/test_reckless.py
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,32 @@ def test_search(node_factory):
assert r.search_stdout('found testplugpass in source: https://github.com/lightningd/plugins')


def test_search_partial_match(node_factory):
"""test that partial/substring search returns multiple matches"""
n = get_reckless_node(node_factory)

# Search for partial name "testplug" - should find all test plugins
r = reckless([f"--network={NETWORK}", "search", "testplug"], dir=n.lightning_dir)
# Should show the "Plugins matching" header
assert r.search_stdout("Plugins matching 'testplug':")
# Should list multiple plugins (all start with "testplug")
assert r.search_stdout('testplugpass')
assert r.search_stdout('testplugfail')
assert r.search_stdout('testplugpyproj')
assert r.search_stdout('testpluguv')

# Search for "pass" - should find testplugpass
r = reckless([f"--network={NETWORK}", "search", "pass"], dir=n.lightning_dir)
assert r.search_stdout("Plugins matching 'pass':")
assert r.search_stdout('testplugpass')
# Should not find plugins without "pass" in name
assert not r.search_stdout('testplugfail')

# Search for something that doesn't exist
r = reckless([f"--network={NETWORK}", "search", "nonexistent"], dir=n.lightning_dir)
assert r.search_stdout("Search exhausted all sources")


def test_install(node_factory):
"""test search, git clone, and installation to folder."""
n = get_reckless_node(node_factory)
Expand Down
62 changes: 53 additions & 9 deletions tools/reckless
Original file line number Diff line number Diff line change
Expand Up @@ -1548,6 +1548,30 @@ def uninstall(plugin_name: str) -> str:
return "uninstalled"


def _get_all_plugins_from_source(src: str) -> list:
"""Get all plugin directories from a source repository.
Returns a list of (plugin_name, source_url) tuples."""
plugins = []
srctype = Source.get_type(src)
if srctype == Source.UNKNOWN:
return plugins

try:
root = SourceDir(src, srctype=srctype)
root.populate()
except Exception as e:
log.debug(f"Failed to populate source {src}: {e}")
return plugins

for item in root.contents:
if isinstance(item, SourceDir):
# Skip archive directories
if 'archive' in item.name.lower():
continue
plugins.append((item.name, src))
return plugins


def search(plugin_name: str) -> Union[InstInfo, None]:
"""searches plugin index for plugin"""
ordered_sources = RECKLESS_SOURCES.copy()
Expand All @@ -1563,6 +1587,22 @@ def search(plugin_name: str) -> Union[InstInfo, None]:
if Source.get_type(src) in [Source.DIRECTORY, Source.LOCAL_REPO]:
ordered_sources.remove(src)
ordered_sources.insert(0, src)

# First, collect all partial matches to display to user
partial_matches = []
for source in ordered_sources:
for plugin_name_found, src_url in _get_all_plugins_from_source(source):
if plugin_name.lower() in plugin_name_found.lower():
partial_matches.append((plugin_name_found, src_url))

# Display all partial matches
if partial_matches:
log.info(f"Plugins matching '{plugin_name}':")
for name, src_url in partial_matches:
log.info(f" {name} ({src_url})")

# Now try exact match for installation purposes
exact_match = None
for source in ordered_sources:
srctype = Source.get_type(source)
if srctype == Source.UNKNOWN:
Expand All @@ -1573,17 +1613,21 @@ def search(plugin_name: str) -> Union[InstInfo, None]:
found = _source_search(plugin_name, source)
if found:
log.debug(f"{found}, {found.srctype}")
if not found:
continue
log.info(f"found {found.name} in source: {found.source_loc}")
log.debug(f"entry: {found.entry}")
if found.subdir:
log.debug(f'sub-directory: {found.subdir}')
exact_match = found
break

if exact_match:
log.info(f"found {exact_match.name} in source: {exact_match.source_loc}")
log.debug(f"entry: {exact_match.entry}")
if exact_match.subdir:
log.debug(f'sub-directory: {exact_match.subdir}')
global LAST_FOUND
# Stashing the search result saves install() a call to _source_search.
LAST_FOUND = found
return str(found.source_loc)
log.info("Search exhausted all sources")
LAST_FOUND = exact_match
return str(exact_match.source_loc)

if not partial_matches:
log.info("Search exhausted all sources")
return None


Expand Down
Loading