Skip to content

Commit 2c73b8d

Browse files
committed
feat(tracking): honor DO_NOT_TRACK and COMFY_NO_TELEMETRY env vars
Short-circuits both the consent prompt and the per-event dispatch when either variable is set to a non-empty, non-zero value, per the consoledonottrack.com spec. Suppresses the non-TTY auto-enable path so no anonymous user_id is persisted before consent. Signed-off-by: Alexander Piskun <bigcat88@icloud.com>
1 parent 56ada5b commit 2c73b8d

2 files changed

Lines changed: 122 additions & 0 deletions

File tree

comfy_cli/tracking.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,21 @@
5454
_session_only_tracking = False
5555

5656

57+
def _telemetry_disabled_by_env() -> bool:
58+
"""Return True if telemetry is suppressed via environment variable.
59+
60+
Honors the cross-tool ``DO_NOT_TRACK`` convention
61+
(https://consoledonottrack.com/) and the project-specific
62+
``COMFY_NO_TELEMETRY``. Per the spec, any value other than empty or
63+
``"0"`` opts out.
64+
"""
65+
for name in ("DO_NOT_TRACK", "COMFY_NO_TELEMETRY"):
66+
val = os.environ.get(name, "")
67+
if val and val != "0":
68+
return True
69+
return False
70+
71+
5772
class TelemetryProvider(Protocol):
5873
enabled: bool
5974

