ci(docs): activate docs-site analytics wiring + privacy facts#456
Conversation
astro.config.mjs injects the Umami script only when UMAMI_WEBSITE_ID is set at build time, but the Build docs step had no env block — GitHub Actions does not auto-expose repo variables to run steps, so analytics never activated. Pass the public website id via vars.UMAMI_WEBSITE_ID. When the variable is unset (dev, forks), the script is omitted entirely.
Replaces the pre-launch placeholder with the metrics.fro.bot data posture: no raw IPs or personal identifiers stored, monthly-rotating one-way hash for visitor counting, approximate location derived locally with no third-party calls, Do-Not-Track visitors not recorded.
fro-bot
left a comment
There was a problem hiding this comment.
Verdict: PASS
Tight, well-scoped launch-mechanism PR. The workflow fix correctly addresses a real GitHub Actions gotcha (repo vars.* are not auto-exposed to run: steps), and the gating in astro.config.mjs:109 means an unset variable is a true no-op — safe to merge before the variable is configured. Privacy copy aligns with documented Umami defaults plus the data-do-not-track="true" config already in astro.config.mjs.
Blocking issues
None.
Non-blocking concerns
- Retention silently dropped from the privacy page. The previous heading was "Retention and IP handling" with a placeholder note that retention specifics would be added before launch. The new content (
docs/src/content/docs/privacy.mdx:40-42) renames the section to "IP addresses and identifiers" and removes the retention promise entirely without stating a retention period. If the livemetrics.fro.botinstance has a configured retention window (Umami defaults to indefinite unless data deletion is configured), a one-sentence statement — even "data is retained indefinitely in aggregate form" — would close the loop the prior copy opened. As written, a careful reader notices the topic was dropped rather than answered. - Workflow env scope.
UMAMI_WEBSITE_IDis scoped to the single "Build docs" step (.github/workflows/docs.yaml:81-84), which is correct and minimal. No action needed; flagging for awareness if a future step also needs it (e.g., a preview-build matrix), it will need its ownenv:block. - No
workflow_dispatchinput fallback. The PR description's activation plan calls for a manualworkflow_dispatchto deploy with the variable set. Sincevars.UMAMI_WEBSITE_IDis repo-wide, that already works — but if you ever want to deploy a one-off build with a different/empty id (e.g., to verify the unset path on the live deploy pipeline without unsetting the repo var), consider aworkflow_dispatchinput that overrides. Not needed for this PR.
Missing tests
None required. The PR description documents a manual two-mode build verification (script present/absent depending on env), which is the right test for this surface — there's no unit-testable seam here, and adding a workflow-lint test for a four-line env: block would be overkill. The astro config already gates correctly at astro.config.mjs:109.
Risk assessment: LOW
- Blast radius: docs site only. No plugin runtime, no published package, no config/tool/system.transform hook surface touched.
- Regression likelihood: very low. The workflow change is additive (
env:block on one step); whenvars.UMAMI_WEBSITE_IDis unset, the value is empty string,process.env.UMAMI_WEBSITE_IDis falsy, and the script array stays empty — identical to today's behavior. - Security exposure: none. The website id is public by design (ships in client HTML); using
varsrather thansecretsis correct. No new injection surface — the value flows into adata-website-idattribute, not into shell or HTML body. - Privacy/compliance: the new privacy copy makes stronger affirmative claims ("No raw IP addresses or personal identifiers are stored", "one-way hash that rotates monthly"). These match Umami's documented behavior, but they are now load-bearing statements about the live
metrics.fro.botinstance configuration. Worth a one-time confirmation that the instance is not configured to log raw IPs (e.g., via reverse-proxy access logs feeding into the same retention scope) before flipping the variable on.
Run Summary
| Field | Value |
|---|---|
| Event | pull_request |
| Repository | marcusrbrown/systematic |
| Run ID | 26670020365 |
| Cache | hit |
| Session | ses_1899a1992ffemiXq70W8BUW2ZP |
Summary
Wires up the final launch unit: makes the docs-site analytics actually activatable, and fills the privacy page with the real data-handling facts. This is the mechanism behind the public launch — it does not itself turn analytics on.
What's included
ci(docs): threadUMAMI_WEBSITE_IDinto the docs build.astro.config.mjsonly injects the Umami script whenUMAMI_WEBSITE_IDis set at build time, but the "Build docs" step had noenv:block — GitHub Actions doesn't auto-expose repo variables torun:steps, so the script never shipped even if the variable existed. Now the build passesvars.UMAMI_WEBSITE_IDthrough. Unset (dev, forks, or before the variable is configured) → no script at all.docs: privacy page IP/identifier facts. Replaces the pre-launch placeholder with the livemetrics.fro.botposture: no raw IPs or personal identifiers stored, monthly-rotating one-way hash for visitor counting, approximate location derived locally with no third-party calls, Do-Not-Track visitors not recorded.Verified
The env gating was tested both ways against a real build:
UMAMI_WEBSITE_IDunsetUMAMI_WEBSITE_IDsetdata-website-idWorkflow YAML parses clean.
docs:buildproduces 112 pages in both modes.Activation is a separate, manual step (not in this PR)
Merging this PR does not start collecting analytics. After merge, the launch is:
UMAMI_WEBSITE_ID(the website id is public — it ships in client HTML — so a variable, not a secret).Docsworkflow viaworkflow_dispatchto build + deploy with the variable set.metrics.fro.botreceives a pageview.Release impact
ci(docs)/docs— no published-package surface change, so no version bump. The docs site deploys on the next release or a manual dispatch.