Skip to content

stephenbyrne99/hogsync

Repository files navigation

Hogsync

Type-safe PostHog feature flags with automated sync and local development overrides.

Features

  • 🔄 Bi-directional sync - Generate TypeScript from JSON, sync to PostHog
  • 🎯 Type safety - Full TypeScript support with generated types
  • đź”§ Local development - Override flags locally without touching PostHog
  • ⚡ GitHub Action - Automated CI/CD integration

Installation

Quick Install (Recommended)

curl -fsSL https://raw.githubusercontent.com/stephenbyrne99/hogsync/main/install.sh | bash

Package Managers

Method Platform Command
Shell Script Linux, macOS curl -fsSL https://raw.githubusercontent.com/stephenbyrne99/hogsync/main/install.sh | bash
npm All platforms npm install -g hogsync
Homebrew macOS brew install stephenbyrne99/tap/hogsync (coming soon)
Scoop Windows scoop install hogsync (coming soon)
Arch AUR Linux yay -S hogsync-bin (coming soon)

Manual Download

Download the latest binary from GitHub Releases:

  • Linux x64: hogsync-linux-x64.zip
  • Linux ARM64: hogsync-linux-arm64.zip
  • macOS x64: hogsync-darwin-x64.zip
  • macOS ARM64: hogsync-darwin-arm64.zip

Note: Windows binaries are not currently available in automated releases. Windows users can install via npm: npm install -g hogsync

Usage Examples

# Direct execution (recommended for CI/build scripts)
npx hogsync generate
bunx hogsync generate

# After global installation
npm install -g hogsync
hogsync generate  # Binary is available globally

# As project dependency
npm install --save-dev hogsync
# Then in package.json scripts:
"scripts": {
  "generate:flags": "hogsync generate"
}

Quick Start

Initialize Configuration

npx hogsync init

This creates a hogsync.config.js file:

module.exports = {
  // Directory containing feature flag JSON files
  flagsDir: 'feature-flags',
  
  // Output file for generated TypeScript
  outputFile: 'src/generated/feature-flags.ts',
  
  // PostHog configuration
  posthog: {
    host: process.env.POSTHOG_HOST || 'https://app.posthog.com',
    projectId: process.env.POSTHOG_PROJECT_ID,
    apiToken: process.env.POSTHOG_API_TOKEN,
  },
  
  // Generation options
  generation: {
    includeLocalConfigs: true,
    namingConvention: 'snake_case', // 'camelCase', 'snake_case', 'SCREAMING_SNAKE_CASE'
  }
};

Create Feature Flag Files

Create JSON files in your feature-flags/ directory:

// feature-flags/dark-mode.flag.json
{
  "key": "dark-mode",
  "name": "Dark Mode Toggle",
  "active": true,
  "description": "Enable dark mode theme",
  "filters": {
    "groups": [
      {
        "properties": [
          {
            "key": "email",
            "operator": "icontains",
            "value": "@company.com"
          }
        ]
      }
    ]
  }
}

Generate TypeScript

npx hogsync generate

This generates type-safe constants:

// src/generated/feature-flags.ts
export const FeatureFlags = {
  dark_mode: 'dark-mode',
  ai_chat: 'ai-chat',
} as const;

export type FeatureFlag = typeof FeatureFlags[keyof typeof FeatureFlags];

export const LocalFeatureFlags = {
  dark_mode: {
    key: 'dark-mode',
    name: 'Dark Mode Toggle',
    enabled: true,
  },
} as const;

Sync to PostHog

# Set environment variables
export POSTHOG_PROJECT_ID="your-project-id"
export POSTHOG_API_TOKEN="your-api-token"

# Sync flags to PostHog
npx hogsync sync

Framework Usage

React

First, install the React package:

npm install @stephen_byrne_/hogsync-react

The createFeatureFlags function automatically detects your framework and environment:

import { useFeatureFlagEnabled as usePostHogFeatureFlagEnabled } from "posthog-js/react";
import { LocalFeatureFlags } from "./generated/feature-flags"; // Generated by hogsync
import { createFeatureFlags } from "@stephen_byrne_/hogsync-react";

const {
  useFeatureFlagEnabled,
  getLocalFeatureFlagConfig,
} = createFeatureFlags(usePostHogFeatureFlagEnabled, LocalFeatureFlags);

