Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
8e49377
first pass
dmarticus Jan 8, 2026
7d16808
new tests
dmarticus Jan 8, 2026
ffbe642
updates
dmarticus Jan 8, 2026
97ca903
Merge branch 'master' into chore/refactor-user-blast-radius
dmarticus Jan 8, 2026
7c36535
edge case property matching logic + tests
dmarticus Jan 8, 2026
4c81931
clean up some edge cases around other operators
dmarticus Jan 8, 2026
49ad335
Merge branch 'master' into chore/refactor-user-blast-radius
dmarticus Jan 8, 2026
c78e23a
fist pass
dmarticus Jan 9, 2026
35da331
added tests for the new operators
dmarticus Jan 9, 2026
ac9816e
Merge branch 'master' into chore/refactor-user-blast-radius
dmarticus Jan 9, 2026
280f5de
Merge remote-tracking branch 'origin/chore/refactor-user-blast-radius…
dmarticus Jan 9, 2026
4d2776d
oh yeah
dmarticus Jan 9, 2026
5ae08a2
fix some failing tests
dmarticus Jan 9, 2026
e67c575
got mypy happy
dmarticus Jan 10, 2026
9a5ffb3
Merge branch 'master' into feat/support-semver-targeting-in-feature-f…
dmarticus Jan 10, 2026
d6cc8b3
formatting
dmarticus Jan 10, 2026
6a38fef
chore(ci): update backend test timings
github-actions[bot] Jan 11, 2026
8959ec5
merge conflicts
dmarticus Jan 11, 2026
c73f506
Merge branch 'master' into feat/support-semver-comparisons-in-user-bl…
dmarticus Jan 12, 2026
6ac2b1a
git markers
dmarticus Jan 12, 2026
7f8cdec
Merge branch 'feat/support-semver-comparisons-in-user-blast-radius' o…
dmarticus Jan 12, 2026
2d07717
Merge branch 'feat/support-semver-comparisons-in-user-blast-radius' i…
dmarticus Jan 12, 2026
48e5953
handling some greptile feedback
dmarticus Jan 12, 2026
23edb1c
fix compiler
dmarticus Jan 12, 2026
e09ae4f
Merge branch 'feat/support-semver-targeting-in-feature-flags' into fe…
dmarticus Jan 12, 2026
4d21524
merge conflict
dmarticus Jan 12, 2026
f616d6a
no but fix this up tho
dmarticus Jan 12, 2026
9dced88
fix test
dmarticus Jan 12, 2026
5683802
Merge branch 'feat/support-semver-targeting-in-feature-flags' into fe…
dmarticus Jan 12, 2026
ca08675
*hagrid voice* I shouldn't have done that; I should NOT have done that
dmarticus Jan 12, 2026
b8f245c
this isn't relevant either!
dmarticus Jan 12, 2026
3d5a672
updates
dmarticus Jan 12, 2026
4530bee
one more fix
dmarticus Jan 12, 2026
61a486a
Merge branch 'feat/support-semver-targeting-in-feature-flags' into fe…
dmarticus Jan 12, 2026
329b15c
hell ya
dmarticus Jan 12, 2026
0b5cb35
resolve merge conflicts
dmarticus Jan 13, 2026
128f1ad
missing operator
dmarticus Jan 13, 2026
ae90cb9
missing operator
dmarticus Jan 13, 2026
92a8088
include a conflict
dmarticus Jan 13, 2026
57a5b90
test(storybook): update UI snapshots
github-actions[bot] Jan 13, 2026
b4dc85b
gate this behind a feature flag
dmarticus Jan 13, 2026
018610b
Merge branch 'feat/support-semver-targeting-in-feature-flags-ui' of g…
dmarticus Jan 13, 2026
c5aaef5
appease tsc and friends
dmarticus Jan 14, 2026
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
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
import { useValues } from 'kea'
import { useEffect, useState } from 'react'

import { LemonDropdownProps, LemonSelect, LemonSelectProps } from '@posthog/lemon-ui'

