Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -60,9 +60,8 @@ NEXT_PUBLIC_E2B_DOMAIN=e2b.dev
# PLAIN_API_KEY=

### LaunchDarkly feature flags
### Use the SDK key for the LaunchDarkly environment selected below.
### Use the SDK key for the desired LaunchDarkly environment.
# LAUNCHDARKLY_SDK_KEY=
# FEATURE_FLAG_ENVIRONMENT=staging

### OTEL Configuration
# OTEL_SERVICE_NAME=
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ Feature flags are evaluated server-side with LaunchDarkly via OpenFeature and hy
3. Use `featureFlags.isEnabled(...)` on the server or `useFeatureFlag(...)` inside dashboard client components.
4. Target users or teams in LaunchDarkly using the `user` and `team` contexts.

Set `LAUNCHDARKLY_SDK_KEY` and `FEATURE_FLAG_ENVIRONMENT=staging|production` for environments that should use LaunchDarkly.
Set `LAUNCHDARKLY_SDK_KEY` for environments that should use LaunchDarkly. The SDK key determines the LaunchDarkly environment.

## Production Deployment

Expand Down
14 changes: 0 additions & 14 deletions scripts/check-app-env.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,19 +34,5 @@ const schema = appEnvSchema
path: ['PLAIN_API_KEY'],
}
)
.refine(
(data) => {
if (data.LAUNCHDARKLY_SDK_KEY) {
return !!data.FEATURE_FLAG_ENVIRONMENT
}

return true
},
{
message:
'LAUNCHDARKLY_SDK_KEY is set, but FEATURE_FLAG_ENVIRONMENT is missing',
path: ['FEATURE_FLAG_ENVIRONMENT'],
}
)

validateEnv(schema)
16 changes: 0 additions & 16 deletions src/core/modules/feature-flags/context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,20 +8,4 @@ export type FeatureFlagContext = {
slug?: string
name?: string
}
environment?: 'production' | 'staging'
}

export function getFeatureFlagEnvironment(): FeatureFlagContext['environment'] {
switch (process.env.FEATURE_FLAG_ENVIRONMENT) {
case 'production':
case 'staging':
return process.env.FEATURE_FLAG_ENVIRONMENT
}

switch (process.env.VERCEL_ENV) {
case 'production':
return 'production'
default:
return 'staging'
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,7 @@ import type {
EvaluationDetails,
JsonValue,
} from '@openfeature/server-sdk'
import {
type FeatureFlagContext,
getFeatureFlagEnvironment,
} from '@/core/modules/feature-flags/context'
import type { FeatureFlagContext } from '@/core/modules/feature-flags/context'
import type { FeatureFlagDefinition } from '@/core/modules/feature-flags/types'
import { l, serializeErrorForLog } from '@/core/shared/clients/logger/logger'
import { getOpenFeatureServerClient } from './openfeature-client.server'
Expand All @@ -32,12 +29,10 @@ function definedStringAttributes(
export function createOpenFeatureEvaluationContext(
context: FeatureFlagContext
): EvaluationContext {
const environment = context.environment ?? getFeatureFlagEnvironment()
const user = {
targetingKey: context.user.id,
...definedStringAttributes({
email: context.user.email,
environment,
}),
}

Expand All @@ -56,7 +51,6 @@ export function createOpenFeatureEvaluationContext(
...definedStringAttributes({
name: context.team.name,
slug: context.team.slug,
environment,
}),
},
}
Expand Down
1 change: 0 additions & 1 deletion src/lib/env.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ export const serverSchema = z.object({
POSTHOG_PROJECT_ID: z.string().min(1).optional(),

LAUNCHDARKLY_SDK_KEY: z.string().min(1).optional(),
FEATURE_FLAG_ENVIRONMENT: z.enum(['production', 'staging']).optional(),

AUTH_SECRET: z.string().min(1).optional(),
AUTH_TRUST_HOST: z.string().optional(),
Expand Down
37 changes: 2 additions & 35 deletions tests/unit/feature-flags.test.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,5 @@
import { afterEach, describe, expect, it, vi } from 'vitest'
import {
type FeatureFlagContext,
getFeatureFlagEnvironment,
} from '@/core/modules/feature-flags/context'
import { describe, expect, it, vi } from 'vitest'
import type { FeatureFlagContext } from '@/core/modules/feature-flags/context'
import { FEATURE_FLAGS } from '@/core/modules/feature-flags/definitions'
import { createFeatureFlagService } from '@/core/modules/feature-flags/feature-flags.server'
import { createOpenFeatureEvaluationContext } from '@/core/modules/feature-flags/launchdarkly-openfeature-provider.server'
Expand All @@ -17,13 +14,8 @@ const context = {
slug: 'team-slug',
name: 'Team Name',
},
environment: 'staging',
} satisfies FeatureFlagContext

afterEach(() => {
vi.unstubAllEnvs()
})

describe('createFeatureFlagService', () => {
it('evaluates boolean flags through the provider', async () => {
const provider = {
Expand Down Expand Up @@ -94,13 +86,11 @@ describe('createOpenFeatureEvaluationContext', () => {
user: {
targetingKey: 'user-id',
email: 'user@example.com',
environment: 'staging',
},
team: {
targetingKey: 'team-id',
name: 'Team Name',
slug: 'team-slug',
environment: 'staging',
},
})
})
Expand All @@ -117,29 +107,6 @@ describe('createOpenFeatureEvaluationContext', () => {
kind: 'user',
targetingKey: 'user-id',
email: 'user@example.com',
environment: 'staging',
})
})
})

describe('getFeatureFlagEnvironment', () => {
it('uses the explicit feature flag environment', () => {
vi.stubEnv('FEATURE_FLAG_ENVIRONMENT', 'production')

expect(getFeatureFlagEnvironment()).toBe('production')
})

it('maps production Vercel deployments to production', () => {
vi.stubEnv('FEATURE_FLAG_ENVIRONMENT', '')
vi.stubEnv('VERCEL_ENV', 'production')

expect(getFeatureFlagEnvironment()).toBe('production')
})

it('maps non-production deployments to staging', () => {
vi.stubEnv('FEATURE_FLAG_ENVIRONMENT', '')
vi.stubEnv('VERCEL_ENV', 'preview')

expect(getFeatureFlagEnvironment()).toBe('staging')
})
})
Loading