From ec2e91d4ac39dd63d429a29e63576b33602bd7f3 Mon Sep 17 00:00:00 2001 From: Research Assistant Date: Sat, 25 Apr 2026 00:17:54 +0800 Subject: [PATCH] feat(update): smart update with auto-detection + Windows one-click script paperforge update: - Auto-detects installation method (pip/pip-editable/git/zip) - Routes to appropriate update strategy - pip users: pip install --upgrade - pip-editable users: git pull + pip install -e . - git users: git pull - unknown: zip download fallback scripts/update-paperforge.ps1: - Windows one-click update script - Auto-detects install method - Supports -Force (skip confirm) and -DryRun - Clear colored output with error handling AGENTS.md: - Added comprehensive update section with 4 methods - Documented auto-update and PowerShell script --- 05_Bases/Literature Hub.base | 133 ++++++++++++ 05_Bases/PaperForge.base | 133 ++++++++++++ .../PaperForge/config/domain-collections.json | 3 + AGENTS.md | 57 +++++- paperforge/worker/update.py | 72 ++++++- scripts/update-paperforge.ps1 | 192 ++++++++++++++++++ 6 files changed, 581 insertions(+), 9 deletions(-) create mode 100644 05_Bases/Literature Hub.base create mode 100644 05_Bases/PaperForge.base create mode 100644 99_System/PaperForge/config/domain-collections.json create mode 100644 scripts/update-paperforge.ps1 diff --git a/05_Bases/Literature Hub.base b/05_Bases/Literature Hub.base new file mode 100644 index 00000000..f88c6775 --- /dev/null +++ b/05_Bases/Literature Hub.base @@ -0,0 +1,133 @@ +filters: + and: + - file.inFolder("03_Resources/LiteratureControl/library-records") +properties: + zotero_key: + displayName: "Zotero Key" + title: + displayName: "Title" + year: + displayName: "Year" + has_pdf: + displayName: "PDF" + do_ocr: + displayName: "OCR" + analyze: + displayName: "Analyze" + ocr_status: + displayName: "OCR Status" + deep_reading_status: + displayName: "Deep Reading" + pdf_path: + displayName: "PDF Path" + fulltext_md_path: + displayName: "Fulltext" +views: +# PAPERFORGE_VIEW: 控制面板 + - type: table + name: "控制面板" + order: + - file.name + - title + - year + - has_pdf + - do_ocr + - analyze + - ocr_status + - deep_reading_status + - pdf_path + - fulltext_md_path + + +# PAPERFORGE_VIEW: 推荐分析 + - type: table + name: "推荐分析" + order: + - year + - title + - has_pdf + - do_ocr + - analyze + - ocr_status + - deep_reading_status + - pdf_path + - fulltext_md_path + filter: 'analyze = true AND recommend_analyze = true' + +# PAPERFORGE_VIEW: 待 OCR + - type: table + name: "待 OCR" + order: + - year + - title + - has_pdf + - do_ocr + - ocr_status + - pdf_path + filter: 'do_ocr = true AND ocr_status = "pending"' + +# PAPERFORGE_VIEW: OCR 完成 + - type: table + name: "OCR 完成" + order: + - year + - title + - has_pdf + - do_ocr + - ocr_status + - pdf_path + filter: 'ocr_status = "done"' + +# PAPERFORGE_VIEW: 待深度阅读 + - type: table + name: "待深度阅读" + order: + - year + - title + - has_pdf + - do_ocr + - analyze + - ocr_status + - deep_reading_status + - pdf_path + filter: 'analyze = true AND ocr_status = "done" AND deep_reading_status = "pending"' + +# PAPERFORGE_VIEW: 深度阅读完成 + - type: table + name: "深度阅读完成" + order: + - year + - title + - has_pdf + - do_ocr + - analyze + - ocr_status + - deep_reading_status + - pdf_path + filter: 'deep_reading_status = "done"' + +# PAPERFORGE_VIEW: 正式卡片 + - type: table + name: "正式卡片" + order: + - title + - year + - has_pdf + - deep_reading_status + - pdf_path + filter: 'deep_reading_status = "done"' + +# PAPERFORGE_VIEW: 全记录 + - type: table + name: "全记录" + order: + - title + - year + - has_pdf + - do_ocr + - analyze + - ocr_status + - deep_reading_status + - pdf_path + - fulltext_md_path + diff --git a/05_Bases/PaperForge.base b/05_Bases/PaperForge.base new file mode 100644 index 00000000..f88c6775 --- /dev/null +++ b/05_Bases/PaperForge.base @@ -0,0 +1,133 @@ +filters: + and: + - file.inFolder("03_Resources/LiteratureControl/library-records") +properties: + zotero_key: + displayName: "Zotero Key" + title: + displayName: "Title" + year: + displayName: "Year" + has_pdf: + displayName: "PDF" + do_ocr: + displayName: "OCR" + analyze: + displayName: "Analyze" + ocr_status: + displayName: "OCR Status" + deep_reading_status: + displayName: "Deep Reading" + pdf_path: + displayName: "PDF Path" + fulltext_md_path: + displayName: "Fulltext" +views: +# PAPERFORGE_VIEW: 控制面板 + - type: table + name: "控制面板" + order: + - file.name + - title + - year + - has_pdf + - do_ocr + - analyze + - ocr_status + - deep_reading_status + - pdf_path + - fulltext_md_path + + +# PAPERFORGE_VIEW: 推荐分析 + - type: table + name: "推荐分析" + order: + - year + - title + - has_pdf + - do_ocr + - analyze + - ocr_status + - deep_reading_status + - pdf_path + - fulltext_md_path + filter: 'analyze = true AND recommend_analyze = true' + +# PAPERFORGE_VIEW: 待 OCR + - type: table + name: "待 OCR" + order: + - year + - title + - has_pdf + - do_ocr + - ocr_status + - pdf_path + filter: 'do_ocr = true AND ocr_status = "pending"' + +# PAPERFORGE_VIEW: OCR 完成 + - type: table + name: "OCR 完成" + order: + - year + - title + - has_pdf + - do_ocr + - ocr_status + - pdf_path + filter: 'ocr_status = "done"' + +# PAPERFORGE_VIEW: 待深度阅读 + - type: table + name: "待深度阅读" + order: + - year + - title + - has_pdf + - do_ocr + - analyze + - ocr_status + - deep_reading_status + - pdf_path + filter: 'analyze = true AND ocr_status = "done" AND deep_reading_status = "pending"' + +# PAPERFORGE_VIEW: 深度阅读完成 + - type: table + name: "深度阅读完成" + order: + - year + - title + - has_pdf + - do_ocr + - analyze + - ocr_status + - deep_reading_status + - pdf_path + filter: 'deep_reading_status = "done"' + +# PAPERFORGE_VIEW: 正式卡片 + - type: table + name: "正式卡片" + order: + - title + - year + - has_pdf + - deep_reading_status + - pdf_path + filter: 'deep_reading_status = "done"' + +# PAPERFORGE_VIEW: 全记录 + - type: table + name: "全记录" + order: + - title + - year + - has_pdf + - do_ocr + - analyze + - ocr_status + - deep_reading_status + - pdf_path + - fulltext_md_path + diff --git a/99_System/PaperForge/config/domain-collections.json b/99_System/PaperForge/config/domain-collections.json new file mode 100644 index 00000000..12389079 --- /dev/null +++ b/99_System/PaperForge/config/domain-collections.json @@ -0,0 +1,3 @@ +{ + "domains": [] +} \ No newline at end of file diff --git a/AGENTS.md b/AGENTS.md index 546a584e..e499a746 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -455,15 +455,62 @@ paperforge doctor ## 10. 升级与维护 ### 更新 PaperForge 代码 + +#### 方式 1:自动更新(推荐) + +```bash +# 自动检测安装方式并更新 +paperforge update +``` + +系统会自动检测你是通过 pip、git 还是手动安装,并执行对应的更新方式。 + +#### 方式 2:Windows 一键脚本 + +双击运行 Vault 根目录下的 `scripts/update-paperforge.ps1`: +- 自动检测安装方式 +- 自动执行更新 +- 无需手动输入命令 + +```powershell +# 或在 PowerShell 中执行 +.\scripts\update-paperforge.ps1 + +# 强制更新(跳过确认) +.\scripts\update-paperforge.ps1 -Force + +# 只检测不更新 +.\scripts\update-paperforge.ps1 -DryRun +``` + +#### 方式 3:手动更新 + +**pip 安装用户:** +```bash +pip install --upgrade paperforge +``` + +**pip editable 安装用户:** ```bash cd 你的Vault路径 -# 如果你有 git 跟踪 PaperForge -git pull origin main - -# 或手动复制更新文件 -cp -r 新下载的scripts/* /PaperForge/worker/scripts/ +git pull origin master +pip install -e . ``` +**git clone 用户:** +```bash +cd 你的Vault路径 +git pull origin master +``` + +#### 方式 4:手动复制(最后手段) + +```bash +cp -r 新下载的代码/* / +``` + +> [!WARNING] 手动复制容易遗漏文件,建议优先使用自动更新。 + ### 备份注意事项 - `/` 和 `/PaperForge/ocr/` 包含你的数据,需备份 - `.env` 包含 API Key,不要提交到 git diff --git a/paperforge/worker/update.py b/paperforge/worker/update.py index 7868b93f..1126107d 100644 --- a/paperforge/worker/update.py +++ b/paperforge/worker/update.py @@ -299,7 +299,49 @@ def _rollback(vault: Path, backup_dir: Path) -> None: _log("[OK] 回滚完成", "g") -def update_via_git(vault: Path) -> bool: +def _detect_install_method() -> tuple[str, Path | None]: + """Detect how paperforge is installed.""" + import paperforge + pkg_dir = Path(paperforge.__file__).parent.resolve() + + # Check if installed in site-packages (pip install) + if "site-packages" in str(pkg_dir) or "dist-packages" in str(pkg_dir): + return ("pip", pkg_dir) + + # Check if in editable mode (pip install -e .) + if pkg_dir.name == "paperforge" and (pkg_dir.parent / ".git").exists(): + return ("pip-editable", pkg_dir.parent) + + # Check if vault has .git (git clone) + vault = Path.cwd() + if (vault / ".git").exists(): + return ("git", vault) + + return ("unknown", None) + + +def _update_via_pip(editable: bool = False) -> bool: + """Update via pip install.""" + cmd = [sys.executable, "-m", "pip", "install"] + if editable: + cmd.extend(["-e", "."]) + else: + cmd.append("--upgrade") + cmd.append("paperforge") + + _log(f"[INFO] 执行: {' '.join(cmd)}", "b") + r = subprocess.run(cmd, capture_output=True, text=True, encoding="utf-8") + if r.returncode != 0: + _log(f"[ERR] pip 更新失败: {r.stderr}", "r") + return False + _log("[OK] pip 更新成功", "g") + if r.stdout.strip(): + print(r.stdout) + return True + + +def _update_via_git(vault: Path) -> bool: + """Update via git pull.""" if not (vault / ".git").is_dir(): _log("[ERR] 不是 git 仓库", "r") return False @@ -383,10 +425,32 @@ def run_update(vault: Path) -> int: if ans not in ("y", "yes"): _log("[INFO] 已取消", "c") return 0 - if (vault / ".git").is_dir(): - success = update_via_git(vault) + + # Auto-detect installation method + method, path = _detect_install_method() + _log(f"[检测] 安装方式: {method}", "c") + + if method == "pip": + _log("[INFO] 通过 pip 更新...", "b") + success = _update_via_pip(editable=False) + elif method == "pip-editable": + _log("[INFO] 通过 pip editable 模式更新...", "b") + # For editable install, need to git pull first then reinstall + if path and (path / ".git").exists(): + success = _update_via_git(path) + if success: + _log("[INFO] 重新安装 editable 模式...", "b") + os.chdir(path) + success = _update_via_pip(editable=True) + else: + _log("[WARN] 无法找到 git 仓库,尝试 pip 更新...", "y") + success = _update_via_pip(editable=False) + elif method == "git": + success = _update_via_git(vault) else: - success = update_via_zip(vault) + _log("[WARN] 未检测到标准安装方式,尝试 zip 下载...", "y") + success = _update_via_zip(vault) + if success: _log("\n[OK] 更新完成!请重启 Obsidian", "g") return 0 if success else 1 diff --git a/scripts/update-paperforge.ps1 b/scripts/update-paperforge.ps1 new file mode 100644 index 00000000..a06bff58 --- /dev/null +++ b/scripts/update-paperforge.ps1 @@ -0,0 +1,192 @@ +# PaperForge Lite Update Script for Windows +# 一键更新脚本 — 双击运行即可 +# +# 功能: +# 1. 自动检测安装方式(pip/pip-editable/git/zip) +# 2. 执行对应的更新命令 +# 3. 如果自动检测失败,提供手动选项 +# +# 用法: +# 1. 右键此文件 → "使用 PowerShell 运行" +# 2. 或在终端执行: .\update-paperforge.ps1 + +param( + [switch]$Force, # 跳过确认提示 + [switch]$DryRun # 只检测不执行 +) + +$ErrorActionPreference = "Stop" + +# 颜色输出 +function Write-Color($text, $color = "White") { + Write-Host $text -ForegroundColor $color +} + +Write-Color @" +======================================== + PaperForge Lite 更新助手 +======================================== +"@ "Cyan" + +# 检测 Python +$python = Get-Command python -ErrorAction SilentlyContinue +if (-not $python) { + $python = Get-Command python3 -ErrorAction SilentlyContinue +} +if (-not $python) { + Write-Color "[错误] 未找到 Python。请先安装 Python 3.10+" "Red" + exit 1 +} + +$pyVersion = & $python.Source --version 2>&1 +Write-Color "[检测] Python: $pyVersion" "Gray" + +# 检测 pip +$pip = Get-Command pip -ErrorAction SilentlyContinue +if (-not $pip) { + $pip = Get-Command pip3 -ErrorAction SilentlyContinue +} +if (-not $pip) { + Write-Color "[错误] 未找到 pip。请检查 Python 安装" "Red" + exit 1 +} + +# 检测 paperforge 安装方式 +Write-Color "`n[检测] 正在分析 PaperForge 安装方式..." "Yellow" + +$installMethod = "unknown" +$installPath = $null + +try { + $pkgPath = & $python.Source -c "import paperforge; print(paperforge.__file__)" 2>$null + if ($pkgPath) { + $pkgDir = Split-Path $pkgPath -Parent + $installPath = $pkgDir + + if ($pkgDir -match "site-packages|dist-packages") { + # pip install (non-editable) + $installMethod = "pip" + } elseif (Test-Path (Join-Path $pkgDir "..\.git")) { + # pip install -e . + $installMethod = "pip-editable" + $installPath = Resolve-Path (Join-Path $pkgDir "..") | Select-Object -ExpandProperty Path + } + } +} catch { + # paperforge not installed via pip +} + +# 如果上面没检测到,检查当前目录是否有 .git +if ($installMethod -eq "unknown" -and (Test-Path ".git")) { + $installMethod = "git" + $installPath = (Get-Location).Path +} + +# 显示检测结果 +Write-Color "[结果] 安装方式: $installMethod" $(if ($installMethod -ne "unknown") { "Green" } else { "Red" }) +if ($installPath) { + Write-Color "[路径] $installPath" "Gray" +} + +if ($DryRun) { + Write-Color "`n[干运行模式] 仅检测,不执行更新" "Yellow" + exit 0 +} + +# 根据安装方式执行更新 +$success = $false + +switch ($installMethod) { + "pip" { + Write-Color "`n[更新] 通过 pip 升级..." "Cyan" + Write-Color "命令: pip install --upgrade paperforge" "Gray" + + if (-not $Force) { + $confirm = Read-Host "确认更新? [y/N]" + if ($confirm -notmatch "^[Yy]") { + Write-Color "[取消] 更新已取消" "Yellow" + exit 0 + } + } + + & $pip.Source install --upgrade paperforge + $success = ($LASTEXITCODE -eq 0) + } + + "pip-editable" { + Write-Color "`n[更新] pip editable 模式 detected" "Cyan" + Write-Color "步骤 1: git pull 拉取最新代码..." "Yellow" + + if (-not $Force) { + $confirm = Read-Host "确认更新? [y/N]" + if ($confirm -notmatch "^[Yy]") { + Write-Color "[取消] 更新已取消" "Yellow" + exit 0 + } + } + + Push-Location $installPath + try { + git pull origin master + if ($LASTEXITCODE -eq 0) { + Write-Color "步骤 2: 重新安装 editable 模式..." "Yellow" + & $pip.Source install -e . + $success = ($LASTEXITCODE -eq 0) + } + } finally { + Pop-Location + } + } + + "git" { + Write-Color "`n[更新] 通过 git pull 更新..." "Cyan" + Write-Color "命令: git pull origin master" "Gray" + + if (-not $Force) { + $confirm = Read-Host "确认更新? [y/N]" + if ($confirm -notmatch "^[Yy]") { + Write-Color "[取消] 更新已取消" "Yellow" + exit 0 + } + } + + git pull origin master + $success = ($LASTEXITCODE -eq 0) + } + + default { + Write-Color "`n[错误] 无法自动检测安装方式" "Red" + Write-Color @" + +可能的解决方案: +1. 如果你是通过 pip 安装的: + pip install --upgrade paperforge + +2. 如果你是通过 git clone 的: + git pull origin master + pip install -e . + +3. 如果你不确定: + - 删除现有安装 + - 重新运行: pip install -e . +"@ "Yellow" + exit 1 + } +} + +# 验证更新 +if ($success) { + Write-Color "`n[验证] 检查更新结果..." "Cyan" + try { + $newVersion = & $python.Source -c "import paperforge; print(paperforge.__version__)" 2>$null + Write-Color "[成功] 更新完成!当前版本: $newVersion" "Green" + Write-Color "`n提示: 请重启 Obsidian 以应用更新" "Yellow" + } catch { + Write-Color "[成功] 更新完成!请重启 Obsidian" "Green" + } + exit 0 +} else { + Write-Color "`n[失败] 更新过程中出现错误" "Red" + Write-Color "建议: 检查网络连接,或稍后重试" "Yellow" + exit 1 +}