237 lines
7.5 KiB
Python
237 lines
7.5 KiB
Python
"""
|
|
Publish plugins to OpenWebUI Community
|
|
使用 OpenWebUICommunityClient 发布插件到官方社区
|
|
|
|
用法:
|
|
python scripts/publish_plugin.py # 更新已发布的插件(版本变化时)
|
|
python scripts/publish_plugin.py --force # 强制更新所有已发布的插件
|
|
python scripts/publish_plugin.py --new plugins/actions/xxx # 首次发布指定目录的新插件
|
|
python scripts/publish_plugin.py --new plugins/actions/xxx --force # 强制发布新插件
|
|
"""
|
|
|
|
import os
|
|
import sys
|
|
import re
|
|
import argparse
|
|
|
|
# Add current directory to path
|
|
sys.path.append(os.path.dirname(os.path.abspath(__file__)))
|
|
|
|
from openwebui_community_client import get_client
|
|
|
|
|
|
def find_existing_plugins(plugins_dir: str) -> list:
|
|
"""查找所有已发布的插件文件(有 openwebui_id 的)"""
|
|
plugins = []
|
|
for root, dirs, files in os.walk(plugins_dir):
|
|
# Exclude debug directory
|
|
if "debug" in dirs:
|
|
dirs.remove("debug")
|
|
|
|
for file in files:
|
|
if file.endswith(".py") and not file.startswith("__"):
|
|
file_path = os.path.join(root, file)
|
|
with open(file_path, "r", encoding="utf-8") as f:
|
|
content = f.read(2000)
|
|
|
|
id_match = re.search(
|
|
r"(?:openwebui_id|post_id):\s*([a-z0-9-]+)", content
|
|
)
|
|
if id_match:
|
|
plugins.append(
|
|
{
|
|
"file_path": file_path,
|
|
"post_id": id_match.group(1).strip(),
|
|
}
|
|
)
|
|
return plugins
|
|
|
|
|
|
def find_new_plugins_in_dir(target_dir: str) -> list:
|
|
"""查找指定目录中没有 openwebui_id 的新插件"""
|
|
plugins = []
|
|
|
|
if not os.path.isdir(target_dir):
|
|
print(f"Error: {target_dir} is not a directory")
|
|
return plugins
|
|
|
|
for file in os.listdir(target_dir):
|
|
if file.endswith(".py") and not file.startswith("__"):
|
|
file_path = os.path.join(target_dir, file)
|
|
if not os.path.isfile(file_path):
|
|
continue
|
|
|
|
with open(file_path, "r", encoding="utf-8") as f:
|
|
content = f.read(2000)
|
|
|
|
# 检查是否有 frontmatter (title)
|
|
title_match = re.search(r"title:\s*(.+)", content)
|
|
if not title_match:
|
|
continue
|
|
|
|
# 检查是否已有 ID
|
|
id_match = re.search(r"(?:openwebui_id|post_id):\s*([a-z0-9-]+)", content)
|
|
if id_match:
|
|
print(f" ⚠️ {file} already has ID, will update instead")
|
|
plugins.append(
|
|
{
|
|
"file_path": file_path,
|
|
"title": title_match.group(1).strip(),
|
|
"post_id": id_match.group(1).strip(),
|
|
"is_new": False,
|
|
}
|
|
)
|
|
else:
|
|
plugins.append(
|
|
{
|
|
"file_path": file_path,
|
|
"title": title_match.group(1).strip(),
|
|
"post_id": None,
|
|
"is_new": True,
|
|
}
|
|
)
|
|
|
|
return plugins
|
|
|
|
|
|
def main():
|
|
parser = argparse.ArgumentParser(
|
|
description="Publish plugins to OpenWebUI Market",
|
|
formatter_class=argparse.RawDescriptionHelpFormatter,
|
|
epilog="""
|
|
Examples:
|
|
# Update existing plugins (with version check)
|
|
python scripts/publish_plugin.py
|
|
|
|
# Force update all existing plugins
|
|
python scripts/publish_plugin.py --force
|
|
|
|
# Publish new plugins from a specific directory
|
|
python scripts/publish_plugin.py --new plugins/actions/summary
|
|
|
|
# Preview what would be done
|
|
python scripts/publish_plugin.py --new plugins/actions/summary --dry-run
|
|
""",
|
|
)
|
|
parser.add_argument(
|
|
"--force", action="store_true", help="Force update even if version matches"
|
|
)
|
|
parser.add_argument(
|
|
"--new",
|
|
metavar="DIR",
|
|
help="Publish new plugins from the specified directory (required for first-time publishing)",
|
|
)
|
|
parser.add_argument(
|
|
"--dry-run",
|
|
action="store_true",
|
|
help="Show what would be done without actually publishing",
|
|
)
|
|
args = parser.parse_args()
|
|
|
|
try:
|
|
client = get_client()
|
|
except ValueError as e:
|
|
print(f"Error: {e}")
|
|
sys.exit(1)
|
|
|
|
base_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
|
plugins_dir = os.path.join(base_dir, "plugins")
|
|
|
|
updated = 0
|
|
created = 0
|
|
skipped = 0
|
|
failed = 0
|
|
|
|
# 处理新插件发布
|
|
if args.new:
|
|
target_dir = args.new
|
|
if not os.path.isabs(target_dir):
|
|
target_dir = os.path.join(base_dir, target_dir)
|
|
|
|
print(f"🆕 Publishing new plugins from: {target_dir}\n")
|
|
new_plugins = find_new_plugins_in_dir(target_dir)
|
|
|
|
if not new_plugins:
|
|
print("No plugins found in the specified directory.")
|
|
return
|
|
|
|
for plugin in new_plugins:
|
|
file_path = plugin["file_path"]
|
|
file_name = os.path.basename(file_path)
|
|
title = plugin["title"]
|
|
is_new = plugin.get("is_new", True)
|
|
|
|
if is_new:
|
|
print(f"🆕 Creating: {file_name} ({title})")
|
|
else:
|
|
print(f"📦 Updating: {file_name} (ID: {plugin['post_id'][:8]}...)")
|
|
|
|
if args.dry_run:
|
|
print(f" [DRY-RUN] Would {'create' if is_new else 'update'}")
|
|
continue
|
|
|
|
success, message = client.publish_plugin_from_file(
|
|
file_path, force=args.force, auto_create=True
|
|
)
|
|
|
|
if success:
|
|
if "Created" in message:
|
|
print(f" 🎉 {message}")
|
|
created += 1
|
|
elif "Skipped" in message:
|
|
print(f" ⏭️ {message}")
|
|
skipped += 1
|
|
else:
|
|
print(f" ✅ {message}")
|
|
updated += 1
|
|
else:
|
|
print(f" ❌ {message}")
|
|
failed += 1
|
|
|
|
# 处理已有插件更新
|
|
else:
|
|
existing_plugins = find_existing_plugins(plugins_dir)
|
|
print(f"Found {len(existing_plugins)} existing plugins with OpenWebUI ID.\n")
|
|
|
|
if not existing_plugins:
|
|
print("No existing plugins to update.")
|
|
print(
|
|
"\n💡 Tip: Use --new <dir> to publish new plugins from a specific directory"
|
|
)
|
|
return
|
|
|
|
for plugin in existing_plugins:
|
|
file_path = plugin["file_path"]
|
|
file_name = os.path.basename(file_path)
|
|
post_id = plugin["post_id"]
|
|
|
|
print(f"📦 {file_name} (ID: {post_id[:8]}...)")
|
|
|
|
if args.dry_run:
|
|
print(f" [DRY-RUN] Would update")
|
|
continue
|
|
|
|
success, message = client.publish_plugin_from_file(
|
|
file_path, force=args.force, auto_create=False # 不自动创建,只更新
|
|
)
|
|
|
|
if success:
|
|
if "Skipped" in message:
|
|
print(f" ⏭️ {message}")
|
|
skipped += 1
|
|
else:
|
|
print(f" ✅ {message}")
|
|
updated += 1
|
|
else:
|
|
print(f" ❌ {message}")
|
|
failed += 1
|
|
|
|
print(f"\n{'='*50}")
|
|
print(
|
|
f"Finished: {created} created, {updated} updated, {skipped} skipped, {failed} failed"
|
|
)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|