← all branches

chore/ph-annot

needs attentionviewing older commit
3a88710 · fullpre-PRreviewed 2026-07-30 01:43 UTC0H · 2M · 7L · 7I
The branch
Purpose
Add PostHog deploy annotation markers to the prod release pipeline so metric changes in dashboards can be attributed to specific releases at a glance.
Goal
Annotate every PostHog time-series with a vertical deploy marker after each successful Vercel production release, turning the analytics timeline into a causal narrative.
Sub-goals
  • SG-1: New posthog-annotate CI job in release-promote.yml (NON-GATING, continue-on-error)
  • SG-2: Standalone scripts/posthog-annotate.mjs that POSTs a deploy annotation to the PostHog API
The changes (whole branch)
What
Added a new posthog-annotate GitHub Actions job that runs after preflight + vercel-promote + postflight, and a new scripts/posthog-annotate.mjs script that calls the PostHog annotations API.
Why
Without deploy markers, a metric jump is indistinguishable from a behaviour change, a product change, or an instrumentation regression.
Areas
.github/workflows/release-promote.yml+420scripts/posthog-annotate.mjs+990
Blast
2 files, +141 lines. CI-only change — no app code, domain logic, or database touched. Non-gating (continue-on-error: true).
non-gating ci-only no-app-code
ci· no open PR — CI status unavailablecoderabbit· no .coderabbit.yaml in repo

Findings · 15

correctness3

low

Script exits 0 on auth failure, masking misconfigured secrets

scripts/posthog-annotate.mjs:40

When the API returns 401/403 (wrong or expired key), the script logs a warning and exits 0. The job is continue-on-error:true so a non-zero exit would still not block the release — but it would surface as a failed step in the UI, making the misconfiguration visible. Consider exiting 1 on 4xx auth errors so operators notice the credential problem.

info

POSTHOG_PROJECT_ID hardcoded as string literal

.github/workflows/release-promote.yml

Not a security issue (project IDs are not secret), but a project migration requires a code change rather than a config update.

info

date_marker uses time of annotation job, not actual deploy timestamp

scripts/posthog-annotate.mjs:27

If the posthog-annotate job is delayed by queue time, the annotation timestamp will be slightly later than the real moment. Acceptable, but passing a preflight output timestamp would be more precise.

security4

medium

SSRF via user-controlled POSTHOG_HOST variable

.github/workflows/release-promote.yml

POSTHOG_HOST is sourced from a GitHub Actions variable (vars.POSTHOG_HOST) with a fallback to 'https://us.posthog.com'. A compromised or misconfigured variable could redirect the HTTP POST — carrying the POSTHOG_PERSONAL_API_KEY bearer token — to an attacker-controlled host. The script only strips a trailing slash with no origin allowlist or URL validation. Mitigation: hardcode the host constant in the script, or assert the scheme is https and the hostname ends with `.posthog.com` before fetching.

low

Actor and deployment URL embedded in annotation content without sanitisation

scripts/posthog-annotate.mjs

github.actor and the production deployment URL are interpolated directly into the annotation content. Low impact here (internal PostHog annotation), but a actor.replace guard adds defence-in-depth against control-character injection in CI logs.

info

API key could surface via error response echoing

scripts/posthog-annotate.mjs

On non-OK response, up to 300 chars of raw response body are logged. If PostHog ever echoes back auth context in error bodies, the key could surface in CI logs. Consider omitting the body on 401/403.

info

actions/checkout and actions/setup-node not pinned to commit SHAs

.github/workflows/release-promote.yml

Uses floating @v4 tags. A compromised upstream action repo could execute arbitrary code in a job that holds POSTHOG_PERSONAL_API_KEY. Note: if the rest of the workflow uses the same floating tags this is consistent rather than a regression.

conventions1

low

Emoji in annotation content may cause encoding issues in some log aggregators

scripts/posthog-annotate.mjs

buildContent() emits a rocket emoji. PostHog's API handles UTF-8, but some CI log consumers or annotation search UIs can mishandle non-ASCII. Confirm PostHog renders it correctly or consider an ASCII alternative.

tests2

medium

No unit tests for buildContent() or main() logic

scripts/posthog-annotate.mjs

The script contains non-trivial logic: buildContent() assembles content from multiple env vars with fallbacks and conditional concatenation; main() guards on missing credentials, handles HTTP errors, and parses JSON. These are testable functions. A typo in buildContent or wrong HOST interpolation would silently produce a malformed annotation. The continue-on-error:true boundary limits blast radius but does not substitute for test coverage.

info

buildContent is not exported — testability requires minor refactor

scripts/posthog-annotate.mjs:8

The top-level main().catch(...) call makes importing the file for unit tests awkward. Exporting buildContent as a named export would make the content logic trivially testable.

improvement5

low

POSTHOG_PROJECT_ID hardcoded in workflow instead of a variable

.github/workflows/release-promote.yml

POSTHOG_PROJECT_ID is hardcoded as "334265" inline. POSTHOG_HOST correctly uses vars.POSTHOG_HOST — this should use vars.POSTHOG_PROJECT_ID for consistency, and to avoid a code change if the project ID ever changes.

low

Checkout + Node setup just to run a single fetch call adds ~20-30s

.github/workflows/release-promote.yml

The job checks out the full repo and installs Node 20 to run a ~30-line script that does one HTTP POST with no npm deps. A curl one-liner would be faster and remove the Node version dependency entirely. Not blocking, but worth considering for job startup time.

low

buildContent() conditional appends could use filter/join

scripts/posthog-annotate.mjs

The four conditional appends could be: [env, sha, actor && 'by ' + actor, url].filter(Boolean).join(' · ') — more idiomatic and easier to extend.

info

res.text() truncation limit (300 chars) is undocumented

scripts/posthog-annotate.mjs

Truncating error body to 300 chars is reasonable but the limit is arbitrary. A comment explaining the rationale would help future editors.

info

date_marker anchored to annotation time rather than commit time

scripts/posthog-annotate.mjs

Passing the Git commit author date via a preflight output would anchor the marker to when the code shipped rather than when the CI step ran. Low impact since the job runs immediately after promote.

History · 3 commits

  1. 70e9fcbneeds attentionincremental0H · 2M · 5L2026-07-30 20:43
  2. 5d8dfd6needs attentionfull0H · 1M · 5L2026-07-30 17:33
  3. 3a88710needs attentionfull0H · 2M · 7L2026-07-30 01:43current