Repository navigation
fix(windows): register and qualify GLib and Cairo TLS callbacks #56
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
Merged
Merged
Changes from all commits
Commits
Show all changes
7 commits
Select commit
Hold shift + click to select a range
233103c
fix(windows): register and qualify GLib TLS callbacks
zoeyrose b3c4b2c
fix(windows): register and qualify Cairo TLS initialization
zoeyrose aa90f07
fix(windows): isolate Cairo qualification with repaired GLib
zoeyrose f01f650
ci(windows): qualify scoped GLib TLS repair natively
zoeyrose 2a2b3f1
ci(windows): require native Cairo TLS qualification alongside GLib
zoeyrose f65b231
ci(windows): build Cairo controls with the sealed GLib repair
zoeyrose 8e02715
ci(windows): require full validation for TLS workflow changes
zoeyrose 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,220 @@ | ||
| # Copyright (c) 2026 Atrinik contributors | ||
| # SPDX-License-Identifier: MIT | ||
| name: Qualify Windows GLib and Cairo TLS repairs | ||
|
|
||
| on: | ||
| pull_request: | ||
| paths: | ||
| - 'windows/**' | ||
| - 'llvm-toolchain.json' | ||
| - 'tools/tests/test_windows_glib_workflow.py' | ||
| - '.github/workflows/windows-glib-qualification.yml' | ||
| workflow_dispatch: | ||
|
|
||
| permissions: | ||
| contents: read | ||
|
|
||
| concurrency: | ||
| group: windows-glib-qualification-${{ github.event.pull_request.number || github.ref }} | ||
| cancel-in-progress: true | ||
|
|
||
| # This scoped proof never publishes or promotes an SDK. Full image qualification | ||
| # remains owned by validate.yml and publish-windows.yml. | ||
| jobs: | ||
| cross-build: | ||
| name: Build scoped GLib and Cairo TLS qualification bundles | ||
| runs-on: ubuntu-26.04 | ||
| timeout-minutes: 10 | ||
| env: | ||
| SDK_IMAGE: ghcr.io/atrinik/windows-build:candidate-sha-1d82af6036b831d97cc470e2230357047e4c78f8@sha256:914acf5c3c05546ca4fb45e47362d754ec717a9062f6ebcc1b81b3ab7d0ad325 | ||
| steps: | ||
| - name: Check out qualification sources | ||
| uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 | ||
| with: | ||
| persist-credentials: false | ||
|
|
||
| - name: Stage and verify immutable library source | ||
| shell: bash | ||
| run: | | ||
| set -euo pipefail | ||
| mkdir -p build/glib-qualification/{source,downloads,provenance} | ||
| git archive HEAD | tar -x -C build/glib-qualification/source | ||
| # Only this pinned archive is fetched on the host; the build worker | ||
| # has neither registry credentials nor network access. | ||
| python3 - <<'PY' | ||
| import json | ||
| from pathlib import Path | ||
| records = json.loads(Path('windows/dependencies.json').read_text())['dependencies'] | ||
| inputs = { | ||
| 'glib': { | ||
| 'version': '2.89.3', | ||
| 'url': 'https://download.gnome.org/sources/glib/2.89/glib-2.89.3.tar.xz', | ||
| 'archive': 'glib-2.89.3.tar.xz', | ||
| 'sha256': '09fd1e99f991067749ad66090e482ec4bd6514ad53abb4e9fbbdc2a4d2753532', | ||
| }, | ||
| 'cairo': { | ||
| 'version': '1.18.4', | ||
| 'url': 'https://cairographics.org/releases/cairo-1.18.4.tar.xz', | ||
| 'archive': 'cairo-1.18.4.tar.xz', | ||
| 'sha256': '445ed8208a6e4823de1226a74ca319d3600e83f6369f99b14265006599c32ccb', | ||
| }, | ||
| } | ||
| for library, expected in inputs.items(): | ||
| package, = [item for item in records if item['name'] == library] | ||
| if any(package.get(key) != value for key, value in expected.items()): | ||
| raise SystemExit('Library source differs from the scoped qualification input') | ||
| Path(f'build/glib-qualification/provenance/{library}-source.json').write_text( | ||
| json.dumps({'library': library, **expected}, indent=2) + '\n') | ||
| Path(f'build/glib-qualification/downloads/{library}-url.txt').write_text(expected['url']) | ||
| Path(f'build/glib-qualification/downloads/{library}.sha256').write_text( | ||
| expected['sha256'] + f' build/glib-qualification/downloads/{library}.tar.xz\n') | ||
| PY | ||
| for library in glib cairo; do | ||
| curl --fail --location --proto '=https' --tlsv1.2 \ | ||
| --connect-timeout 15 --max-time 60 \ | ||
| "$(cat "build/glib-qualification/downloads/${library}-url.txt")" \ | ||
| -o "build/glib-qualification/downloads/${library}.tar.xz" | ||
| sha256sum --check --strict "build/glib-qualification/downloads/${library}.sha256" | ||
| done | ||
| git rev-parse HEAD > build/glib-qualification/provenance/source-commit.txt | ||
| git rev-parse 'HEAD^{tree}' > build/glib-qualification/provenance/source-tree.txt | ||
| printf '%s\n' "${SDK_IMAGE}" > build/glib-qualification/provenance/sdk-image.txt | ||
| sha256sum windows/qualify-glib-tls.py windows/run-glib-tls-qualification.ps1 \ | ||
| > build/glib-qualification/provenance/qualification-scripts.sha256 | ||
|
|
||
| - name: Cross-build in an isolated LLVM SDK worker | ||
| shell: bash | ||
| run: | | ||
| set -euo pipefail | ||
| # Anonymous public digest access only; no login or package permissions. | ||
| timeout 120s docker pull "${SDK_IMAGE}" | ||
| cpuset=$(python3 - <<'PY' | ||
| import os | ||
| cpus = sorted(os.sched_getaffinity(0)) | ||
| if len(cpus) < 2: | ||
| raise SystemExit('Qualification requires two available runner CPUs') | ||
| print(','.join(map(str, cpus[:2]))) | ||
| PY | ||
| ) | ||
| printf 'cpuset=%s\ncpus=2\nmemory=8g\nmemory-swap=8g\npids=512\nnofile=4096\n' \ | ||
| "${cpuset}" > build/glib-qualification/provenance/worker-limits.txt | ||
| worker=$(docker create --network none --user 1000:1000 \ | ||
| --cpuset-cpus "${cpuset}" --cpus 2 --memory 8g --memory-swap 8g \ | ||
| --pids-limit 512 --ulimit nofile=4096:4096 --cap-drop ALL \ | ||
| --security-opt no-new-privileges --env HOME=/tmp/glib-home \ | ||
| --workdir /tmp/qualification/source "${SDK_IMAGE}" \ | ||
| sh -eu -c ' | ||
| python3 windows/qualify-glib-tls.py --library glib \ | ||
| --source-archive /tmp/qualification/downloads/glib.tar.xz \ | ||
| --work-dir /tmp/qualification/work/glib --output /tmp/qualification/bundle/glib | ||
| python3 windows/qualify-glib-tls.py --library cairo \ | ||
| --source-archive /tmp/qualification/downloads/cairo.tar.xz \ | ||
| --work-dir /tmp/qualification/work/cairo --output /tmp/qualification/bundle/cairo \ | ||
| --glib-bundle /tmp/qualification/bundle/glib | ||
| ') | ||
| cleanup() { docker rm --force "${worker}" >/dev/null; } | ||
| trap cleanup EXIT | ||
| # Preserve the fixed worker UID without mounting the runner workspace. | ||
| tar --owner=1000 --group=1000 --transform='s,^glib-qualification,qualification,' \ | ||
| -c -C build glib-qualification | docker cp --archive - "${worker}:/tmp" | ||
| status=0 | ||
| timeout 300s docker start --attach "${worker}" \ | ||
| > build/glib-cross-build.log 2>&1 || status=$? | ||
| cat build/glib-cross-build.log | ||
| if [[ "${status}" -eq 0 ]]; then | ||
| status=$(docker inspect --format '{{.State.ExitCode}}' "${worker}") | ||
| fi | ||
| if [[ "${status}" -ne 0 ]]; then | ||
| exit "${status}" | ||
| fi | ||
| docker cp "${worker}:/tmp/qualification/bundle" build/glib-native-bundle | ||
| for library in glib cairo; do | ||
| cp -R build/glib-qualification/provenance "build/glib-native-bundle/${library}/provenance" | ||
| cp windows/qualify-glib-tls.py windows/run-glib-tls-qualification.ps1 \ | ||
| "build/glib-native-bundle/${library}/provenance/" | ||
| cp "build/glib-qualification/downloads/${library}.tar.xz" \ | ||
| "build/glib-native-bundle/${library}/provenance/" | ||
| done | ||
| # Bound artifact size and reject links before uploading executable input. | ||
| python3 - <<'PY' | ||
| from pathlib import Path | ||
| root = Path('build/glib-native-bundle') | ||
| files = list(root.rglob('*')) | ||
| if any(item.is_symlink() for item in files): | ||
| raise SystemExit('Native bundle contains a link') | ||
| if sum(item.stat().st_size for item in files if item.is_file()) > 100 * 1024 * 1024: | ||
| raise SystemExit('Native bundle exceeds 100 MiB') | ||
| for library in ('glib', 'cairo'): | ||
| for lane in ('candidate', 'original', 'relinked'): | ||
| if not (root / library / lane).is_dir(): | ||
| raise SystemExit(f'Missing qualification lane: {library}/{lane}') | ||
| for name in ('qualification.json', 'run-glib-tls-qualification.ps1'): | ||
| if not (root / library / name).is_file(): | ||
| raise SystemExit(f'Missing qualification artifact: {library}/{name}') | ||
| PY | ||
|
|
||
| - name: Upload bounded native library bundle | ||
| uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7 | ||
| with: | ||
| name: windows-glib-cairo-tls-native-bundle | ||
| path: build/glib-native-bundle | ||
| if-no-files-found: error | ||
| retention-days: 3 | ||
|
|
||
| - name: Retain cross-build evidence | ||
| if: always() | ||
| uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7 | ||
| with: | ||
| name: windows-glib-cairo-tls-cross-build-evidence | ||
| path: | | ||
| build/glib-cross-build.log | ||
| build/glib-qualification/provenance | ||
| if-no-files-found: warn | ||
| retention-days: 3 | ||
|
|
||
| native-windows: | ||
| name: Verify GLib and Cairo TLS repairs and negative controls on Windows | ||
| needs: cross-build | ||
| permissions: {} | ||
| runs-on: windows-2025 | ||
| timeout-minutes: 5 | ||
| steps: | ||
| - name: Download scoped native GLib and Cairo bundles | ||
| uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8 | ||
| with: | ||
| name: windows-glib-cairo-tls-native-bundle | ||
| path: build/glib-native-bundle | ||
|
|
||
| - name: Require native TLS lifecycle and negative controls | ||
| shell: pwsh | ||
| run: | | ||
| $ErrorActionPreference = 'Stop' | ||
| foreach ($library in @('glib', 'cairo')) { | ||
| $bundle = (Resolve-Path "build/glib-native-bundle/$library").Path | ||
| $metadata = Get-Content -Raw (Join-Path $bundle 'qualification.json') | ConvertFrom-Json | ||
| if ($metadata.library -ne $library) { throw "Bundle library mismatch: $library" } | ||
| & pwsh -NoLogo -NoProfile -NonInteractive -File (Join-Path $bundle 'run-glib-tls-qualification.ps1') | ||
| if ($LASTEXITCODE -ne 0) { throw "Native $library qualification failed: $LASTEXITCODE" } | ||
| $results = @(Get-Content -Raw (Join-Path $bundle 'native-results.json') | ConvertFrom-Json) | ||
| if ($results.Count -ne 3 -or @($results | Where-Object { $_.passed -ne $true }).Count -ne 0) { | ||
| throw "Native $library qualification did not pass all three lanes" | ||
| } | ||
| $lanes = @($results.lane | Sort-Object) | ||
| if (($lanes -join ',') -ne 'candidate,original,relinked') { | ||
| throw "Native $library qualification lane mismatch" | ||
| } | ||
| } | ||
|
|
||
| - name: Retain native TLS qualification evidence | ||
| if: always() | ||
| uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7 | ||
| with: | ||
| name: windows-glib-tls-native-evidence | ||
| path: | | ||
| build/glib-native-bundle/*/qualification.json | ||
| build/glib-native-bundle/*/provenance | ||
| build/glib-native-bundle/**/*.json | ||
| build/glib-native-bundle/**/*.stdout.txt | ||
| build/glib-native-bundle/**/*.stderr.txt | ||
| if-no-files-found: error | ||
| retention-days: 3 | ||
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
Oops, something went wrong.
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.