From ae8595ca92b8b8634741780d0aa2510d603a6c68 Mon Sep 17 00:00:00 2001 From: jaenster Date: Sat, 16 May 2026 14:33:32 +0200 Subject: [PATCH] ci: bundle release zip with loader, SHA256SUMS, and Sigstore provenance The existing workflow shipped a bare Charon.dll on tag push. Users still had to source the DLLLoader separately and trust the upload by hand. This switches the release output to a self-contained Charon-vX.X.X-x86.zip (Charon.dll + dbghelp.dll + readme.md + SHA256SUMS.txt) matching the unzip-to-D2-directory install flow in the readme. The DLLLoader is pinned to upstream tag 0.1.3 and its dbghelp.dll is verified against a hardcoded sha256 before bundling, so an upstream asset swap can't slip a different binary into the zip. Adds a Sigstore-signed build provenance attestation on the zip and pdb (actions/attest-build-provenance). Anyone can verify a release artifact came from this repo+workflow with: gh attestation verify Charon-vX.X.X-x86.zip --repo blizzhackers/Charon Authenticode code signing is intentionally skipped: Charon.dll isn't a standalone exe that hits SmartScreen, it's side-loaded into Game.exe via DLLLoader. Real certs cost money, self-signed ones still warn, and provenance attestations cover the supply-chain integrity question that actually applies here without key management or expiry. Other changes: - Symbols (.pdb) uploaded as a separate workflow artifact and published on the release for crash debugging. - All actions pinned to commit shas with version comments. --- .github/workflows/build.yml | 114 ++++++++++++++++++++++++++++++++---- 1 file changed, 101 insertions(+), 13 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 5544d88..6878c40 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -8,6 +8,13 @@ on: branches: [ "**" ] workflow_dispatch: +# Loader bundled with releases. Distributed as dbghelp.dll so Game.exe +# side-loads it. Pinned by tag + sha256 so an upstream asset swap can't +# slip a different binary into our zip. +env: + DLLLOADER_TAG: "0.1.3" + DLLLOADER_SHA256: "2157e70e7264064fb0651f1b10fb166926baa26d406578ec5ca19a4d5fa3daec" + jobs: build: runs-on: windows-latest @@ -18,10 +25,10 @@ jobs: config: [ Release, Debug ] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: Add MSBuild to PATH - uses: microsoft/setup-msbuild@v2 + uses: microsoft/setup-msbuild@30375c66a4eea26614e0d39710365f22f8b0af57 # v3 - name: Configure (CMake, x86) run: cmake -S . -B build -A Win32 -T v142 @@ -29,7 +36,7 @@ jobs: - name: Build run: cmake --build build --config ${{ matrix.config }} --parallel - - name: Locate artifact + - name: Locate artifacts id: locate shell: pwsh run: | @@ -37,37 +44,118 @@ jobs: Where-Object { $_.FullName -match "\\${{ matrix.config }}\\" } | Select-Object -First 1 if (-not $dll) { Write-Error "Charon.dll not found"; exit 1 } + $pdb = Get-ChildItem -Path . -Recurse -Filter Charon.pdb | + Where-Object { $_.FullName -match "\\${{ matrix.config }}\\" } | + Select-Object -First 1 "dll_path=$($dll.FullName)" | Out-File -FilePath $env:GITHUB_OUTPUT -Append + if ($pdb) { + "pdb_path=$($pdb.FullName)" | Out-File -FilePath $env:GITHUB_OUTPUT -Append + Write-Host "PDB: $($pdb.FullName) ($([math]::Round($pdb.Length/1KB,1)) KB)" + } Write-Host "Built: $($dll.FullName) ($([math]::Round($dll.Length/1KB,1)) KB)" - - name: Upload artifact - uses: actions/upload-artifact@v4 + - name: Upload DLL artifact + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: name: Charon-${{ matrix.config }}-x86 path: ${{ steps.locate.outputs.dll_path }} if-no-files-found: error + - name: Upload PDB artifact + if: steps.locate.outputs.pdb_path != '' + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + with: + name: Charon-${{ matrix.config }}-x86-symbols + path: ${{ steps.locate.outputs.pdb_path }} + if-no-files-found: warn + release: needs: build if: startsWith(github.ref, 'refs/tags/v') runs-on: ubuntu-latest permissions: - contents: write + contents: write # publish release + id-token: write # sigstore OIDC for provenance + attestations: write # store the attestation steps: + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + - name: Download Release DLL - uses: actions/download-artifact@v4 + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 with: name: Charon-Release-x86 - path: release + path: stage/dll + + - name: Download Release symbols + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 + with: + name: Charon-Release-x86-symbols + path: stage/pdb + + - name: Fetch DLLLoader (dbghelp.dll) and verify hash + run: | + set -euo pipefail + mkdir -p stage/loader + url="https://github.com/Nishimura-Katsuo/DLLLoader/releases/download/${DLLLOADER_TAG}/dbghelp.dll" + curl -fSL --retry 3 --retry-delay 2 -o stage/loader/dbghelp.dll "$url" + echo "${DLLLOADER_SHA256} stage/loader/dbghelp.dll" | sha256sum -c - - - name: Rename for release + - name: Assemble release bundle + id: bundle run: | - mv release/Charon.dll "release/Charon-${{ github.ref_name }}-x86.dll" - ls -la release/ + set -euo pipefail + tag="${GITHUB_REF_NAME}" + stem="Charon-${tag}-x86" + dist="dist/${stem}" + mkdir -p "${dist}" dist + + cp stage/dll/Charon.dll "${dist}/Charon.dll" + cp stage/loader/dbghelp.dll "${dist}/dbghelp.dll" + cp readme.md "${dist}/readme.md" + + ( cd "${dist}" && sha256sum Charon.dll dbghelp.dll > SHA256SUMS.txt ) + + ( cd dist && zip -r "${stem}.zip" "${stem}" ) + + cp stage/pdb/Charon.pdb "dist/${stem}.pdb" + + ( cd dist && sha256sum "${stem}.zip" "${stem}.pdb" > "${stem}.SHA256SUMS.txt" ) + + ls -la dist/ + { + echo "stem=${stem}" + echo "zip=dist/${stem}.zip" + echo "pdb=dist/${stem}.pdb" + echo "sums=dist/${stem}.SHA256SUMS.txt" + } >> "$GITHUB_OUTPUT" + + - name: Attest build provenance + uses: actions/attest-build-provenance@a2bbfa25375fe432b6a289bc6b6cd05ecd0c4c32 # v4.1.0 + with: + subject-path: | + ${{ steps.bundle.outputs.zip }} + ${{ steps.bundle.outputs.pdb }} - name: Publish GitHub Release - uses: softprops/action-gh-release@v2 + uses: softprops/action-gh-release@b4309332981a82ec1c5618f44dd2e27cc8bfbfda # v3.0.0 with: - files: release/*.dll + files: | + ${{ steps.bundle.outputs.zip }} + ${{ steps.bundle.outputs.pdb }} + ${{ steps.bundle.outputs.sums }} generate_release_notes: true + body: | + ## Install + 1. Unzip `${{ steps.bundle.outputs.stem }}.zip` into your Diablo 2 directory (where `Game.exe` is). + 2. Make a shortcut to `Game.exe` and append `-loaddll Charon.dll` to the target line. + 3. `dbghelp.dll` is the DLLLoader (pinned to upstream `${{ env.DLLLOADER_TAG }}`); it's what gets side-loaded by `Game.exe`. + + Do not run alongside other Diablo 2 modifications. + + ## Verify + - SHA256 sums of release assets are in `${{ steps.bundle.outputs.stem }}.SHA256SUMS.txt`. + - The `.zip` and `.pdb` carry a Sigstore-signed build provenance attestation. Verify with: + ``` + gh attestation verify ${{ steps.bundle.outputs.stem }}.zip --repo ${{ github.repository }} + ```