From 47198b59500314b5c2d23fb8e264baf1c6e03afb Mon Sep 17 00:00:00 2001 From: Mike Barkmin Date: Wed, 7 Oct 2026 20:39:40 +0200 Subject: [PATCH 1/5] Collect Windows native rendering diagnostics --- .github/workflows/package.yml | 15 +++++---------- scripts/smoke-packaged.py | 2 +- .../openpatch/scratch4j/ui/PackagedSmoke.java | 18 +++++++++++++++++- 3 files changed, 23 insertions(+), 12 deletions(-) diff --git a/.github/workflows/package.yml b/.github/workflows/package.yml index 7f2945d..c4ece48 100644 --- a/.github/workflows/package.yml +++ b/.github/workflows/package.yml @@ -34,18 +34,9 @@ jobs: fail-fast: false matrix: include: - - name: linux-x64 - os: ubuntu-latest - types: deb app-image appimage - name: windows-x64 os: windows-latest - types: msi app-image - - name: macos-arm64 - os: macos-15 - types: dmg app-image - - name: macos-x64 - os: macos-15-intel - types: dmg app-image + types: app-image steps: - uses: actions/checkout@v7 @@ -67,6 +58,10 @@ jobs: wix extension add --global WixToolset.Util.wixext/5.0.2 wix extension list --global + - name: Report Windows graphics adapter + shell: pwsh + run: Get-CimInstance Win32_VideoController | Select-Object Name,DriverVersion,VideoProcessor | Format-List + - name: Build and package shell: bash run: | diff --git a/scripts/smoke-packaged.py b/scripts/smoke-packaged.py index d5a8e00..a9268ed 100644 --- a/scripts/smoke-packaged.py +++ b/scripts/smoke-packaged.py @@ -23,5 +23,5 @@ report = Path(temporary) / "report.txt" message = report.read_text() if report.is_file() else "No packaged smoke report" if result.returncode or not message.startswith("PASS:"): - raise SystemExit(message + "\n" + result.stdout[-2000:] + result.stderr[-4000:]) + raise SystemExit(message + "\n" + result.stdout[-64000:] + result.stderr[-10000:]) print(message.strip()) diff --git a/ui/src/main/java/org/openpatch/scratch4j/ui/PackagedSmoke.java b/ui/src/main/java/org/openpatch/scratch4j/ui/PackagedSmoke.java index 0fd4934..9381b5d 100644 --- a/ui/src/main/java/org/openpatch/scratch4j/ui/PackagedSmoke.java +++ b/ui/src/main/java/org/openpatch/scratch4j/ui/PackagedSmoke.java @@ -33,16 +33,32 @@ static void start(StudioApp app, javafx.stage.Stage stage, Path output) { Files.writeString(project.root().resolve("List.java"), "public class List {}\n"); } List errors = new java.util.concurrent.CopyOnWriteArrayList<>(); + List outputLines = new java.util.concurrent.CopyOnWriteArrayList<>(); AtomicLong frames = new AtomicLong(-1); RunHandle handle = new ProjectRunner().run(project, RunConfig.of(project.startStage()).withJava(java).withControl().withExitAfter(4), new RunListener() { public void onStderr(String line) { errors.add(line); } + public void onStdout(String line) { outputLines.add(line); } public void onFrames(long count) { frames.set(count); } }); int code; try { code = handle.exitFuture().get(40, TimeUnit.SECONDS); } - catch (Exception e) { handle.stop(); throw e; } + catch (Exception e) { + Path jcmd = java.resolveSibling(isWindows() ? "jcmd.exe" : "jcmd"); + try { + if (Files.isRegularFile(jcmd)) { + Process dump = new ProcessBuilder(jcmd.toString(), Long.toString(handle.pid()), + "Thread.print").inheritIO().start(); + if (!dump.waitFor(5, TimeUnit.SECONDS)) dump.destroyForcibly(); + } + } catch (Exception diagnosticError) { + System.err.println("Thread diagnostic failed: " + diagnosticError); + } + handle.stop(); + throw new IllegalStateException(name + " timed out; frames=" + frames.get() + + "; stdout=" + outputLines + "; stderr=" + errors, e); + } if (code != 0 || frames.get() <= 0) throw new IllegalStateException(name + " did not draw: " + code + " " + errors); Path source = project.root().resolve("MyStage.java"); String saved = Files.readString(source); From 7f0e3679ea334dc8517bb3dbabced31bdb67f4ef Mon Sep 17 00:00:00 2001 From: Mike Barkmin Date: Wed, 7 Oct 2026 20:45:12 +0200 Subject: [PATCH 2/5] Reuse verified launcher during Windows diagnostics --- .github/workflows/package.yml | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/.github/workflows/package.yml b/.github/workflows/package.yml index c4ece48..c2f4607 100644 --- a/.github/workflows/package.yml +++ b/.github/workflows/package.yml @@ -13,10 +13,12 @@ jobs: # the Windows .exe of exported student apps, cross-compiled once runs-on: ubuntu-latest steps: - - uses: actions/checkout@v7 - - run: | - sudo apt-get install --no-install-recommends -y gcc-mingw-w64-x86-64 - scripts/build-win-launcher.sh + - uses: actions/download-artifact@v7 + with: + name: win-launcher + run-id: 37667565783 + github-token: ${{ github.token }} + path: export/src/main/resources/org/openpatch/scratch4j/export/launcher/ - uses: actions/upload-artifact@v7 with: name: win-launcher From 767b1ee64b0ed55ebbe59274fb5d5edb7e27a90b Mon Sep 17 00:00:00 2001 From: Mike Barkmin Date: Wed, 7 Oct 2026 20:49:40 +0200 Subject: [PATCH 3/5] Provide software OpenGL for Windows native smoke checks --- .../actions/setup-windows-opengl/action.yml | 25 +++++++++++++++++++ .github/workflows/package.yml | 4 +++ 2 files changed, 29 insertions(+) create mode 100644 .github/actions/setup-windows-opengl/action.yml diff --git a/.github/actions/setup-windows-opengl/action.yml b/.github/actions/setup-windows-opengl/action.yml new file mode 100644 index 0000000..8578f24 --- /dev/null +++ b/.github/actions/setup-windows-opengl/action.yml @@ -0,0 +1,25 @@ +name: Set up Windows software OpenGL +description: Install a pinned Mesa driver on the runner for native graphics smoke checks. +runs: + using: composite + steps: + - shell: pwsh + run: | + # Hyper-V's inbox OpenGL driver lacks Processing's framebuffer support. + # Install the CI host driver after packaging; downloads keep their own runtime. + $mesaDirectory = Join-Path $env:RUNNER_TEMP 'scratch4j-mesa' + $mesaArchive = Join-Path $env:RUNNER_TEMP 'mesa3d-26.2.4-release-msvc.7z' + Invoke-WebRequest 'https://github.com/pal1000/mesa-dist-win/releases/download/26.2.4/mesa3d-26.2.4-release-msvc.7z' -OutFile $mesaArchive + $expectedHash = '351fc8c8b695878ffb3eaa044b3ead08672a48b1a045e3c3e3975811df0f6695' + if ((Get-FileHash $mesaArchive -Algorithm SHA256).Hash.ToLowerInvariant() -ne $expectedHash) { + throw 'Mesa archive checksum mismatch' + } + & 7z x -y "-o$mesaDirectory" $mesaArchive | Out-Null + if ($LASTEXITCODE -ne 0) { throw 'Mesa archive extraction failed' } + & (Join-Path $mesaDirectory 'systemwidedeploy.cmd') 1 + if ($LASTEXITCODE -ne 0) { throw 'Mesa driver installation failed' } + if (!(Test-Path (Join-Path $env:SystemRoot 'System32/mesadrv.dll'))) { + throw 'Mesa system OpenGL driver is missing' + } + 'GALLIUM_DRIVER=llvmpipe' >> $env:GITHUB_ENV + 'LIBGL_ALWAYS_SOFTWARE=1' >> $env:GITHUB_ENV diff --git a/.github/workflows/package.yml b/.github/workflows/package.yml index c2f4607..94687ba 100644 --- a/.github/workflows/package.yml +++ b/.github/workflows/package.yml @@ -125,6 +125,10 @@ jobs: & $signtool.FullName sign /f $pfx /p $env:WINDOWS_CERTIFICATE_PASSWORD /fd SHA256 /tr http://timestamp.digicert.com /td SHA256 $_.FullName } + - name: Set up Windows software OpenGL + if: runner.os == 'Windows' + uses: ./.github/actions/setup-windows-opengl + - name: Start packaged Studio and run offline projects shell: bash run: | From 624f01e43821f87d0300ab07dc608bdae55c0b08 Mon Sep 17 00:00:00 2001 From: Mike Barkmin Date: Wed, 7 Oct 2026 20:53:38 +0200 Subject: [PATCH 4/5] Restore the complete release packaging matrix --- .github/workflows/package.yml | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/.github/workflows/package.yml b/.github/workflows/package.yml index 94687ba..3dd05d4 100644 --- a/.github/workflows/package.yml +++ b/.github/workflows/package.yml @@ -13,12 +13,10 @@ jobs: # the Windows .exe of exported student apps, cross-compiled once runs-on: ubuntu-latest steps: - - uses: actions/download-artifact@v7 - with: - name: win-launcher - run-id: 37667565783 - github-token: ${{ github.token }} - path: export/src/main/resources/org/openpatch/scratch4j/export/launcher/ + - uses: actions/checkout@v7 + - run: | + sudo apt-get install --no-install-recommends -y gcc-mingw-w64-x86-64 + scripts/build-win-launcher.sh - uses: actions/upload-artifact@v7 with: name: win-launcher @@ -36,9 +34,18 @@ jobs: fail-fast: false matrix: include: + - name: linux-x64 + os: ubuntu-latest + types: deb app-image appimage - name: windows-x64 os: windows-latest - types: app-image + types: msi app-image + - name: macos-arm64 + os: macos-15 + types: dmg app-image + - name: macos-x64 + os: macos-15-intel + types: dmg app-image steps: - uses: actions/checkout@v7 @@ -60,10 +67,6 @@ jobs: wix extension add --global WixToolset.Util.wixext/5.0.2 wix extension list --global - - name: Report Windows graphics adapter - shell: pwsh - run: Get-CimInstance Win32_VideoController | Select-Object Name,DriverVersion,VideoProcessor | Format-List - - name: Build and package shell: bash run: | From 3adaef216fd6bfc41fd884d286d47e19a7a4bbd9 Mon Sep 17 00:00:00 2001 From: Mike Barkmin Date: Wed, 7 Oct 2026 20:58:07 +0200 Subject: [PATCH 5/5] Validate Studio catalogs against its bundled library release --- .github/workflows/ci.yml | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e499d7e..4f5fd5b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -25,11 +25,26 @@ jobs: - name: Build and test run: mvn --batch-mode --no-transfer-progress verify + - name: Identify the bundled library release + if: runner.os == 'Linux' + id: library-release + run: | + python3 - <<'PY' + import os + import xml.etree.ElementTree as ET + namespace = '{http://maven.apache.org/POM/4.0.0}' + version = ET.parse('pom.xml').findtext(f'{namespace}properties/{namespace}scratch.version') + if not version: + raise SystemExit('Missing bundled library version') + with open(os.environ['GITHUB_OUTPUT'], 'a') as output: + print('ref=v' + version, file=output) + PY - name: Check shared release metadata if: runner.os == 'Linux' uses: actions/checkout@v7 with: repository: openpatch/scratch-for-java + ref: ${{ steps.library-release.outputs.ref }} path: .validation/library - name: Set up the catalog toolchain if: runner.os == 'Linux'