export function MyComponent() {
  const isDarkModeEnabled = useFeatureFlagEnabled('dark-mode');
  const isNewFeatureEnabled = useFeatureFlagEnabled('new-feature');
  
  return (
    <div className={isDarkModeEnabled ? 'dark' : 'light'}>
      {isNewFeatureEnabled && <NewFeatureComponent />}
    </div>
  );
}

Supported frameworks (auto-detected):

  • Next.js - Detects process.env.NODE_ENV and NEXT_PUBLIC_NODE_ENV
  • Vite - Detects import.meta.env.DEV and import.meta.env.MODE
  • Create React App - Detects process.env.NODE_ENV
  • Browser environments - Falls back to hostname-based detection

Manual Configuration

For custom environments or testing:

const {
  useFeatureFlagEnabled,
  getLocalFeatureFlagConfig,
} = createFeatureFlags(usePostHogFeatureFlagEnabled, LocalFeatureFlags, {
  isDevelopment: window.location.hostname.includes('staging'),
  debug: false, // Disable debug logging
});

GitHub Action

⚡ Fast Setup: The GitHub Action now downloads pre-built binaries instead of compiling from source, making it ~10× faster with no dependency installation required.

Basic Usage

Create .github/workflows/feature-flags.yml:

name: Sync Feature Flags

on:
  push:
    branches: [main, develop]
    paths: ['feature-flags/**']
  pull_request:
    branches: [main]
    paths: ['feature-flags/**']

jobs:
  sync-flags:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      
      - name: Sync PostHog Feature Flags
        uses: stephenbyrne99/hogsync@v1
        with:
          posthog-project-id: ${{ secrets.POSTHOG_PROJECT_ID }}
          posthog-api-token: ${{ secrets.POSTHOG_API_TOKEN }}

Advanced Configuration

name: Feature Flags CI/CD

on:
  push:
    branches: [main]
    paths: ['feature-flags/**']

jobs:
  validate-and-sync:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      
      # Validate flags first
      - name: Validate Feature Flags
        uses: stephenbyrne99/hogsync@v1
        with:
          posthog-project-id: ${{ secrets.POSTHOG_PROJECT_ID }}
          posthog-api-token: ${{ secrets.POSTHOG_API_TOKEN }}
          generate-only: 'true'
          flags-dir: 'custom-flags'
          output-file: 'src/types/feature-flags.ts'
      
      # Commit generated types
      - name: Commit generated types
        run: |
          git config --local user.email "action@github.com"
          git config --local user.name "GitHub Action"
          git add src/types/feature-flags.ts
          git diff --staged --quiet || git commit -m "Update feature flag types"
          git push
      
      # Sync to PostHog only on main branch
      - name: Sync to PostHog
        if: github.ref == 'refs/heads/main'
        uses: stephenbyrne99/hogsync@v1
        with:
          posthog-project-id: ${{ secrets.POSTHOG_PROJECT_ID }}
          posthog-api-token: ${{ secrets.POSTHOG_API_TOKEN }}
          posthog-host: ${{ secrets.POSTHOG_HOST }}
          sync-only: 'true'

Action Inputs

Input Description Required Default
version HogSync version to use ❌ latest
posthog-project-id PostHog project ID âś… -
posthog-api-token PostHog API token âś… -
posthog-host PostHog host URL ❌ https://app.posthog.com
flags-dir Directory containing flag JSON files ❌ feature-flags
output-file Output file for TypeScript ❌ src/generated/feature-flags.ts
config-file Path to configuration file ❌ hogsync.config.js
sync-only Only sync, skip generation ❌ false
generate-only Only generate, skip sync ❌ false

Repository Secrets

Set these in your repository settings:

  • POSTHOG_PROJECT_ID - Your PostHog project ID
  • POSTHOG_API_TOKEN - Your PostHog API token
  • POSTHOG_HOST - (Optional) Custom PostHog host

CLI Commands

# Initialize configuration
npx hogsync init

# Generate TypeScript from flags
npx hogsync generate

# Sync flags to PostHog
npx hogsync sync

# Validate flag definitions
npx hogsync validate

# Watch for changes (coming soon)
npx hogsync watch

# Show help
npx hogsync --help

# Show version
npx hogsync --version

Development Workflow

1. Local Development

In development, feature flags use your local JSON configurations:

// In development: uses LocalFeatureFlags (from JSON)
// In production: uses PostHog API
const isEnabled = useFeatureFlagEnabled('new-feature');

The hook automatically detects your environment and shows debug information:

