mirror of
https://github.com/bombsquad-community/plugin-manager.git
synced 2025-10-08 14:54:36 +00:00
Specify branch names in custom plugin sources
This commit is contained in:
parent
a656912a47
commit
8a8df69e36
4 changed files with 101 additions and 66 deletions
|
|
@ -1,5 +1,13 @@
|
||||||
## Plugin Manager (dd-mm-yyyy)
|
## Plugin Manager (dd-mm-yyyy)
|
||||||
|
|
||||||
|
### 1.0.1 (30-06-2023)
|
||||||
|
|
||||||
|
- Allow specifying branch names in custom sources.
|
||||||
|
|
||||||
|
### 1.0.0 (20-06-2023)
|
||||||
|
|
||||||
|
- Migrate plugin manager's source code to API 8.
|
||||||
|
|
||||||
### 0.3.5 (16-06-2023)
|
### 0.3.5 (16-06-2023)
|
||||||
|
|
||||||
- Replace the "Loading..." text with the exception message in case something goes wrong.
|
- Replace the "Loading..." text with the exception message in case something goes wrong.
|
||||||
|
|
|
||||||
|
|
@ -184,6 +184,8 @@ That's it! Now you can make a [pull request](../../compare) with both the update
|
||||||
- Check out [bombsquad-community/sample-plugin-source](https://github.com/bombsquad-community/sample-plugin-source) as an example.
|
- Check out [bombsquad-community/sample-plugin-source](https://github.com/bombsquad-community/sample-plugin-source) as an example.
|
||||||
You can choose to show up plugins from this repository in your plugin manager by adding `bombsquad-community/sample-plugin-source`
|
You can choose to show up plugins from this repository in your plugin manager by adding `bombsquad-community/sample-plugin-source`
|
||||||
as a custom source through the category selection popup window in-game.
|
as a custom source through the category selection popup window in-game.
|
||||||
|
- Plugin manager will default to picking up plugins from the `main` branch of the custom source repository. You
|
||||||
|
can specify a different branch by suffixing the source URI with `@branchname`, such as `bombsquad-community/sample-plugin-source@experimental`.
|
||||||
|
|
||||||
#### Known 3rd Party Plugin Sources
|
#### Known 3rd Party Plugin Sources
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
{
|
{
|
||||||
"plugin_manager_url": "https://github.com/bombsquad-community/plugin-manager/{content_type}/{tag}/plugin_manager.py",
|
"plugin_manager_url": "https://github.com/bombsquad-community/plugin-manager/{content_type}/{tag}/plugin_manager.py",
|
||||||
"versions": {
|
"versions": {
|
||||||
|
"1.0.1": null,
|
||||||
"1.0.0": {
|
"1.0.0": {
|
||||||
"api_version": 8,
|
"api_version": 8,
|
||||||
"commit_sha": "0b55bc2",
|
"commit_sha": "0b55bc2",
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,7 @@ import pathlib
|
||||||
import contextlib
|
import contextlib
|
||||||
import hashlib
|
import hashlib
|
||||||
import copy
|
import copy
|
||||||
|
import traceback
|
||||||
|
|
||||||
from typing import Union, Optional
|
from typing import Union, Optional
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
|
@ -31,7 +32,7 @@ _env = _babase.env()
|
||||||
_uiscale = bui.app.ui_v1.uiscale
|
_uiscale = bui.app.ui_v1.uiscale
|
||||||
|
|
||||||
|
|
||||||
PLUGIN_MANAGER_VERSION = "1.0.0"
|
PLUGIN_MANAGER_VERSION = "1.0.1"
|
||||||
REPOSITORY_URL = "https://github.com/bombsquad-community/plugin-manager"
|
REPOSITORY_URL = "https://github.com/bombsquad-community/plugin-manager"
|
||||||
# Current tag can be changed to "staging" or any other branch in
|
# Current tag can be changed to "staging" or any other branch in
|
||||||
# plugin manager repo for testing purpose.
|
# plugin manager repo for testing purpose.
|
||||||
|
|
@ -70,19 +71,27 @@ DISCORD_URL = "https://ballistica.net/discord"
|
||||||
_CACHE = {}
|
_CACHE = {}
|
||||||
|
|
||||||
|
|
||||||
class MD5CheckSumFailedError(Exception):
|
class MD5CheckSumFailed(Exception):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class PluginNotInstalledError(Exception):
|
class PluginNotInstalled(Exception):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class CategoryDoesNotExistError(Exception):
|
class CategoryDoesNotExist(Exception):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class NoCompatibleVersionError(Exception):
|
class NoCompatibleVersion(Exception):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class PluginSourceNetworkError(Exception):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class CategoryMetadataParseError(Exception):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -109,7 +118,7 @@ def stream_network_response_to_file(request, file, md5sum=None, retries=3):
|
||||||
content += chunk
|
content += chunk
|
||||||
if md5sum and hashlib.md5(content).hexdigest() != md5sum:
|
if md5sum and hashlib.md5(content).hexdigest() != md5sum:
|
||||||
if retries <= 0:
|
if retries <= 0:
|
||||||
raise MD5CheckSumFailedError("MD5 checksum match failed.")
|
raise MD5CheckSumFailed("MD5 checksum match failed.")
|
||||||
return stream_network_response_to_file(
|
return stream_network_response_to_file(
|
||||||
request,
|
request,
|
||||||
file,
|
file,
|
||||||
|
|
@ -276,7 +285,7 @@ class StartupTasks:
|
||||||
bui.screenmessage(f"Plugin Manager is being updated to v{to_version}")
|
bui.screenmessage(f"Plugin Manager is being updated to v{to_version}")
|
||||||
try:
|
try:
|
||||||
await self.plugin_manager.update(to_version, commit_sha)
|
await self.plugin_manager.update(to_version, commit_sha)
|
||||||
except MD5CheckSumFailedError:
|
except MD5CheckSumFailed:
|
||||||
bui.getsound('error').play()
|
bui.getsound('error').play()
|
||||||
else:
|
else:
|
||||||
bui.screenmessage("Update successful. Restart game to reload changes.",
|
bui.screenmessage("Update successful. Restart game to reload changes.",
|
||||||
|
|
@ -301,7 +310,7 @@ class StartupTasks:
|
||||||
return False
|
return False
|
||||||
try:
|
try:
|
||||||
plugin.latest_compatible_version
|
plugin.latest_compatible_version
|
||||||
except NoCompatibleVersionError:
|
except NoCompatibleVersion:
|
||||||
return False
|
return False
|
||||||
else:
|
else:
|
||||||
return True
|
return True
|
||||||
|
|
@ -354,9 +363,9 @@ class StartupTasks:
|
||||||
|
|
||||||
|
|
||||||
class Category:
|
class Category:
|
||||||
def __init__(self, meta_url, is_3rd_party=False):
|
def __init__(self, meta_url, tag=CURRENT_TAG):
|
||||||
self.meta_url = meta_url
|
self.meta_url = meta_url
|
||||||
self.is_3rd_party = is_3rd_party
|
self.tag = tag
|
||||||
self.request_headers = HEADERS
|
self.request_headers = HEADERS
|
||||||
self._metadata = _CACHE.get("categories", {}).get(meta_url, {}).get("metadata")
|
self._metadata = _CACHE.get("categories", {}).get(meta_url, {}).get("metadata")
|
||||||
self._plugins = _CACHE.get("categories", {}).get(meta_url, {}).get("plugins")
|
self._plugins = _CACHE.get("categories", {}).get(meta_url, {}).get("plugins")
|
||||||
|
|
@ -365,9 +374,8 @@ class Category:
|
||||||
if self._metadata is None:
|
if self._metadata is None:
|
||||||
# Let's keep depending on the "main" branch for 3rd party sources
|
# Let's keep depending on the "main" branch for 3rd party sources
|
||||||
# even if we're using a different branch of plugin manager's repository.
|
# even if we're using a different branch of plugin manager's repository.
|
||||||
tag = "main" if self.is_3rd_party else CURRENT_TAG
|
|
||||||
request = urllib.request.Request(
|
request = urllib.request.Request(
|
||||||
self.meta_url.format(content_type="raw", tag=tag),
|
self.meta_url.format(content_type="raw", tag=self.tag),
|
||||||
headers=self.request_headers,
|
headers=self.request_headers,
|
||||||
)
|
)
|
||||||
response = await async_send_network_request(request)
|
response = await async_send_network_request(request)
|
||||||
|
|
@ -375,11 +383,13 @@ class Category:
|
||||||
self.set_category_global_cache("metadata", self._metadata)
|
self.set_category_global_cache("metadata", self._metadata)
|
||||||
return self
|
return self
|
||||||
|
|
||||||
async def is_valid(self):
|
async def validate(self):
|
||||||
try:
|
try:
|
||||||
await self.fetch_metadata()
|
await self.fetch_metadata()
|
||||||
except urllib.error.HTTPError:
|
except urllib.error.HTTPError as e:
|
||||||
return False
|
raise PluginSourceNetworkError(str(e))
|
||||||
|
except json.decoder.JSONDecodeError as e:
|
||||||
|
raise CategoryMetadataParseError(f"Failed to parse JSON: {str(e)}")
|
||||||
try:
|
try:
|
||||||
await asyncio.gather(
|
await asyncio.gather(
|
||||||
self.get_name(),
|
self.get_name(),
|
||||||
|
|
@ -388,7 +398,7 @@ class Category:
|
||||||
self.get_plugins(),
|
self.get_plugins(),
|
||||||
)
|
)
|
||||||
except KeyError:
|
except KeyError:
|
||||||
return False
|
raise CategoryMetadataParseError(f"Failed to parse JSON; missing required fields.")
|
||||||
else:
|
else:
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
@ -411,7 +421,7 @@ class Category:
|
||||||
Plugin(
|
Plugin(
|
||||||
plugin_info,
|
plugin_info,
|
||||||
f"{await self.get_plugins_base_url()}/{plugin_info[0]}.py",
|
f"{await self.get_plugins_base_url()}/{plugin_info[0]}.py",
|
||||||
is_3rd_party=self.is_3rd_party,
|
tag=self.tag,
|
||||||
)
|
)
|
||||||
for plugin_info in self._metadata["plugins"].items()
|
for plugin_info in self._metadata["plugins"].items()
|
||||||
])
|
])
|
||||||
|
|
@ -514,19 +524,19 @@ class PluginLocal:
|
||||||
fout.write(content)
|
fout.write(content)
|
||||||
|
|
||||||
def has_settings(self):
|
def has_settings(self):
|
||||||
for plugin_entry_point, plugin_class in babase.app.plugins.active_plugins.items():
|
for plugin_entry_point, plugin_spec in bui.app.plugins.plugin_specs.items():
|
||||||
if plugin_entry_point.startswith(self._entry_point_initials):
|
if plugin_entry_point.startswith(self._entry_point_initials):
|
||||||
return plugin_class.has_settings_ui()
|
return plugin_spec.plugin.has_settings_ui()
|
||||||
|
|
||||||
def launch_settings(self, source_widget):
|
def launch_settings(self, source_widget):
|
||||||
for plugin_entry_point, plugin_class in babase.app.plugins.active_plugins.items():
|
for plugin_entry_point, plugin_spec in bui.app.plugins.plugin_specs.items():
|
||||||
if plugin_entry_point.startswith(self._entry_point_initials):
|
if plugin_entry_point.startswith(self._entry_point_initials):
|
||||||
return plugin_class.show_settings_ui(source_widget)
|
return plugin_spec.plugin.show_settings_ui(source_widget)
|
||||||
|
|
||||||
async def get_content(self):
|
async def get_content(self):
|
||||||
if self._content is None:
|
if self._content is None:
|
||||||
if not self.is_installed:
|
if not self.is_installed:
|
||||||
raise PluginNotInstalledError("Plugin is not available locally.")
|
raise PluginNotInstalled("Plugin is not available locally.")
|
||||||
loop = asyncio.get_event_loop()
|
loop = asyncio.get_event_loop()
|
||||||
self._content = await loop.run_in_executor(None, self._get_content)
|
self._content = await loop.run_in_executor(None, self._get_content)
|
||||||
return self._content
|
return self._content
|
||||||
|
|
@ -613,7 +623,8 @@ class PluginLocal:
|
||||||
if entry_point not in babase.app.config["Plugins"]:
|
if entry_point not in babase.app.config["Plugins"]:
|
||||||
babase.app.config["Plugins"][entry_point] = {}
|
babase.app.config["Plugins"][entry_point] = {}
|
||||||
babase.app.config["Plugins"][entry_point]["enabled"] = True
|
babase.app.config["Plugins"][entry_point]["enabled"] = True
|
||||||
if entry_point not in babase.app.plugins.active_plugins:
|
plugin_spec = bui.app.plugins.plugin_specs.get(entry_point)
|
||||||
|
if plugin_spec not in bui.app.plugins.active_plugins:
|
||||||
self.load_plugin(entry_point)
|
self.load_plugin(entry_point)
|
||||||
bui.screenmessage(f"{entry_point} loaded")
|
bui.screenmessage(f"{entry_point} loaded")
|
||||||
if await self.has_minigames():
|
if await self.has_minigames():
|
||||||
|
|
@ -623,9 +634,14 @@ class PluginLocal:
|
||||||
|
|
||||||
def load_plugin(self, entry_point):
|
def load_plugin(self, entry_point):
|
||||||
plugin_class = babase._general.getclass(entry_point, babase.Plugin)
|
plugin_class = babase._general.getclass(entry_point, babase.Plugin)
|
||||||
loaded_plugin_class = plugin_class()
|
loaded_plugin_instance = plugin_class()
|
||||||
loaded_plugin_class.on_app_running()
|
loaded_plugin_instance.on_app_running()
|
||||||
babase.app.plugins.active_plugins[entry_point] = loaded_plugin_class
|
|
||||||
|
plugin_spec = babase.PluginSpec(class_path=entry_point, loadable=True)
|
||||||
|
plugin_spec.enabled = True
|
||||||
|
plugin_spec.plugin = loaded_plugin_instance
|
||||||
|
bui.app.plugins.plugin_specs[entry_point] = plugin_spec
|
||||||
|
bui.app.plugins.active_plugins.append(plugin_spec.plugin)
|
||||||
|
|
||||||
def disable(self):
|
def disable(self):
|
||||||
for entry_point, plugin_info in babase.app.config["Plugins"].items():
|
for entry_point, plugin_info in babase.app.config["Plugins"].items():
|
||||||
|
|
@ -674,7 +690,7 @@ class PluginLocal:
|
||||||
|
|
||||||
|
|
||||||
class PluginVersion:
|
class PluginVersion:
|
||||||
def __init__(self, plugin, version, tag=None):
|
def __init__(self, plugin, version, tag=CURRENT_TAG):
|
||||||
self.number, info = version
|
self.number, info = version
|
||||||
self.plugin = plugin
|
self.plugin = plugin
|
||||||
self.api_version = info["api_version"]
|
self.api_version = info["api_version"]
|
||||||
|
|
@ -682,10 +698,8 @@ class PluginVersion:
|
||||||
self.commit_sha = info["commit_sha"]
|
self.commit_sha = info["commit_sha"]
|
||||||
self.md5sum = info["md5sum"]
|
self.md5sum = info["md5sum"]
|
||||||
|
|
||||||
if tag is None:
|
|
||||||
tag = self.commit_sha
|
|
||||||
|
|
||||||
self.download_url = self.plugin.url.format(content_type="raw", tag=tag)
|
self.download_url = self.plugin.url.format(content_type="raw", tag=tag)
|
||||||
|
print(self.download_url)
|
||||||
self.view_url = self.plugin.url.format(content_type="blob", tag=tag)
|
self.view_url = self.plugin.url.format(content_type="blob", tag=tag)
|
||||||
|
|
||||||
def __eq__(self, plugin_version):
|
def __eq__(self, plugin_version):
|
||||||
|
|
@ -713,7 +727,7 @@ class PluginVersion:
|
||||||
async def install(self, suppress_screenmessage=False):
|
async def install(self, suppress_screenmessage=False):
|
||||||
try:
|
try:
|
||||||
local_plugin = await self._download()
|
local_plugin = await self._download()
|
||||||
except MD5CheckSumFailedError:
|
except MD5CheckSumFailed:
|
||||||
if not suppress_screenmessage:
|
if not suppress_screenmessage:
|
||||||
bui.screenmessage(
|
bui.screenmessage(
|
||||||
f"{self.plugin.name} failed MD5 checksum during installation", color=(1, 0, 0))
|
f"{self.plugin.name} failed MD5 checksum during installation", color=(1, 0, 0))
|
||||||
|
|
@ -728,20 +742,14 @@ class PluginVersion:
|
||||||
|
|
||||||
|
|
||||||
class Plugin:
|
class Plugin:
|
||||||
def __init__(self, plugin, url, is_3rd_party=False):
|
def __init__(self, plugin, url, tag=CURRENT_TAG):
|
||||||
"""
|
"""
|
||||||
Initialize a plugin from network repository.
|
Initialize a plugin from network repository.
|
||||||
"""
|
"""
|
||||||
self.name, self.info = plugin
|
self.name, self.info = plugin
|
||||||
self.is_3rd_party = is_3rd_party
|
|
||||||
self.install_path = os.path.join(PLUGIN_DIRECTORY, f"{self.name}.py")
|
self.install_path = os.path.join(PLUGIN_DIRECTORY, f"{self.name}.py")
|
||||||
# if is_3rd_party:
|
|
||||||
# tag = CURRENT_TAG
|
|
||||||
# else:
|
|
||||||
# tag = CURRENT_TAG
|
|
||||||
tag = CURRENT_TAG
|
|
||||||
self.url = url
|
self.url = url
|
||||||
self.download_url = url.format(content_type="raw", tag=tag)
|
self.tag = tag
|
||||||
self._local_plugin = None
|
self._local_plugin = None
|
||||||
|
|
||||||
self._versions = None
|
self._versions = None
|
||||||
|
|
@ -773,6 +781,7 @@ class Plugin:
|
||||||
PluginVersion(
|
PluginVersion(
|
||||||
self,
|
self,
|
||||||
version,
|
version,
|
||||||
|
tag=self.tag,
|
||||||
) for version in self.info["versions"].items()
|
) for version in self.info["versions"].items()
|
||||||
]
|
]
|
||||||
return self._versions
|
return self._versions
|
||||||
|
|
@ -783,7 +792,7 @@ class Plugin:
|
||||||
self._latest_version = PluginVersion(
|
self._latest_version = PluginVersion(
|
||||||
self,
|
self,
|
||||||
tuple(self.info["versions"].items())[0],
|
tuple(self.info["versions"].items())[0],
|
||||||
tag=CURRENT_TAG,
|
tag=self.tag,
|
||||||
)
|
)
|
||||||
return self._latest_version
|
return self._latest_version
|
||||||
|
|
||||||
|
|
@ -795,18 +804,18 @@ class Plugin:
|
||||||
self._latest_compatible_version = PluginVersion(
|
self._latest_compatible_version = PluginVersion(
|
||||||
self,
|
self,
|
||||||
(number, info),
|
(number, info),
|
||||||
CURRENT_TAG if self.latest_version.number == number else None
|
tag=self.tag if self.latest_version.number == number else info["commit_sha"]
|
||||||
)
|
)
|
||||||
break
|
break
|
||||||
if self._latest_compatible_version is None:
|
if self._latest_compatible_version is None:
|
||||||
raise NoCompatibleVersionError(
|
raise NoCompatibleVersion(
|
||||||
f"{self.name} has no version compatible with API {babase.app.api_version}."
|
f"{self.name} has no version compatible with API {babase.app.api_version}."
|
||||||
)
|
)
|
||||||
return self._latest_compatible_version
|
return self._latest_compatible_version
|
||||||
|
|
||||||
def get_local(self):
|
def get_local(self):
|
||||||
if not self.is_installed:
|
if not self.is_installed:
|
||||||
raise PluginNotInstalledError(
|
raise PluginNotInstalled(
|
||||||
f"{self.name} needs to be installed to get its local plugin.")
|
f"{self.name} needs to be installed to get its local plugin.")
|
||||||
if self._local_plugin is None:
|
if self._local_plugin is None:
|
||||||
self._local_plugin = PluginLocal(self.name)
|
self._local_plugin = PluginLocal(self.name)
|
||||||
|
|
@ -825,7 +834,7 @@ class Plugin:
|
||||||
def has_update(self):
|
def has_update(self):
|
||||||
try:
|
try:
|
||||||
latest_compatible_version = self.latest_compatible_version
|
latest_compatible_version = self.latest_compatible_version
|
||||||
except NoCompatibleVersionError:
|
except NoCompatibleVersion:
|
||||||
return False
|
return False
|
||||||
else:
|
else:
|
||||||
return self.get_local().version != latest_compatible_version.number
|
return self.get_local().version != latest_compatible_version.number
|
||||||
|
|
@ -874,8 +883,6 @@ class PluginWindow(popup.PopupWindow):
|
||||||
return partitioned_string
|
return partitioned_string
|
||||||
|
|
||||||
async def draw_ui(self):
|
async def draw_ui(self):
|
||||||
# print(babase.app.plugins.active_plugins)
|
|
||||||
|
|
||||||
bui.getsound('swish').play()
|
bui.getsound('swish').play()
|
||||||
b_text_color = (0.75, 0.7, 0.8)
|
b_text_color = (0.75, 0.7, 0.8)
|
||||||
s = 1.25 if _uiscale is babase.UIScale.SMALL else 1.39 if babase.UIScale.MEDIUM else 1.67
|
s = 1.25 if _uiscale is babase.UIScale.SMALL else 1.39 if babase.UIScale.MEDIUM else 1.67
|
||||||
|
|
@ -1177,14 +1184,22 @@ class PluginManager:
|
||||||
self.categories["All"] = None
|
self.categories["All"] = None
|
||||||
|
|
||||||
requests = []
|
requests = []
|
||||||
for plugin_category_url in plugin_index["categories"]:
|
for meta_url in plugin_index["categories"]:
|
||||||
category = Category(plugin_category_url)
|
category = Category(meta_url)
|
||||||
request = category.fetch_metadata()
|
request = category.fetch_metadata()
|
||||||
requests.append(request)
|
requests.append(request)
|
||||||
for repository in babase.app.config["Community Plugin Manager"]["Custom Sources"]:
|
for source in babase.app.config["Community Plugin Manager"]["Custom Sources"]:
|
||||||
plugin_category_url = partial_format(plugin_index["external_source_url"],
|
source_splits = source.split("@", maxsplit=1)
|
||||||
repository=repository)
|
if len(source_splits) == 1:
|
||||||
category = Category(plugin_category_url, is_3rd_party=True)
|
# Fallack to `main` if `@branchname` isn't specified in an external source URI.
|
||||||
|
source_repo, source_tag = source_splits[0], "main"
|
||||||
|
else:
|
||||||
|
source_repo, source_tag = source_splits
|
||||||
|
meta_url = partial_format(
|
||||||
|
plugin_index["external_source_url"],
|
||||||
|
repository=source_repo,
|
||||||
|
)
|
||||||
|
category = Category(meta_url, tag=source_tag)
|
||||||
request = category.fetch_metadata()
|
request = category.fetch_metadata()
|
||||||
requests.append(request)
|
requests.append(request)
|
||||||
categories = await asyncio.gather(*requests)
|
categories = await asyncio.gather(*requests)
|
||||||
|
|
@ -1247,7 +1262,7 @@ class PluginManager:
|
||||||
response = await async_send_network_request(download_url)
|
response = await async_send_network_request(download_url)
|
||||||
content = response.read()
|
content = response.read()
|
||||||
if hashlib.md5(content).hexdigest() != to_version_info["md5sum"]:
|
if hashlib.md5(content).hexdigest() != to_version_info["md5sum"]:
|
||||||
raise MD5CheckSumFailedError("MD5 checksum failed during plugin manager update.")
|
raise MD5CheckSumFailed("MD5 checksum failed during plugin manager update.")
|
||||||
with open(self.module_path, "wb") as fout:
|
with open(self.module_path, "wb") as fout:
|
||||||
fout.write(content)
|
fout.write(content)
|
||||||
return to_version_info
|
return to_version_info
|
||||||
|
|
@ -1396,14 +1411,23 @@ class PluginSourcesWindow(popup.PopupWindow):
|
||||||
|
|
||||||
async def add_source(self):
|
async def add_source(self):
|
||||||
source = bui.textwidget(query=self._add_source_widget)
|
source = bui.textwidget(query=self._add_source_widget)
|
||||||
meta_url = _CACHE["index"]["external_source_url"].format(
|
# External source URIs can optionally suffix `@branchname`, for example:
|
||||||
repository=source,
|
# `bombsquad-community/sample-plugin-source@experimental`
|
||||||
content_type="raw",
|
source_splits = source.split("@", maxsplit=1)
|
||||||
tag=CURRENT_TAG
|
if len(source_splits) == 1:
|
||||||
|
# Fallack to `main` if `@branchname` isn't specified in an external source URI.
|
||||||
|
source_repo, source_tag = source_splits[0], "main"
|
||||||
|
else:
|
||||||
|
source_repo, source_tag = source_splits
|
||||||
|
meta_url = partial_format(
|
||||||
|
_CACHE["index"]["external_source_url"],
|
||||||
|
repository=source_repo,
|
||||||
)
|
)
|
||||||
category = Category(meta_url, is_3rd_party=True)
|
category = Category(meta_url, tag=source_tag)
|
||||||
if not await category.is_valid():
|
try:
|
||||||
bui.screenmessage("Enter a valid plugin source", color=(1, 0, 0))
|
await category.validate()
|
||||||
|
except (PluginSourceNetworkError, CategoryMetadataParseError) as e:
|
||||||
|
bui.screenmessage(str(e), color=(1, 0, 0))
|
||||||
bui.getsound('error').play()
|
bui.getsound('error').play()
|
||||||
return
|
return
|
||||||
if source in babase.app.config["Community Plugin Manager"]["Custom Sources"]:
|
if source in babase.app.config["Community Plugin Manager"]["Custom Sources"]:
|
||||||
|
|
@ -1564,7 +1588,7 @@ class PluginManagerWindow(bui.Window):
|
||||||
# User probably went back before a bui.Window could finish loading.
|
# User probably went back before a bui.Window could finish loading.
|
||||||
pass
|
pass
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
ba.textwidget(edit=self._plugin_manager_status_text,
|
bui.textwidget(edit=self._plugin_manager_status_text,
|
||||||
text=str(e))
|
text=str(e))
|
||||||
raise
|
raise
|
||||||
|
|
||||||
|
|
@ -1684,7 +1708,7 @@ class PluginManagerWindow(bui.Window):
|
||||||
continue
|
continue
|
||||||
try:
|
try:
|
||||||
await self.draw_plugin_names(self.selected_category, search_term=filter_text)
|
await self.draw_plugin_names(self.selected_category, search_term=filter_text)
|
||||||
except CategoryDoesNotExistError:
|
except CategoryDoesNotExist:
|
||||||
pass
|
pass
|
||||||
# XXX: This may be more efficient, but we need a way to get a plugin's textwidget
|
# XXX: This may be more efficient, but we need a way to get a plugin's textwidget
|
||||||
# attributes like color, position and more.
|
# attributes like color, position and more.
|
||||||
|
|
@ -1775,7 +1799,7 @@ class PluginManagerWindow(bui.Window):
|
||||||
try:
|
try:
|
||||||
category_plugins = await self.plugin_manager.categories[category].get_plugins()
|
category_plugins = await self.plugin_manager.categories[category].get_plugins()
|
||||||
except (KeyError, AttributeError):
|
except (KeyError, AttributeError):
|
||||||
raise CategoryDoesNotExistError(f"{category} does not exist.")
|
raise CategoryDoesNotExist(f"{category} does not exist.")
|
||||||
|
|
||||||
if search_term:
|
if search_term:
|
||||||
def search_term_filterer(plugin): return self.search_term_filterer(plugin, search_term)
|
def search_term_filterer(plugin): return self.search_term_filterer(plugin, search_term)
|
||||||
|
|
@ -1800,7 +1824,7 @@ class PluginManagerWindow(bui.Window):
|
||||||
async def draw_plugin_name(self, plugin):
|
async def draw_plugin_name(self, plugin):
|
||||||
try:
|
try:
|
||||||
latest_compatible_version = plugin.latest_compatible_version
|
latest_compatible_version = plugin.latest_compatible_version
|
||||||
except NoCompatibleVersionError:
|
except NoCompatibleVersion:
|
||||||
# We currently don't show plugins that have no compatible versions.
|
# We currently don't show plugins that have no compatible versions.
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|
@ -2077,7 +2101,7 @@ class PluginManagerSettingsWindow(popup.PopupWindow):
|
||||||
async def update(self, to_version=None, commit_sha=None):
|
async def update(self, to_version=None, commit_sha=None):
|
||||||
try:
|
try:
|
||||||
await self._plugin_manager.update(to_version, commit_sha)
|
await self._plugin_manager.update(to_version, commit_sha)
|
||||||
except MD5CheckSumFailedError:
|
except MD5CheckSumFailed:
|
||||||
bui.screenmessage("MD5 checksum failed during plugin manager update", color=(1, 0, 0))
|
bui.screenmessage("MD5 checksum failed during plugin manager update", color=(1, 0, 0))
|
||||||
bui.getsound('error').play()
|
bui.getsound('error').play()
|
||||||
else:
|
else:
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue