Gallery发布.ps1
|
<#PSScriptInfo
.VERSION 2.0.0 .GUID 7e38b9f1-8c62-4a53-9e14-d8c7f2a6b10d .AUTHOR 埃博拉酱-机器人 .COMPANYNAME 一致行动党 .COPYRIGHT (c) 2026 一致行动党。MIT 许可证。 .TAGS PSGallery publish api key secure encrypt DPAPI PowerShell 发布 密钥 加密 .LICENSEURI https://opensource.org/licenses/MIT .EXTERNALMODULEDEPENDENCIES Microsoft.PowerShell.PSResourceGet .RELEASENOTES 密钥改成存在用户级 #> <# .SYNOPSIS 将 PowerShell 模块发布到 PowerShell Gallery,首次输入 API 密钥后自动加密保存,后续无需再次输入。 .DESCRIPTION 使用 Export-Clixml 将 PSGallery API 密钥加密保存到用户目录(Windows DPAPI,仅本机当前用户可解密)。 首次运行提示输入密钥并保存,后续运行自动读取,实现一键发布。密钥文件存储在 %APPDATA%\PSGallery\.apikey,与项目目录无关。 .PARAMETER 模块路径 要发布的模块所在文件夹路径(或 .psd1 清单文件路径),或要发布的单个 .ps1 脚本文件路径。默认在当前工作目录下查找。 .PARAMETER 自身 指定此开关时,将 Gallery发布 脚本自身发布到 PowerShell Gallery。 .PARAMETER ApiKey 指定新的 PowerShell Gallery API 密钥,将覆盖已保存的密钥。若不指定,则尝试从已保存文件读取,读取失败则交互式输入。 .EXAMPLE .\Gallery发布.ps1 在当前目录下查找模块并发布到 PSGallery。 .EXAMPLE .\Gallery发布.ps1 -模块路径 ".\GitHub加速" 发布 GitHub加速 模块到 PSGallery。 .EXAMPLE .\Gallery发布.ps1 .\某脚本.ps1 将单个脚本文件 某脚本.ps1 发布到 PSGallery。 .EXAMPLE .\Gallery发布.ps1 -自身 将 Gallery发布 脚本自身发布到 PSGallery。 .EXAMPLE .\Gallery发布.ps1 -ApiKey "新密钥" 使用新的 API 密钥发布,并加密保存以供后续使用。 #> [CmdletBinding(DefaultParameterSetName = "模块")] param( [Parameter(Position = 0, ParameterSetName = "模块")] [string]$模块路径 = ".", [Parameter(Mandatory = $true, ParameterSetName = "自身")] [switch]$自身, [Parameter()] [string]$ApiKey ) Set-StrictMode -Version Latest $ErrorActionPreference = "Stop" # ============================================================ # 0. 确定密钥文件位置(用户级固定路径,与发布目标目录无关) # ============================================================ if ($env:APPDATA) { $密钥目录 = Join-Path $env:APPDATA "PSGallery" } else { $密钥目录 = Join-Path $HOME ".psgallery" } if (-not (Test-Path -LiteralPath $密钥目录)) { New-Item -ItemType Directory -Path $密钥目录 -Force | Out-Null } $密钥文件 = Join-Path $密钥目录 ".apikey" # ============================================================ # 0b. 确定要发布的对象 # ============================================================ if ($自身) { # 发布自身脚本 $脚本文件 = $MyInvocation.MyCommand.Path if (-not $脚本文件) { throw "无法确定脚本文件路径。" } $脚本文件 = Resolve-Path $脚本文件 $发布目标 = "脚本" # 标记,后续分支处理 } elseif ((Test-Path -LiteralPath $模块路径 -PathType Leaf) -and $模块路径 -like "*.ps1") { # 发布单个脚本文件 $脚本文件 = Resolve-Path $模块路径 $发布目标 = "脚本" } else { # 发布模块 if (Test-Path -LiteralPath $模块路径 -PathType Container) { $模块文件夹 = Resolve-Path $模块路径 } elseif (Test-Path -LiteralPath $模块路径 -PathType Leaf) { $模块文件夹 = Split-Path -Parent (Resolve-Path $模块路径) } else { throw "模块路径不存在:$模块路径" } $清单文件 = Get-ChildItem -LiteralPath $模块文件夹 -Filter "*.psd1" | Select-Object -First 1 if (-not $清单文件) { throw "在 $模块文件夹 中未找到 .psd1 清单文件。" } $模块信息 = Test-ModuleManifest -Path $清单文件.FullName $模块名称 = $模块信息.Name $版本 = $模块信息.Version $发布目标 = "模块" } # ============================================================ # 1. 获取 API 密钥(优先级:参数 > 已保存文件 > 交互输入) # ============================================================ if ($ApiKey) { # 通过参数提供了新密钥 → 加密保存(覆盖旧文件) $安全密钥 = ConvertTo-SecureString $ApiKey -AsPlainText -Force $安全密钥 | Export-Clixml -LiteralPath $密钥文件 Write-Host "已加密保存新 API 密钥(仅本机当前用户可解密)。" -ForegroundColor Green } else { # 尝试从加密文件读取 if (Test-Path -LiteralPath $密钥文件) { try { $安全串 = Import-Clixml -LiteralPath $密钥文件 $ApiKey = [System.Runtime.InteropServices.Marshal]::PtrToStringAuto( [System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($安全串) ) if (-not $ApiKey) { throw "解密结果为空" } Write-Host "已从加密文件读取 API 密钥。" -ForegroundColor Green } catch { Write-Host "加密文件损坏或无法解密,将重新获取。($($_.Exception.Message))" -ForegroundColor Yellow } } # 若仍无密钥则交互输入 if (-not $ApiKey) { Write-Host "请输入 PowerShell Gallery API 密钥:" -ForegroundColor Yellow Write-Host " (获取方式:PowerShell Gallery 右上角 → API Keys → Create)" -ForegroundColor DarkGray $安全密钥 = Read-Host -AsSecureString $ApiKey = [System.Runtime.InteropServices.Marshal]::PtrToStringAuto( [System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($安全密钥) ) if (-not $ApiKey) { throw "未提供 API 密钥,无法发布。" } # 用 Export-Clixml 保存,原生支持 SecureString DPAPI 加密 $安全密钥 | Export-Clixml -LiteralPath $密钥文件 Write-Host "已加密保存(仅本机当前用户可解密)。" -ForegroundColor Green } } # ============================================================ # 3. 确保 PSResourceGet 已安装 # ============================================================ $新发布模块 = Get-Module -ListAvailable Microsoft.PowerShell.PSResourceGet if (-not $新发布模块) { Write-Host "正在安装 Microsoft.PowerShell.PSResourceGet ..." -ForegroundColor Yellow Install-Module Microsoft.PowerShell.PSResourceGet -Repository PSGallery -Scope CurrentUser -Force } # ============================================================ # 4. 发布 # ============================================================ if ($发布目标 -eq "脚本") { Write-Host "正在发布脚本 $([System.IO.Path]::GetFileName($脚本文件)) 到 PSGallery ..." Publish-PSResource -Path $脚本文件 -ApiKey $ApiKey -Repository PSGallery } else { Write-Host "准备发布 $模块名称 v$版本 ..." Write-Host "正在发布到 PSGallery ..." Publish-PSResource -Path $模块文件夹 -ApiKey $ApiKey -Repository PSGallery } Write-Host "发布完成!" -ForegroundColor Green |