Files
pptopic/setup.ps1

360 lines
12 KiB
PowerShell
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#Requires -Version 5.1
<#
.SYNOPSIS
pptopic 一键安装脚本
.DESCRIPTION
自动完成以下安装步骤:
1. 安装 uvPython 包管理器)
2. 安装 Python 3.13
3. 安装 pngquant可选但推荐
4. 安装 pptopic
5. 验证安装结果
本脚本会自动处理 PowerShell 执行策略和当前会话 PATH 刷新,
安装完成后无需手动重启 PowerShell。
.PARAMETER SkipPngquant
跳过 pngquant 安装。
.PARAMETER Force
强制重新安装 uv 和 pngquant。
.PARAMETER SkipPptopicInstall
只安装环境,不安装 pptopic调试用
.EXAMPLE
.\setup.ps1
.EXAMPLE
.\setup.ps1 -SkipPngquant
#>
param(
[switch]$SkipPngquant,
[switch]$Force,
[switch]$SkipPptopicInstall
)
$ErrorActionPreference = "Stop"
$InformationPreference = "Continue"
$ProjectRoot = $PSScriptRoot
if ([string]::IsNullOrWhiteSpace($ProjectRoot)) {
$ProjectRoot = (Get-Location).Path
}
# ============================================================
# 工具函数
# ============================================================
function Test-CommandExists {
param([string]$Name)
try {
$cmd = Get-Command $Name -ErrorAction Stop
return $cmd.Source
} catch {
return $null
}
}
function Add-ToCurrentPath {
param([string]$LiteralPath)
if ([string]::IsNullOrWhiteSpace($LiteralPath)) {
return
}
$currentPaths = $env:Path -split ';' | ForEach-Object { $_.TrimEnd('\') }
$normalizedPath = $LiteralPath.TrimEnd('\')
if ($normalizedPath -in $currentPaths) {
return
}
if (-not (Test-Path $LiteralPath)) {
return
}
$env:Path = "$LiteralPath;$env:Path"
Write-Information "已临时将 $LiteralPath 加入当前会话 PATH"
}
function Get-UvInstallDir {
# uv-installer.ps1 的安装位置优先级
$candidates = @()
if ($env:XDG_BIN_HOME) {
$candidates += $env:XDG_BIN_HOME
}
if ($env:XDG_DATA_HOME) {
$candidates += (Join-Path $env:XDG_DATA_HOME "../bin")
}
$candidates += (Join-Path $HOME ".local\bin")
foreach ($candidate in $candidates) {
$resolved = $null
try {
$resolved = (Resolve-Path $candidate -ErrorAction SilentlyContinue).Path
} catch {
$resolved = $candidate
}
if ($resolved -and (Test-Path $resolved)) {
return $resolved
}
}
# 默认返回最后一个候选
return Join-Path $HOME ".local\bin"
}
function Invoke-NativeCommand {
# 安全地调用原生程序uv/pngquant 等)。
# 这些程序的进度/提示信息常写到 stderr例如 "Python 3.13 is already installed"
# 而本脚本 $ErrorActionPreference="Stop" 会把 stderr 当作终止错误抛出,导致重跑误失败。
# 此函数临时关闭 Stop合并 stdout/stderr并按退出码判断成败。
param(
[Parameter(Mandatory)][string]$LiteralCommand,
[string]$FailureMessage
)
$prevEAP = $ErrorActionPreference
$ErrorActionPreference = 'Continue'
try {
$output = Invoke-Expression "$LiteralCommand 2>&1"
} finally {
$ErrorActionPreference = $prevEAP
}
if ($LASTEXITCODE -ne 0) {
$msg = if ($FailureMessage) { $FailureMessage } else { "命令失败" }
throw "$msg(退出码 $LASTEXITCODE$($output -join "`n")"
}
return $output
}
function Test-PowerPointInstalled {
# 通过注册表 ProgID 检测,不启动 PowerPoint 进程
try {
$null = Get-Item "Registry::HKEY_CLASSES_ROOT\PowerPoint.Application" -ErrorAction Stop
return $true
} catch {
return $false
}
}
function Get-PngquantInstallDir {
# pngquant 可能的安装位置:默认 %APPDATA%\pngquant或 scoop 的 shims 目录
$candidates = @("$env:APPDATA\pngquant")
if ($env:SCOOP) {
$candidates += (Join-Path $env:SCOOP "shims")
}
$candidates += (Join-Path $HOME "scoop\shims")
foreach ($candidate in $candidates) {
if (Test-Path $candidate) {
return $candidate
}
}
# 找不到就返回默认即便不存在Add-ToCurrentPath 内部会跳过)
return "$env:APPDATA\pngquant"
}
function Invoke-Step {
param(
[Parameter(Mandatory)][string]$Title,
[Parameter(Mandatory)][scriptblock]$Action
)
Write-Information ""
Write-Information "========================================"
Write-Information " $Title"
Write-Information "========================================"
try {
& $Action
} catch {
Write-Information ""
Write-Information "失败:$_"
throw
}
}
# ============================================================
# 主流程
# ============================================================
Write-Information ""
Write-Information "========================================"
Write-Information " pptopic 一键安装程序"
Write-Information "========================================"
Write-Information ""
Write-Information "项目目录:$ProjectRoot"
if ($SkipPngquant) {
Write-Information "已指定 -SkipPngquant将跳过 pngquant 安装。"
}
if ($Force) {
Write-Information "已指定 -Force将强制重新安装 uv / pngquant。"
}
# 第一步:安装 uv
Invoke-Step -Title "步骤 1/5安装 uv" -Action {
$uvPath = Test-CommandExists -Name "uv"
if ($uvPath -and -not $Force) {
Write-Information "uv 已存在:$uvPath"
} else {
$installer = Join-Path $ProjectRoot "uv-installer.ps1"
if (-not (Test-Path $installer)) {
throw "找不到 uv 安装脚本:$installer"
}
Write-Information "正在运行 uv-installer.ps1..."
& $installer
}
# uv-installer.ps1 会修改注册表 PATH但当前会话不会立即生效需要手动加入
$uvInstallDir = Get-UvInstallDir
Add-ToCurrentPath -LiteralPath $uvInstallDir
# 再次验证
$uvPath = Test-CommandExists -Name "uv"
if (-not $uvPath) {
throw "uv 安装后仍无法在当前会话中找到,请尝试重启终端后重试。"
}
Write-Information "uv 路径:$uvPath"
}
# 第二步:安装 Python 3.13
Invoke-Step -Title "步骤 2/5安装 Python 3.13" -Action {
Write-Information "正在使用 uv 安装 Python 3.13..."
# uv 在 Python 已安装时会向 stderr 输出 "Python 3.13 is already installed"(退出码仍为 0
# 通过 Invoke-NativeCommand 绕过 Stop 模式对 stderr 的误判。
$output = Invoke-NativeCommand -LiteralCommand "uv python install 3.13" -FailureMessage "uv python install 失败"
Write-Information ($output -join "`n")
Write-Information "Python 安装完成。"
}
# 第三步:安装 pngquant
if (-not $SkipPngquant) {
Invoke-Step -Title "步骤 3/5安装 pngquant" -Action {
$pngquantPath = Test-CommandExists -Name "pngquant"
if ($pngquantPath -and -not $Force) {
Write-Information "pngquant 已存在:$pngquantPath"
} else {
$installer = Join-Path $ProjectRoot "install-pngquant.ps1"
if (-not (Test-Path $installer)) {
throw "找不到 pngquant 安装脚本:$installer"
}
if ($Force) {
Write-Information "正在运行 install-pngquant.ps1 -Force..."
& $installer -Force
} else {
Write-Information "正在运行 install-pngquant.ps1..."
& $installer
}
}
# install-pngquant.ps1 内部已刷新当前会话 PATH这里再做一次保险
# 覆盖默认安装位置和 scoop 安装位置两种情况
Add-ToCurrentPath -LiteralPath (Get-PngquantInstallDir)
$pngquantPath = Test-CommandExists -Name "pngquant"
if (-not $pngquantPath) {
throw "pngquant 安装后仍无法在当前会话中找到,请尝试重启终端后重试。"
}
Write-Information "pngquant 路径:$pngquantPath"
}
} else {
Write-Information ""
Write-Information "========================================"
Write-Information " 步骤 3/5跳过 pngquant 安装"
Write-Information "========================================"
}
# 第四步:安装 pptopic
if (-not $SkipPptopicInstall) {
Invoke-Step -Title "步骤 4/5安装 pptopic" -Action {
$pyproject = Join-Path $ProjectRoot "pyproject.toml"
if (-not (Test-Path $pyproject)) {
throw "找不到 pyproject.toml$pyproject"
}
Write-Information "正在使用 uv tool install -e $ProjectRoot 安装 pptopic..."
# uv 的进度/解析信息会写 stderr通过 Invoke-NativeCommand 绕过 Stop 模式误判。
$output = Invoke-NativeCommand -LiteralCommand "uv tool install -e `"$ProjectRoot`"" -FailureMessage "uv tool install 失败"
Write-Information ($output -join "`n")
Write-Information "pptopic 安装完成。"
}
} else {
Write-Information ""
Write-Information "========================================"
Write-Information " 步骤 4/5跳过 pptopic 安装"
Write-Information "========================================"
}
# 第五步:验证
Invoke-Step -Title "步骤 5/5验证安装" -Action {
# 版本命令可能写到 stderr部分 CLI 行为不一致),
# 在 Stop 模式下会误报失败,统一通过 Invoke-NativeCommand 安全调用。
Write-Information ""
Write-Information "--- uv 版本 ---"
$v = Invoke-NativeCommand -LiteralCommand "uv --version" -FailureMessage "uv 版本查询失败"
Write-Information ($v -join "`n")
if (-not $SkipPngquant) {
Write-Information ""
Write-Information "--- pngquant 版本 ---"
$v = Invoke-NativeCommand -LiteralCommand "pngquant --version" -FailureMessage "pngquant 版本查询失败"
Write-Information ($v -join "`n")
}
if (-not $SkipPptopicInstall) {
Write-Information ""
Write-Information "--- pptopic 版本 ---"
$v = Invoke-NativeCommand -LiteralCommand "pptopic version" -FailureMessage "pptopic 版本查询失败"
Write-Information ($v -join "`n")
}
# PowerPoint 是运行时必需依赖win32com 调用),但安装阶段无法自动安装,
# 这里只做检测并给出明确提示,把缺失问题前置,避免用户装完后才发现用不了。
Write-Information ""
Write-Information "--- PowerPoint 检测 ---"
if (Test-PowerPointInstalled) {
Write-Information "PowerPoint 已安装(检测到 PowerPoint.Application 注册项)。"
} else {
Write-Information "警告:未检测到 PowerPoint。"
Write-Information "pptopic 导出 PPTX 时需要调用 PowerPointOffice 2016 或更新版本)。"
Write-Information "请先安装 PowerPoint否则 'pptopic export' 命令将无法运行。"
}
}
# ============================================================
# 完成
# ============================================================
Write-Information ""
Write-Information "========================================"
Write-Information " pptopic 一键安装完成!"
Write-Information "========================================"
Write-Information ""
if (-not $SkipPptopicInstall) {
Write-Information "你可以立即在当前窗口使用以下命令:"
Write-Information " pptopic export 你的文件.pptx"
Write-Information " pptopic export 你的文件.pptx --optimize --max-height 29999 -o result.png"
}
Write-Information ""
Write-Information "提示:本脚本已自动刷新当前 PowerShell 会话的 PATH无需重启终端。"
Write-Information "如果关闭此窗口后在新窗口中找不到命令,请重新运行一次 setup.bat。"
Write-Information ""