Claude-code-plugins-plus-skills posthog-ci-integration
install
source · Clone the upstream repo
git clone https://github.com/jeremylongshore/claude-code-plugins-plus-skills
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/jeremylongshore/claude-code-plugins-plus-skills "$T" && mkdir -p ~/.claude/skills && cp -r "$T/plugins/saas-packs/posthog-pack/skills/posthog-ci-integration" ~/.claude/skills/jeremylongshore-claude-code-plugins-plus-skills-posthog-ci-integration && rm -rf "$T"
manifest:
plugins/saas-packs/posthog-pack/skills/posthog-ci-integration/SKILL.mdsource content
PostHog CI Integration
Overview
Set up CI/CD pipelines for PostHog integrations. Covers mocked unit tests (no API key needed), integration tests against a PostHog dev project, and deployment annotations that mark releases in your PostHog timeline.
Prerequisites
- GitHub repository with Actions enabled
- PostHog dev project API key for integration tests
- PostHog personal API key for deployment annotations
- npm/pnpm project with vitest or jest
Instructions
Step 1: Configure GitHub Secrets
set -euo pipefail # Project key for integration tests (phc_... from dev project) gh secret set POSTHOG_TEST_KEY --body "phc_dev_project_key" # Personal key for deployment annotations (phx_...) gh secret set POSTHOG_PERSONAL_API_KEY --body "phx_your_personal_key" # Project ID for annotations gh secret set POSTHOG_PROJECT_ID --body "12345"
Step 2: GitHub Actions Workflow
# .github/workflows/posthog-tests.yml name: PostHog Tests on: push: branches: [main] pull_request: branches: [main] jobs: unit-tests: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: node-version: '20' cache: 'npm' - run: npm ci - run: npm test -- --coverage # Unit tests use mocked PostHog — no API key needed integration-tests: runs-on: ubuntu-latest if: github.event_name == 'push' && github.ref == 'refs/heads/main' needs: unit-tests env: NEXT_PUBLIC_POSTHOG_KEY: ${{ secrets.POSTHOG_TEST_KEY }} POSTHOG_HOST: https://us.i.posthog.com steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: node-version: '20' cache: 'npm' - run: npm ci - run: npm run test:integration timeout-minutes: 5 annotate-deploy: runs-on: ubuntu-latest if: github.event_name == 'push' && github.ref == 'refs/heads/main' needs: integration-tests steps: - name: Create PostHog annotation env: POSTHOG_PERSONAL_API_KEY: ${{ secrets.POSTHOG_PERSONAL_API_KEY }} POSTHOG_PROJECT_ID: ${{ secrets.POSTHOG_PROJECT_ID }} run: | curl -X POST "https://app.posthog.com/api/projects/$POSTHOG_PROJECT_ID/annotations/" \ -H "Authorization: Bearer $POSTHOG_PERSONAL_API_KEY" \ -H "Content-Type: application/json" \ -d "{ \"content\": \"Deploy: ${GITHUB_SHA::8} — ${GITHUB_EVENT_HEAD_COMMIT_MESSAGE:-$(git log -1 --pretty=%s)}\", \"date_marker\": \"$(date -u +%Y-%m-%dT%H:%M:%SZ)\", \"scope\": \"project\" }"
Step 3: Unit Tests with Mocked PostHog
// tests/analytics.test.ts — runs in CI without any API keys import { describe, it, expect, vi, beforeEach } from 'vitest'; vi.mock('posthog-node', () => ({ PostHog: vi.fn().mockImplementation(() => ({ capture: vi.fn(), identify: vi.fn(), getFeatureFlag: vi.fn().mockResolvedValue('control'), isFeatureEnabled: vi.fn().mockResolvedValue(true), getAllFlags: vi.fn().mockResolvedValue({ 'new-feature': true }), flush: vi.fn().mockResolvedValue(undefined), shutdown: vi.fn().mockResolvedValue(undefined), })), })); import { PostHog } from 'posthog-node'; describe('PostHog Analytics', () => { let ph: InstanceType<typeof PostHog>; beforeEach(() => { vi.clearAllMocks(); ph = new PostHog('phc_test'); }); it('captures events with required properties', () => { ph.capture({ distinctId: 'user-1', event: 'subscription_started', properties: { plan: 'pro', interval: 'annual' }, }); expect(ph.capture).toHaveBeenCalledWith( expect.objectContaining({ event: 'subscription_started', properties: expect.objectContaining({ plan: 'pro' }), }) ); }); it('evaluates feature flags with default fallback', async () => { const enabled = await ph.isFeatureEnabled('new-feature', 'user-1'); expect(enabled).toBe(true); }); it('gets multivariate flag variant', async () => { const variant = await ph.getFeatureFlag('experiment', 'user-1'); expect(variant).toBe('control'); }); });
Step 4: Integration Tests (Real PostHog Project)
// tests/integration/posthog.test.ts import { describe, it, expect, afterAll } from 'vitest'; import { PostHog } from 'posthog-node'; const KEY = process.env.NEXT_PUBLIC_POSTHOG_KEY; describe.skipIf(!KEY)('PostHog Integration', () => { const ph = new PostHog(KEY!, { host: process.env.POSTHOG_HOST || 'https://us.i.posthog.com', flushAt: 1, flushInterval: 0, }); afterAll(async () => await ph.shutdown()); it('captures and flushes an event', async () => { ph.capture({ distinctId: `ci-${Date.now()}`, event: 'ci_integration_test', properties: { ci: true, run_id: process.env.GITHUB_RUN_ID || 'local', }, }); await expect(ph.flush()).resolves.not.toThrow(); }); it('evaluates feature flags', async () => { const flags = await ph.getAllFlags(`ci-${Date.now()}`); expect(typeof flags).toBe('object'); }); it('resolves decide endpoint', async () => { const response = await fetch('https://us.i.posthog.com/decide/?v=3', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ api_key: KEY, distinct_id: 'ci-test' }), }); expect(response.status).toBe(200); const data = await response.json(); expect(data).toHaveProperty('featureFlags'); }); });
Step 5: Package Scripts
{ "scripts": { "test": "vitest run", "test:integration": "vitest run tests/integration/", "test:coverage": "vitest run --coverage" } }
Error Handling
| Issue | Cause | Solution |
|---|---|---|
| Integration tests fail in CI | Secret not configured | Run |
| Tests timeout | PostHog unreachable from CI runner | Increase timeout, add retry |
| Annotation fails | Wrong personal key | Verify key, check project ID |
| Mock type mismatch | PostHog SDK updated | Update mock to match new SDK exports |
Output
- Unit test suite with mocked PostHog (runs everywhere, no keys needed)
- Integration test suite against PostHog dev project (runs on main only)
- Deployment annotations marking each release in PostHog timeline
- GitHub Actions workflow with proper secret management
Resources
Next Steps
For deployment patterns, see
posthog-deploy-integration.