diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index eb736fb947..02c952e48c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -12,6 +12,9 @@ permissions: id-token: write contents: read +env: + DO_NOT_TRACK: "1" + jobs: setup: strategy: diff --git a/CHANGELOG.md b/CHANGELOG.md index 44718af363..3e971aee40 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,7 +1,9 @@ -## 0.25.3-dev0 +## 0.26.0 ### Fixes +- **Restore default-on library-load telemetry**: The lightweight library-load ping once again runs by default when `unstructured` is imported. Set either `DO_NOT_TRACK` or `SCARF_NO_ANALYTICS` to any non-empty value (after trimming whitespace) before import to disable it; empty and whitespace-only values retain the default behavior. This removes the `UNSTRUCTURED_TELEMETRY_ENABLED` explicit opt-in gate introduced by #4281 while preserving the existing endpoint and payload. The best-effort `nvidia-smi` GPU probe now has a one-second timeout, and GPU-probe and request failures remain non-fatal. + - **Stop the `GLOBAL_WORKING_DIR` tests from disturbing other pytest-xdist workers**: test-only change, no library behavior changes. The two tests exercising `GLOBAL_WORKING_DIR_ENABLED` now redirect the working dir to a private `tmp_path` and restore `tempfile.tempdir` unconditionally, rather than moving the shared pgid-keyed directory aside mid-run and leaving the worker's `tempfile.tempdir` pointed at it. That shared path made `test_dockerfile` fail intermittently, with an unrelated test dying inside `tempfile`. ## 0.25.2 diff --git a/Makefile b/Makefile index 2c82270587..70a0601a1f 100644 --- a/Makefile +++ b/Makefile @@ -160,6 +160,7 @@ docker-test: -v ${CURRENT_DIR}/test_unstructured:/home/notebook-user/test_unstructured \ -v ${CURRENT_DIR}/test_unstructured_ingest:/home/notebook-user/test_unstructured_ingest \ $(if $(wildcard uns_test_env_file),--env-file uns_test_env_file,) \ + --env DO_NOT_TRACK=1 \ $(DOCKER_IMAGE) \ bash -c "uv sync --locked --all-extras --group test --no-install-project && \ CI=$(CI) \ diff --git a/README.md b/README.md index 9bb939b7a3..0a97be753d 100644 --- a/README.md +++ b/README.md @@ -290,4 +290,4 @@ Encountered a bug? Please create a new [GitHub issue](https://github.com/Unstruc ## :chart_with_upwards_trend: Analytics -Telemetry is **off by default**. To opt in, set `UNSTRUCTURED_TELEMETRY_ENABLED=true` (or `=1`) before importing `unstructured`. To opt out, set `DO_NOT_TRACK` or `SCARF_NO_ANALYTICS` to any non-empty value (e.g. `true`, `1`, `yes`, `false`, `0`—any non-empty string opts out); opt-out takes precedence. Unset the variable or leave it empty if you do not want to opt out. See our [Privacy Policy](https://unstructured.io/privacy-policy). +Unstructured sends a lightweight library-load analytics ping by default when it is imported. To opt out before importing `unstructured`, set either `DO_NOT_TRACK` or `SCARF_NO_ANALYTICS` to any non-empty value after trimming whitespace (for example, `true`, `1`, `yes`, `false`, or `0`); either variable disables the ping. Unset the variables or leave them empty or whitespace-only to retain the default behavior. See our [Privacy Policy](https://unstructured.io/privacy-policy). diff --git a/conftest.py b/conftest.py new file mode 100644 index 0000000000..073b13e646 --- /dev/null +++ b/conftest.py @@ -0,0 +1,6 @@ +"""Global test configuration.""" + +import os + +# Package import telemetry is default-on; force ordinary test collection to opt out. +os.environ["DO_NOT_TRACK"] = "1" diff --git a/scripts/image/test-outbound-connectivity.sh b/scripts/image/test-outbound-connectivity.sh index d2e33a37bc..4bd9da7375 100755 --- a/scripts/image/test-outbound-connectivity.sh +++ b/scripts/image/test-outbound-connectivity.sh @@ -61,14 +61,12 @@ fi # ---------- scenario‑specific settings -------------------------------- DO_NOT_TRACK="" -UNSTRUCTURED_TELEMETRY_ENABLED="" HF_HUB_OFFLINE="" REMOVE_CACHE=0 case "$SCENARIO" in baseline) ;; missing-models) REMOVE_CACHE=1 ;; analytics-online-only) - UNSTRUCTURED_TELEMETRY_ENABLED=1 HF_HUB_OFFLINE=1 ;; offline) @@ -93,7 +91,6 @@ CID=$(docker run -d --rm --name "sut_${SCENARIO}" \ --network "$NET" \ --cap-add NET_RAW --cap-add NET_ADMIN \ -e DO_NOT_TRACK="$DO_NOT_TRACK" \ - -e UNSTRUCTURED_TELEMETRY_ENABLED="$UNSTRUCTURED_TELEMETRY_ENABLED" \ -e HF_HUB_OFFLINE="$HF_HUB_OFFLINE" \ --entrypoint /bin/sh "$IMAGE" -c "sleep infinity") echo "Container: $CID (scenario $SCENARIO)" @@ -132,7 +129,7 @@ fi docker exec -i -e PYTHONUNBUFFERED=1 "$CID" python - < None: - """Run the analytics ping if enabled by env. Best-effort and non-fatal.""" + """Run the library-load analytics ping unless opted out. Best-effort and non-fatal.""" scarf_analytics() diff --git a/unstructured/utils.py b/unstructured/utils.py index 7c0efcaf6a..ba87f63c01 100644 --- a/unstructured/utils.py +++ b/unstructured/utils.py @@ -177,6 +177,9 @@ def only(it: Iterable[Any]) -> Any: return out +_NVIDIA_SMI_TIMEOUT_SECONDS = 1.0 + + def _telemetry_opt_out() -> bool: """True if telemetry should be disabled via env. @@ -188,27 +191,25 @@ def _telemetry_opt_out() -> bool: ) -def _telemetry_opt_in() -> bool: - """True if telemetry is explicitly enabled via env. Only 'true' and '1' opt in.""" - return (os.getenv("UNSTRUCTURED_TELEMETRY_ENABLED") or "").strip().lower() in ( - "true", - "1", - ) - - def scarf_analytics(): - """Send a lightweight analytics ping. Off by default. + """Send a lightweight library-load analytics ping unless it is opted out. - Set UNSTRUCTURED_TELEMETRY_ENABLED=true to opt in. Opt-out env vars (DO_NOT_TRACK, SCARF_NO_ANALYTICS): any non-empty value opts out. """ - if _telemetry_opt_out() or not _telemetry_opt_in(): + if _telemetry_opt_out(): return try: - subprocess.check_output(["nvidia-smi"], stderr=subprocess.DEVNULL) + subprocess.run( + ["nvidia-smi"], + stdin=subprocess.DEVNULL, + stdout=subprocess.DEVNULL, + stderr=subprocess.DEVNULL, + check=True, + timeout=_NVIDIA_SMI_TIMEOUT_SECONDS, + ) gpu_present = True - except (OSError, subprocess.CalledProcessError): + except (OSError, subprocess.CalledProcessError, subprocess.TimeoutExpired): gpu_present = False python_version = ".".join(platform.python_version().split(".")[:2])