Files
winit-helper/bin/main.ps1
2026-05-20 20:25:29 -04:00

276 lines
9.6 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.
# 设置严格模式,遇到错误停止,防止错误的命令雪崩
$ErrorActionPreference = "Stop"
#Set-StrictMode -Version Latest
# === 1. 加载依赖库 ===
# 确保 lib 目录存在
if (-not (Test-Path "$PSScriptRoot\..\lib")) {
Write-Error "Cannot find .\lib, some files missing."
exit 1
}
# === 2. 读取配置 ===
$jsonPath = "$PSScriptRoot\apps.json"
if (-not (Test-Path $jsonPath)) {
Write-Error "Cannot find: $jsonPath"
exit 1
}
Write-Host "Reading configurations..." -ForegroundColor Cyan
# 使用 UTF8 读取防止中文乱码
$apps = Get-Content $jsonPath -Encoding UTF8 | ConvertFrom-Json
$forcePostInstall = $false
function Unblock-WinGetCache {
param(
[Parameter(Mandatory=$true)] [string]$PackageId
)
$wingetCacheRoot = Join-Path $env:TEMP "WinGet"
if (-not (Test-Path $wingetCacheRoot)) {
return
}
$cacheItems = Get-ChildItem -Path $wingetCacheRoot -Recurse -File -ErrorAction SilentlyContinue |
Where-Object { $_.FullName -like "*\$PackageId.*" }
foreach ($item in $cacheItems) {
try {
Unblock-File -LiteralPath $item.FullName -ErrorAction Stop
Write-Host "-> Unblocked cached installer: $($item.FullName)" -ForegroundColor DarkGray
} catch {
Write-Warning "Failed to unblock cached installer: $($item.FullName), $_"
}
}
}
# === 2.1 加载外部开关配置 (selection.ini) ===
# 尝试查找根目录下的 selection.ini (位于 bin 的上一级)
$selectionPath = Join-Path (Split-Path $PSScriptRoot -Parent) "selection.ini"
if (Test-Path $selectionPath) {
Write-Host "Loading selection overrides from selection.ini..." -ForegroundColor Cyan
try {
# 创建哈希表分别存储 App 开关和全局选项
$selectionConfig = @{}
$optionsConfig = @{}
$currentSection = ""
foreach ($line in (Get-Content $selectionPath -Encoding UTF8)) {
$line = $line.Trim()
if ([string]::IsNullOrWhiteSpace($line) -or $line.StartsWith(";") -or $line.StartsWith("#")) {
continue
}
if ($line -match '^\[(.+)\]$') {
$currentSection = $matches[1].Trim()
continue
}
if ($line -notmatch "=") {
continue
}
# 按第一个等号分割,限制分割次数为 2
$parts = $line -split '=', 2
if ($parts.Count -eq 2) {
$key = $parts[0].Trim()
$value = $parts[1].Trim()
if ($currentSection -eq "Options") {
$optionsConfig[$key] = $value
} else {
$selectionConfig[$key] = $value
}
}
}
if ($optionsConfig.ContainsKey("ForcePostInstall")) {
$forcePostInstall = $optionsConfig["ForcePostInstall"] -eq "1"
}
# 应用配置
foreach ($app in $apps) {
if ($selectionConfig.ContainsKey($app.Name)) {
# 转换为整数 1 或 0
$app.Enabled = [int]$selectionConfig[$app.Name]
}
}
} catch {
Write-Warning "Error reading selection.ini: $_"
}
}
# === 2.2 启用本地 Manifest 安装 ===
# winget install --manifest 需要先启用 LocalManifestFiles重复执行不会影响后续安装。
Write-Host "Enabling winget local manifest support..." -ForegroundColor Cyan
winget settings --enable LocalManifestFiles
# === 3. 主循环 ===
foreach ($app in $apps) {
if ($app.Enabled -ne 1) {
Write-Host "[Skipping] $($app.Name)" -ForegroundColor DarkGray
continue # 立即结束本次循环,进入下一个软件
}
Write-Host "`n==========================================" -ForegroundColor Cyan
Write-Host "Installing: $($app.Name)" -ForegroundColor Yellow
Write-Host "=========================================="
if ($app.Id -eq "System.Config") {
# 如果是纯配置项,直接打印跳过信息
Write-Host "[System Config] Skipping install..." -ForegroundColor Magenta
} else {
# --- 步骤 A: Winget 安装 ---
$manifestPath = $null
$manifestTempDir = $null
if (-not [string]::IsNullOrWhiteSpace($app.Manifest)) {
Write-Host "-> Manifest: $($app.Manifest)"
if ($app.Manifest -match '^https?://') {
$manifestTempDir = Join-Path $env:TEMP ("winit-helper-manifest-" + [guid]::NewGuid().ToString("N"))
New-Item -ItemType Directory -Path $manifestTempDir -Force | Out-Null
$manifestFileName = [System.IO.Path]::GetFileName(([uri]$app.Manifest).AbsolutePath)
if ([string]::IsNullOrWhiteSpace($manifestFileName)) {
$manifestFileName = "$($app.Id).yaml"
}
$manifestPath = Join-Path $manifestTempDir $manifestFileName
Write-Host "-> Downloading custom manifest..."
Invoke-WebRequest -Uri $app.Manifest -OutFile $manifestPath
} else {
$manifestPath = $app.Manifest
if (-not [System.IO.Path]::IsPathRooted($manifestPath)) {
$manifestPath = Join-Path $PSScriptRoot $manifestPath
}
if (-not (Test-Path $manifestPath)) {
Write-Error "Cannot find manifest: $manifestPath"
continue
}
}
$wingetArgs = @(
"install",
"--manifest", $manifestPath,
"--silent",
"--accept-package-agreements",
"--accept-source-agreements",
"--disable-interactivity"
# "--scope", "machine"
)
} else {
$wingetArgs = @(
"install",
"--id", $app.Id,
"-e",
"--silent",
"--accept-package-agreements",
"--accept-source-agreements",
"--disable-interactivity"
# "--scope", "machine"
)
# [版本检查逻辑]
# 检查 Version 是否存在且不为空字符串
if (-not [string]::IsNullOrWhiteSpace($app.Version)) {
Write-Host "-> Version: $($app.Version)"
$wingetArgs += "-v"
$wingetArgs += $app.Version
} else {
Write-Host "-> Version: latest"
}
}
Write-Host "-> Installing via winget..."
# 执行安装
try {
& winget @wingetArgs
$exitCode = $LASTEXITCODE
if ($exitCode -eq -1978335231 -and -not [string]::IsNullOrWhiteSpace($app.Manifest)) {
Write-Warning "winget failed after download. Unblocking cached installer and retrying once..."
Unblock-WinGetCache -PackageId $app.Id
& winget @wingetArgs
$exitCode = $LASTEXITCODE
}
} finally {
if ($manifestTempDir -and (Test-Path $manifestTempDir)) {
Remove-Item -LiteralPath $manifestTempDir -Recurse -Force
}
}
# 检查安装结果 (0=成功, -1978335189=已安装)
if ($exitCode -eq 0) {
Write-Host "[Success]" -ForegroundColor Green
} elseif ($exitCode -eq -1978335189) {
if ($forcePostInstall) {
Write-Host "[Skip] Already installed, running PostInstall..." -ForegroundColor Yellow
} else {
Write-Host "[Skip] Already installed" -ForegroundColor Yellow
continue # 已经安装的为了避免覆盖配置,也就不配置了
}
} else {
Write-Error "[Fail] Error code: $exitCode"
continue # 安装失败则跳过后续配置
}
}
# --- 步骤 B: PostInstall 配置 ---
if ($app.PostInstall -and $app.PostInstall.Count -gt 0) {
Write-Host "`n-> Configuring..." -ForegroundColor Cyan
$stepIndex = 0
foreach ($action in $app.PostInstall) {
# [间隔逻辑] 如果这不是第一步,先等待 2 秒
if ($stepIndex -gt 0) {
Write-Host "(Waiting 2 seconds...)" -ForegroundColor DarkGray
Start-Sleep -Seconds 2
}
try {
switch ($action.Type) {
# 1. 复制文件
"FileCopy" {
& "$PSScriptRoot\..\lib\invoke-filecopy.ps1" `
-Source $action.Source `
-Destination $action.Destination
}
# 2. 导入注册表
"RegImport" {
& "$PSScriptRoot\..\lib\invoke-regimport.ps1" `
-Path $action.Path
}
# 3. 执行 CMD
"Command" {
& "$PSScriptRoot\..\lib\invoke-cmdexec.ps1" `
-Command $action.Command `
-WorkDir $action.WorkDir
}
Default {
Write-Warning "`nUnknown action: $($action.Type)"
}
}
} catch {
Write-Error "`nFailed to operate step $($stepIndex + 1): $_"
}
$stepIndex++
}
} else {
Write-Host "-> No configurations." -ForegroundColor DarkGray
}
}
Write-Host "`n=== Done. Need restart ===" -ForegroundColor Green
Pause