@@ -136,6 +151,8 @@ def track_event(event_name: str, properties: Any = None, *, mixpanel_name: str |
136151
``mixpanel_name``, if supplied, overrides the event name on the Mixpanel pipe only — used to keep
137152
legacy Mixpanel event names while PostHog receives the canonical name.
138153
"""
154+
if _telemetry_disabled_by_env():
155+
return
139156
if properties is None:
140157
properties = {}
141158
logging.debug(f"tracking event called with event_name: {event_name} and properties: {properties}")
@@ -187,6 +204,13 @@ def wrapper(*args, **kwargs):
187204
def prompt_tracking_consent(skip_prompt: bool = False, default_value: bool = False):
188205
global _session_only_tracking, user_id
189206

207+
# Env-var opt-out short-circuits everything below: no prompt, no
208+
# auto-enable in non-TTY, no user_id persistence. Per-process only —
209+
# the on-disk consent flag is left untouched so a later run without
210+
# the env var still gets the normal prompt path.
211+
if _telemetry_disabled_by_env():
212+
return
213+
190214
if _session_only_tracking:
191215
return
192216

tests/comfy_cli/test_tracking.py

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -280,3 +280,101 @@ def test_session_only_is_idempotent(self, tracking_module):
280280
first_user_id = tracking_module.user_id
281281
tracking_module.prompt_tracking_consent()
282282
assert tracking_module.user_id == first_user_id
283+
284+
285+
class TestEnvVarOptOut:
286+
@pytest.mark.parametrize("env_var", ["DO_NOT_TRACK", "COMFY_NO_TELEMETRY"])
287+
def test_env_var_blocks_track_event_even_when_config_enabled(self, tracking_module, monkeypatch, env_var):
288+
tracking_module.config_manager.set(constants.CONFIG_KEY_ENABLE_TRACKING, "True")
289+
monkeypatch.setenv(env_var, "1")
290+
tracking_module.track_event("some_event", {"k": "v"})
291+
tracking_module.provider.track.assert_not_called()
292+
293+
@pytest.mark.parametrize("env_var", ["DO_NOT_TRACK", "COMFY_NO_TELEMETRY"])
294+
def test_env_var_blocks_track_event_under_session_only(self, tracking_module, monkeypatch, env_var):
295+
monkeypatch.setenv(env_var, "1")
296+
with patch.object(tracking_module, "_session_only_tracking", True):
297+
tracking_module.track_event("some_event")
298+
tracking_module.provider.track.assert_not_called()
299+
300+
@pytest.mark.parametrize("falsy", ["", "0"])
301+
def test_falsy_values_do_not_block(self, tracking_module, monkeypatch, falsy):
302+
tracking_module.config_manager.set(constants.CONFIG_KEY_ENABLE_TRACKING, "True")
303+
monkeypatch.setenv("DO_NOT_TRACK", falsy)
304+
monkeypatch.setenv("COMFY_NO_TELEMETRY", falsy)
305+
tracking_module.track_event("some_event")
306+
tracking_module.provider.track.assert_called_once()
307+
308+
@pytest.mark.parametrize("env_var", ["DO_NOT_TRACK", "COMFY_NO_TELEMETRY"])
309+
def test_env_var_short_circuits_consent_prompt(self, tracking_module, monkeypatch, env_var):
310+
monkeypatch.setenv(env_var, "1")
311+
with (
312+
patch.object(tracking_module.sys.stdin, "isatty", return_value=True),
313+
patch.object(tracking_module.sys.stdout, "isatty", return_value=True),
314+
patch.object(tracking_module.ui, "prompt_confirm_action") as mock_prompt,
315+
):
316+
tracking_module.prompt_tracking_consent()
317+
mock_prompt.assert_not_called()
318+
assert tracking_module.config_manager.get_bool(constants.CONFIG_KEY_ENABLE_TRACKING) is None
319+
320+
@pytest.mark.parametrize("env_var", ["DO_NOT_TRACK", "COMFY_NO_TELEMETRY"])
321+
def test_env_var_blocks_non_tty_auto_enable_and_user_id_persist(self, tracking_module, monkeypatch, env_var):
322+
# Reporter's core concern (issue #462): in CI/Docker the non-TTY
323+
# branch silently persisted a UUID. Env var must skip that path.
324+
monkeypatch.setenv(env_var, "1")
325+
with (
326+
patch.object(tracking_module.sys.stdin, "isatty", return_value=False),
327+
patch.object(tracking_module.sys.stdout, "isatty", return_value=False),
328+
):
329+
tracking_module.prompt_tracking_consent()
330+
assert tracking_module._session_only_tracking is False
331+
assert tracking_module.config_manager.get(constants.CONFIG_KEY_USER_ID) is None
332+
333+
def test_env_var_does_not_overwrite_existing_consent(self, tracking_module, monkeypatch):
334+
# On-disk consent flag must survive an env-var-suppressed run so a
335+
# subsequent invocation without the env var keeps the user's choice.
336+
tracking_module.config_manager.set(constants.CONFIG_KEY_ENABLE_TRACKING, "True")
337+
monkeypatch.setenv("DO_NOT_TRACK", "1")
338+
tracking_module.prompt_tracking_consent()
339+
assert tracking_module.config_manager.get_bool(constants.CONFIG_KEY_ENABLE_TRACKING) is True
340+
341+
342+
class TestTelemetryDisabledByEnvHelper:
343+
@pytest.fixture(autouse=True)
344+
def _clear_both(self, monkeypatch):
345+
monkeypatch.delenv("DO_NOT_TRACK", raising=False)
346+
monkeypatch.delenv("COMFY_NO_TELEMETRY", raising=False)
347+
348+
def test_unset_returns_false(self, tracking_module):
349+
import comfy_cli.tracking as tm
350+
351+
assert tm._telemetry_disabled_by_env() is False
352+
353+
@pytest.mark.parametrize("env_var", ["DO_NOT_TRACK", "COMFY_NO_TELEMETRY"])
354+
@pytest.mark.parametrize(
355+
"value,expected",
356+
[
357+
# consoledonottrack.com spec: empty or "0" allows tracking; anything else opts out.
358+
("", False),
359+
("0", False),
360+
("1", True),
361+
("true", True),
362+
("yes", True),
363+
("00", True),
364+
("false", True),
365+
],
366+
)
367+
def test_value_semantics(self, tracking_module, monkeypatch, env_var, value, expected):
368+
import comfy_cli.tracking as tm
369+
370+
monkeypatch.setenv(env_var, value)
371+
assert tm._telemetry_disabled_by_env() is expected
372+
373+
def test_either_var_alone_is_sufficient(self, tracking_module, monkeypatch):
374+
import comfy_cli.tracking as tm
375+
376+
monkeypatch.setenv("COMFY_NO_TELEMETRY", "1")
377+
assert tm._telemetry_disabled_by_env() is True
378+
monkeypatch.delenv("COMFY_NO_TELEMETRY")
379+
monkeypatch.setenv("DO_NOT_TRACK", "1")
380+
assert tm._telemetry_disabled_by_env() is True

0 commit comments

Comments
 (0)