PostHog analytics for the CLI and install scripts - #296
Closed
NejcS wants to merge 3 commits into
Closed
Conversation
Send a "cli_render_finished" event when a render ends, keyed on the user's email. The web app already identifies people by email, so a render lands on the same PostHog person as that user's signup, plan and payment. The project token and capture host are not committed. They are placeholders that the publish-to-pypi workflow substitutes from the repository's POSTHOG_PROJECT_TOKEN secret and POSTHOG_CAPTURE_URL variable, and the workflow fails rather than ship a build that would silently report nothing. A source checkout therefore reports nothing at all, unless CODEPLAIN_POSTHOG_PROJECT_TOKEN and CODEPLAIN_POSTHOG_CAPTURE_URL are set -- which is how a dev run can exercise analytics without a release. Consent is shared with crash reporting: CODEPLAIN_TELEMETRY governs both. The email is the only identity the CLI has - it mints no anonymous ids, so a run that never resolved the email (invalid or missing API key) sends nothing. No spec content, paths or error messages are sent; a failure is described by its exception class name only.
Send a "cli_installed" event at the end of both install scripts, keyed on the user's email so an install joins the same PostHog person as their signup. Both installers already call /status to verify the API key; they now read the email out of that response. As with the CLI, the token and host are placeholders. The publish-install-script workflow substitutes them on their way to R2, so the copies users curl are configured while the copies in this repository are not. A clone reports nothing unless CODEPLAIN_POSTHOG_* is set in the environment. The failed-verification path reports too, with install_verified false. An install that never verified a key sends nothing, since there is no identity for it. Analytics honor the same CODEPLAIN_TELEMETRY opt-out as the CLI. The CLI defaults to off outside a production install; the installers default to on, because running an installer is a real user install.
The e2e suite installs and renders for real with a real API key. Set CODEPLAIN_TELEMETRY=0 for the container and for the Windows runs so test installs and test renders do not show up as user activity.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Purpose
Capture two product-analytics events from the codeplain CLI — one when a user finishes installing, one when a render ends — in the same PostHog project the web app already uses, so a user's signup, payment, install and renders appear on one timeline.
Changes and design decisions
Identity is the email address. The web app already calls
posthog.identify(user.email), so CLI events keyed on email land on the same PostHog person as signup, plan selection and payment. No new endpoint was needed:POST /statusand/connection_checkalready return the email. One PostHog project, shared with the web app — that is what makes the linking automatic.Two events.
cli_render_finished— sent from a single call site inmain()'sfinally, placed afterprint_exit_summaryso a slow network can never delay the summary the user is waiting on. Properties:render_id,outcome(succeeded / cancelled / crashed / failed),rendered_functionalities,render_time_seconds,module_count,client_version,os,headless, the three*_script_providedflags, anderror_type.cli_installed— sent at the end of both install scripts, and also on the failed-verification path withinstall_verified: false. Both installers already call/statusto verify the API key, so they read the email out of that response rather than making an extra call.The PostHog token is not committed.
analytics.pyand both install scripts carry__POSTHOG_PROJECT_TOKEN__/__POSTHOG_CAPTURE_URL__placeholders. The two publish workflows substitute them from aPOSTHOG_PROJECT_TOKENsecret and aPOSTHOG_CAPTURE_URLvariable —publish-to-pypibeforeuv build,publish-install-scriptbefore the R2 upload. Both fail the publish if either value is missing or malformed, rather than shipping an artifact that silently reports nothing.Whether substitution happened is decided by a positive check — token starts with
phc_, URL starts withhttps://— not by sniffing for the placeholder, so asedchange cannot quietly disable the check.CODEPLAIN_POSTHOG_PROJECT_TOKENandCODEPLAIN_POSTHOG_CAPTURE_URLlet a source checkout emit events without cutting a release; a clone with neither set reports nothing.Substitution writes through a temp file and copies back with
catrather than usingsed -i, which is GNU-only and would have been unverifiable outside the runner. This also preservesinstall.sh's 755 mode.Consent is shared with crash reporting.
CODEPLAIN_TELEMETRYin{0, false, off}disables analytics and Sentry together. One deliberate asymmetry: the CLI keeps its "off outside a production install" default, while the installers default to on — running an installer is a real user install.Privacy. No spec content, file paths or error messages are sent. A failure is described by its exception class name alone.
error_typeis dropped when the outcome iscancelled, since a cancel also unwinds through an exception and the type would be noise.Known gap, deliberate: installs that never verify an API key, and renders that never resolve the email, send nothing. The CLI mints no anonymous ids anywhere, so those users are invisible until they configure a key. This was a conscious choice, not an oversight — flagging it because it is the one place the funnel has a hole.
Alternatives considered and rejected. Proxying events through the Codeplain API scored best on every axis — no token client-side at all, identity guaranteed server-side, a kill-switch over already-installed CLIs — but needs a new endpoint and a coordinated deploy, so it was deferred. Serving the token from
/statuswould keep it out of the artifacts but still hand it to the client. Keeping it hardcoded was rejected outright.Breaking changes
None for users.
One action required before the next release: set the
POSTHOG_PROJECT_TOKENsecret and thePOSTHOG_CAPTURE_URLvariable in the repository settings. Until they exist,publish-to-pypiandpublish-install-scriptwill fail — by design, so a release cannot silently ship dead analytics.Testing
install.shkeeps mode 755.::error::./statusbody shapes in both shells, including a 401 body, an unreachable API and a non-JSON body.organization_owner_emailnever matches. Both installers surviveset -uwith the optional-step flags unset.install.ps1parses cleanly underpwshand stays pure ASCII.Not verified: no live event has been sent with the real token, since that would write into production PostHog. Smoke test after merge and release, or from source with
CODEPLAIN_POSTHOG_*set. Note PostHog answers HTTP 200 even for an invalid token, so the PostHog UI is the only real confirmation.