feat(flags): add semver targeting to local evaluation#166
Conversation
posthog-go Compliance ReportDate: 2026-03-04 23:16:06 UTC ✅ All Tests Passed!29/29 tests passed Capture Tests✅ 29/29 tests passed View Details
|
There was a problem hiding this comment.
Pull request overview
Adds semantic-version (semver) targeting support to the local feature flag evaluator, aligning Go SDK behavior with existing implementations in other PostHog components.
Changes:
- Bump SDK version to
1.11.0. - Add 9 semver operators to
matchProperty, plus inline semver parsing and range bound computation. - Add extensive unit/integration test coverage for semver parsing and operator behavior.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 6 comments.
| File | Description |
|---|---|
version.go |
Version bump to reflect new feature flag targeting capabilities. |
featureflags.go |
Implements semver operators in matchProperty and adds semver parsing/range helpers. |
feature_flags_matching_test.go |
Adds parsing/comparison tests and operator-level matching tests for semver behavior. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
You can also share your feedback on Copilot code review. Take the survey.
featureflags.go
Outdated
|
|
||
| overrideParsed, err := parseSemver(overrideStr) | ||
| if err != nil { | ||
| return false, &InconclusiveMatchError{fmt.Sprintf("person property value '%s' is not a valid semver", overrideStr)} |
There was a problem hiding this comment.
The error message says "person property value", but matchProperty is used for both person and group properties and doesn't check the property type here. Consider changing this to a neutral "property value" (and likewise in the other semver operator branches) to avoid misleading diagnostics.
| return false, &InconclusiveMatchError{fmt.Sprintf("person property value '%s' is not a valid semver", overrideStr)} | |
| return false, &InconclusiveMatchError{fmt.Sprintf("property value '%s' is not a valid semver", overrideStr)} |
featureflags.go
Outdated
| return false, &InconclusiveMatchError{fmt.Sprintf("person property value '%s' is not a valid semver", overrideStr)} | ||
| } |
There was a problem hiding this comment.
The error message says "person property value", but matchProperty is used for both person and group properties and doesn't check the property type here. Consider changing this to a neutral "property value" to avoid misleading diagnostics.
featureflags.go
Outdated
| return false, &InconclusiveMatchError{fmt.Sprintf("person property value '%s' is not a valid semver", overrideStr)} | ||
| } |
There was a problem hiding this comment.
The error message says "person property value", but matchProperty is used for both person and group properties and doesn't check the property type here. Consider changing this to a neutral "property value" to avoid misleading diagnostics.
featureflags.go
Outdated
| return false, &InconclusiveMatchError{fmt.Sprintf("person property value '%s' is not a valid semver", overrideStr)} | ||
| } |
There was a problem hiding this comment.
The error message says "person property value", but matchProperty is used for both person and group properties and doesn't check the property type here. Consider changing this to a neutral "property value" to avoid misleading diagnostics.
featureflags.go
Outdated
| if idx := strings.Index(text, "-"); idx >= 0 { | ||
| text = text[:idx] | ||
| } | ||
| if idx := strings.Index(text, "+"); idx >= 0 { | ||
| text = text[:idx] |
There was a problem hiding this comment.
The semver suffix stripping treats the first '-' or '+' anywhere in the string as a pre-release/build delimiter. This means malformed inputs like "1-2.3" or "1.-2.3" can be truncated and parsed as valid versions (e.g. "1"). Consider validating the core version format (digits/dots only) before stripping suffixes so invalid cores fail parsing instead of silently changing meaning.
| minor := 0 | ||
| if len(parts) > 1 && parts[1] != "" { | ||
| minor, err = strconv.Atoi(parts[1]) | ||
| if err != nil { | ||
| return semverTuple{}, fmt.Errorf("invalid semver: minor version '%s' is not a number", parts[1]) |
There was a problem hiding this comment.
This logic defaults a missing/empty minor component to 0 (and similarly for patch below), which makes malformed strings like "1..3" or "1." parse successfully. If the intent is only to support shortened forms like "1" and "1.2", consider returning an error when a component is present but empty (i.e. a "." was provided with no digits).
- Change "person property value" to "property value" in error messages since matchProperty is used for both person and group properties - Validate core version format before stripping pre-release/build suffixes to reject malformed inputs like "1-2.3" or "-alpha" - Return error when a version component is present but empty (e.g., "1..3" or "1.") instead of silently defaulting to 0 - Add tests for newly rejected malformed inputs
Summary
This matches the behavior already implemented in:
user_blast_radiusposthog#44596Operators Added
semver_eqsemver_neqsemver_gtsemver_gtesemver_ltsemver_ltesemver_tildesemver_caretsemver_wildcardSemver Parsing
The
SemanticVersionhelper class handles:v1.2.3→1.2.3)1.2.3-alpha→1.2.3)1.2.3+build.123→1.2.3)1.2→1.2.0)1.2.3.4→1.2.3)No external semver library is used—parsing is implemented inline per SDK guidelines.
Test plan
SemanticVersionparsing and comparisonLocalEvaluatornetstandard2.1andnet8.0🤖 Generated with Claude Code