mirror of
https://github.com/Cockatrice/Cockatrice.git
synced 2026-11-05 01:23:24 -08:00
* [CrashReporting] Installer log send offer on abort (inc 4) DSN parsed to CPACK_SENTRY_* (scheme-aware for http self-hosts) before include(CPack); new SendInstallerLog.ps1.in builds a byte-accurate Sentry envelope (128 KiB tail cap, X-Sentry-Auth, exit 0/1) with the abort reason read from the log file itself, so the NSIS side needs no string functions and quoting stays trivial. AbortInstall asks consent (attended installs only, silent path untouched, abort never swallowed). Empty/malformed DSN disables the offer; no secrets in repo. * [CrashReporting] Embed installer-log sender via a relative NSIS path The AbortInstall hook embedded SendInstallerLog.ps1 through the absolute CPACK_SENTRY_PS1_PATH, which carries CMake's forward slashes (D:/a/...). makensis does not treat a forward-slash drive path as absolute, so it resolved it relative to project.nsi, found nothing and aborted packaging on the Windows jobs of every branch in the stack. Embed it with the same '..\..\..\' relative path that already resolves NSIS.definitions.nsh: both are configure_file'd into the build root, three levels up from where CPack places project.nsi. Drop the now-unused CPACK_SENTRY_PS1_PATH cache variable. * [CrashReporting] Drop the installer /CRASHTEST hook, tidy the log sender Remove the /CRASHTEST switch and the matching COCKATRICE_SENTRY_DSN environment override on the installer abort path. With no deliberate in-installer trigger left, a package built without a baked DSN must never offer to post a user's log to an endpoint chosen by whatever happens to be in the environment, so the prompt is now gated on CPACK_SENTRY_ENABLED alone. This also retires the comment that claimed /S /R /CRASHTEST reaches the prompt: the switch was evaluated before /R set ReinstallMode, so that form never showed it. Alongside: correct the CPACK_SENTRY_RELEASE comment (PROJECT_VERSION keeps its "-<label>" on Development tags where CPACK_PACKAGE_VERSION does not), delete the unused CPACK_SENTRY_HOST/CPACK_SENTRY_PROJECT_ID cache vars, document the DSN regex's path-prefix fallback, log a declined send, keep the uploaded installer log free of helper-injected newlines, and bound the post with -TimeoutSec/-NonInteractive. * [CrashReporting] Derive the installer DSN vars as plain variables Review nit: every CPACK_SENTRY_* value is recomputed from COCKATRICE_SENTRY_DSN on each configure, so CACHE ... FORCE bought nothing. Set empty defaults once, override them in the match branch, and drop the duplicated else() copies. Still CPACK_-prefixed, so include(CPack) still snapshots them into CPackConfig.cmake. * [CrashReporting] Gate the installer log send at build time Review nit: test CPACK_SENTRY_ENABLED with the NSIS preprocessor instead of copying it into $SentryEnabled and branching at runtime, so a package built without a DSN compiles the prompt out rather than carrying dead code. The configure_file that produces the helper is gated the same way, so a build without reporting neither references nor embeds it. * [CrashReporting] Disclose the Windows user name in the send prompt Review nit: say that the log can include the Windows user name (a real part of what gets uploaded) and drop the "No account needed." clause, which answered a question the prompt never raised. * [CrashReporting] Show progress while the installer log is sent Review nit: DetailPrint a line before nsExec runs, so the details pane says something while the send is in flight instead of staying blank for up to the 60 s /TIMEOUT. * [CrashReporting] Rename the Sentry event local in the installer helper Review nit: $Event is a PowerShell automatic variable, so name the local $sentryEvent instead. Also drop $firstLine: Select-String returns the matching line alone, so the reason is already one physical line and the extra split added nothing. --------- Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
212 lines
11 KiB
CMake
212 lines
11 KiB
CMake
# Crash reporting via sentry-native (Sentry protocol, MIT).
|
|
# Included once from the root CMakeLists.txt, right after the sanitizer/coverage helpers.
|
|
#
|
|
# Inputs:
|
|
# WITH_CRASH_REPORTING -- build the client with the sentry-native SDK (default OFF, our release
|
|
# CI passes ON; packagers and from-source builds stay SDK-free until this
|
|
# ships something real, so offline package builds never fetch the SDK)
|
|
# COCKATRICE_SENTRY_DSN -- Sentry DSN baked into the build (default empty = SDK init skipped at runtime)
|
|
# COCKATRICE_SENTRY_TAG -- sentry-native release tag to vendor via FetchContent
|
|
# ENABLE_SANITIZERS -- forces the sentry backend to "none" (crashpad vs ASan conflict)
|
|
# Defines: cockatrice_target_enable_crash_reporting(<target>)
|
|
|
|
option(WITH_CRASH_REPORTING "Build with crash reporting via sentry-native" OFF)
|
|
set(COCKATRICE_SENTRY_DSN
|
|
""
|
|
CACHE STRING "Sentry DSN baked into the client; empty disables crash-report upload at runtime"
|
|
)
|
|
# Pinned stable release; tag verified at https://github.com/getsentry/sentry-native/tags.
|
|
set(COCKATRICE_SENTRY_TAG
|
|
"0.17.3"
|
|
CACHE STRING "sentry-native release tag to vendor via FetchContent"
|
|
)
|
|
|
|
# Wire the SDK (or the no-op fallback) into a client target. Always callable: with the
|
|
# option OFF the compile definitions below make crash_reporter.cpp compile to no-ops.
|
|
function(cockatrice_target_enable_crash_reporting _target)
|
|
if(WITH_CRASH_REPORTING AND TARGET sentry::sentry)
|
|
target_link_libraries(${_target} PRIVATE sentry::sentry)
|
|
target_compile_definitions(${_target} PRIVATE COCKATRICE_WITH_CRASH_REPORTING=1)
|
|
else()
|
|
target_compile_definitions(${_target} PRIVATE COCKATRICE_WITH_CRASH_REPORTING=0)
|
|
endif()
|
|
# Always baked in (possibly empty): main() prefers the COCKATRICE_SENTRY_DSN env var at runtime.
|
|
target_compile_definitions(${_target} PRIVATE "COCKATRICE_SENTRY_DSN=\"${COCKATRICE_SENTRY_DSN}\"")
|
|
# Sentry release for events from this target: PROJECT_VERSION (getversion.cmake ran before this
|
|
# file was included). Deliberately not PROJECT_VERSION_FRIENDLY / VERSION_STRING — that carries
|
|
# a build date, so every build of the same version would land in a different Sentry release.
|
|
# PROJECT_VERSION matches what the installer reports in its own log and what CPack derives the
|
|
# package version from, so client and installer events group under one release.
|
|
target_compile_definitions(${_target} PRIVATE "COCKATRICE_SENTRY_RELEASE=\"${PROJECT_VERSION}\"")
|
|
endfunction()
|
|
|
|
if(WITH_CRASH_REPORTING AND WITH_CLIENT)
|
|
include(FetchContent)
|
|
|
|
if(ENABLE_SANITIZERS)
|
|
# The crash handler cannot run under ASan/UBSan, so there is no backend to capture with.
|
|
set(_sentry_backend "none")
|
|
else()
|
|
# Breakpad on every OS (increment 3 probe, 2026-10-14): the crashpad sources
|
|
# do vendor friction-free (FetchContent inits all submodules recursively by
|
|
# default, verified populated in a local build tree), but the crashpad backend
|
|
# would need its out-of-process crashpad_handler binary installed next to the
|
|
# exe, into the macOS bundle, and through NSIS, plus handler-path wiring and
|
|
# notarization care. Breakpad is in-process, needs no extra binary and no
|
|
# packaging on any OS sentry-native builds, so simplicity wins.
|
|
set(_sentry_backend "breakpad")
|
|
endif()
|
|
# FORCE so toggling ENABLE_SANITIZERS on a reused build dir cannot leave a stale backend behind.
|
|
set(SENTRY_BACKEND
|
|
"${_sentry_backend}"
|
|
CACHE STRING "Crash handler backend for sentry-native" FORCE
|
|
)
|
|
unset(_sentry_backend)
|
|
|
|
if(NOT MSVC)
|
|
# Symbol upload needs debug info in Release binaries: the top-level flags build GCC
|
|
# Release as -s -O2 (stripped, no DWARF) and Clang/Apple Release as plain -O2 (no DWARF),
|
|
# both of which upload nothing useful. Keep -g and drop the strip flag while the SDK is
|
|
# on; the OFF default leaves local, distro and Debug builds untouched. MSVC already
|
|
# links /DEBUG with PDBs in release (see top-level CMakeLists.txt), so it needs nothing.
|
|
string(REGEX REPLACE "(^| )-s( |$)" "\\1\\2" CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE}")
|
|
if(NOT CMAKE_CXX_FLAGS_RELEASE MATCHES "(^| )-g( |$)")
|
|
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -g")
|
|
endif()
|
|
endif()
|
|
|
|
# No Qt integration: it runs find_package(Qt6) inside the vendored build, which
|
|
# cannot see our thin/aqt Qt layouts (macOS CI) under foreign toolchains, and its
|
|
# breadcrumbs could carry unredacted log lines into native crash events that bypass
|
|
# before_send. Our attached redacted log preview already carries the log context.
|
|
set(SENTRY_INTEGRATION_QT
|
|
OFF
|
|
CACHE BOOL "Qt log-message breadcrumbs for sentry-native (off: discovery + secret surface)" FORCE
|
|
)
|
|
# The transport stays at the SDK default (curl on Apple/Linux, winhttp on Windows).
|
|
set(SENTRY_BUILD_TESTS
|
|
OFF
|
|
CACHE BOOL "Build sentry-native tests" FORCE
|
|
)
|
|
set(SENTRY_BUILD_EXAMPLES
|
|
OFF
|
|
CACHE BOOL "Build sentry-native examples" FORCE
|
|
)
|
|
|
|
fetchcontent_declare(
|
|
sentry
|
|
GIT_REPOSITORY https://github.com/getsentry/sentry-native.git
|
|
GIT_TAG ${COCKATRICE_SENTRY_TAG}
|
|
GIT_SHALLOW TRUE
|
|
)
|
|
fetchcontent_makeavailable(sentry)
|
|
|
|
# Vendored sentry-native (and its breakpad/crashpad trees) warn under our -Wall -Wextra
|
|
# (e.g. breakpad's -Wmissing-field-initializers), and WARNING_AS_ERROR would fail the build
|
|
# on code we cannot fix. Those findings are not actionable for us, so opt the whole vendored
|
|
# tree out of warnings-as-errors while keeping the warnings themselves visible; our own
|
|
# targets keep -Werror. Interface libraries compile nothing, so they are skipped outright:
|
|
# an INTERFACE -Wno-error would propagate to our consumers and weaken their build.
|
|
function(_cockatrice_sentry_opt_out_of_werror _dir)
|
|
get_property(
|
|
_sentry_targets
|
|
DIRECTORY "${_dir}"
|
|
PROPERTY BUILDSYSTEM_TARGETS
|
|
)
|
|
foreach(_sentry_target IN LISTS _sentry_targets)
|
|
get_target_property(_sentry_type ${_sentry_target} TYPE)
|
|
if(_sentry_type STREQUAL "INTERFACE_LIBRARY")
|
|
continue()
|
|
elseif(_sentry_type MATCHES "LIBRARY|EXECUTABLE")
|
|
get_target_property(_sentry_imported ${_sentry_target} IMPORTED)
|
|
if(NOT _sentry_imported)
|
|
target_compile_options(${_sentry_target} PRIVATE -Wno-error)
|
|
endif()
|
|
endif()
|
|
endforeach()
|
|
get_property(
|
|
_sentry_subdirs
|
|
DIRECTORY "${_dir}"
|
|
PROPERTY SUBDIRECTORIES
|
|
)
|
|
foreach(_sentry_subdir IN LISTS _sentry_subdirs)
|
|
_cockatrice_sentry_opt_out_of_werror("${_sentry_subdir}")
|
|
endforeach()
|
|
endfunction()
|
|
if(NOT MSVC)
|
|
_cockatrice_sentry_opt_out_of_werror("${sentry_SOURCE_DIR}")
|
|
endif()
|
|
|
|
message(STATUS "Crash reporting: sentry-native ${COCKATRICE_SENTRY_TAG} (backend: ${SENTRY_BACKEND})")
|
|
else()
|
|
message(STATUS "Crash reporting: SDK not vendored "
|
|
"(WITH_CRASH_REPORTING=${WITH_CRASH_REPORTING}, WITH_CLIENT=${WITH_CLIENT})"
|
|
)
|
|
endif()
|
|
|
|
# Packaged release binaries must not ship the DWARF that the symbol upload needs: it is
|
|
# 80-90% of an unstripped binary and nobody downloads a 500 MB installer to get it. The
|
|
# build tree keeps -g (CI uploads symbols from there); the *packaged copy* is split at
|
|
# install time by a CPack install script, which extracts the debug info next to the staging
|
|
# dir and strips the binary. Only ELF (Linux/BSD) needs this: Windows links /DEBUG with
|
|
# separate PDBs, and macOS keeps its dSYM extracted from the bundle.
|
|
option(COCKATRICE_SPLIT_DEBUG_SYMBOLS "Strip DWARF out of packaged release binaries" ON)
|
|
set(COCKATRICE_DEBUG_SYMBOL_DIR
|
|
"${CMAKE_BINARY_DIR}/debug-symbols"
|
|
CACHE PATH "Where the packaged binaries' split debug information is written (kept out of the package)"
|
|
)
|
|
if(WITH_CRASH_REPORTING
|
|
AND WITH_CLIENT
|
|
AND COCKATRICE_SPLIT_DEBUG_SYMBOLS
|
|
AND CMAKE_BUILD_TYPE STREQUAL "Release"
|
|
AND UNIX
|
|
AND NOT APPLE
|
|
)
|
|
find_program(COCKATRICE_OBJCOPY NAMES objcopy)
|
|
find_program(COCKATRICE_STRIP_TOOL NAMES strip)
|
|
if(COCKATRICE_OBJCOPY AND COCKATRICE_STRIP_TOOL)
|
|
configure_file(
|
|
"${CMAKE_CURRENT_LIST_DIR}/SplitDebugSymbols.cmake.in" "${CMAKE_BINARY_DIR}/cmake/SplitDebugSymbols.cmake" @ONLY
|
|
)
|
|
# PRE_BUILD runs after CPack stages the install tree and before the package is produced.
|
|
# CPACK_INSTALL_SCRIPTS would run too early (staging is still empty); the script walks
|
|
# CPACK_TEMPORARY_DIRECTORY, never CMAKE_INSTALL_PREFIX, and refuses to run otherwise.
|
|
list(APPEND CPACK_PRE_BUILD_SCRIPTS "${CMAKE_BINARY_DIR}/cmake/SplitDebugSymbols.cmake")
|
|
message(STATUS "Crash reporting: packaged binaries stripped, debug info split to ${COCKATRICE_DEBUG_SYMBOL_DIR}")
|
|
else()
|
|
message(WARNING "COCKATRICE_SPLIT_DEBUG_SYMBOLS is ON but objcopy/strip were not found; "
|
|
"packaged release binaries will keep their debug information"
|
|
)
|
|
endif()
|
|
endif()
|
|
|
|
# Installer failure reporting (increment 4): parse COCKATRICE_SENTRY_DSN into the parts the Windows installer
|
|
# needs. These are CPACK_* so CPack snapshots them at include(CPack) time and substitutes them into
|
|
# cmake/NSIS.template.in at package time; on non-Windows they are inert (only the NSIS template consumes them).
|
|
# Set from COCKATRICE_SENTRY_DSN on every configure, so a changed or removed DSN can never leave a stale value
|
|
# behind. Plain variables, not CACHE: they only need to be in scope when include(CPack) later snapshots every
|
|
# CPACK_* variable into CPackConfig.cmake.
|
|
set(CPACK_SENTRY_ENABLED "0")
|
|
set(CPACK_SENTRY_KEY "")
|
|
set(CPACK_SENTRY_ENVELOPE_URL "")
|
|
set(CPACK_SENTRY_DSN_FULL "")
|
|
# Deliberately PROJECT_VERSION: that is what the client bakes as its Sentry release and what CI passes to
|
|
# `sentry-cli debug-files upload`, so installer and client events group under one release. It is not the
|
|
# CPACK_PACKAGE_VERSION the installer logs in its own log line — on Development tags PROJECT_VERSION carries a
|
|
# "-<label>" suffix (getversion.cmake) while CPACK_PACKAGE_VERSION is only MAJOR.MINOR.PATCH.
|
|
set(CPACK_SENTRY_RELEASE "${PROJECT_VERSION}")
|
|
|
|
# Canonical <scheme>://<key>@<host>/<project-id>. Deliberately unanchored, so a trailing slash still matches.
|
|
# A self-hosted DSN with a path prefix (e.g. GlitchTip behind a subpath) does not match and falls through to the
|
|
# disabled-and-warn branch instead of deriving a wrong endpoint from the tail of the path.
|
|
if(COCKATRICE_SENTRY_DSN MATCHES "(https?)://([^@]+)@([^/]+)/([0-9]+)")
|
|
set(CPACK_SENTRY_ENABLED "1")
|
|
set(CPACK_SENTRY_KEY "${CMAKE_MATCH_2}")
|
|
# Scheme comes from the DSN, not an assumption: self-hosted (GlitchTip) DSNs may be plain http.
|
|
set(CPACK_SENTRY_ENVELOPE_URL "${CMAKE_MATCH_1}://${CMAKE_MATCH_3}/api/${CMAKE_MATCH_4}/envelope/")
|
|
set(CPACK_SENTRY_DSN_FULL "${COCKATRICE_SENTRY_DSN}")
|
|
elseif(NOT COCKATRICE_SENTRY_DSN STREQUAL "")
|
|
message(WARNING "COCKATRICE_SENTRY_DSN is set but malformed "
|
|
"(expected <scheme>://<key>@<host>/<project-id>); installer log reporting disabled"
|
|
)
|
|
endif()
|