-
-
Notifications
You must be signed in to change notification settings - Fork 3.6k
Automate WinUtil title screen generation #4965
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
mewclouds
wants to merge
5
commits into
ChrisTitusTech:main
Choose a base branch
from
mewclouds:docs/automate-title-screen-generation
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
5 commits
Select commit
Hold shift + click to select a range
608f56f
feat: add title screen generation tooling
mewclouds b590860
ci: automate title screen updates
mewclouds 3b4598c
docs: update AGENTS.md and SPEC.md with title-screen generation details
mewclouds 61d3db4
fix: address review comments
mewclouds 47a1a6c
fix: deselect bitmap before reading capture pixels
mewclouds File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,207 @@ | ||
| name: Generate WinUtil title screen | ||
|
|
||
| on: | ||
| workflow_dispatch: | ||
|
|
||
| permissions: | ||
| contents: read | ||
|
|
||
| concurrency: | ||
| group: winutil-title-screen | ||
| cancel-in-progress: true | ||
|
|
||
| jobs: | ||
| generate: | ||
| runs-on: windows-latest | ||
| timeout-minutes: 15 | ||
| env: | ||
| WINUTIL_CAPTURE_WIDTH: "1920" | ||
| WINUTIL_CAPTURE_HEIGHT: "1080" | ||
|
|
||
| defaults: | ||
| run: | ||
| shell: pwsh | ||
|
|
||
| steps: | ||
| - name: Checkout repository | ||
| uses: actions/checkout@v7 | ||
| with: | ||
| ref: main | ||
|
coderabbitai[bot] marked this conversation as resolved.
|
||
| persist-credentials: false | ||
|
|
||
| - name: Install uv and Python | ||
| uses: astral-sh/setup-uv@v9.0.0 | ||
| with: | ||
| version: "0.12.0" | ||
| python-version: "3.13" | ||
| enable-cache: false | ||
|
|
||
| - name: Set display resolution | ||
| run: | | ||
| # Set-DisplayResolution is provided by Windows PowerShell's ServerCore | ||
| # module, so this step intentionally calls powershell.exe from pwsh. | ||
| & powershell.exe -NoLogo -NoProfile -Command @' | ||
| Set-DisplayResolution ` | ||
| -Width $env:WINUTIL_CAPTURE_WIDTH ` | ||
| -Height $env:WINUTIL_CAPTURE_HEIGHT ` | ||
| -Force | ||
| '@ | ||
|
|
||
| if ($LASTEXITCODE -ne 0) { | ||
| throw "Failed to set the runner display resolution." | ||
| } | ||
|
|
||
| Add-Type @" | ||
| using System.Runtime.InteropServices; | ||
|
|
||
| public static class ResolutionCheck { | ||
| [DllImport("user32.dll")] | ||
| public static extern int GetSystemMetrics(int index); | ||
| } | ||
| "@ | ||
|
|
||
| $actualWidth = [ResolutionCheck]::GetSystemMetrics(0) | ||
| $actualHeight = [ResolutionCheck]::GetSystemMetrics(1) | ||
| if ( | ||
| $actualWidth -ne [int]$env:WINUTIL_CAPTURE_WIDTH -or | ||
| $actualHeight -ne [int]$env:WINUTIL_CAPTURE_HEIGHT | ||
| ) { | ||
| throw ( | ||
| "The hosted runner rejected the requested resolution. " + | ||
| "Requested: $env:WINUTIL_CAPTURE_WIDTH" + | ||
| "x$env:WINUTIL_CAPTURE_HEIGHT; " + | ||
| "actual: ${actualWidth}x${actualHeight}." | ||
| ) | ||
| } | ||
|
|
||
| - name: Report display environment | ||
| run: | | ||
| Add-Type @" | ||
| using System.Runtime.InteropServices; | ||
|
|
||
| public static class DisplayInfo { | ||
| [DllImport("user32.dll")] | ||
| public static extern int GetSystemMetrics(int index); | ||
| } | ||
| "@ | ||
|
|
||
| $width = [DisplayInfo]::GetSystemMetrics(0) | ||
| $height = [DisplayInfo]::GetSystemMetrics(1) | ||
| $sessionId = [Diagnostics.Process]::GetCurrentProcess().SessionId | ||
|
|
||
| Write-Host "Resolution: ${width}x${height}" | ||
| Write-Host "Session ID: $sessionId" | ||
| Write-Host "User: $env:USERNAME" | ||
|
|
||
| - name: Compile WinUtil | ||
| run: | | ||
| Set-ExecutionPolicy Bypass -Scope Process -Force | ||
| ./Compile.ps1 | ||
|
|
||
| - name: Launch WinUtil | ||
| run: | | ||
| $scriptPath = Join-Path $env:GITHUB_WORKSPACE "winutil.ps1" | ||
| $command = "& '$scriptPath'" | ||
| $bytes = [Text.Encoding]::Unicode.GetBytes($command) | ||
| $encodedCommand = [Convert]::ToBase64String($bytes) | ||
|
|
||
| # Hide the console host while leaving the WPF window available. | ||
| $process = Start-Process powershell.exe -ArgumentList @( | ||
| "-NoLogo", | ||
| "-NoProfile", | ||
| "-ExecutionPolicy", "Bypass", | ||
| "-EncodedCommand", $encodedCommand | ||
| ) -WindowStyle Hidden -PassThru | ||
|
|
||
| "WINUTIL_HOST_PID=$($process.Id)" | | ||
| Out-File $env:GITHUB_ENV -Append -Encoding utf8 | ||
|
|
||
| $deadline = (Get-Date).AddSeconds(90) | ||
| do { | ||
| Start-Sleep -Seconds 2 | ||
| # A versioned title excludes the unversioned console host. | ||
| $window = Get-Process | | ||
| Where-Object { $_.MainWindowTitle -match '^WinUtil\s+\d' } | | ||
| Select-Object -First 1 | ||
| } until ($window -or (Get-Date) -ge $deadline) | ||
|
|
||
| if (-not $window) { | ||
| throw "WinUtil did not expose a window within 90 seconds." | ||
| } | ||
|
|
||
| Write-Host "WinUtil HWND: $($window.MainWindowHandle)" | ||
| Write-Host "WinUtil title: $($window.MainWindowTitle)" | ||
|
|
||
| # The exact HWND avoids desktop-enumeration ambiguity. Python validates | ||
| # the title and WPF class before using it. | ||
| "WINUTIL_HWND=$($window.MainWindowHandle)" | | ||
| Out-File $env:GITHUB_ENV -Append -Encoding utf8 | ||
|
|
||
| - name: Generate title screen | ||
| working-directory: tools/title-screen | ||
| run: | | ||
| & uv run --locked python automate_title_screen.py ` | ||
| --output ..\..\docs\src\assets\branding\title-screen.png 2>&1 | | ||
| Tee-Object -FilePath "$env:RUNNER_TEMP\title-screen-capture.log" | ||
|
|
||
| if ($LASTEXITCODE -ne 0) { | ||
| throw "Title-screen automation exited with code $LASTEXITCODE." | ||
| } | ||
|
|
||
| - name: Create title-screen update pull request | ||
| id: cpr | ||
| uses: peter-evans/create-pull-request@v8 | ||
| with: | ||
| token: ${{ secrets.AUTO_MERGE }} | ||
| add-paths: docs/src/assets/branding/title-screen.png | ||
| base: main | ||
| branch: title-screen-update | ||
| delete-branch: true | ||
| commit-message: "docs: update WinUtil title screen" | ||
| title: "docs: update WinUtil title screen" | ||
| body: | | ||
| Regenerates the WinUtil Light and Dark title-screen composite from the current main branch. | ||
|
|
||
| Source workflow: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} | ||
| labels: | | ||
| automated | ||
| documentation | ||
| skip-changelog | ||
|
|
||
| - name: Report pull request | ||
| if: steps.cpr.outputs.pull-request-url | ||
| env: | ||
| PR_OPERATION: ${{ steps.cpr.outputs.pull-request-operation }} | ||
| PR_URL: ${{ steps.cpr.outputs.pull-request-url }} | ||
| run: | | ||
| Write-Host "Pull request $env:PR_OPERATION`: $env:PR_URL" | ||
|
|
||
| - name: Inspect UI Automation on failure | ||
| if: failure() | ||
| continue-on-error: true | ||
| working-directory: tools/title-screen | ||
| run: | | ||
| uv run --locked python inspect_winutil.py ` | ||
| "$env:RUNNER_TEMP\winutil-title-screen-inspect.txt" | ||
|
|
||
| - name: Upload failure diagnostics | ||
| if: failure() | ||
| uses: actions/upload-artifact@v7 | ||
| with: | ||
| name: winutil-title-screen-failure-${{ github.run_number }} | ||
| path: | | ||
| docs/src/assets/branding/title-screen.png | ||
| ${{ runner.temp }}/title-screen-capture.log | ||
| ${{ runner.temp }}/winutil-title-screen-inspect.txt | ||
| if-no-files-found: warn | ||
| retention-days: 14 | ||
|
|
||
| - name: Close WinUtil | ||
| if: always() | ||
| run: | | ||
| if ($env:WINUTIL_HOST_PID) { | ||
| Stop-Process ` | ||
| -Id ([int]$env:WINUTIL_HOST_PID) ` | ||
| -Force ` | ||
| -ErrorAction SilentlyContinue | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -10,6 +10,9 @@ testResults.xml | |
| # general software/os specific | ||
| desktop.ini | ||
| .DS_Store | ||
| __pycache__/ | ||
| *.pyc | ||
| .venv/ | ||
|
|
||
| .vscode/ | ||
| .idea/ | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| 3.13 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,75 @@ | ||
| # WinUtil title screen | ||
|
|
||
| This tool generates the Light and Dark composite used as WinUtil's title screen | ||
| in the repository README and documentation site. It opens the Tweaks tab, | ||
| captures both themes, and combines them into one PNG. The two raw captures are | ||
| temporary and are removed when the command finishes. | ||
|
|
||
| ## Requirements | ||
|
|
||
| - Windows with an interactive desktop | ||
| - [uv](https://docs.astral.sh/uv/) | ||
| - WinUtil compiled and running | ||
| - An elevated PowerShell terminal | ||
|
|
||
| Run the commands below from `tools/title-screen`. | ||
|
|
||
| ## Generate and review a test image | ||
|
|
||
| Start WinUtil from the repository root: | ||
|
|
||
| ```powershell | ||
| .\Compile.ps1 -Run | ||
| ``` | ||
|
|
||
| With WinUtil still open, return to this directory in an elevated terminal and | ||
| generate a test image: | ||
|
|
||
| ```powershell | ||
| uv run --locked python automate_title_screen.py --output "$env:TEMP\winutil-title-screen.png" | ||
| ``` | ||
|
|
||
| Open the resulting PNG and check that: | ||
|
|
||
| - the Tweaks tab is shown, | ||
| - the Light theme is on the upper-left side of the diagonal, | ||
| - the Dark theme is on the lower-right side, and | ||
| - no desktop background or other windows are visible. | ||
|
|
||
| The automation works whether WinUtil starts in Light or Dark mode. It leaves the | ||
| window on the Tweaks tab in Light mode. | ||
|
|
||
| ## Automation | ||
|
|
||
| The title screen is updated through a manually triggered GitHub Actions workflow. | ||
| When the generated image changes, the workflow opens or updates a pull request | ||
| for review. It does not merge the pull request automatically. | ||
|
|
||
| Failed runs upload the capture log, UI Automation inspection, and available image | ||
| as diagnostic artifacts for 14 days. | ||
|
|
||
| ## Tests | ||
|
|
||
| The tests cover theme detection and composite image generation without opening | ||
| WinUtil: | ||
|
|
||
| ```powershell | ||
| uv run --locked python -m unittest discover | ||
| ``` | ||
|
|
||
| ## Troubleshooting | ||
|
|
||
| If WinUtil cannot be found, make sure the compiled WPF window is open and that | ||
| the terminal is elevated. The script deliberately ignores editors, terminals, | ||
| and browser windows that merely contain "WinUtil" in their title. | ||
|
|
||
| If a tab or theme control cannot be found, capture the UI Automation tree while | ||
| WinUtil is open: | ||
|
|
||
| ```powershell | ||
| uv run --locked python inspect_winutil.py "$env:TEMP\winutil-inspect.txt" | ||
| ``` | ||
|
|
||
| The inspector opens the theme menu before recording its controls. Attach the | ||
| text file when reporting a failure. It contains window and control metadata, not | ||
| the generated screenshots. | ||
|
mewclouds marked this conversation as resolved.
|
||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.