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
This commit is contained in:
Research Assistant 2026-04-25 00:17:54 +08:00
parent 660204352f
commit ec2e91d4ac
6 changed files with 581 additions and 9 deletions

View file

@ -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

133
05_Bases/PaperForge.base Normal file
View file

@ -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

View file

@ -0,0 +1,3 @@
{
"domains": []
}

View file

@ -455,15 +455,62 @@ paperforge doctor
## 10. 升级与维护
### 更新 PaperForge 代码
#### 方式 1自动更新推荐
```bash
# 自动检测安装方式并更新
paperforge update
```
系统会自动检测你是通过 pip、git 还是手动安装,并执行对应的更新方式。
#### 方式 2Windows 一键脚本
双击运行 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/* <system_dir>/PaperForge/worker/scripts/
git pull origin master
pip install -e .
```
**git clone 用户:**
```bash
cd 你的Vault路径
git pull origin master
```
#### 方式 4手动复制最后手段
```bash
cp -r 新下载的代码/* <vault_path>/
```
> [!WARNING] 手动复制容易遗漏文件,建议优先使用自动更新。
### 备份注意事项
- `<resources_dir>/``<system_dir>/PaperForge/ocr/` 包含你的数据,需备份
- `.env` 包含 API Key不要提交到 git

View file

@ -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

View file

@ -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
}