import { allOperatorsToHumanName } from 'lib/components/DefinitionPopover/utils'
import { FEATURE_FLAGS } from 'lib/constants'
import { dayjs } from 'lib/dayjs'
import { featureFlagLogic } from 'lib/logic/featureFlagLogic'
import {
allOperatorsMapping,
chooseOperatorMap,
Expand All @@ -13,6 +16,7 @@ import {
isOperatorMulti,
isOperatorRange,
isOperatorRegex,
isOperatorSemver,
} from 'lib/utils'

import {
Expand Down Expand Up @@ -107,6 +111,8 @@ export function OperatorValueSelect({
operatorAllowlist,
forceSingleSelect,
}: OperatorValueSelectProps): JSX.Element {
const { featureFlags } = useValues(featureFlagLogic)
const semverTargetingEnabled = !!featureFlags[FEATURE_FLAGS.SEMVER_TARGETING]
const lookupKey = type === PropertyFilterType.DataWarehousePersonProperty ? 'id' : 'name'
const propertyDefinition = propertyDefinitions.find((pd) => pd[lookupKey] === propertyKey)

Expand Down Expand Up @@ -157,6 +163,10 @@ export function OperatorValueSelect({
const operatorMapping: Record<string, string> = chooseOperatorMap(propertyType)

let operators = (Object.keys(operatorMapping) as Array<PropertyOperator>).filter((op) => {
// Filter out semver operators if feature flag is not enabled
if (!semverTargetingEnabled && isOperatorSemver(op)) {
return false
}
return !operatorAllowlist || operatorAllowlist.includes(op)
})

Expand Down Expand Up @@ -190,7 +200,7 @@ export function OperatorValueSelect({
}
setCurrentOperator(defaultProperty)
}
}, [propertyDefinition, propertyKey, operator, operatorAllowlist]) // oxlint-disable-line react-hooks/exhaustive-deps
}, [propertyDefinition, propertyKey, operator, operatorAllowlist, semverTargetingEnabled]) // oxlint-disable-line react-hooks/exhaustive-deps
return (
<>
<div data-attr="taxonomic-operator">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,10 @@ export function PropertyValue({
const isAssigneeProperty =
propertyKey && describeProperty(propertyKey, propertyDefinitionType) === PropertyType.Assignee

// TODO: Add semver input validation when a semver operator is selected.
// This will require detecting isOperatorSemver(operator) and validating the input
// matches semver format (e.g., "1.2.3", "1.2.3-alpha", etc.)

const load = useCallback(
(newInput: string | undefined): void => {
loadPropertyValues({
Expand Down
1 change: 1 addition & 0 deletions frontend/src/lib/constants.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -334,6 +334,7 @@ export const FEATURE_FLAGS = {
SCHEMA_MANAGEMENT: 'schema-management', // owner: @aspicer
SEEKBAR_PREVIEW_SCRUBBING: 'seekbar-preview-scrubbing', // owner: @pauldambra #team-replay
SESSIONS_EXPLORER: 'sessions-explorer', // owner: @jabahamondes #team-web-analytics
SEMVER_TARGETING: 'semver-targeting', // owner: #team-feature-flags
SHOPIFY_DWH: 'shopify-dwh', // owner: @andrew #team-data-stack
SHOW_REFERRER_FAVICON: 'show-referrer-favicon', // owner: @jordanm-posthog #team-web-analytics
SHOW_REPLAY_FILTERS_FEEDBACK_BUTTON: 'show-replay-filters-feedback-button', // owner: @ksvat #team-replay
Expand Down
46 changes: 35 additions & 11 deletions frontend/src/lib/utils.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -263,6 +263,15 @@ export const stringOperatorMap: Record<string, string> = {
not_regex: "≁ doesn't match regex",
is_set: '✓ is set',
is_not_set: '✕ is not set',
semver_eq: '= equals (semver)',
semver_neq: '≠ not equal (semver)',
semver_gt: '> greater than (semver)',
semver_gte: '≥ greater than or equal (semver)',
semver_lt: '< less than (semver)',
semver_lte: '≤ less than or equal (semver)',
semver_tilde: '~ tilde range (semver)',
semver_caret: '^ caret range (semver)',
semver_wildcard: '* wildcard (semver)',
}

export const stringArrayOperatorMap: Record<string, string> = {
Expand Down Expand Up @@ -330,15 +339,15 @@ export const cleanedPathOperatorMap: Record<string, string> = {
}

export const semverOperatorMap: Record<string, string> = {
semver_eq: '= equals',
semver_neq: '≠ not equal',
semver_gt: '> greater than',
semver_gte: '≥ greater than or equal',
semver_lt: '< less than',
semver_lte: '≤ less than or equal',
semver_tilde: '~ tilde range',
semver_caret: '^ caret range',
semver_wildcard: '* wildcard',
semver_eq: '= equals (semver)',
semver_neq: '≠ not equal (semver)',
semver_gt: '> greater than (semver)',
semver_gte: '≥ greater than or equal (semver)',
semver_lt: '< less than (semver)',
semver_lte: '≤ less than or equal (semver)',
semver_tilde: '~ tilde range (semver)',
semver_caret: '^ caret range (semver)',
semver_wildcard: '* wildcard (semver)',
}

export const assigneeOperatorMap: Record<string, string> = {
Expand All @@ -351,7 +360,7 @@ export const allOperatorsMapping: Record<string, string> = {
...assigneeOperatorMap,
...stickinessOperatorMap,
...dateTimeOperatorMap,
...stringOperatorMap,
...semverOperatorMap,
...stringArrayOperatorMap,
...numericOperatorMap,
...genericOperatorMap,
Expand All @@ -361,7 +370,7 @@ export const allOperatorsMapping: Record<string, string> = {
...cohortOperatorMap,
...featureFlagOperatorMap,
...cleanedPathOperatorMap,
...semverOperatorMap,
...stringOperatorMap,
// slight overkill to spread all of these into the map
// but gives freedom for them to diverge more over time
}
Expand All @@ -377,6 +386,7 @@ const operatorMappingChoice: Record<keyof typeof PropertyType, Record<string, st
Flag: featureFlagOperatorMap,
Assignee: assigneeOperatorMap,
StringArray: stringArrayOperatorMap,
Semver: semverOperatorMap,
}

export function chooseOperatorMap(propertyType: PropertyType | undefined): Record<string, string> {
Expand Down Expand Up @@ -407,6 +417,20 @@ export function isOperatorRegex(operator: PropertyOperator): boolean {
return [PropertyOperator.Regex, PropertyOperator.NotRegex].includes(operator)
}

export function isOperatorSemver(operator: PropertyOperator): boolean {
return [
PropertyOperator.SemverEq,
PropertyOperator.SemverNeq,
PropertyOperator.SemverGt,
PropertyOperator.SemverGte,
PropertyOperator.SemverLt,
PropertyOperator.SemverLte,
PropertyOperator.SemverTilde,
PropertyOperator.SemverCaret,
PropertyOperator.SemverWildcard,
].includes(operator)
}

export function isOperatorRange(operator: PropertyOperator): boolean {
return [
PropertyOperator.GreaterThan,
Expand Down
1 change: 1 addition & 0 deletions frontend/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4000,6 +4000,7 @@ export enum PropertyType {
Assignee = 'Assignee',
StringArray = 'StringArray',
Flag = 'Flag',
Semver = 'Semver',
}

export enum PropertyDefinitionType {
Expand Down
6 changes: 3 additions & 3 deletions rust/feature-flags/src/properties/property_matching.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,12 +35,12 @@ pub fn to_f64_representation(value: &Value) -> Option<f64> {

/// Strip 'v' prefix if present (e.g., "v1.2.3" -> "1.2.3")
fn normalize_version_string(version: &str) -> &str {
version.strip_prefix('v').unwrap_or(version)
version.strip_prefix('v').unwrap_or(version).trim()
}

pub fn to_semver_representation(value: &Value) -> Option<Version> {
let version_string = to_string_representation(value);
let normalized = normalize_version_string(&version_string).trim();
let normalized = normalize_version_string(&version_string);
// TODO: Build metadata (e.g., "1.0.0+build.1") is not currently supported because
// our `sortableSemver` method in ClickHouse/HogQL doesn't support it yet.
// For semver equality checks, use regular string equality operators instead.
Expand Down Expand Up @@ -298,7 +298,7 @@ pub fn match_property(

// Build the version requirement string based on the operator
let version_string = to_string_representation(value);
let normalized_version = normalize_version_string(&version_string).trim();
let normalized_version = normalize_version_string(&version_string);

let requirement_string = match operator {
OperatorType::SemverTilde => format!("~{normalized_version}"),
Expand Down
Loading