posthog-ci-integration
Sets up PostHog integration testing and deployment tracking in GitHub Actions.
Install
mkdir -p .claude/skills/posthog-ci-integration && curl -L -o skill.zip "https://agentskills.codes/api/skills/download/7275" && unzip -o skill.zip -d .claude/skills/posthog-ci-integration && rm skill.zipInstalls to .claude/skills/posthog-ci-integration
Activation
This is the description your AI agent reads to decide when to run this skill — the better it matches your request, the more reliably it fires.
Configure PostHog CI/CD with GitHub Actions: unit tests with mockedKey capabilities
- →Configure GitHub secrets for PostHog API keys and project ID
- →Set up GitHub Actions workflows for unit and integration tests
- →Mock PostHog SDK for unit tests without API keys
- →Run integration tests against a PostHog dev project
- →Create deployment annotations in PostHog for release tracking
How it works
The skill configures GitHub secrets for PostHog API keys and project ID. It then sets up GitHub Actions workflows to run unit tests with a mocked PostHog SDK and integration tests against a PostHog development project, also creating deployment annotations.
Inputs & outputs
When to use posthog-ci-integration
- →Adding PostHog to CI/CD pipelines
- →Mocking PostHog in unit tests
- →Configuring integration tests for PostHog
- →Marking deployments in PostHog analytics
About this skill
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 gh secret set POSTHOG_TEST_KEY |
| Tests timeout | PostHog unreachable from CI runner | Increase timeout, add retry |
| Annotation fails | Wrong personal key | Verify phx_ 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.
When not to use it
- →When not using npm/pnpm with vitest or jest
- →When not needing deployment annotations in PostHog
Prerequisites
Limitations
- →Integration tests require a PostHog dev project API key
- →Deployment annotations require a PostHog personal API key and project ID
- →Mock type mismatches can occur if the PostHog SDK is updated
How it compares
This skill automates PostHog CI/CD setup with GitHub Actions, which differs from manually configuring testing and deployment tracking for PostHog.
Compared to similar skills
posthog-ci-integration side by side with the closest alternatives in the catalog.
| Skill | Installs | Updated | Safety | Difficulty |
|---|---|---|---|---|
| posthog-ci-integration (this skill) | 1 | 27d | Caution | Intermediate |
| cicd-diagnostics | 1 | 4mo | Review | Advanced |
| verification-quality-assurance | 1 | 3mo | Review | Intermediate |
| circleci-why-flaky | 0 | 1mo | Review | Intermediate |
Try saying
Example prompts that trigger this skill in your AI assistant.
More by jeremylongshore
View all by jeremylongshore →You might also like
cicd-diagnostics
dotCMS
Diagnoses DotCMS GitHub Actions failures (PR builds, merge queue, nightly, trunk). Analyzes failed tests, root causes, compares runs. Use for "fails in GitHub", "merge queue failure", "PR build failed", "nightly build issue".
verification-quality-assurance
ruvnet
Comprehensive truth scoring, code quality verification, and automatic rollback system with 0.95 accuracy threshold for ensuring high-quality agent outputs and codebase reliability.
circleci-why-flaky
mui
Classify recent CircleCI workflow failures into buckets so you can see what's flake vs what's a real bug. Use when CI on a branch is failing repeatedly and you want to know whether retries will fix it. Infers project + branch from the current git repo; defaults to last 7 days.
regression-testing
upex-galaxy
Execute regression test suites via CI/CD, analyze results, classify failures, and produce GO/NO-GO release decisions. Use when running regression, smoke, or sanity suites through GitHub Actions, monitoring workflow runs, downloading Allure or Playwright artifacts, classifying failures (REGRESSION vs
ci
PioneersHub
Run the local CI pipeline (ruff, bandit, pytest, sonar-scanner) and refresh SonarQube.
qa-tester
svilupp
Browser automation QA testing skill. Systematically tests web applications for functionality, security, and usability issues. Reports findings by severity (CRITICAL/HIGH/MEDIUM/LOW) with immediate alerts for critical failures.