11import importlib .metadata
2+ import os
23import subprocess
34from pathlib import Path
45from typing import Annotated
1516plugin = typer .Typer ()
1617
1718
19+ @plugin .command ()
20+ def catalog ():
21+ """See all available plugins."""
22+
23+ _display_plugin_list ()
24+
25+
1826@plugin .command (name = "list" )
1927def list_plugins ():
2028 """List installed plugins."""
@@ -58,7 +66,7 @@ def install(
5866
5967 if not already_installed or upgrade :
6068 try :
61- _ , version_to_install , _ = _get_latest_version (f"minimal-pba-cli-plugin-{ name } " )
69+ _ , version_to_install , _ = get_latest_version (f"minimal-pba-cli-plugin-{ name } " )
6270 except HTTPError as e :
6371 if e .response is not None and e .response .status_code == 404 :
6472 raise typer .BadParameter (
@@ -76,15 +84,15 @@ def install(
7684 if already_installed :
7785 args .append ("--force" )
7886
79- _run_external_subprocess (args )
87+ run_external_subprocess (args )
8088
8189
8290@plugin .command ()
8391def install_local (path : Annotated [Path , typer .Argument (help = "Path to the plugin directory." )]):
8492 """Install a local plugin."""
8593
8694 typer .echo (f"Installing plugin from '{ path } '..." )
87- _run_external_subprocess ([
95+ run_external_subprocess ([
8896 "pipx" ,
8997 "inject" ,
9098 "--editable" ,
@@ -99,7 +107,7 @@ def uninstall(name: Annotated[str, typer.Argument(help="Name of the plugin to un
99107 """Uninstall a plugin."""
100108
101109 typer .echo (f"Uninstalling plugin '{ name } '..." )
102- _run_external_subprocess ([
110+ run_external_subprocess ([
103111 "pipx" ,
104112 "uninject" ,
105113 "minimal-pba-cli" ,
@@ -116,7 +124,7 @@ def _get_installed_version(name: str) -> Version | None:
116124 return None
117125
118126
119- def _get_latest_version (name : str ) -> tuple [Version | None , Version , bool ]:
127+ def get_latest_version (name : str ) -> tuple [Version | None , Version , bool ]:
120128 """Get the latest published version of a package."""
121129
122130 url = f"https://pypi.org/pypi/{ name } /json"
@@ -144,7 +152,7 @@ def find_plugins() -> dict[str, dict[str, str]]:
144152 return plugins
145153
146154
147- def _run_external_subprocess (args : list [str ]) -> subprocess .CompletedProcess :
155+ def run_external_subprocess (args : list [str ]) -> subprocess .CompletedProcess :
148156 """Run an external subprocess and return the result."""
149157
150158 result = subprocess .run (args , capture_output = True , encoding = "utf-8" )
@@ -159,3 +167,60 @@ def _run_external_subprocess(args: list[str]) -> subprocess.CompletedProcess:
159167 raise typer .Exit (code = result .returncode )
160168
161169 return result
170+
171+
172+ def _get_packages_matching_name (prefix : str ) -> list [dict [str , str ]]:
173+ if "LIBRARIES_IO_API_KEY" not in os .environ :
174+ typer .secho (
175+ "LIBRARIES_IO_API_KEY environment variable not set. "
176+ "Create a free libraries.io account to get an API key and set it to use the plugin catalog." ,
177+ fg = typer .colors .RED ,
178+ )
179+ raise typer .Exit (1 )
180+ results = requests .get ("https://libraries.io/api/search" , params = {"q" : prefix , "platforms" : "pypi" , "api_key" : os .getenv ("LIBRARIES_IO_API_KEY" )})
181+ return [
182+ {
183+ "name" : package ["name" ],
184+ "summary" : package ["description" ],
185+ }
186+ for package in results .json ()
187+ if package ["name" ].startswith (prefix )
188+ ]
189+
190+
191+ def _display_plugin_list ():
192+ console = Console ()
193+
194+ table = Table (
195+ "Name" ,
196+ "Description" ,
197+ "Latest version" ,
198+ "Installed" ,
199+ title = "Available CLI plugins" ,
200+ min_width = 50 ,
201+ highlight = True ,
202+ )
203+
204+ available_plugins = _get_packages_matching_name ("minimal-pba-cli-plugin-" )
205+
206+ for plugin in sorted (available_plugins , key = lambda x : x ["name" ]):
207+ plugin_current_version , plugin_latest_version , plugin_outdated = get_latest_version (plugin ["name" ])
208+ output = "False"
209+
210+ if plugin_current_version :
211+ color = "yellow" if plugin_outdated else "green"
212+ output = f"[{ color } ]{ plugin_current_version } [/{ color } ]"
213+
214+ plugin_full_name = plugin ["name" ]
215+ plugin_short_name = plugin_full_name .replace ("minimal-pba-cli-plugin-" , "" )
216+
217+ table .add_row (
218+ f"[link=https://capstan-backstage.prod.cirrostratus.org/catalog/default/component/{ plugin_full_name } ]{ plugin_short_name } [/link]" ,
219+ plugin ["summary" ],
220+ str (plugin_latest_version ),
221+ output ,
222+ )
223+
224+ print ()
225+ console .print (table )
226+ console .print ("\n Install a plugin using [bold cyan]pba-cli plugin install <plugin-name>[/bold cyan]." )
0 commit comments