245 lines
8.5 KiB
PowerShell
245 lines
8.5 KiB
PowerShell
# 设置严格模式,遇到错误停止,防止错误的命令雪崩
|
||
$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
|
||
|
||
# === 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
|
||
} 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
|