From 9fb2072e62f860b07f095f2f721a0fa946c75d6a Mon Sep 17 00:00:00 2001 From: nicklavoie Date: Sun, 22 Feb 2026 17:30:13 -0500 Subject: [PATCH] Add script to install latest GitHub build artifact to a target directory --- PerformanceOverlay/Overlays.cs | 2 +- README.md | 16 ++++ scripts/install_from_github_build.ps1 | 115 ++++++++++++++++++++++++++ 3 files changed, 132 insertions(+), 1 deletion(-) create mode 100644 scripts/install_from_github_build.ps1 diff --git a/PerformanceOverlay/Overlays.cs b/PerformanceOverlay/Overlays.cs index 589c039..78dafe4 100644 --- a/PerformanceOverlay/Overlays.cs +++ b/PerformanceOverlay/Overlays.cs @@ -143,7 +143,7 @@ namespace PerformanceOverlay Nested = { new Entry("{CPU_%} %"), - new Entry("{CPU_W} W") { Include = { OverlayMode.Minimal } }, + new Entry("{CPU_W} W"), new Entry("{CPU_T} C") { IgnoreMissing = true, Include = { OverlayMode.Detail } }, new Entry("{MEM_GB} GiB") { Include = { OverlayMode.Detail } } } diff --git a/README.md b/README.md index ed6ab31..b6061b8 100644 --- a/README.md +++ b/README.md @@ -25,6 +25,22 @@ dotnet build PerformanceOverlay/PerformanceOverlay.csproj --configuration Releas GitHub Actions is configured to build only `PerformanceOverlay` and publish a ZIP artifact. +## Install latest GitHub build artifact (PowerShell) + +To update an existing install directory with files from the latest successful `PerformanceOverlay` GitHub Actions build artifact: + +```powershell +powershell -ExecutionPolicy Bypass -File scripts/install_from_github_build.ps1 -DestinationDir "C:\SteamDeckTools" +``` + +Useful options: + +- `-Repository owner/repo` (default: `nicklavoie/steam-deck-tools`) +- `-Ref ` (default: `main`) +- `-ArtifactNamePattern "PerformanceOverlay-*.zip"` +- `-CleanDestination` (remove destination contents before copying) +- `-GitHubToken ` (or set `GITHUB_TOKEN`) for higher API rate limits + ## Credits Based on the original Steam Deck Tools project by Kamil TrzciƄski. diff --git a/scripts/install_from_github_build.ps1 b/scripts/install_from_github_build.ps1 new file mode 100644 index 0000000..28574ec --- /dev/null +++ b/scripts/install_from_github_build.ps1 @@ -0,0 +1,115 @@ +param( + [Parameter(Mandatory = $true)] + [string]$DestinationDir, + + [string]$Repository = "nicklavoie/steam-deck-tools", + [string]$WorkflowFile = "build_performance_overlay.yaml", + [string]$ArtifactNamePattern = "PerformanceOverlay-*.zip", + [string]$Ref = "main", + [string]$GitHubToken, + [switch]$CleanDestination +) + +$ErrorActionPreference = 'Stop' + +function Get-AuthHeaders { + param([string]$Token) + + $headers = @{ + 'Accept' = 'application/vnd.github+json' + 'User-Agent' = 'steam-deck-tools-installer-script' + 'X-GitHub-Api-Version' = '2022-11-28' + } + + if ($Token) { + $headers['Authorization'] = "Bearer $Token" + } + + return $headers +} + +if (-not $GitHubToken -and $env:GITHUB_TOKEN) { + $GitHubToken = $env:GITHUB_TOKEN +} + +$headers = Get-AuthHeaders -Token $GitHubToken + +if ($Repository -notmatch '^[^/]+/[^/]+$') { + throw "Repository must be in 'owner/repo' format." +} + +$owner, $repo = $Repository.Split('/') + +Write-Host "Looking up latest successful workflow run for $Repository ($WorkflowFile) on ref '$Ref'..." +$runsUrl = "https://api.github.com/repos/$owner/$repo/actions/workflows/$WorkflowFile/runs?status=success&per_page=20&branch=$Ref" +$runsResponse = Invoke-RestMethod -Uri $runsUrl -Headers $headers -Method Get + +$run = $runsResponse.workflow_runs | + Where-Object { $_.conclusion -eq 'success' -and $_.status -eq 'completed' } | + Select-Object -First 1 + +if (-not $run) { + throw "No successful completed workflow runs found for $WorkflowFile on branch '$Ref'." +} + +Write-Host "Using workflow run #$($run.run_number) (id: $($run.id))." + +$artifactsUrl = "https://api.github.com/repos/$owner/$repo/actions/runs/$($run.id)/artifacts?per_page=100" +$artifactsResponse = Invoke-RestMethod -Uri $artifactsUrl -Headers $headers -Method Get + +$artifact = $artifactsResponse.artifacts | + Where-Object { -not $_.expired -and $_.name -like $ArtifactNamePattern } | + Sort-Object created_at -Descending | + Select-Object -First 1 + +if (-not $artifact) { + $available = ($artifactsResponse.artifacts | ForEach-Object { $_.name }) -join ', ' + throw "No non-expired artifact matching '$ArtifactNamePattern' found in run $($run.id). Available artifacts: $available" +} + +Write-Host "Selected artifact: $($artifact.name)" + +$tempRoot = Join-Path ([System.IO.Path]::GetTempPath()) ("sdt-install-" + [System.Guid]::NewGuid().ToString('N')) +$artifactZip = Join-Path $tempRoot "artifact_container.zip" +$artifactExtractDir = Join-Path $tempRoot "artifact_container" +$payloadExtractDir = Join-Path $tempRoot "payload" + +New-Item -ItemType Directory -Path $tempRoot -Force | Out-Null + +try { + $encodedArtifactName = [uri]::EscapeDataString($artifact.name) + $downloadUrl = "https://nightly.link/$owner/$repo/actions/runs/$($run.id)/$encodedArtifactName.zip" + + Write-Host "Downloading artifact from nightly.link..." + Invoke-WebRequest -Uri $downloadUrl -OutFile $artifactZip -UseBasicParsing + + Write-Host "Extracting artifact container..." + Expand-Archive -Path $artifactZip -DestinationPath $artifactExtractDir -Force + + $innerZip = Get-ChildItem -Path $artifactExtractDir -Filter *.zip -File | Select-Object -First 1 + if (-not $innerZip) { + throw "Expected an inner zip inside artifact container, but none was found." + } + + Write-Host "Extracting payload zip: $($innerZip.Name)" + Expand-Archive -Path $innerZip.FullName -DestinationPath $payloadExtractDir -Force + + if (-not (Test-Path -LiteralPath $DestinationDir)) { + New-Item -ItemType Directory -Path $DestinationDir -Force | Out-Null + } + + if ($CleanDestination) { + Write-Host "Cleaning destination directory: $DestinationDir" + Get-ChildItem -LiteralPath $DestinationDir -Force | Remove-Item -Recurse -Force + } + + Write-Host "Copying payload files to destination: $DestinationDir" + Copy-Item -Path (Join-Path $payloadExtractDir '*') -Destination $DestinationDir -Recurse -Force + + Write-Host "Done. Updated files in '$DestinationDir' from artifact '$($artifact.name)'." +} +finally { + if (Test-Path -LiteralPath $tempRoot) { + Remove-Item -LiteralPath $tempRoot -Recurse -Force + } +}