From 33b5418bbf8f382d76fc533b1c0503a41ea5f55b Mon Sep 17 00:00:00 2001 From: Brendan Kellam Date: Mon, 25 Nov 2024 21:04:52 -0800 Subject: [PATCH] Pass PostHog key at build time (#92) --- .github/workflows/ghcr-publish.yml | 1 + Dockerfile | 10 +++++++--- entrypoint.sh | 14 ++++++++++++-- packages/backend/.env | 3 --- packages/backend/src/environment.ts | 12 ++++++------ packages/backend/src/posthog.ts | 22 +++++++++++++--------- packages/web/.env | 3 --- packages/web/src/app/posthogProvider.tsx | 7 ++++--- packages/web/src/lib/environment.client.ts | 6 +++--- packages/web/src/lib/environment.ts | 2 +- packages/web/src/lib/utils.ts | 2 +- 11 files changed, 48 insertions(+), 34 deletions(-) diff --git a/.github/workflows/ghcr-publish.yml b/.github/workflows/ghcr-publish.yml index 162f796f..0863a349 100644 --- a/.github/workflows/ghcr-publish.yml +++ b/.github/workflows/ghcr-publish.yml @@ -75,6 +75,7 @@ jobs: outputs: type=image,name=${{ env.REGISTRY_IMAGE }},push-by-digest=true,name-canonical=true,push=true build-args: | SOURCEBOT_VERSION=${{ github.ref_name }} + POSTHOG_PAPIK=${{ secrets.POSTHOG_PAPIK }} - name: Export digest run: | diff --git a/Dockerfile b/Dockerfile index 79e3b452..327f04e7 100644 --- a/Dockerfile +++ b/Dockerfile @@ -26,6 +26,7 @@ ENV NEXT_TELEMETRY_DISABLED=1 # @see: https://phase.dev/blog/nextjs-public-runtime-variables/ ARG NEXT_PUBLIC_SOURCEBOT_TELEMETRY_DISABLED=BAKED_NEXT_PUBLIC_SOURCEBOT_TELEMETRY_DISABLED ARG NEXT_PUBLIC_SOURCEBOT_VERSION=BAKED_NEXT_PUBLIC_SOURCEBOT_VERSION +ENV NEXT_PUBLIC_POSTHOG_PAPIK=BAKED_NEXT_PUBLIC_POSTHOG_PAPIK # @note: leading "/" is required for the basePath property. @see: https://nextjs.org/docs/app/api-reference/next-config-js/basePath ARG NEXT_PUBLIC_DOMAIN_SUB_PATH=/BAKED_NEXT_PUBLIC_DOMAIN_SUB_PATH RUN yarn workspace @sourcebot/web build @@ -61,9 +62,12 @@ ENV SOURCEBOT_LOG_LEVEL=info # will serve from http(s)://example.com/sb ENV DOMAIN_SUB_PATH=/ -# @note: This is also set in .env -ENV POSTHOG_KEY=phc_VFn4CkEGHRdlVyOOw8mfkoj1DKVoG6y1007EClvzAnS -ENV NEXT_PUBLIC_POSTHOG_KEY=$POSTHOG_KEY +# PAPIK = Project API Key +# Note that this key does not need to be kept secret, so it's not +# necessary to use Docker build secrets here. +# @see: https://posthog.com/tutorials/api-capture-events#authenticating-with-the-project-api-key +ARG POSTHOG_PAPIK= +ENV POSTHOG_PAPIK=$POSTHOG_PAPIK # Sourcebot collects anonymous usage data using [PostHog](https://posthog.com/). Uncomment this line to disable. # ENV SOURCEBOT_TELEMETRY_DISABLED=1 diff --git a/entrypoint.sh b/entrypoint.sh index c9663ef1..6bc3b70a 100644 --- a/entrypoint.sh +++ b/entrypoint.sh @@ -3,6 +3,12 @@ set -e echo -e "\e[34m[Info] Sourcebot version: $SOURCEBOT_VERSION\e[0m" +# If we don't have a PostHog key, then we need to disable telemetry. +if [ -z "$POSTHOG_PAPIK" ]; then + echo -e "\e[33m[Warning] POSTHOG_PAPIK was not set. Setting SOURCEBOT_TELEMETRY_DISABLED.\e[0m" + export SOURCEBOT_TELEMETRY_DISABLED=1 +fi + # Issue a info message about telemetry if [ ! -z "$SOURCEBOT_TELEMETRY_DISABLED" ]; then echo -e "\e[34m[Info] Disabling telemetry since SOURCEBOT_TELEMETRY_DISABLED was set.\e[0m" @@ -25,7 +31,7 @@ if [ ! -f "$FIRST_RUN_FILE" ]; then # (if telemetry is enabled) if [ -z "$SOURCEBOT_TELEMETRY_DISABLED" ]; then curl -L -s --header "Content-Type: application/json" -d '{ - "api_key": "'"$POSTHOG_KEY"'", + "api_key": "'"$POSTHOG_PAPIK"'", "event": "install", "distinct_id": "'"$SOURCEBOT_INSTALL_ID"'", "properties": { @@ -43,7 +49,7 @@ else if [ -z "$SOURCEBOT_TELEMETRY_DISABLED" ]; then curl -L -s --header "Content-Type: application/json" -d '{ - "api_key": "'"$POSTHOG_KEY"'", + "api_key": "'"$POSTHOG_PAPIK"'", "event": "upgrade", "distinct_id": "'"$SOURCEBOT_INSTALL_ID"'", "properties": { @@ -83,12 +89,16 @@ echo -e "\e[34m[Info] Using config file at: '$CONFIG_PATH'.\e[0m" export NEXT_PUBLIC_SOURCEBOT_VERSION="$SOURCEBOT_VERSION" fi + # Always infer NEXT_PUBLIC_POSTHOG_PAPIK + export NEXT_PUBLIC_POSTHOG_PAPIK="$POSTHOG_PAPIK" + # Iterate over all .js files in .next & public, making substitutions for the `BAKED_` sentinal values # with their actual desired runtime value. find /app/packages/web/public /app/packages/web/.next -type f -name "*.js" | while read file; do sed -i "s|BAKED_NEXT_PUBLIC_SOURCEBOT_TELEMETRY_DISABLED|${NEXT_PUBLIC_SOURCEBOT_TELEMETRY_DISABLED}|g" "$file" sed -i "s|BAKED_NEXT_PUBLIC_SOURCEBOT_VERSION|${NEXT_PUBLIC_SOURCEBOT_VERSION}|g" "$file" + sed -i "s|BAKED_NEXT_PUBLIC_POSTHOG_PAPIK|${NEXT_PUBLIC_POSTHOG_PAPIK}|g" "$file" done } diff --git a/packages/backend/.env b/packages/backend/.env index cdcc9711..baa6a2c1 100644 --- a/packages/backend/.env +++ b/packages/backend/.env @@ -1,4 +1 @@ POSTHOG_HOST=https://us.i.posthog.com - -# @note: This is also set in the Dockerfile -POSTHOG_KEY=phc_VFn4CkEGHRdlVyOOw8mfkoj1DKVoG6y1007EClvzAnS \ No newline at end of file diff --git a/packages/backend/src/environment.ts b/packages/backend/src/environment.ts index 160816d1..a9fee07d 100644 --- a/packages/backend/src/environment.ts +++ b/packages/backend/src/environment.ts @@ -1,6 +1,6 @@ import dotenv from 'dotenv'; -export const getEnv = (env: string | undefined, defaultValue = '') => { +export const getEnv = (env: string | undefined, defaultValue?: string) => { return env ?? defaultValue; } @@ -15,9 +15,9 @@ dotenv.config({ path: './.env', }); -export const SOURCEBOT_LOG_LEVEL = getEnv(process.env.SOURCEBOT_LOG_LEVEL, 'info'); -export const SOURCEBOT_TELEMETRY_DISABLED = getEnvBoolean(process.env.SOURCEBOT_TELEMETRY_DISABLED, false); -export const SOURCEBOT_INSTALL_ID = getEnv(process.env.SOURCEBOT_INSTALL_ID, 'unknown'); -export const SOURCEBOT_VERSION = getEnv(process.env.SOURCEBOT_VERSION, 'unknown'); -export const POSTHOG_KEY = getEnv(process.env.POSTHOG_KEY); +export const SOURCEBOT_LOG_LEVEL = getEnv(process.env.SOURCEBOT_LOG_LEVEL, 'info')!; +export const SOURCEBOT_TELEMETRY_DISABLED = getEnvBoolean(process.env.SOURCEBOT_TELEMETRY_DISABLED, false)!; +export const SOURCEBOT_INSTALL_ID = getEnv(process.env.SOURCEBOT_INSTALL_ID, 'unknown')!; +export const SOURCEBOT_VERSION = getEnv(process.env.SOURCEBOT_VERSION, 'unknown')!; +export const POSTHOG_PAPIK = getEnv(process.env.POSTHOG_PAPIK); export const POSTHOG_HOST = getEnv(process.env.POSTHOG_HOST); diff --git a/packages/backend/src/posthog.ts b/packages/backend/src/posthog.ts index f9bc487c..58287424 100644 --- a/packages/backend/src/posthog.ts +++ b/packages/backend/src/posthog.ts @@ -1,20 +1,24 @@ import { PostHog } from 'posthog-node'; import { PosthogEvent, PosthogEventMap } from './posthogEvents.js'; -import { POSTHOG_HOST, POSTHOG_KEY, SOURCEBOT_INSTALL_ID, SOURCEBOT_TELEMETRY_DISABLED, SOURCEBOT_VERSION } from './environment.js'; +import { POSTHOG_HOST, POSTHOG_PAPIK, SOURCEBOT_INSTALL_ID, SOURCEBOT_TELEMETRY_DISABLED, SOURCEBOT_VERSION } from './environment.js'; -const posthog = new PostHog( - POSTHOG_KEY, - { - host: POSTHOG_HOST, - } -); +let posthog: PostHog | undefined = undefined; + +if (POSTHOG_PAPIK) { + posthog = new PostHog( + POSTHOG_PAPIK, + { + host: POSTHOG_HOST, + } + ); +} export function captureEvent(event: E, properties: PosthogEventMap[E]) { if (SOURCEBOT_TELEMETRY_DISABLED) { return; } - posthog.capture({ + posthog?.capture({ distinctId: SOURCEBOT_INSTALL_ID, event: event, properties: { @@ -24,4 +28,4 @@ export function captureEvent(event: E, properties: Posth }); } -await posthog.shutdown(); \ No newline at end of file +await posthog?.shutdown(); \ No newline at end of file diff --git a/packages/web/.env b/packages/web/.env index 58940ffa..c0fc3865 100644 --- a/packages/web/.env +++ b/packages/web/.env @@ -1,6 +1,3 @@ NEXT_PUBLIC_POSTHOG_HOST=https://us.i.posthog.com NEXT_PUBLIC_POSTHOG_ASSET_HOST=https://us-assets.i.posthog.com NEXT_PUBLIC_POSTHOG_UI_HOST=https://us.posthog.com - -# @note: This is also set in the Dockerfile. -NEXT_PUBLIC_POSTHOG_KEY=phc_VFn4CkEGHRdlVyOOw8mfkoj1DKVoG6y1007EClvzAnS \ No newline at end of file diff --git a/packages/web/src/app/posthogProvider.tsx b/packages/web/src/app/posthogProvider.tsx index 1cee74f1..e556678f 100644 --- a/packages/web/src/app/posthogProvider.tsx +++ b/packages/web/src/app/posthogProvider.tsx @@ -1,15 +1,16 @@ 'use client' -import { NEXT_PUBLIC_POSTHOG_KEY, NEXT_PUBLIC_POSTHOG_UI_HOST, NEXT_PUBLIC_SOURCEBOT_TELEMETRY_DISABLED } from '@/lib/environment.client' +import { NEXT_PUBLIC_POSTHOG_PAPIK, NEXT_PUBLIC_POSTHOG_UI_HOST, NEXT_PUBLIC_SOURCEBOT_TELEMETRY_DISABLED } from '@/lib/environment.client' import posthog from 'posthog-js' import { PostHogProvider } from 'posthog-js/react' import { resolveServerPath } from './api/(client)/client' +import { isDefined } from '@/lib/utils' if (typeof window !== 'undefined') { - if (!NEXT_PUBLIC_SOURCEBOT_TELEMETRY_DISABLED) { + if (!NEXT_PUBLIC_SOURCEBOT_TELEMETRY_DISABLED && isDefined(NEXT_PUBLIC_POSTHOG_PAPIK)) { // @see next.config.mjs for path rewrites to the "/ingest" route. const posthogHostPath = resolveServerPath('/ingest'); - posthog.init(NEXT_PUBLIC_POSTHOG_KEY!, { + posthog.init(NEXT_PUBLIC_POSTHOG_PAPIK, { api_host: posthogHostPath, ui_host: NEXT_PUBLIC_POSTHOG_UI_HOST, person_profiles: 'identified_only', diff --git a/packages/web/src/lib/environment.client.ts b/packages/web/src/lib/environment.client.ts index 85251888..117ebf7e 100644 --- a/packages/web/src/lib/environment.client.ts +++ b/packages/web/src/lib/environment.client.ts @@ -2,10 +2,10 @@ import 'client-only'; import { getEnv, getEnvBoolean } from "./utils"; -export const NEXT_PUBLIC_POSTHOG_KEY = getEnv(process.env.NEXT_PUBLIC_POSTHOG_KEY); +export const NEXT_PUBLIC_POSTHOG_PAPIK = getEnv(process.env.NEXT_PUBLIC_POSTHOG_PAPIK); export const NEXT_PUBLIC_POSTHOG_HOST = getEnv(process.env.NEXT_PUBLIC_POSTHOG_HOST); export const NEXT_PUBLIC_POSTHOG_UI_HOST = getEnv(process.env.NEXT_PUBLIC_POSTHOG_UI_HOST); export const NEXT_PUBLIC_POSTHOG_ASSET_HOST = getEnv(process.env.NEXT_PUBLIC_POSTHOG_ASSET_HOST); export const NEXT_PUBLIC_SOURCEBOT_TELEMETRY_DISABLED = getEnvBoolean(process.env.NEXT_PUBLIC_SOURCEBOT_TELEMETRY_DISABLED, false); -export const NEXT_PUBLIC_SOURCEBOT_VERSION = getEnv(process.env.NEXT_PUBLIC_SOURCEBOT_VERSION, "unknown"); -export const NEXT_PUBLIC_DOMAIN_SUB_PATH = getEnv(process.env.NEXT_PUBLIC_DOMAIN_SUB_PATH, ""); +export const NEXT_PUBLIC_SOURCEBOT_VERSION = getEnv(process.env.NEXT_PUBLIC_SOURCEBOT_VERSION, "unknown")!; +export const NEXT_PUBLIC_DOMAIN_SUB_PATH = getEnv(process.env.NEXT_PUBLIC_DOMAIN_SUB_PATH, "")!; diff --git a/packages/web/src/lib/environment.ts b/packages/web/src/lib/environment.ts index be2a1386..0102da6d 100644 --- a/packages/web/src/lib/environment.ts +++ b/packages/web/src/lib/environment.ts @@ -2,7 +2,7 @@ import 'server-only'; import { getEnv, getEnvNumber } from "./utils"; -export const ZOEKT_WEBSERVER_URL = getEnv(process.env.ZOEKT_WEBSERVER_URL, "http://localhost:6070"); +export const ZOEKT_WEBSERVER_URL = getEnv(process.env.ZOEKT_WEBSERVER_URL, "http://localhost:6070")!; export const SHARD_MAX_MATCH_COUNT = getEnvNumber(process.env.SHARD_MAX_MATCH_COUNT, 10000); export const TOTAL_MAX_MATCH_COUNT = getEnvNumber(process.env.TOTAL_MAX_MATCH_COUNT, 100000); export const NODE_ENV = process.env.NODE_ENV; diff --git a/packages/web/src/lib/utils.ts b/packages/web/src/lib/utils.ts index e77de129..c2a46df3 100644 --- a/packages/web/src/lib/utils.ts +++ b/packages/web/src/lib/utils.ts @@ -99,7 +99,7 @@ export const isServiceError = (data: unknown): data is ServiceError => { 'message' in data; } -export const getEnv = (env: string | undefined, defaultValue = '') => { +export const getEnv = (env: string | undefined, defaultValue?: string) => { return env ?? defaultValue; }