[Feature Flag Debug] dark-mode: {
  framework: 'next.js',
  isDevelopment: true,
  flagKey: 'dark_mode',
  localConfig: { key: 'dark-mode', name: 'Dark Mode', enabled: true },
  enabled: true,
  posthogEnabled: undefined
}

Framework Detection:

  • vite - Detected via import.meta.env
  • next.js - Detected via NEXT_PUBLIC_NODE_ENV
  • create-react-app - Detected via REACT_APP_* env vars
  • browser - Fallback for browser environments
  • unknown - When framework cannot be determined

2. Package.json Scripts

Add to your package.json:

{
  "scripts": {
    "flags:generate": "npx hogsync generate",
    "flags:sync": "npx hogsync sync",
    "flags:validate": "npx hogsync validate",
    "dev": "npm run flags:generate && next dev"
  }
}

3. Build Scripts (Vercel, Netlify, etc.)

For build environments that support bunx:

{
  "scripts": {
    "build": "bunx hogsync generate && next build",
    "dev": "bunx hogsync generate && next dev"
  }
}

4. Development with Auto-Updates

For automatic flag generation during development, set up your scripts to regenerate flags before starting your dev server:

{
  "scripts": {
    "dev": "bun run flags:generate && next dev --port 3001",
    "build": "bun run flags:generate && next build",
    "flags:generate": "npx hogsync generate",
    "flags:sync": "npx hogsync sync",
    "flags:validate": "npx hogsync validate",
    "start": "next start --port 3001",
    "lint": "next lint",
    "check-types": "tsc --noEmit",
    "format": "biome format --write ."
  }
}

This ensures your TypeScript types are always up-to-date when you start development or build your project.

5. Git Hooks

Pre-commit hook to validate flags:

#!/bin/sh
# .git/hooks/pre-commit
npx hogsync validate

Advanced Configuration

Custom Naming Conventions

// hogsync.config.js
module.exports = {
  generation: {
    namingConvention: 'camelCase', // darkMode instead of dark_mode
  }
};

Multiple Environments

module.exports = {
  environments: {
    development: {
      posthog: { projectId: 'dev-project' }
    },
    production: {
      posthog: { projectId: 'prod-project' }
    }
  }
};

Custom Development Detection

// Override auto-detection for specific environments
const hooks = createFeatureFlags(usePostHogFeatureFlagEnabled, LocalFeatureFlags, {
  isDevelopment: window.location.hostname.includes('staging'),
  debug: false, // Disable logging
});

// Force development mode for testing
const testHooks = createFeatureFlags(usePostHogFeatureFlagEnabled, LocalFeatureFlags, {
  isDevelopment: true, // Always use local flags
  debug: false
});

Best Practices

1. Flag Naming

Use kebab-case for flag keys:

{
  "key": "new-checkout-flow",
  "name": "New Checkout Flow"
}

2. Gradual Rollouts

Use PostHog's percentage rollouts:

{
  "key": "beta-feature",
  "active": true,
  "filters": {
    "groups": [
      {
        "rollout_percentage": 10
      }
    ]
  }
}

3. Feature Flag Cleanup

Remove flags after full rollout:

  1. Set flag to 100% rollout
  2. Remove conditional code
  3. Delete flag JSON file
  4. Run npx hogsync generate

4. Testing

Use manual overrides in tests:

// Always use local flags in tests
const testHooks = createFeatureFlags(mockPostHogHook, LocalFeatureFlags, {
  isDevelopment: true, // Force local flags
  debug: false // Disable logging
});

Migration from Existing Setup

If you have existing PostHog feature flags:

  1. Export flags from PostHog
  2. Create JSON files matching the structure
  3. Run npx hogsync-cli generate
  4. Update your code to use the new hooks

Troubleshooting

Environment Detection Issues

If auto-detection fails, manually specify:

// Manual override for edge cases
const hooks = createFeatureFlags(usePostHogFeatureFlagEnabled, LocalFeatureFlags, {
  isDevelopment: process.env.NODE_ENV === 'development'
});

Build Issues

Ensure the React package is installed separately:

// This works
import { LocalFeatureFlags } from "./generated/feature-flags";
import { createFeatureFlags } from "@stephen_byrne_/hogsync-react";

// This might cause issues in non-React projects
import { createFeatureFlags } from "hogsync"; // Don't do this

Disclaimer

I have no association or affiliation with PostHog, and was just using this internally to manage my own feature flags.

License

MIT - see LICENSE file for details.

About

Manage posthog feature flags from code with local editing support

Resources

License

Contributing

Stars

Watchers

Forks

Packages

No packages published