Skip to content

feat(analytics): opt-in Firebase Analytics on generated pages - #93

Merged
anchildress1 merged 4 commits into
mainfrom
feat/firebase-analytics
Jun 26, 2026
Merged

feat(analytics): opt-in Firebase Analytics on generated pages#93
anchildress1 merged 4 commits into
mainfrom
feat/firebase-analytics

Conversation

@anchildress1

Copy link
Copy Markdown
Owner

What 🔧

Adds Firebase Analytics (GA4) to the generated mirror site — opt-in, fork-safe.

  • A shared firebase_analytics() Jinja global injects the Firebase Analytics module snippet before </head> on every page type: index, posts (file + inline fallback), and comment pages
  • Registered on both Jinja envs (core/utils.py + generator.py)
  • Firebase JS SDK pinned to 12.15.0 (CDN ESM import — no build step needed for the static site)

Fork safety 🛡️

  • Gated on the FIREBASE_WEB_CONFIG env var (Firebase web config JSON)
  • Snippet emits only when the config is present and has a measurementId
  • Forks leave it unset → zero analytics, no leakage to the upstream GA4 property
  • Mirrors the existing github.repository_owner == 'anchildress1' Firebase-deploy guard
  • Wired through the generate-site action input → publish.yaml reads vars.FIREBASE_WEB_CONFIG; deploy-gh-pages.yml (fork path) is intentionally not wired

Security 🔒

  • The embedded JSON is owner-controlled repo config (Firebase web config is public, not a secret)
  • < is escaped to < so a stray </script> can't break out — inert regardless of source
  • Bandit B704 suppressed with justification at the single call site

Verification ✅

  • make ai-checks — format, lint, security, complexity, tests all pass (coverage 88.74% > 85%)
  • New tests in test_core_utils.py: unset / invalid JSON / missing measurementId → empty; valid config → snippet + measurementId + SDK version present; end-to-end injection into INDEX_TMPL
  • actionlint clean on publish.yaml

⚠️ Action required to enable it

This is wired but dormant until you add the repo variable. Set FIREBASE_WEB_CONFIG (Settings → Variables → Actions) to the JSON config — the next publish run will start emitting analytics. It's public web config, safe to store as a plain variable (not a secret).

To preview locally: export FIREBASE_WEB_CONFIG='{...}' before running the generator.

🤖 Generated with Claude Code

Copilot AI review requested due to automatic review settings June 24, 2026 12:59

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: cb7519c79a

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread .github/actions/generate-site/action.yml

Copilot AI left a comment

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.

Pull request overview

Adds an opt-in, fork-safe Firebase Analytics (GA4) snippet to the generated static mirror site by exposing a shared Jinja global that injects the module-based Firebase Analytics initialization into the <head> of all page types.

Changes:

  • Implement firebase_analytics_snippet() in core/utils.py, gated by FIREBASE_WEB_CONFIG and requiring a measurementId, and register it as the firebase_analytics() Jinja global.
  • Inject {{ firebase_analytics() }} into post templates (file + inline fallback), index template, and comment note pages (generator template).
  • Wire FIREBASE_WEB_CONFIG through the upstream publish workflow into the composite generate-site action, and add tests covering enabled/disabled behavior and index injection.

Reviewed changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated no comments.

Show a summary per file
File Description
tests/test_core_utils.py Adds unit tests for analytics snippet gating and verifies index template injection.
src/devto_mirror/templates/post_template.html Injects firebase_analytics() into the file-based post template head.
src/devto_mirror/site_generation/generator.py Registers the Jinja global on the generator env and injects it into comment note pages (and inline post fallback in this module).
src/devto_mirror/core/utils.py Implements the gated snippet builder, pins the SDK version, escapes <, returns safe Markup, and registers the Jinja global on the core env.
.github/workflows/publish.yaml Documents the new repo variable and passes it into the generate-site action (upstream-only deploy).
.github/actions/generate-site/action.yml Adds firebase_web_config input and forwards it to FIREBASE_WEB_CONFIG for the generator step.

anchildress1 and others added 4 commits June 25, 2026 22:59
Injects the Firebase Analytics module snippet into every generated page
(index, posts, comments) via a shared `firebase_analytics()` Jinja global.

Gated on the FIREBASE_WEB_CONFIG env var (Firebase web config JSON): the
snippet is emitted only when the config is present and carries a
measurementId. Forks leave it unset and ship no analytics — mirroring the
existing owner-only Firebase deploy guard. Wired through the generate-site
action input and the FIREBASE_WEB_CONFIG repo variable in publish.yaml.

The embedded JSON is owner-controlled repo config; '<' is escaped to
neutralize any </script> breakout.

Generated-by: Claude Opus 4.8
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
renderer.py is the last writer of index.html; without the env var the
deployed homepage shipped without the analytics snippet while posts and
comments had it. Closes a gap flagged in PR review.

Generated-by: Claude Opus 4.8
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
pre-push only ran a subset (test + detect-secrets + validate-site),
letting commits push without format/lint/complexity. Collapse it to
`make ai-checks` so the push gate matches the manual gate exactly;
detect-secrets and validate-site already run inside ai-checks.

Generated-by: Claude Opus 4.8
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: Ashley Childress <anchildress1@gmail.com>
pre-push now runs the slow checks not done at commit time — full tests,
complexity, site validation — instead of redoing format/lint/security
that pre-commit already covers. Drops detect-secrets here since
pre-commit's `make security` already runs it.

Generated-by: Claude Opus 4.8
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: Ashley Childress <anchildress1@gmail.com>
@anchildress1
anchildress1 force-pushed the feat/firebase-analytics branch from 752b938 to 180907e Compare June 26, 2026 03:00
@anchildress1
anchildress1 merged commit f79789d into main Jun 26, 2026
4 checks passed
@anchildress1
anchildress1 deleted the feat/firebase-analytics branch June 26, 2026 03:15
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.

2 participants