From 9d085c0e34f70a630adb6c262747071b1fae0e36 Mon Sep 17 00:00:00 2001 From: Fernando Date: Sat, 15 Aug 2026 16:41:57 -0400 Subject: [PATCH 1/2] fix(telemetry): stop the unit-test suite from sending Sentry events MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Sentry showed a steady stream of 'cli.not-a-command' transactions — the literal fixture string from CLIPipelineRun.UnknownCommand. The test drives the real CLIPipeline::run, and on fresh CI runners the first-launch logic auto-enabled telemetry and sent REAL transactions to production every run. - new QTMESH_NO_TELEMETRY env opt-out: SESSION-ONLY (no QSettings write, no 'stored permanently' notice) for CI/containers/tests, distinct from the persisting --no-telemetry flag - the unit-test main sets it unconditionally, so the suite can never phone home regardless of runner state (also fixes a fork-death-test SIGSEGV the QSettings write path triggered) - unknown-command breadcrumb carries the sanitized attempted command so genuine user typos remain minable for alias suggestions (transaction name 'cli.' already carried it) Co-Authored-By: Claude Fable 5 --- CLAUDE.md | 2 +- src/CLIPipeline.cpp | 21 +++++++++++++++++++-- src/test_main.cpp | 7 +++++++ 3 files changed, 27 insertions(+), 3 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index 271c63db..09a87054 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -193,7 +193,7 @@ qtmesh ps1 dump-vram game.cue --bios scph1001.bin --frames 300 -o vram.png # sn # xvfb-run -a qtmesh ps1 capture game.cue --bios scph1001.bin -o out.gltf ``` -CLI mode is activated by: (1) invoking via the `qtmesh` symlink, (2) passing `--cli`, or (3) using a recognized subcommand (`info`, `fix`, `convert`, `anim`, `validate`, `lod`, `pose`, `turntable`, `isometric`, `scan`, `material`, `hdri`, `light`, `pack-textures`, `normal-from-height`, `atlas`, `atlas-apply`, `memory`, `analyze`, `vertex-cache`, `decimate`, `optimize`, `uv`, `retopo`, `skin`, `rig`, `facerig`, `segment`, `generate3d`, `mocap`, `ps1`, `cloud`) as the first argument. Use `--verbose` to see Ogre/engine debug output. Use `--no-telemetry` to permanently opt out of anonymous usage data collection. +CLI mode is activated by: (1) invoking via the `qtmesh` symlink, (2) passing `--cli`, or (3) using a recognized subcommand (`info`, `fix`, `convert`, `anim`, `validate`, `lod`, `pose`, `turntable`, `isometric`, `scan`, `material`, `hdri`, `light`, `pack-textures`, `normal-from-height`, `atlas`, `atlas-apply`, `memory`, `analyze`, `vertex-cache`, `decimate`, `optimize`, `uv`, `retopo`, `skin`, `rig`, `facerig`, `segment`, `generate3d`, `mocap`, `ps1`, `cloud`) as the first argument. Use `--verbose` to see Ogre/engine debug output. Use `--no-telemetry` to permanently opt out of anonymous usage data collection (the `QTMESH_NO_TELEMETRY` env var opts out per-process without persisting — CI/containers/tests; the unit-test main sets it so the suite never phones home). If Xcode SDK is updated, clear CMake cache (`rm build_local/CMakeCache.txt`) and reconfigure. diff --git a/src/CLIPipeline.cpp b/src/CLIPipeline.cpp index 83921b5b..30267a80 100644 --- a/src/CLIPipeline.cpp +++ b/src/CLIPipeline.cpp @@ -1519,6 +1519,15 @@ QString CLIPipeline::formatMeshInfoJson(const MeshInfo& info) int CLIPipeline::run(int argc, char* argv[]) { // Pre-scan for --verbose and --no-telemetry before anything else + // QTMESH_NO_TELEMETRY: SESSION-ONLY environment opt-out (CI / unit tests / + // containers) — suppresses all telemetry for this process WITHOUT + // persisting a preference or printing the opt-out notice. Without it the + // first-launch auto-enable made the TEST SUITE send real Sentry + // transactions from CI — e.g. the CLIPipelineRun.UnknownCommand fixture + // showed up in production telemetry as "cli.not-a-command". + const bool envNoTelemetry = qEnvironmentVariableIsSet("QTMESH_NO_TELEMETRY"); + if (envNoTelemetry) + s_noTelemetry = true; for (int i = 1; i < argc; ++i) { QString arg(argv[i]); if (arg == "--verbose") s_verbose = true; @@ -1583,7 +1592,10 @@ int CLIPipeline::run(int argc, char* argv[]) // On first run (no stored preference), show a one-time notice and enable. // In ephemeral environments (Docker), QTMESH_NO_TELEMETRY_NOTICE=1 // suppresses the notice to avoid printing it on every container run. - if (s_noTelemetry) { + if (envNoTelemetry) { + // Session-only: no QSettings write, no notice — the init below is + // simply skipped for this process. + } else if (s_noTelemetry) { SentryReporter::setEnabled(false); err() << "Telemetry disabled. This preference is stored permanently." << Qt::endl; } else if (SentryReporter::isFirstLaunch()) { @@ -1593,7 +1605,7 @@ int CLIPipeline::run(int argc, char* argv[]) "Use --no-telemetry to disable." << Qt::endl; } - if (SentryReporter::isEnabled()) { + if (!s_noTelemetry && SentryReporter::isEnabled()) { SentryReporter::configureSession(QStringLiteral("cli")); SentryReporter::initialize(); } @@ -1648,6 +1660,11 @@ int CLIPipeline::run(int argc, char* argv[]) if (rc < 0) { err() << "Error: Unknown command '" << cmd << "'" << Qt::endl; + // Keep the attempted command visible in telemetry (sanitized — path/ + // file-looking tokens are redacted) so typo patterns can inform + // aliases/suggestions. The transaction name carries it too. + SentryReporter::addBreadcrumb("cli", + QString("Unknown command: %1").arg(cmd)); printUsage(); rc = 2; } diff --git a/src/test_main.cpp b/src/test_main.cpp index d315f1bf..97174869 100644 --- a/src/test_main.cpp +++ b/src/test_main.cpp @@ -109,6 +109,13 @@ int main(int argc, char **argv) qputenv(guard, "1"); } + // Never send telemetry from the test suite. Without this, tests that + // drive CLIPipeline::run (e.g. CLIPipelineRun.UnknownCommand) hit the + // first-launch auto-enable on fresh CI runners and sent REAL Sentry + // transactions — the production "cli.not-a-command" noise. + if (!qEnvironmentVariableIsSet("QTMESH_NO_TELEMETRY")) + qputenv("QTMESH_NO_TELEMETRY", "1"); + #ifdef ENABLE_MOCAP MocapCameraHints::ensureMultimediaBackendSafe(); #endif From 9bf5020fd7c8ced0818da069b63b2025afa587f5 Mon Sep 17 00:00:00 2001 From: Fernando Date: Sat, 15 Aug 2026 16:59:59 -0400 Subject: [PATCH 2/2] fix(telemetry): explicit --no-telemetry persists even under QTMESH_NO_TELEMETRY (review) Co-Authored-By: Claude Fable 5 --- src/CLIPipeline.cpp | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/src/CLIPipeline.cpp b/src/CLIPipeline.cpp index 30267a80..7a874c4a 100644 --- a/src/CLIPipeline.cpp +++ b/src/CLIPipeline.cpp @@ -1526,12 +1526,11 @@ int CLIPipeline::run(int argc, char* argv[]) // transactions from CI — e.g. the CLIPipelineRun.UnknownCommand fixture // showed up in production telemetry as "cli.not-a-command". const bool envNoTelemetry = qEnvironmentVariableIsSet("QTMESH_NO_TELEMETRY"); - if (envNoTelemetry) - s_noTelemetry = true; + bool flagNoTelemetry = false; for (int i = 1; i < argc; ++i) { QString arg(argv[i]); if (arg == "--verbose") s_verbose = true; - if (arg == "--no-telemetry") s_noTelemetry = true; + if (arg == "--no-telemetry") flagNoTelemetry = true; } // Find the subcommand (skip executable name and --cli flag) @@ -1585,6 +1584,7 @@ int CLIPipeline::run(int argc, char* argv[]) // --no-telemetry also suppresses gamification events at every call site // for this process — without this, operation notes inside the subcommands // would land in the persistent queue and flush on a later run (#796). + s_noTelemetry = envNoTelemetry || flagNoTelemetry; if (s_noTelemetry) GamificationManager::setEmissionSuspended(true); @@ -1592,12 +1592,14 @@ int CLIPipeline::run(int argc, char* argv[]) // On first run (no stored preference), show a one-time notice and enable. // In ephemeral environments (Docker), QTMESH_NO_TELEMETRY_NOTICE=1 // suppresses the notice to avoid printing it on every container run. - if (envNoTelemetry) { - // Session-only: no QSettings write, no notice — the init below is - // simply skipped for this process. - } else if (s_noTelemetry) { + if (flagNoTelemetry) { + // The explicit flag persists — even when the session env var is also + // set (the user asked for the permanent preference). SentryReporter::setEnabled(false); err() << "Telemetry disabled. This preference is stored permanently." << Qt::endl; + } else if (envNoTelemetry) { + // Session-only: no QSettings write, no notice — the init below is + // simply skipped for this process. } else if (SentryReporter::isFirstLaunch()) { SentryReporter::setEnabled(true); if (!qEnvironmentVariableIsSet("QTMESH_NO_TELEMETRY_NOTICE"))