Skip to content

WT-1344 inject Plausible analytics for EU visitors#1502

Open
stephendherrera wants to merge 4 commits into
mainfrom
WT-1344--plausible
Open

WT-1344 inject Plausible analytics for EU visitors#1502
stephendherrera wants to merge 4 commits into
mainfrom
WT-1344--plausible

Conversation

@stephendherrera

Copy link
Copy Markdown
Contributor

One-line summary

Inject the Plausible analytics script for EU/consent-country visitors only, gated behind a plausible waffle switch.

Significant changes and points to review

  • EU-only gating is server-side. A new plausible_enabled(country_code) helper (misc.py) is the single source of truth — it returns true only when the plausible waffle switch is on, PLAUSIBLE_DOMAIN is configured, and the visitor is in a DATA_CONSENT_COUNTRIES (EU/EEA + CH + UK) country. Reused across all three base templates so non-EU visitors never receive the script or its data attributes.
  • Client script (plausible.es6.js) reads data-plausible-domain / data-plausible-src at call time, bails on DNT/GPC, defines the window.plausible queue stub, then injects the deferred script — following Plausible's documented install.
  • CSP (settings/init.py): the script host is added to script-src and connect-src only when PLAUSIBLE_DOMAIN is set. Note: assumes events post to the same host as the script (true for Plausible Cloud); a self-hosted instance ingesting on a separate host would need that added too.
  • Settings: PLAUSIBLE_DOMAIN (default empty → disabled) and PLAUSIBLE_SCRIPT_URL (default https://plausible.io/js/script.js). Demo env var added but commented out so demos stay dark by default.

Issue / Bugzilla link

WT-1344

Testing

  1. Automated: new Jasmine spec for the client script (DNT/GPC bail-out, queue stub, script injection, missing-attr guard) and pytest cases for the helper (EU on / non-EU off / switch off / no-domain off). Run npm run jasmine and make test.
  2. Manual (non-prod): set PLAUSIBLE_DOMAIN, create+enable the plausible waffle switch, then:
    • Load any page with ?geo=DE → confirm the Plausible <script> and data-plausible-* attributes are present in the <head>.
    • Load with ?geo=US → confirm the script and attributes are absent.
    • With ?geo=DE and DNT or GPC enabled in the browser → confirm the script does not load.

Load the Plausible script only for EU/consent countries, gated behind a
`plausible` waffle switch and a configured PLAUSIBLE_DOMAIN. The gating
rule lives in a single `plausible_enabled(country_code)` helper reused
across the base templates. Adds the Plausible host to the script-src and
connect-src CSP directives when configured, plus unit tests for the
helper and the client script.
@codecov

codecov Bot commented Jun 12, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 63.63636% with 4 lines in your changes missing coverage. Please review.
✅ Project coverage is 80.88%. Comparing base (bb6a977) to head (b78e386).
⚠️ Report is 8 commits behind head on main.

Files with missing lines Patch % Lines
springfield/settings/__init__.py 33.33% 4 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #1502      +/-   ##
==========================================
+ Coverage   79.38%   80.88%   +1.50%     
==========================================
  Files         154      147       -7     
  Lines       10582    10145     -437     
==========================================
- Hits         8400     8206     -194     
+ Misses       2182     1939     -243     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

GTM_CONTAINER_ID: "GTM-MSTZDDXN"
LOG_LEVEL: INFO
LOCAL_DB_UPDATE: "True"
# PLAUSIBLE_DOMAIN: "firefox.com"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🚗 No a full review, just passing through, but why did you comment it out? If default is disabled, maybe consider the var being empty/null.

Configuration should never live in comments. It creates ambiguity, breaks tooling that audits env vars, and makes the default behavior opaque. The codebase already has a documented default (empty string).

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can then also get rid of the noqa: F405.

@slightlyoffbeat

Copy link
Copy Markdown
Contributor

Took this for a spin locally

Verification

  • Brought up runserver with PLAUSIBLE_DOMAIN=firefox.com.
  • ?geo=DE (and FR/GB/CH/NO): data-plausible-domain, data-plausible-src, and the plausible bundle <script> all render in <head>
  • ?geo=US (and CA/JP): zero plausible markers on the page ✓
  • CSP header includes plausible.io in both script-src and connect-src
  • Compiled bundle contains the expected data-plausible-domain / data-plausible-src / data-domain / defer literals ✓
  • pytest -k plausible → 4/4 pass; npm run jasmine → 7/7 pass

Asks before merge

  1. Consolidate the four new helper tests into one @pytest.mark.parametrize. Suggested:

    @pytest.mark.parametrize(
        "country_code, domain, switch_on, expected",
        [
            ("DE", "firefox.com", True, True),
            ("US", "firefox.com", True, False),
            ("DE", "firefox.com", False, False),
            ("DE", "", True, False),
        ],
    )
    def test_plausible_enabled(country_code, domain, switch_on, expected):
        with override_settings(PLAUSIBLE_DOMAIN=domain), patch.object(misc, "switch", return_value=switch_on):
            assert misc.plausible_enabled(country_code) is expected
  2. +1 to @janriokrause on the cloudrun-demo.env.yaml line

Non-blocking, but worth resolving while it's cheap

  1. Queue stub is skipped on DNT/GPC. In plausible.es6.js, init() bails before defineQueueStub() when DNT or GPC is set, so window.plausible is never defined. Today that's fine (nothing calls it). The moment a follow-up PR adds window.plausible('Some Event', ...) somewhere, every EU + DNT visitor throws TypeError: window.plausible is not a function at that callsite. Plausible's own install docs define the stub unconditionally for exactly this reason. Two-line reorder:

    Plausible.init = () => {
        Plausible.defineQueueStub();
        if (gpcEnabled() || dntEnabled()) {
            return;
        }
        Plausible.loadScript();
    };

Happy to take this as a follow-up if you'd rather keep PR scope tight, but it's cheap to do now and matches the upstream install pattern.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants