Skip to content

Build

Build #312

Workflow file for this run

name: Build
on:
push:
branches: [main, master]
pull_request:
branches: [main, master]
workflow_dispatch:
inputs:
sentry_crash_test:
description: "After building, run --sentry-crash-test to send a real crash to Sentry (verifies symbolication)"
type: boolean
default: false
jobs:
build-linux:
# runs-on: ubuntu-20.04
runs-on: ubuntu-22.04
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
submodules: recursive
fetch-depth: 0
fetch-tags: true
- name: Install Qt6
uses: jurplel/install-qt-action@v4
with:
version: "6.7.0"
modules: "qtshadertools"
dir: "${{ github.workspace }}/Qt"
cache: true
- name: Install dependencies
run: |
sudo apt-get update
sudo apt-get install -y \
build-essential \
cmake \
libgl1-mesa-dev \
libxkbcommon-dev \
libvulkan-dev \
libxcb-cursor0 \
libxcb-shape0-dev \
libcurl4-openssl-dev
- name: Configure CMake
run: cmake -B build -G "Unix Makefiles" -DCMAKE_BUILD_TYPE=RelWithDebInfo -DSENTRY_BACKEND=crashpad -DTEXTURELAB_SENTRY_DSN="${{ secrets.SENTRY_DSN }}"
- name: Build
run: cmake --build build --target texturelab --parallel $(nproc)
- name: Upload debug symbols to Sentry
env:
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
SENTRY_ORG: ${{ secrets.SENTRY_ORG }}
SENTRY_PROJECT: ${{ secrets.SENTRY_PROJECT }}
run: |
curl -sL https://sentry.io/get-cli/ | bash
# Upload the FULL, unstripped binary. It contains debug info AND the
# .eh_frame unwind info (CFI) that Sentry needs to walk a minidump's
# stack. objcopy --only-keep-debug drops the unwind info, which left
# crashes unsymbolicated ("debug information files are missing").
sentry-cli debug-files upload --include-sources \
build/src/texturelab/texturelab
# Strip the shipped copy to shrink the AppImage. The GNU build-id
# (== Sentry Debug ID) survives stripping, so the DIF we just
# uploaded still matches the binary that ships and crashes.
strip build/src/texturelab/texturelab
- name: Sentry crash test (manual)
if: ${{ github.event_name == 'workflow_dispatch' && github.event.inputs.sentry_crash_test == 'true' }}
run: |
export QT_QPA_PLATFORM=offscreen
# Crashes before the main window opens; crashpad_handler (copied next to
# the binary at build time) uploads the minidump via the built-in DSN.
build/src/texturelab/texturelab --sentry-crash-test || true
echo "waiting for crashpad to upload the minidump..."
sleep 25
- name: Install LinuxDeploy
uses: miurahr/install-linuxdeploy-action@v1
with:
plugins: qt appimage
- name: Create AppImage
env:
APPIMAGE_EXTRACT_AND_RUN: 1
DEPLOY_STDCXX: 1
run: |
export QMAKE=${{ github.workspace }}/Qt/Qt/6.7.0/gcc_64/bin/qmake
export PATH=${{ github.workspace }}/Qt/Qt/6.7.0/gcc_64/bin:$PATH
export LD_LIBRARY_PATH=${{ github.workspace }}/Qt/Qt/6.7.0/gcc_64/lib:$LD_LIBRARY_PATH
# Create desktop file
cat > texturelab.desktop << 'EOF'
[Desktop Entry]
Type=Application
Name=TextureLab
Exec=texturelab
Icon=texturelab
Categories=Graphics;
EOF
# Create a placeholder icon (256x256 PNG with "TL" text)
cp src/icons/logo.png texturelab.png
# Bundle crashpad_handler alongside the main binary
mkdir -p AppDir/usr/bin
cp build/src/texturelab/crashpad_handler AppDir/usr/bin/ || \
cp build/_deps/sentry-build/crashpad_build/handler/crashpad_handler AppDir/usr/bin/
linuxdeploy-x86_64.AppImage \
--appdir AppDir \
--executable build/src/texturelab/texturelab \
--desktop-file texturelab.desktop \
--icon-file texturelab.png \
--plugin qt \
--output appimage
- name: Upload Linux artifact
uses: actions/upload-artifact@v4
with:
name: texturelab-linux
path: "*.AppImage"
build-windows:
runs-on: windows-2022
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
submodules: recursive
fetch-depth: 0
fetch-tags: true
- name: Install Qt6
uses: jurplel/install-qt-action@v4
with:
version: "6.7.0"
arch: "win64_msvc2019_64"
modules: "qtshadertools"
dir: "${{ github.workspace }}/Qt"
cache: true
- name: Configure CMake
run: cmake -B build -G "Visual Studio 17 2022" -A x64 -DSENTRY_BACKEND=crashpad -DTEXTURELAB_SENTRY_DSN="${{ secrets.SENTRY_DSN }}"
- name: Build
run: cmake --build build --target texturelab --config Release --parallel
- name: Upload debug symbols to Sentry
shell: pwsh
env:
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
SENTRY_ORG: ${{ secrets.SENTRY_ORG }}
SENTRY_PROJECT: ${{ secrets.SENTRY_PROJECT }}
run: |
Invoke-WebRequest -Uri "https://github.com/getsentry/sentry-cli/releases/latest/download/sentry-cli-Windows-x86_64.exe" -OutFile "sentry-cli.exe"
$exe = "build\src\texturelab\Release\texturelab.exe"
$pdb = "build\src\texturelab\Release\texturelab.pdb"
if (-not (Test-Path $pdb)) { Write-Error "texturelab.pdb not found — build produced no debug info; Sentry cannot symbolicate."; exit 1 }
# Log the Debug IDs. The exe's Debug ID (from its CodeView record) MUST
# be non-null and match the pdb, or crash minidumps stay unsymbolicated.
Write-Host "== exe Debug ID =="; .\sentry-cli.exe debug-files check $exe
Write-Host "== pdb Debug ID =="; .\sentry-cli.exe debug-files check $pdb
.\sentry-cli.exe debug-files upload --include-sources "build\src\texturelab\Release\"
- name: Deploy Qt dependencies
shell: pwsh
run: |
New-Item -ItemType Directory -Path deploy
Copy-Item "build\src\texturelab\Release\texturelab.exe" deploy\
& "${{ github.workspace }}\Qt\Qt\6.7.0\msvc2019_64\bin\windeployqt.exe" "deploy\texturelab.exe" --release --no-translations
$handler = @(
"build\src\texturelab\Release\crashpad_handler.exe",
"build\src\texturelab\crashpad_handler.exe",
"build\_deps\sentry-build\crashpad_build\handler\Release\crashpad_handler.exe"
) | Where-Object { Test-Path $_ } | Select-Object -First 1
if ($handler) { Copy-Item $handler deploy\ } else { Write-Warning "crashpad_handler.exe not found, skipping" }
- name: Sentry crash test (manual)
if: ${{ github.event_name == 'workflow_dispatch' && github.event.inputs.sentry_crash_test == 'true' }}
shell: pwsh
run: |
$env:QT_QPA_PLATFORM = "offscreen"
# Run the deployed bundle (Qt DLLs + crashpad_handler.exe alongside the
# exe). Crashes before the main window; crashpad uploads the minidump
# via the built-in DSN. Debug IDs match the PDB uploaded above.
$p = Start-Process -FilePath "deploy\texturelab.exe" -ArgumentList "--sentry-crash-test" -PassThru
if (-not $p.WaitForExit(60000)) { $p.Kill(); Write-Warning "timed out waiting for crash" }
else { Write-Host "app exited with code $($p.ExitCode) (crash expected)" }
Write-Host "waiting for crashpad to upload the minidump..."
Start-Sleep -Seconds 25
- name: Upload Windows artifact
uses: actions/upload-artifact@v4
with:
name: texturelab-windows
path: deploy/
build-macos:
runs-on: macos-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
submodules: recursive
fetch-depth: 0
fetch-tags: true
- name: Install Qt6
uses: jurplel/install-qt-action@v4
with:
version: "6.7.0"
modules: "qtshadertools"
dir: "${{ github.workspace }}/Qt"
cache: true
- name: Configure CMake
run: cmake -B build -G "Unix Makefiles" -DCMAKE_BUILD_TYPE=RelWithDebInfo -DTEXTURELAB_SENTRY_DSN="${{ secrets.SENTRY_DSN }}"
- name: Build
run: cmake --build build --target texturelab --parallel $(sysctl -n hw.ncpu)
- name: Generate dSYM and upload to Sentry
env:
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
SENTRY_ORG: ${{ secrets.SENTRY_ORG }}
SENTRY_PROJECT: ${{ secrets.SENTRY_PROJECT }}
run: |
dsymutil build/src/texturelab/texturelab.app/Contents/MacOS/texturelab \
-o texturelab.dSYM
strip build/src/texturelab/texturelab.app/Contents/MacOS/texturelab
curl -sL https://sentry.io/get-cli/ | bash
sentry-cli debug-files upload --include-sources texturelab.dSYM
- name: Upload macOS artifact
uses: actions/upload-artifact@v4
with:
name: texturelab-macos
path: build/src/texturelab/texturelab.app
deploy:
needs: [build-linux, build-windows, build-macos]
if: always()
runs-on: ubuntu-latest
steps:
- name: Download artifacts
if: ${{ needs.build-linux.result == 'success' || needs.build-windows.result == 'success' || needs.build-macos.result == 'success' }}
uses: actions/download-artifact@v4
with:
path: artifacts
- name: Zip and upload to S3
if: ${{ needs.build-linux.result == 'success' || needs.build-windows.result == 'success' || needs.build-macos.result == 'success' }}
env:
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
AWS_DEFAULT_REGION: us-east-2
SHA: ${{ github.sha }}
run: |
cd artifacts
if [ -d texturelab-linux ]; then zip -r ../linux-$SHA.zip texturelab-linux/; fi
if [ -d texturelab-windows ]; then zip -r ../windows-$SHA.zip texturelab-windows/; fi
if [ -d texturelab-macos ]; then zip -r ../macos-$SHA.zip texturelab-macos/; fi
cd ..
for f in linux-$SHA.zip windows-$SHA.zip macos-$SHA.zip; do
[ -f "$f" ] && aws s3 cp "$f" s3://texturelab-nightlies/
done
- name: Post to Discord
env:
DISCORD_WEBHOOK: ${{ secrets.DISCORD_WEBHOOK }}
BRANCH: ${{ github.ref_name }}
SHA: ${{ github.sha }}
RUN_ID: ${{ github.run_id }}
REPO: ${{ github.repository }}
LINUX_RESULT: ${{ needs.build-linux.result }}
WINDOWS_RESULT: ${{ needs.build-windows.result }}
MACOS_RESULT: ${{ needs.build-macos.result }}
run: |
SHORT_SHA="${SHA:0:7}"
RUN_URL="https://github.com/$REPO/actions/runs/$RUN_ID"
S3_BASE="https://texturelab-nightlies.s3.us-east-2.amazonaws.com"
status_emoji() {
case "$1" in
success) echo "✅" ;;
failure) echo "❌" ;;
cancelled) echo "⏹️" ;;
*) echo "⚠️" ;;
esac
}
download_line() {
local result="$1" label="$2" url="$3"
if [[ "$result" == "success" ]]; then
echo "$(status_emoji "$result") **$label** — [Download]($url)"
else
echo "$(status_emoji "$result") **$label** — build $result"
fi
}
if [[ "$LINUX_RESULT" == "success" && "$WINDOWS_RESULT" == "success" && "$MACOS_RESULT" == "success" ]]; then
TITLE="Build succeeded — $BRANCH @ $SHORT_SHA"
COLOR=3066993
else
TITLE="Build failed — $BRANCH @ $SHORT_SHA"
COLOR=15158332
fi
DESCRIPTION="$(download_line "$LINUX_RESULT" "Linux" "$S3_BASE/linux-$SHA.zip")\n$(download_line "$WINDOWS_RESULT" "Windows" "$S3_BASE/windows-$SHA.zip")\n$(download_line "$MACOS_RESULT" "macOS" "$S3_BASE/macos-$SHA.zip")\n\n[View run]($RUN_URL)"
curl -s -X POST "$DISCORD_WEBHOOK" \
-H "Content-Type: application/json" \
-d "{
\"embeds\": [{
\"title\": \"$TITLE\",
\"description\": \"$DESCRIPTION\",
\"color\": $COLOR,
\"url\": \"$RUN_URL\"
}]
}"