feat: 添加 README 文件同步工具到 OpenWebUI 社区
This commit is contained in:
267
plugins/debug/common_tools/update_readmes_to_market.py
Normal file
267
plugins/debug/common_tools/update_readmes_to_market.py
Normal file
@@ -0,0 +1,267 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
======================================================================
|
||||
Staged README Synchronizer to OpenWebUI Community
|
||||
暂存 README 文件同步到 OpenWebUI 社区工具
|
||||
======================================================================
|
||||
|
||||
PURPOSE / 用途:
|
||||
--------------
|
||||
This script synchronizes staged README.md/README_CN.md files to their
|
||||
corresponding OpenWebUI Community posts automatically. It's designed for
|
||||
batch updating documentation content without modifying plugin versions
|
||||
or media attachments.
|
||||
|
||||
本脚本自动将暂存的 README.md/README_CN.md 文件同步到对应的 OpenWebUI
|
||||
社区帖子。专为批量更新文档内容设计,不修改插件版本或媒体附件。
|
||||
|
||||
USAGE / 使用方法:
|
||||
----------------
|
||||
1. Set up environment:
|
||||
配置环境:
|
||||
|
||||
Create a .env file in the repository root with:
|
||||
在仓库根目录创建 .env 文件,包含:
|
||||
|
||||
OPENWEBUI_API_KEY=your_api_key_here
|
||||
|
||||
2. Stage README files to sync:
|
||||
暂存需要同步的 README 文件:
|
||||
|
||||
git add plugins/actions/my_plugin/README.md
|
||||
git add plugins/actions/my_plugin/README_CN.md
|
||||
|
||||
3. Run the script:
|
||||
运行脚本:
|
||||
|
||||
python plugins/debug/common_tools/update_readmes_to_market.py
|
||||
|
||||
WORKFLOW / 工作流程:
|
||||
-------------------
|
||||
1. Load OPENWEBUI_API_KEY from .env file
|
||||
从 .env 文件加载 OPENWEBUI_API_KEY
|
||||
|
||||
2. Get list of staged README.md/README_CN.md files via git
|
||||
通过 git 获取暂存的 README.md/README_CN.md 文件列表
|
||||
|
||||
3. For each staged README:
|
||||
对于每个暂存的 README:
|
||||
|
||||
a. Locate the corresponding plugin .py file
|
||||
定位对应的插件 .py 文件
|
||||
|
||||
b. Extract openwebui_id/post_id from plugin frontmatter
|
||||
从插件前置信息中提取 openwebui_id/post_id
|
||||
|
||||
c. Fetch existing post data from OpenWebUI Community API
|
||||
从 OpenWebUI 社区 API 获取现有帖子数据
|
||||
|
||||
d. Update post content with new README content
|
||||
用新的 README 内容更新帖子内容
|
||||
|
||||
e. Push changes via API (preserves version & media)
|
||||
通过 API 推送更改(保留版本和媒体)
|
||||
|
||||
REQUIREMENTS / 依赖要求:
|
||||
-----------------------
|
||||
- python-dotenv: For loading .env configuration
|
||||
用于加载 .env 配置文件
|
||||
- Git repository: Must be run from a git-tracked workspace
|
||||
必须在 git 跟踪的工作区中运行
|
||||
|
||||
KEY FEATURES / 关键特性:
|
||||
-----------------------
|
||||
✅ Only updates content field (不仅更新内容字段)
|
||||
✅ Skips files without openwebui_id (跳过没有 openwebui_id 的文件)
|
||||
✅ Automatically matches CN/EN plugin files (自动匹配中英文插件文件)
|
||||
✅ Safe: Won't modify version or media fields (安全:不会修改版本或媒体字段)
|
||||
|
||||
NOTES / 注意事项:
|
||||
---------------
|
||||
- This is a DEBUG/DEVELOPMENT tool, not for production workflows
|
||||
这是一个调试/开发工具,不用于生产工作流
|
||||
|
||||
- Always verify changes in OpenWebUI Community after sync
|
||||
同步后务必在 OpenWebUI 社区中验证更改
|
||||
|
||||
- Requires valid API key with update permissions
|
||||
需要具有更新权限的有效 API 密钥
|
||||
|
||||
AUTHOR / 作者:
|
||||
-------------
|
||||
Fu-Jie
|
||||
GitHub: https://github.com/Fu-Jie/awesome-openwebui
|
||||
|
||||
======================================================================
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import importlib.util
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
import subprocess
|
||||
from pathlib import Path
|
||||
from typing import Dict, Optional, List
|
||||
|
||||
|
||||
def _load_dotenv(repo_root: Path) -> None:
|
||||
try:
|
||||
from dotenv import load_dotenv # type: ignore
|
||||
except Exception as exc: # pragma: no cover
|
||||
print("Missing dependency: python-dotenv. Please install it and retry.")
|
||||
raise SystemExit(1) from exc
|
||||
|
||||
env_path = repo_root / ".env"
|
||||
load_dotenv(env_path)
|
||||
|
||||
|
||||
def _get_repo_root() -> Path:
|
||||
return Path(__file__).resolve().parents[3]
|
||||
|
||||
|
||||
def _get_staged_readmes(repo_root: Path) -> List[Path]:
|
||||
try:
|
||||
output = subprocess.check_output(
|
||||
[
|
||||
"git",
|
||||
"-C",
|
||||
str(repo_root),
|
||||
"diff",
|
||||
"--cached",
|
||||
"--name-only",
|
||||
"--",
|
||||
"*.md",
|
||||
],
|
||||
text=True,
|
||||
)
|
||||
except subprocess.CalledProcessError as exc:
|
||||
print(f"Failed to read staged files: {exc}")
|
||||
return []
|
||||
|
||||
paths = []
|
||||
for line in output.splitlines():
|
||||
line = line.strip()
|
||||
if not line:
|
||||
continue
|
||||
if line.endswith("README.md") or line.endswith("README_CN.md"):
|
||||
paths.append(repo_root / line)
|
||||
return paths
|
||||
|
||||
|
||||
def _parse_frontmatter(content: str) -> Dict[str, str]:
|
||||
match = re.search(r'^\s*"""\n(.*?)\n"""', content, re.DOTALL)
|
||||
if not match:
|
||||
match = re.search(r'"""\n(.*?)\n"""', content, re.DOTALL)
|
||||
if not match:
|
||||
return {}
|
||||
|
||||
frontmatter = match.group(1)
|
||||
meta: Dict[str, str] = {}
|
||||
for line in frontmatter.split("\n"):
|
||||
if ":" in line:
|
||||
key, value = line.split(":", 1)
|
||||
meta[key.strip()] = value.strip()
|
||||
return meta
|
||||
|
||||
|
||||
def _find_plugin_file(readme_path: Path) -> Optional[Path]:
|
||||
plugin_dir = readme_path.parent
|
||||
is_cn = readme_path.name.lower().endswith("readme_cn.md")
|
||||
|
||||
py_files = [
|
||||
p
|
||||
for p in plugin_dir.glob("*.py")
|
||||
if p.name != "__init__.py" and not p.name.startswith("test_")
|
||||
]
|
||||
if not py_files:
|
||||
return None
|
||||
|
||||
cn_files = [p for p in py_files if p.stem.endswith("_cn")]
|
||||
en_files = [p for p in py_files if not p.stem.endswith("_cn")]
|
||||
|
||||
candidates = cn_files + en_files if is_cn else en_files + cn_files
|
||||
|
||||
# Prefer files that contain openwebui_id/post_id in frontmatter
|
||||
for candidate in candidates:
|
||||
post_id = _get_post_id(candidate)
|
||||
if post_id:
|
||||
return candidate
|
||||
|
||||
return candidates[0] if candidates else None
|
||||
|
||||
|
||||
def _get_post_id(plugin_file: Path) -> Optional[str]:
|
||||
try:
|
||||
content = plugin_file.read_text(encoding="utf-8")
|
||||
except Exception:
|
||||
return None
|
||||
|
||||
meta = _parse_frontmatter(content)
|
||||
return meta.get("openwebui_id") or meta.get("post_id")
|
||||
|
||||
|
||||
def main() -> int:
|
||||
repo_root = _get_repo_root()
|
||||
_load_dotenv(repo_root)
|
||||
|
||||
api_key = os.environ.get("OPENWEBUI_API_KEY")
|
||||
if not api_key:
|
||||
print("OPENWEBUI_API_KEY is not set in environment.")
|
||||
return 1
|
||||
|
||||
client_module_path = repo_root / "scripts" / "openwebui_community_client.py"
|
||||
spec = importlib.util.spec_from_file_location(
|
||||
"openwebui_community_client", client_module_path
|
||||
)
|
||||
if not spec or not spec.loader:
|
||||
print("Failed to load openwebui_community_client module.")
|
||||
return 1
|
||||
|
||||
module = importlib.util.module_from_spec(spec)
|
||||
spec.loader.exec_module(module)
|
||||
client = module.get_client(api_key)
|
||||
|
||||
staged_readmes = _get_staged_readmes(repo_root)
|
||||
if not staged_readmes:
|
||||
print("No staged README files found.")
|
||||
return 0
|
||||
|
||||
for readme_path in staged_readmes:
|
||||
if not readme_path.exists():
|
||||
print(f"Skipped (missing): {readme_path}")
|
||||
continue
|
||||
|
||||
plugin_file = _find_plugin_file(readme_path)
|
||||
if not plugin_file:
|
||||
print(f"Skipped (no plugin file): {readme_path}")
|
||||
continue
|
||||
|
||||
post_id = _get_post_id(plugin_file)
|
||||
if not post_id:
|
||||
print(f"Skipped (no openwebui_id): {readme_path}")
|
||||
continue
|
||||
|
||||
try:
|
||||
post_data = client.get_post(post_id)
|
||||
if not post_data:
|
||||
print(f"Skipped (post not found): {readme_path}")
|
||||
continue
|
||||
|
||||
readme_content = readme_path.read_text(encoding="utf-8")
|
||||
|
||||
# Update README content only, keep other fields unchanged.
|
||||
post_data["content"] = readme_content
|
||||
|
||||
ok = client.update_post(post_id, post_data)
|
||||
if ok:
|
||||
print(f"Updated README -> {readme_path} (post_id: {post_id})")
|
||||
except Exception as exc:
|
||||
print(f"Failed: {readme_path} ({exc})")
|
||||
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
Reference in New Issue
Block a user