diff --git a/CLAUDE.md b/CLAUDE.md index 025c11f3..47a628ca 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 9803b98d..01416d9a 100644 --- a/src/CLIPipeline.cpp +++ b/src/CLIPipeline.cpp @@ -1522,10 +1522,18 @@ 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"); + 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) @@ -1579,6 +1587,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); @@ -1586,9 +1595,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 (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")) @@ -1596,7 +1610,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(); } @@ -1651,6 +1665,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