From 06f5bbdaf12d061ca2f6d460fd0339f5408119de Mon Sep 17 00:00:00 2001 From: JSONbored <49853598+JSONbored@users.noreply.github.com> Date: Sat, 25 Jul 2026 05:13:41 -0700 Subject: [PATCH] fix(selfhost): upload self-host source maps to PostHog, not Sentry Replaces the self-host release pipeline's @sentry/cli sourcemap inject/ upload/finalize steps with posthog-cli's simpler inject/upload (no separate release-create/set-commits/finalize lifecycle -- PostHog release metadata is a byproduct of the upload itself). Fixes a currently-broken step in the process: "Validate Sentry release" called review-enrichment/scripts/validate-sentry-release.mjs, which #8615 already deleted -- any official release cut since then would have failed there. Renames SENTRY_RELEASE -> POSTHOG_RELEASE throughout scripts/deploy-selfhost-prebuilt.sh (the operator-facing local sourcemap upload path) and scripts/selfhost-post-update-check.sh, matching what src/selfhost/posthog.ts's resolvePostHogRelease already reads. Replaces the stale "Sentry error tracking" block in .env.example (self-host has had zero SENTRY_* code paths since #8617) with the real PostHog vars, plus the CLI-specific POSTHOG_CLI_API_KEY/POSTHOG_CLI_PROJECT_ID/POSTHOG_CLI_HOST used only by this upload step. Requires POSTHOG_CLI_API_KEY (secret) and POSTHOG_CLI_PROJECT_ID (var) to be configured in the release and release-beta GitHub environments before the next official/beta image build -- neither exists yet. --- .env.example | 57 +++++---- .github/workflows/release-selfhost.yml | 111 ++++++------------ scripts/deploy-selfhost-image.sh | 2 +- scripts/deploy-selfhost-prebuilt.sh | 59 +++++----- scripts/selfhost-post-update-check.sh | 2 +- .../docs-selfhost-update-rollback.test.ts | 2 +- ...st.ts => selfhost-posthog-release.test.ts} | 50 ++++---- 7 files changed, 127 insertions(+), 156 deletions(-) rename test/unit/{selfhost-sentry-release.test.ts => selfhost-posthog-release.test.ts} (66%) diff --git a/.env.example b/.env.example index e6b63c0f9a..9a952fdb25 100644 --- a/.env.example +++ b/.env.example @@ -617,27 +617,38 @@ REDIS_URL=redis://redis:6379 # REQUIRED for the self-host review # OTEL_TRACES_SAMPLER=parentbased_traceidratio # OTEL_TRACES_SAMPLER_ARG=0.05 # sample 5% of root traces when using parentbased_traceidratio -# --- Sentry error tracking (optional) --- -# SENTRY_DSN= # enables self-host Sentry capture; unset = complete no-op -# SENTRY_DSN_FILE= # optional mounted secret file; the generic *_FILE loader -# # (src/selfhost/load-file-secrets.ts) resolves it into SENTRY_DSN -# SENTRY_ENVIRONMENT=production # the code's own default (src/selfhost/sentry.ts); set any label you -# # want to slice by in Sentry (staging, selfhost, ...) — nothing -# # behaves differently per value, it only tags the events -# SENTRY_SERVER_NAME= # clean name for THIS instance in Sentry (e.g. loopover-us-east); +# --- PostHog error tracking (optional, replaces Sentry) --- +# POSTHOG_API_KEY= # enables self-host PostHog capture; unset = complete no-op. The +# # same key also activates MCP tool-call telemetry (src/mcp/telemetry.ts) +# # -- one PostHog project covers both surfaces. +# POSTHOG_API_KEY_FILE= # optional mounted secret file; the generic *_FILE loader +# # (src/selfhost/load-file-secrets.ts) resolves it into POSTHOG_API_KEY +# POSTHOG_HOST= # ingestion host; unset defaults to US-cloud (https://us.i.posthog.com). +# # Set https://eu.i.posthog.com for EU-cloud. +# POSTHOG_ENVIRONMENT=production # the code's own default; set any label you want to slice by in +# # PostHog (staging, selfhost, ...) — nothing behaves differently +# # per value, it only tags the events +# POSTHOG_SERVER_NAME= # clean name for THIS instance in PostHog (e.g. loopover-us-east); # # unset defaults to the OS hostname — never the public-origin URL -# SENTRY_TRACES_SAMPLE_RATE= # traces/spans are off when blank/unset; errors still report. Set a LOW -# # rate (e.g. 0.05) with SENTRY_DSN to sample review tracing: each -# # sampled review emits a connected trace — the queue-job span -# # (whole-review latency) with the AI-provider span nested — so you can -# # filter slow or failed STAGES in Sentry without reading scattered logs. -# # Spans carry only safe dimensions (repo, job type, provider/model); -# # never prompts, diffs, tokens, or bodies. -# SENTRY_RELEASE= # custom images only: set this ONLY when you uploaded source maps for -# # the exact built bundle under this exact release id. Future official +# POSTHOG_MIN_SEVERITY= # error/critical reach PostHog by default; set to warning or info to +# # see more while actively debugging (POSTHOG_REPO_MIN_SEVERITY is +# # the same, but a JSON {repoFullName: severity} per-repo override map) +# POSTHOG_RELEASE= # custom images only: set this ONLY when you uploaded source maps for +# # the exact built bundle under this exact release id. Official # # images bake LOOPOVER_VERSION=loopover-selfhost@ (see # # scripts/deploy-selfhost-prebuilt.sh), so do not override -# # SENTRY_RELEASE for those images. +# # POSTHOG_RELEASE for those images. +# +# Distributed tracing (beta): set OTEL_TRACES_EXPORTER=otlp (below) with POSTHOG_API_KEY above and no explicit +# OTEL_EXPORTER_OTLP_* override to send traces straight to PostHog's own distributed-tracing product +# (https://posthog.com/docs/distributed-tracing) -- same project, same auth, no extra config needed. +# +# Uploading source maps for a CUSTOM image build (official images already ship symbolicated) -- +# POSTHOG_CLI_API_KEY is a PERSONAL PostHog API key (distinct from the project-scoped POSTHOG_API_KEY above), +# used only at build/deploy time by scripts/deploy-selfhost-prebuilt.sh, never baked into the runtime image: +# POSTHOG_CLI_API_KEY= # personal API key with error_tracking read+write scopes +# POSTHOG_CLI_PROJECT_ID= # numeric PostHog project id +# POSTHOG_CLI_HOST= # override only for EU-cloud or self-hosted PostHog # OTEL_METRIC_EXPORT_INTERVAL=10000 # ms between metric exports (default 10s here; CLI default is 60s) # OTEL_METRICS_EXPORTER=otlp # metrics-side counterpart to OTEL_TRACES_EXPORTER; empty = no app metrics # OTEL_EXPORTER_OTLP_ENDPOINT=http://otel-collector:4318 # override only for an external collector @@ -650,10 +661,12 @@ REDIS_URL=redis://redis:6379 # REQUIRED for the self-host review # data source after Grafana is up with: ./scripts/setup-github-datasource.sh (reads GITHUB_TOKEN below). # GITHUB_TOKEN= # fine-grained PAT: Pull requests:read, Issues:read, Contents:read # -# In-Grafana Sentry error/issue visualization (#5369, distinct from SENTRY_DSN above, which is for error -# REPORTING). Install is automatic (GF_INSTALL_PLUGINS); add the data source after Grafana is up with: -# ./scripts/setup-sentry-datasource.sh (reads the two vars below). SENTRY_DSN is NOT reusable here -- a DSN -# authenticates event ingestion, not the read/query API this datasource needs. +# In-Grafana Sentry error/issue visualization (#5369). Self-host no longer sends new events to Sentry +# (error tracking now goes through PostHog, above) -- this panel only remains useful for an operator's own +# PRE-EXISTING Sentry issue history in an org they still separately maintain; pending removal (epic #8286). +# Install is automatic (GF_INSTALL_PLUGINS); add the data source after Grafana is up with: +# ./scripts/setup-sentry-datasource.sh (reads the two vars below). A Sentry event-ingestion DSN is NOT +# reusable here -- this datasource needs a separate read/query API token instead. # SENTRY_API_TOKEN= # Sentry -> Settings -> Developer Settings -> Custom # # Integrations -> New Internal Integration, with Read access on # # Project, Issue & Event, and Organization scopes diff --git a/.github/workflows/release-selfhost.yml b/.github/workflows/release-selfhost.yml index 114035f61c..96b8f89739 100644 --- a/.github/workflows/release-selfhost.yml +++ b/.github/workflows/release-selfhost.yml @@ -50,9 +50,7 @@ jobs: # reads only event fields because a job's `environment:` is resolved before any step runs. environment: ${{ github.event_name == 'workflow_dispatch' && github.actor == 'github-actions[bot]' && github.ref_type == 'tag' && inputs.create_github_release && contains(inputs.version, '-beta.') && 'release-beta' || 'release' }} env: - SENTRY_ORG: jsonbored - SENTRY_PROJECT: loopover - SENTRY_CLI_PACKAGE: "@sentry/cli@3.6.0" + POSTHOG_CLI_PACKAGE: "@posthog/cli@0.9.1" steps: - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7 with: @@ -90,7 +88,7 @@ jobs: fi # #1937: a stable X.Y.Z tag is the only kind that ever moved `latest` or an unmarked GitHub # Release; a prerelease tag (X.Y.Z-rc.N / X.Y.Z-beta.N) publishes the SAME image/provenance/SBOM/ - # Sentry pipeline below, just flagged as prerelease and never pushed under `latest` (see the + # PostHog pipeline below, just flagged as prerelease and never pushed under `latest` (see the # "Resolve image tags" and "GitHub Release" steps). if ! printf '%s' "$VERSION" | grep -Eq '^[0-9]+\.[0-9]+\.[0-9]+(-(rc|beta)\.[0-9]+)?$'; then echo "expected semver version X.Y.Z, or a prerelease X.Y.Z-rc.N / X.Y.Z-beta.N, got $VERSION" >&2 @@ -118,7 +116,7 @@ jobs: echo "prerelease=${PRERELEASE}" } >> "$GITHUB_OUTPUT" - # Release jobs receive publishing/Sentry credentials, so avoid shared dependency caches here. + # Release jobs receive publishing/PostHog credentials, so avoid shared dependency caches here. - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7 with: node-version-file: .nvmrc @@ -140,57 +138,39 @@ jobs: - name: Validate release source map run: node --experimental-strip-types scripts/validate-selfhost-sourcemap.ts - - name: Detect Sentry release token - id: sentry + - name: Detect PostHog release token + id: posthog env: - SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }} + POSTHOG_CLI_API_KEY: ${{ secrets.POSTHOG_CLI_API_KEY }} run: | - if [ -n "$SENTRY_AUTH_TOKEN" ]; then + if [ -n "$POSTHOG_CLI_API_KEY" ]; then echo "enabled=true" >> "$GITHUB_OUTPUT" else echo "enabled=false" >> "$GITHUB_OUTPUT" fi - - name: Require Sentry token for official release - if: github.repository == 'JSONbored/loopover' && steps.sentry.outputs.enabled != 'true' + - name: Require PostHog token for official release + if: github.repository == 'JSONbored/loopover' && steps.posthog.outputs.enabled != 'true' run: | - echo "::error::Configure SENTRY_AUTH_TOKEN in the release environment before publishing official Orb images." + echo "::error::Configure POSTHOG_CLI_API_KEY (a personal API key) in the release environment before publishing official Orb images." exit 1 - - name: Upload Sentry source maps - if: steps.sentry.outputs.enabled == 'true' + - name: Upload PostHog source maps + if: steps.posthog.outputs.enabled == 'true' env: - SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }} - SENTRY_ORG: ${{ vars.SENTRY_ORG || 'jsonbored' }} - SENTRY_PROJECT: ${{ vars.SENTRY_PROJECT || 'loopover' }} - SENTRY_URL: ${{ vars.SENTRY_URL }} - SENTRY_RELEASE: ${{ steps.version.outputs.release }} - SENTRY_REPOSITORY: ${{ github.repository }} - SENTRY_COMMIT_SHA: ${{ github.sha }} + POSTHOG_CLI_API_KEY: ${{ secrets.POSTHOG_CLI_API_KEY }} + POSTHOG_CLI_PROJECT_ID: ${{ vars.POSTHOG_CLI_PROJECT_ID }} + POSTHOG_CLI_HOST: ${{ vars.POSTHOG_CLI_HOST }} + POSTHOG_RELEASE: ${{ steps.version.outputs.release }} run: | set -euo pipefail - test -n "$SENTRY_AUTH_TOKEN" - test -n "$SENTRY_ORG" - test -n "$SENTRY_PROJECT" - if [ -z "${SENTRY_URL:-}" ]; then unset SENTRY_URL; fi - npx -y "$SENTRY_CLI_PACKAGE" releases new "$SENTRY_RELEASE" - # `sentry-cli releases set-commits --commit` (even without --ignore-missing) can silently leave - # the release with ZERO associated commits against a repo connected via the modern GitHub App - # integration -- confirmed empirically cutting the first orb-v0.1.0-beta.1 release: the CLI - # printed a success table but `GET .../releases//commits/` stayed `[]`. A direct PUT to the - # release resource with an inline `commits` array resolves the same repo/commit against the - # SAME integration correctly and immediately, so use that instead of relying on the CLI here -- - # it's what the later "Validate Sentry release" step's `SENTRY_REQUIRE_COMMITS=true` actually - # depends on being correct. - curl -sf -X PUT \ - -H "Authorization: Bearer ${SENTRY_AUTH_TOKEN}" \ - -H "Content-Type: application/json" \ - "${SENTRY_URL:-https://sentry.io}/api/0/organizations/${SENTRY_ORG}/releases/$(node -e "process.stdout.write(encodeURIComponent(process.env.SENTRY_RELEASE))")/" \ - -d "$(node -e "process.stdout.write(JSON.stringify({commits:[{repository: process.env.SENTRY_REPOSITORY, id: process.env.SENTRY_COMMIT_SHA}]}))")" \ - >/dev/null - npx -y "$SENTRY_CLI_PACKAGE" sourcemaps inject dist + test -n "$POSTHOG_CLI_API_KEY" + test -n "$POSTHOG_CLI_PROJECT_ID" + # No separate "create release" step -- PostHog release metadata is a byproduct of the inject/ + # upload calls below, unlike Sentry's releases/commits/deploys/finalize lifecycle this replaces. + npx -y "$POSTHOG_CLI_PACKAGE" sourcemap inject --directory dist --release-version "$POSTHOG_RELEASE" node --experimental-strip-types scripts/validate-selfhost-sourcemap.ts - npx -y "$SENTRY_CLI_PACKAGE" sourcemaps upload --release="$SENTRY_RELEASE" --validate --wait --strict dist + npx -y "$POSTHOG_CLI_PACKAGE" sourcemap upload --directory dist --release-version "$POSTHOG_RELEASE" - uses: docker/setup-qemu-action@96fe6ef7f33517b61c61be40b68a1882f3264fb8 # v4 - uses: docker/setup-buildx-action@bb05f3f5519dd87d3ba754cc423b652a5edd6d2c # v4 @@ -270,50 +250,31 @@ jobs: cache-from: type=gha,scope=release-orb cache-to: type=gha,mode=max,scope=release-orb - - name: Finalize Sentry release - if: steps.sentry.outputs.enabled == 'true' + - name: Validate PostHog release + if: steps.posthog.outputs.enabled == 'true' env: - SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }} - SENTRY_ORG: ${{ vars.SENTRY_ORG || 'jsonbored' }} - SENTRY_PROJECT: ${{ vars.SENTRY_PROJECT || 'loopover' }} - SENTRY_URL: ${{ vars.SENTRY_URL }} - SENTRY_RELEASE: ${{ steps.version.outputs.release }} + POSTHOG_CLI_API_KEY: ${{ secrets.POSTHOG_CLI_API_KEY }} + POSTHOG_CLI_PROJECT_ID: ${{ vars.POSTHOG_CLI_PROJECT_ID }} + POSTHOG_CLI_HOST: ${{ vars.POSTHOG_CLI_HOST }} + POSTHOG_RELEASE: ${{ steps.version.outputs.release }} run: | set -euo pipefail - if [ -z "${SENTRY_URL:-}" ]; then unset SENTRY_URL; fi - npx -y "$SENTRY_CLI_PACKAGE" releases finalize "$SENTRY_RELEASE" - - - name: Validate Sentry release - env: - SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }} - SENTRY_ORG: ${{ vars.SENTRY_ORG || 'jsonbored' }} - SENTRY_PROJECT: ${{ vars.SENTRY_PROJECT || 'loopover' }} - SENTRY_URL: ${{ vars.SENTRY_URL }} - SENTRY_RELEASE: ${{ steps.version.outputs.release }} - SENTRY_REPOSITORY: ${{ github.repository }} - SENTRY_COMMIT_SHA: ${{ github.sha }} - SENTRY_REQUIRE_COMMITS: "true" - SENTRY_REQUIRE_DEPLOY: "false" - SENTRY_REQUIRE_FINALIZED: "true" - run: | - set -euo pipefail - # The CLI's `set-commits` was replaced with a direct PUT above after it silently no-op'd on - # orb-v0.1.0-beta.1, but Sentry's release-commits read path can still lag briefly behind a - # write. review-enrichment/src/upload-sourcemaps.ts's runReleaseValidation() already - # retry-polls this same script for REES's Railway deploy path -- mirror that here instead of - # failing the whole release build on a transient read-lag. + # PostHog's release-read path can lag briefly behind the upload write above. + # review-enrichment/src/upload-sourcemaps.ts's runReleaseValidation() already retry-polls this + # same script for REES's own deploy path -- mirror that here instead of failing the whole + # release build on a transient read-lag. attempts=5 delay_secs=10 for attempt in $(seq 1 "$attempts"); do - if node review-enrichment/scripts/validate-sentry-release.mjs; then + if node review-enrichment/scripts/validate-posthog-release.mjs; then exit 0 fi if [ "$attempt" -lt "$attempts" ]; then - echo "::warning::Sentry release validation attempt ${attempt}/${attempts} failed; retrying in ${delay_secs}s" + echo "::warning::PostHog release validation attempt ${attempt}/${attempts} failed; retrying in ${delay_secs}s" sleep "$delay_secs" fi done - echo "::error::Sentry release validation failed after ${attempts} attempts" + echo "::error::PostHog release validation failed after ${attempts} attempts" exit 1 - name: GitHub Release @@ -346,7 +307,7 @@ jobs: Multi-arch (linux/amd64 + linux/arm64). See https://loopover.ai/docs/maintainer-self-hosting for setup. Includes the Claude Code / Codex subscription CLIs by default; credentials stay runtime-only. - Sentry release id baked into the image: \`${RELEASE_ID}\`. + PostHog release id baked into the image: \`${RELEASE_ID}\`. EOF )" # #1937: a prerelease tag never becomes the repo's "Latest release" and is visibly marked as such diff --git a/scripts/deploy-selfhost-image.sh b/scripts/deploy-selfhost-image.sh index e46c7fb9b7..a17f2af2d5 100755 --- a/scripts/deploy-selfhost-image.sh +++ b/scripts/deploy-selfhost-image.sh @@ -10,7 +10,7 @@ # ./scripts/deploy-selfhost-image.sh ghcr.io/jsonbored/loopover-selfhost:orb-v0.1.0 # LOOPOVER_IMAGE=ghcr.io/jsonbored/loopover-selfhost@sha256:... ./scripts/deploy-selfhost-image.sh # -# The image itself carries official release metadata. Set SENTRY_RELEASE only for custom images whose +# The image itself carries official release metadata. Set POSTHOG_RELEASE only for custom images whose # source maps were uploaded under that exact id. # # Optional Infisical secrets (#5120), see docs: diff --git a/scripts/deploy-selfhost-prebuilt.sh b/scripts/deploy-selfhost-prebuilt.sh index 87a40f75a8..c36a71b608 100755 --- a/scripts/deploy-selfhost-prebuilt.sh +++ b/scripts/deploy-selfhost-prebuilt.sh @@ -7,9 +7,9 @@ # ./scripts/deploy-selfhost-prebuilt.sh # # Optional knobs: -# SENTRY_RELEASE=loopover-selfhost@edge-abc123 ./scripts/deploy-selfhost-prebuilt.sh +# POSTHOG_RELEASE=loopover-selfhost@edge-abc123 ./scripts/deploy-selfhost-prebuilt.sh # SELFHOST_COMPOSE_FILES="docker-compose.yml docker-compose.override.yml" ./scripts/deploy-selfhost-prebuilt.sh -# SELFHOST_SKIP_SENTRY_UPLOAD=1 ./scripts/deploy-selfhost-prebuilt.sh +# SELFHOST_SKIP_POSTHOG_UPLOAD=1 ./scripts/deploy-selfhost-prebuilt.sh # SELFHOST_USE_INFISICAL=1 ./scripts/deploy-selfhost-prebuilt.sh # opt-in Infisical secrets (#5120), see docs set -euo pipefail @@ -19,8 +19,8 @@ SERVICE="${SELFHOST_SERVICE:-loopover}" # #8395: same override + default deploy-selfhost-image.sh already uses, so both deploy paths honour one # health-check budget. HEALTH_TIMEOUT_SECONDS="${SELFHOST_HEALTH_TIMEOUT_SECONDS:-180}" -SKIP_SENTRY_UPLOAD="${SELFHOST_SKIP_SENTRY_UPLOAD:-0}" -SENTRY_CLI_PACKAGE="${SENTRY_CLI_PACKAGE:-@sentry/cli@3.6.0}" +SKIP_POSTHOG_UPLOAD="${SELFHOST_SKIP_POSTHOG_UPLOAD:-0}" +POSTHOG_CLI_PACKAGE="${POSTHOG_CLI_PACKAGE:-@posthog/cli@0.9.1}" SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" # shellcheck source=lib/selfhost-deploy-common.sh @@ -42,42 +42,41 @@ run_node_build() { sh -lc 'npm ci --ignore-scripts && npm --workspace @loopover/engine run build && node --experimental-strip-types scripts/build-selfhost.ts --all && node --experimental-strip-types scripts/validate-selfhost-sourcemap.ts' } -run_sentry_upload() { - local auth_token org project uid gid +run_posthog_upload() { + local api_key project_id host uid gid - auth_token="${SENTRY_AUTH_TOKEN:-$(env_get SENTRY_AUTH_TOKEN || true)}" - org="${SENTRY_ORG:-$(env_get SENTRY_ORG || true)}" - project="${SENTRY_PROJECT:-$(env_get SENTRY_PROJECT || true)}" + api_key="${POSTHOG_CLI_API_KEY:-$(env_get POSTHOG_CLI_API_KEY || true)}" + project_id="${POSTHOG_CLI_PROJECT_ID:-$(env_get POSTHOG_CLI_PROJECT_ID || true)}" + host="${POSTHOG_CLI_HOST:-$(env_get POSTHOG_CLI_HOST || true)}" - if [ "$SKIP_SENTRY_UPLOAD" = "1" ]; then - echo "selfhost deploy: skipping Sentry upload (SELFHOST_SKIP_SENTRY_UPLOAD=1)" + if [ "$SKIP_POSTHOG_UPLOAD" = "1" ]; then + echo "selfhost deploy: skipping PostHog upload (SELFHOST_SKIP_POSTHOG_UPLOAD=1)" return 0 fi - if [ -z "$auth_token" ] || [ -z "$org" ] || [ -z "$project" ]; then - echo "selfhost deploy: skipping Sentry upload (SENTRY_AUTH_TOKEN, SENTRY_ORG, or SENTRY_PROJECT is missing)" + if [ -z "$api_key" ] || [ -z "$project_id" ]; then + echo "selfhost deploy: skipping PostHog upload (POSTHOG_CLI_API_KEY or POSTHOG_CLI_PROJECT_ID is missing)" return 0 fi uid="$(id -u)" gid="$(id -g)" - echo "selfhost deploy: injecting and uploading Sentry source maps for $SENTRY_RELEASE" + echo "selfhost deploy: injecting and uploading PostHog source maps for $POSTHOG_RELEASE" docker run --rm \ -e HOME=/tmp \ -e npm_config_cache=/tmp/.npm \ - -e SENTRY_LOAD_DOTENV=0 \ - -e SENTRY_RELEASE \ - -e SENTRY_AUTH_TOKEN="$auth_token" \ - -e SENTRY_ORG="$org" \ - -e SENTRY_PROJECT="$project" \ - -e SENTRY_CLI_PACKAGE="$SENTRY_CLI_PACKAGE" \ + -e POSTHOG_RELEASE \ + -e POSTHOG_CLI_API_KEY="$api_key" \ + -e POSTHOG_CLI_PROJECT_ID="$project_id" \ + ${host:+-e POSTHOG_CLI_HOST="$host"} \ + -e POSTHOG_CLI_PACKAGE="$POSTHOG_CLI_PACKAGE" \ -e HOST_UID="$uid" \ -e HOST_GID="$gid" \ -v "$PWD:/work" \ -w /work \ "$NODE_IMAGE" \ - sh -lc 'apt-get update >/dev/null && apt-get install -y --no-install-recommends ca-certificates git >/dev/null && git config --global --add safe.directory /work && (npx -y "$SENTRY_CLI_PACKAGE" releases new "$SENTRY_RELEASE" >/tmp/loopover-sentry-release-new.log 2>&1 || true) && npx -y "$SENTRY_CLI_PACKAGE" releases set-commits "$SENTRY_RELEASE" --auto && npx -y "$SENTRY_CLI_PACKAGE" sourcemaps inject dist && node --experimental-strip-types scripts/validate-selfhost-sourcemap.ts && npx -y "$SENTRY_CLI_PACKAGE" sourcemaps upload --release="$SENTRY_RELEASE" dist && npx -y "$SENTRY_CLI_PACKAGE" releases finalize "$SENTRY_RELEASE" && chown -R "$HOST_UID:$HOST_GID" dist node_modules package-lock.json' + sh -lc 'apt-get update >/dev/null && apt-get install -y --no-install-recommends ca-certificates >/dev/null && npx -y "$POSTHOG_CLI_PACKAGE" sourcemap inject --directory dist --release-version "$POSTHOG_RELEASE" && node --experimental-strip-types scripts/validate-selfhost-sourcemap.ts && npx -y "$POSTHOG_CLI_PACKAGE" sourcemap upload --directory dist --release-version "$POSTHOG_RELEASE" && chown -R "$HOST_UID:$HOST_GID" dist node_modules package-lock.json' } run_init_secrets() { @@ -99,12 +98,12 @@ services: build: target: runtime-prebuilt args: - LOOPOVER_VERSION: "\${SENTRY_RELEASE}" + LOOPOVER_VERSION: "\${POSTHOG_RELEASE}" INSTALL_AI_CLIS: "\${INSTALL_AI_CLIS:-true}" INSTALL_VISUAL_REVIEW: "\${INSTALL_VISUAL_REVIEW:-false}" environment: - SENTRY_RELEASE: "\${SENTRY_RELEASE}" - LOOPOVER_VERSION: "\${SENTRY_RELEASE}" + POSTHOG_RELEASE: "\${POSTHOG_RELEASE}" + LOOPOVER_VERSION: "\${POSTHOG_RELEASE}" YAML # #7765: capture via a checked assignment so compose_file_args's `exit 1` on a missing compose file @@ -141,15 +140,15 @@ fi # Default to the current checkout on every deploy. Do not reuse a persisted .env value here: # that value is written by the previous deploy and would make future updates report stale # release/version metadata unless the operator remembered to override it manually. -SENTRY_RELEASE="${SENTRY_RELEASE:-loopover-selfhost@$(git rev-parse --short=8 HEAD)}" -export SENTRY_RELEASE +POSTHOG_RELEASE="${POSTHOG_RELEASE:-loopover-selfhost@$(git rev-parse --short=8 HEAD)}" +export POSTHOG_RELEASE -env_put SENTRY_RELEASE "$SENTRY_RELEASE" -env_put LOOPOVER_VERSION "$SENTRY_RELEASE" +env_put POSTHOG_RELEASE "$POSTHOG_RELEASE" +env_put LOOPOVER_VERSION "$POSTHOG_RELEASE" run_node_build run_init_secrets -run_sentry_upload +run_posthog_upload run_compose_deploy -echo "selfhost deploy: complete ($SENTRY_RELEASE)" +echo "selfhost deploy: complete ($POSTHOG_RELEASE)" diff --git a/scripts/selfhost-post-update-check.sh b/scripts/selfhost-post-update-check.sh index 4ec4895b53..a0c0ff4195 100755 --- a/scripts/selfhost-post-update-check.sh +++ b/scripts/selfhost-post-update-check.sh @@ -93,7 +93,7 @@ fi if [ -f "$ENV_FILE" ]; then echo "selfhost post-update check: release metadata from $ENV_FILE" - grep -E '^(LOOPOVER_IMAGE|LOOPOVER_VERSION|SENTRY_RELEASE)=' "$ENV_FILE" || true + grep -E '^(LOOPOVER_IMAGE|LOOPOVER_VERSION|POSTHOG_RELEASE)=' "$ENV_FILE" || true else echo "selfhost post-update check: warning — $ENV_FILE not found (skipping release metadata grep)" >&2 fi diff --git a/test/unit/docs-selfhost-update-rollback.test.ts b/test/unit/docs-selfhost-update-rollback.test.ts index 83c320df41..1bbc8d9962 100644 --- a/test/unit/docs-selfhost-update-rollback.test.ts +++ b/test/unit/docs-selfhost-update-rollback.test.ts @@ -47,7 +47,7 @@ describe("self-host update + rollback docs (#1823)", () => { expect(postUpdateScript).toContain("/ready"); expect(postUpdateScript).toContain("LOOPOVER_IMAGE"); expect(postUpdateScript).toContain("LOOPOVER_VERSION"); - expect(postUpdateScript).toContain("SENTRY_RELEASE"); + expect(postUpdateScript).toContain("POSTHOG_RELEASE"); expect(postUpdateScript).not.toContain("env_put"); expect(postUpdateScript).not.toContain("docker compose down"); }); diff --git a/test/unit/selfhost-sentry-release.test.ts b/test/unit/selfhost-posthog-release.test.ts similarity index 66% rename from test/unit/selfhost-sentry-release.test.ts rename to test/unit/selfhost-posthog-release.test.ts index ed50cd6f12..976d7a42aa 100644 --- a/test/unit/selfhost-sentry-release.test.ts +++ b/test/unit/selfhost-posthog-release.test.ts @@ -3,27 +3,23 @@ import { describe, expect, it } from "vitest"; const read = (path: string) => readFileSync(path, "utf8"); -describe("self-host Sentry release wiring", () => { +describe("self-host PostHog release wiring", () => { it("keeps source-map uploads in the maintainer release workflow only", () => { const releaseWorkflow = read(".github/workflows/release-selfhost.yml"); - expect(releaseWorkflow).toContain('sourcemaps inject dist'); - expect(releaseWorkflow).toContain( - 'sourcemaps upload --release="$SENTRY_RELEASE" --validate --wait --strict dist', - ); - // `sentry-cli releases set-commits --commit` can silently leave a release with zero associated - // commits against a repo connected via the modern GitHub App integration -- a direct PUT to the - // release resource with an inline `commits` array is what actually satisfies the later - // SENTRY_REQUIRE_COMMITS=true validation (confirmed empirically cutting the first beta release). - expect(releaseWorkflow).not.toContain('"$SENTRY_CLI_PACKAGE" releases set-commits'); - expect(releaseWorkflow).toContain( - "/api/0/organizations/${SENTRY_ORG}/releases/", - ); - expect(releaseWorkflow).toContain( - "commits:[{repository: process.env.SENTRY_REPOSITORY, id: process.env.SENTRY_COMMIT_SHA}]", - ); - expect(releaseWorkflow).toContain('SENTRY_CLI_PACKAGE: "@sentry/cli@3.6.0"'); - expect(releaseWorkflow).toContain('npx -y "$SENTRY_CLI_PACKAGE"'); - expect(releaseWorkflow).not.toContain("@sentry/cli@latest"); + expect(releaseWorkflow).toContain("sourcemap inject --directory dist --release-version"); + expect(releaseWorkflow).toContain("sourcemap upload --directory dist --release-version"); + // No separate "create release"/"set-commits"/"finalize" steps -- PostHog release metadata is a + // byproduct of the inject/upload calls themselves, unlike Sentry's releases/commits/deploys/finalize + // lifecycle this replaced. + expect(releaseWorkflow).not.toContain("releases new"); + expect(releaseWorkflow).not.toContain("releases set-commits"); + expect(releaseWorkflow).not.toContain("releases finalize"); + expect(releaseWorkflow).not.toContain("Finalize"); + expect(releaseWorkflow).toContain('POSTHOG_CLI_PACKAGE: "@posthog/cli@0.9.1"'); + expect(releaseWorkflow).toContain('npx -y "$POSTHOG_CLI_PACKAGE"'); + expect(releaseWorkflow).not.toContain("@posthog/cli@latest"); + expect(releaseWorkflow).toContain("node review-enrichment/scripts/validate-posthog-release.mjs"); + expect(releaseWorkflow).not.toContain("review-enrichment/scripts/validate-sentry-release.mjs"); expect(releaseWorkflow).toContain('"orb-v*"'); expect(releaseWorkflow).toContain('orb-v*) VERSION="${REF_NAME#orb-v}"'); expect(releaseWorkflow).toContain("tag=orb-v${VERSION}"); @@ -53,18 +49,20 @@ describe("self-host Sentry release wiring", () => { expect(releaseWorkflow).not.toContain('"selfhost-v*"'); expect(releaseWorkflow).not.toContain('VERSION="${REF_NAME#selfhost-v}"'); expect(releaseWorkflow).not.toContain("type=raw,value=${{ steps.version.outputs.v }}"); - expect(releaseWorkflow).toContain("Validate Sentry release"); - expect(releaseWorkflow).toContain('SENTRY_REQUIRE_FINALIZED: "true"'); + expect(releaseWorkflow).toContain("Validate PostHog release"); + expect(releaseWorkflow).toContain("Require PostHog token for official release"); + expect(releaseWorkflow).not.toContain("SENTRY_"); const edgeDeployScript = read("scripts/deploy-selfhost-prebuilt.sh"); expect(edgeDeployScript).toContain( - 'SENTRY_CLI_PACKAGE="${SENTRY_CLI_PACKAGE:-@sentry/cli@3.6.0}"', + 'POSTHOG_CLI_PACKAGE="${POSTHOG_CLI_PACKAGE:-@posthog/cli@0.9.1}"', ); expect(edgeDeployScript).toContain( - 'SENTRY_RELEASE="${SENTRY_RELEASE:-loopover-selfhost@$(git rev-parse --short=8 HEAD)}"', + 'POSTHOG_RELEASE="${POSTHOG_RELEASE:-loopover-selfhost@$(git rev-parse --short=8 HEAD)}"', ); - expect(edgeDeployScript).not.toContain('env_get SENTRY_RELEASE'); - expect(edgeDeployScript).not.toContain("@sentry/cli@latest"); + expect(edgeDeployScript).not.toContain("env_get SENTRY_RELEASE"); + expect(edgeDeployScript).not.toContain("@posthog/cli@latest"); + expect(edgeDeployScript).not.toContain("SENTRY_"); expect(releaseWorkflow).toContain("target: runtime-prebuilt"); expect(releaseWorkflow).toContain( "LOOPOVER_VERSION=${{ steps.version.outputs.release }}", @@ -75,7 +73,7 @@ describe("self-host Sentry release wiring", () => { "Dockerfile", ".github/workflows/selfhost.yml", ]) { - expect(read(path)).not.toContain("sourcemaps upload"); + expect(read(path)).not.toContain("sourcemap upload"); } });