mirror of
https://github.com/tw93/Mole.git
synced 2026-03-22 20:50:06 +00:00
fix(windows): harden update and optimize flows
This commit is contained in:
112
bin/optimize.ps1
112
bin/optimize.ps1
@@ -120,10 +120,23 @@ function Show-SystemHealth {
|
|||||||
# Optimization Tasks
|
# Optimization Tasks
|
||||||
# ============================================================================
|
# ============================================================================
|
||||||
|
|
||||||
|
function Get-SystemDriveLetter {
|
||||||
|
if (-not $env:SystemDrive) {
|
||||||
|
throw "SystemDrive environment variable is not set."
|
||||||
|
}
|
||||||
|
|
||||||
|
$match = [regex]::Match($env:SystemDrive, '^[A-Za-z]')
|
||||||
|
if (-not $match.Success) {
|
||||||
|
throw "Could not determine the system drive letter from '$env:SystemDrive'."
|
||||||
|
}
|
||||||
|
|
||||||
|
return $match.Value.ToUpperInvariant()
|
||||||
|
}
|
||||||
|
|
||||||
function Optimize-DiskDrive {
|
function Optimize-DiskDrive {
|
||||||
<#
|
<#
|
||||||
.SYNOPSIS
|
.SYNOPSIS
|
||||||
Optimize disk (defrag for HDD, TRIM for SSD)
|
Optimize the system drive using Windows defaults
|
||||||
#>
|
#>
|
||||||
|
|
||||||
$esc = [char]27
|
$esc = [char]27
|
||||||
@@ -143,20 +156,10 @@ function Optimize-DiskDrive {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
# Check if SSD or HDD
|
$systemDriveLetter = Get-SystemDriveLetter
|
||||||
$diskNumber = (Get-Partition -DriveLetter $env:SystemDrive[0]).DiskNumber
|
Write-Host " Running Windows default optimization on drive ${systemDriveLetter}:..."
|
||||||
$mediaType = (Get-PhysicalDisk | Where-Object { $_.DeviceId -eq $diskNumber }).MediaType
|
$null = Optimize-Volume -DriveLetter $systemDriveLetter -ErrorAction Stop
|
||||||
|
Write-Host " $esc[32m$($script:Icons.Success)$esc[0m Drive optimization completed"
|
||||||
if ($mediaType -eq "SSD") {
|
|
||||||
Write-Host " Running TRIM on SSD..."
|
|
||||||
$null = Optimize-Volume -DriveLetter $env:SystemDrive[0] -ReTrim -ErrorAction Stop
|
|
||||||
Write-Host " $esc[32m$($script:Icons.Success)$esc[0m SSD TRIM completed"
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
Write-Host " Running defragmentation on HDD..."
|
|
||||||
$null = Optimize-Volume -DriveLetter $env:SystemDrive[0] -Defrag -ErrorAction Stop
|
|
||||||
Write-Host " $esc[32m$($script:Icons.Success)$esc[0m Defragmentation completed"
|
|
||||||
}
|
|
||||||
$script:OptimizationsApplied++
|
$script:OptimizationsApplied++
|
||||||
}
|
}
|
||||||
catch {
|
catch {
|
||||||
@@ -623,9 +626,82 @@ function Repair-SearchIndex {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
function Wait-ForServiceStatus {
|
||||||
|
param(
|
||||||
|
[Parameter(Mandatory)]
|
||||||
|
[string]$Name,
|
||||||
|
[Parameter(Mandatory)]
|
||||||
|
[System.ServiceProcess.ServiceControllerStatus]$ExpectedStatus,
|
||||||
|
[int]$TimeoutSeconds = 15
|
||||||
|
)
|
||||||
|
|
||||||
|
$service = Get-Service -Name $Name -ErrorAction Stop
|
||||||
|
$deadline = (Get-Date).AddSeconds($TimeoutSeconds)
|
||||||
|
|
||||||
|
do {
|
||||||
|
$service.Refresh()
|
||||||
|
if ($service.Status -eq $ExpectedStatus) {
|
||||||
|
return $true
|
||||||
|
}
|
||||||
|
Start-Sleep -Milliseconds 500
|
||||||
|
} while ((Get-Date) -lt $deadline)
|
||||||
|
|
||||||
|
$service.Refresh()
|
||||||
|
return $service.Status -eq $ExpectedStatus
|
||||||
|
}
|
||||||
|
|
||||||
|
function Stop-ServiceSafely {
|
||||||
|
param(
|
||||||
|
[Parameter(Mandatory)]
|
||||||
|
[string]$Name,
|
||||||
|
[int]$TimeoutSeconds = 20
|
||||||
|
)
|
||||||
|
|
||||||
|
$service = Get-Service -Name $Name -ErrorAction Stop
|
||||||
|
if ($service.Status -eq [System.ServiceProcess.ServiceControllerStatus]::Stopped) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($service.Status -ne [System.ServiceProcess.ServiceControllerStatus]::StopPending) {
|
||||||
|
try {
|
||||||
|
Stop-Service -Name $Name -Force -ErrorAction Stop
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
$service.Refresh()
|
||||||
|
if ($service.Status -ne [System.ServiceProcess.ServiceControllerStatus]::StopPending) {
|
||||||
|
throw
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (-not (Wait-ForServiceStatus -Name $Name -ExpectedStatus ([System.ServiceProcess.ServiceControllerStatus]::Stopped) -TimeoutSeconds $TimeoutSeconds)) {
|
||||||
|
throw "Windows Search service did not stop within $TimeoutSeconds seconds."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function Start-ServiceSafely {
|
||||||
|
param(
|
||||||
|
[Parameter(Mandatory)]
|
||||||
|
[string]$Name,
|
||||||
|
[int]$TimeoutSeconds = 20
|
||||||
|
)
|
||||||
|
|
||||||
|
$service = Get-Service -Name $Name -ErrorAction Stop
|
||||||
|
if ($service.Status -eq [System.ServiceProcess.ServiceControllerStatus]::Running) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($service.Status -ne [System.ServiceProcess.ServiceControllerStatus]::StartPending) {
|
||||||
|
Start-Service -Name $Name -ErrorAction Stop
|
||||||
|
}
|
||||||
|
|
||||||
|
if (-not (Wait-ForServiceStatus -Name $Name -ExpectedStatus ([System.ServiceProcess.ServiceControllerStatus]::Running) -TimeoutSeconds $TimeoutSeconds)) {
|
||||||
|
throw "Windows Search service did not start within $TimeoutSeconds seconds."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Write-Host " $esc[90mStopping Windows Search service...$esc[0m"
|
Write-Host " $esc[90mStopping Windows Search service...$esc[0m"
|
||||||
Stop-Service -Name "WSearch" -Force -ErrorAction Stop
|
Stop-ServiceSafely -Name "WSearch"
|
||||||
Start-Sleep -Seconds 3
|
|
||||||
|
|
||||||
if (Test-Path $searchIndexPath) {
|
if (Test-Path $searchIndexPath) {
|
||||||
Write-Host " $esc[90mDeleting search index...$esc[0m"
|
Write-Host " $esc[90mDeleting search index...$esc[0m"
|
||||||
@@ -633,7 +709,7 @@ function Repair-SearchIndex {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Write-Host " $esc[90mRestarting Windows Search service...$esc[0m"
|
Write-Host " $esc[90mRestarting Windows Search service...$esc[0m"
|
||||||
Start-Service -Name "WSearch" -ErrorAction Stop
|
Start-ServiceSafely -Name "WSearch"
|
||||||
|
|
||||||
Write-Host " $esc[32m$($script:Icons.Success)$esc[0m Search index reset successfully"
|
Write-Host " $esc[32m$($script:Icons.Success)$esc[0m Search index reset successfully"
|
||||||
Write-Host " $esc[33m$($script:Icons.Warning)$esc[0m Indexing will rebuild in the background (may take hours)"
|
Write-Host " $esc[33m$($script:Icons.Warning)$esc[0m Indexing will rebuild in the background (may take hours)"
|
||||||
|
|||||||
@@ -32,6 +32,44 @@ function Show-UpdateHelp {
|
|||||||
Write-Host ""
|
Write-Host ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function Invoke-GitCommand {
|
||||||
|
param(
|
||||||
|
[string]$WorkingDirectory,
|
||||||
|
[Parameter(Mandatory)]
|
||||||
|
[string[]]$Arguments
|
||||||
|
)
|
||||||
|
|
||||||
|
$previousNativeErrorPreference = $null
|
||||||
|
$hasNativeErrorPreference = $false
|
||||||
|
|
||||||
|
if (Get-Variable -Name PSNativeCommandUseErrorActionPreference -ErrorAction SilentlyContinue) {
|
||||||
|
$previousNativeErrorPreference = $PSNativeCommandUseErrorActionPreference
|
||||||
|
$PSNativeCommandUseErrorActionPreference = $false
|
||||||
|
$hasNativeErrorPreference = $true
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
$gitArguments = @()
|
||||||
|
if ($WorkingDirectory) {
|
||||||
|
$gitArguments += @("-C", $WorkingDirectory)
|
||||||
|
}
|
||||||
|
$gitArguments += $Arguments
|
||||||
|
|
||||||
|
$output = & git @gitArguments 2>&1
|
||||||
|
$exitCode = $LASTEXITCODE
|
||||||
|
|
||||||
|
return [pscustomobject]@{
|
||||||
|
ExitCode = $exitCode
|
||||||
|
Text = ((@($output) | ForEach-Object { "$_" }) -join [Environment]::NewLine).Trim()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
if ($hasNativeErrorPreference) {
|
||||||
|
$PSNativeCommandUseErrorActionPreference = $previousNativeErrorPreference
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function Test-InstallDirOnUserPath {
|
function Test-InstallDirOnUserPath {
|
||||||
param([string]$Path)
|
param([string]$Path)
|
||||||
|
|
||||||
@@ -64,45 +102,49 @@ if (-not (Test-Path $gitDir)) {
|
|||||||
exit 1
|
exit 1
|
||||||
}
|
}
|
||||||
|
|
||||||
$dirtyOutput = & git -C $windowsDir status --porcelain --untracked-files=no 2>&1
|
$dirtyStatus = Invoke-GitCommand -WorkingDirectory $windowsDir -Arguments @("status", "--porcelain", "--untracked-files=no")
|
||||||
if ($LASTEXITCODE -ne 0) {
|
if ($dirtyStatus.ExitCode -ne 0) {
|
||||||
Write-Host "Failed to inspect git status: $dirtyOutput" -ForegroundColor Red
|
Write-Host "Failed to inspect git status: $($dirtyStatus.Text)" -ForegroundColor Red
|
||||||
exit 1
|
exit 1
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($dirtyOutput) {
|
if ($dirtyStatus.Text) {
|
||||||
Write-Host "Local tracked changes detected in the installation directory." -ForegroundColor Red
|
Write-Host "Local tracked changes detected in the installation directory." -ForegroundColor Red
|
||||||
Write-Host "Commit or discard them before running 'mo update'." -ForegroundColor Yellow
|
Write-Host "Commit or discard them before running 'mo update'." -ForegroundColor Yellow
|
||||||
exit 1
|
exit 1
|
||||||
}
|
}
|
||||||
|
|
||||||
$remote = (& git -C $windowsDir remote get-url origin 2>&1).Trim()
|
$remoteResult = Invoke-GitCommand -WorkingDirectory $windowsDir -Arguments @("remote", "get-url", "origin")
|
||||||
if ($LASTEXITCODE -ne 0 -or -not $remote) {
|
$remote = $remoteResult.Text
|
||||||
|
if ($remoteResult.ExitCode -ne 0 -or -not $remote) {
|
||||||
Write-Host "Git remote 'origin' is not configured for this install." -ForegroundColor Red
|
Write-Host "Git remote 'origin' is not configured for this install." -ForegroundColor Red
|
||||||
exit 1
|
exit 1
|
||||||
}
|
}
|
||||||
|
|
||||||
$branch = (& git -C $windowsDir branch --show-current 2>&1).Trim()
|
$branchResult = Invoke-GitCommand -WorkingDirectory $windowsDir -Arguments @("branch", "--show-current")
|
||||||
if ($LASTEXITCODE -ne 0 -or -not $branch) {
|
$branch = $branchResult.Text
|
||||||
|
if ($branchResult.ExitCode -ne 0 -or -not $branch) {
|
||||||
$branch = "windows"
|
$branch = "windows"
|
||||||
}
|
}
|
||||||
|
|
||||||
$before = (& git -C $windowsDir rev-parse --short HEAD 2>&1).Trim()
|
$beforeResult = Invoke-GitCommand -WorkingDirectory $windowsDir -Arguments @("rev-parse", "--short", "HEAD")
|
||||||
if ($LASTEXITCODE -ne 0 -or -not $before) {
|
$before = $beforeResult.Text
|
||||||
|
if ($beforeResult.ExitCode -ne 0 -or -not $before) {
|
||||||
Write-Host "Failed to read current revision." -ForegroundColor Red
|
Write-Host "Failed to read current revision." -ForegroundColor Red
|
||||||
exit 1
|
exit 1
|
||||||
}
|
}
|
||||||
|
|
||||||
Write-Host "Updating source from $remote ($branch)..." -ForegroundColor Cyan
|
Write-Host "Updating source from $remote ($branch)..." -ForegroundColor Cyan
|
||||||
|
|
||||||
$pullOutput = & git -C $windowsDir pull --ff-only origin $branch 2>&1
|
$pullResult = Invoke-GitCommand -WorkingDirectory $windowsDir -Arguments @("pull", "--ff-only", "origin", $branch)
|
||||||
if ($LASTEXITCODE -ne 0) {
|
if ($pullResult.ExitCode -ne 0) {
|
||||||
Write-Host "Failed to update source: $pullOutput" -ForegroundColor Red
|
Write-Host "Failed to update source: $($pullResult.Text)" -ForegroundColor Red
|
||||||
exit 1
|
exit 1
|
||||||
}
|
}
|
||||||
|
|
||||||
$after = (& git -C $windowsDir rev-parse --short HEAD 2>&1).Trim()
|
$afterResult = Invoke-GitCommand -WorkingDirectory $windowsDir -Arguments @("rev-parse", "--short", "HEAD")
|
||||||
if ($LASTEXITCODE -ne 0 -or -not $after) {
|
$after = $afterResult.Text
|
||||||
|
if ($afterResult.ExitCode -ne 0 -or -not $after) {
|
||||||
Write-Host "Updated source, but failed to read the new revision." -ForegroundColor Red
|
Write-Host "Updated source, but failed to read the new revision." -ForegroundColor Red
|
||||||
exit 1
|
exit 1
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -42,6 +42,44 @@ function Test-SourceInstall {
|
|||||||
return (Test-Path (Join-Path $Path ".git"))
|
return (Test-Path (Join-Path $Path ".git"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function Invoke-GitCommand {
|
||||||
|
param(
|
||||||
|
[string]$WorkingDirectory,
|
||||||
|
[Parameter(Mandatory)]
|
||||||
|
[string[]]$Arguments
|
||||||
|
)
|
||||||
|
|
||||||
|
$previousNativeErrorPreference = $null
|
||||||
|
$hasNativeErrorPreference = $false
|
||||||
|
|
||||||
|
if (Get-Variable -Name PSNativeCommandUseErrorActionPreference -ErrorAction SilentlyContinue) {
|
||||||
|
$previousNativeErrorPreference = $PSNativeCommandUseErrorActionPreference
|
||||||
|
$PSNativeCommandUseErrorActionPreference = $false
|
||||||
|
$hasNativeErrorPreference = $true
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
$gitArguments = @()
|
||||||
|
if ($WorkingDirectory) {
|
||||||
|
$gitArguments += @("-C", $WorkingDirectory)
|
||||||
|
}
|
||||||
|
$gitArguments += $Arguments
|
||||||
|
|
||||||
|
$output = & git @gitArguments 2>&1
|
||||||
|
$exitCode = $LASTEXITCODE
|
||||||
|
|
||||||
|
return [pscustomobject]@{
|
||||||
|
ExitCode = $exitCode
|
||||||
|
Text = ((@($output) | ForEach-Object { "$_" }) -join [Environment]::NewLine).Trim()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
if ($hasNativeErrorPreference) {
|
||||||
|
$PSNativeCommandUseErrorActionPreference = $previousNativeErrorPreference
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
# Main installation
|
# Main installation
|
||||||
try {
|
try {
|
||||||
Write-Host ""
|
Write-Host ""
|
||||||
@@ -64,24 +102,24 @@ try {
|
|||||||
if (Test-SourceInstall -Path $InstallDir) {
|
if (Test-SourceInstall -Path $InstallDir) {
|
||||||
Write-Step "Existing source install found, refreshing..."
|
Write-Step "Existing source install found, refreshing..."
|
||||||
|
|
||||||
Push-Location $InstallDir
|
$fetchResult = Invoke-GitCommand -WorkingDirectory $InstallDir -Arguments @("fetch", "--quiet", "origin", "windows")
|
||||||
try {
|
if ($fetchResult.ExitCode -ne 0) {
|
||||||
git fetch --quiet origin windows 2>&1 | Out-Null
|
|
||||||
if ($LASTEXITCODE -ne 0) {
|
|
||||||
Write-ErrorMsg "Failed to fetch latest source"
|
Write-ErrorMsg "Failed to fetch latest source"
|
||||||
|
if ($fetchResult.Text) {
|
||||||
|
Write-Host " $($fetchResult.Text)"
|
||||||
|
}
|
||||||
exit 1
|
exit 1
|
||||||
}
|
}
|
||||||
|
|
||||||
git pull --ff-only origin windows 2>&1 | Out-Null
|
$pullResult = Invoke-GitCommand -WorkingDirectory $InstallDir -Arguments @("pull", "--ff-only", "origin", "windows")
|
||||||
if ($LASTEXITCODE -ne 0) {
|
if ($pullResult.ExitCode -ne 0) {
|
||||||
Write-ErrorMsg "Failed to fast-forward source install"
|
Write-ErrorMsg "Failed to fast-forward source install"
|
||||||
|
if ($pullResult.Text) {
|
||||||
|
Write-Host " $($pullResult.Text)"
|
||||||
|
}
|
||||||
exit 1
|
exit 1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
finally {
|
|
||||||
Pop-Location
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
else {
|
||||||
Write-ErrorMsg "Install directory already exists and is not a source install: $InstallDir"
|
Write-ErrorMsg "Install directory already exists and is not a source install: $InstallDir"
|
||||||
Write-Host " Remove it first or reinstall with the latest quick installer."
|
Write-Host " Remove it first or reinstall with the latest quick installer."
|
||||||
@@ -91,7 +129,14 @@ try {
|
|||||||
else {
|
else {
|
||||||
Write-Step "Cloning Mole source..."
|
Write-Step "Cloning Mole source..."
|
||||||
|
|
||||||
git clone --quiet --depth 1 --branch windows https://github.com/tw93/Mole.git $InstallDir 2>&1 | Out-Null
|
$cloneResult = Invoke-GitCommand -Arguments @("clone", "--quiet", "--depth", "1", "--branch", "windows", "https://github.com/tw93/Mole.git", $InstallDir)
|
||||||
|
if ($cloneResult.ExitCode -ne 0) {
|
||||||
|
Write-ErrorMsg "Failed to clone source installer"
|
||||||
|
if ($cloneResult.Text) {
|
||||||
|
Write-Host " $($cloneResult.Text)"
|
||||||
|
}
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
if (-not (Test-Path (Join-Path $InstallDir "install.ps1"))) {
|
if (-not (Test-Path (Join-Path $InstallDir "install.ps1"))) {
|
||||||
Write-ErrorMsg "Failed to clone source installer"
|
Write-ErrorMsg "Failed to clone source installer"
|
||||||
|
|||||||
Reference in New Issue
Block a user