Skip to content

feat(generic-filters): Add a semver rule condition - #6430

Open
shellmayr wants to merge 14 commits into
masterfrom
shellmayr/feat/version-rule-condition
Open

shellmayr wants to merge 14 commits into
masterfrom
shellmayr/feat/version-rule-condition

Conversation

@shellmayr

@shellmayr shellmayr commented Sep 25, 2026 •

Copy link
Copy Markdown
Member
  • Add a semver rule condition, next to glob and cidr. It compares the version of a release, so generic inbound filters can express version ranges.
  • The comparator is a field of the condition {"op": "semver", "name": "event.release", "comparator": "gte", "value": "1.2.0"}. Comparators are eq, gt, gte, lt, and lte. eq compares by version, so it ignores the build code.
  • Releases are parsed with sentry-release-parser, the way Sentry reads them. A version has one to four numeric components, and missing components are zero, so 1.2 compares as 1.2.0. A pre-release ranks below its final release, and build codes are ignored. A release without a version, such as a commit hash, never matches.
  • Packages: a value without a package, such as 1.2.0, matches releases of every package. A value with a package, such as myapp@1.2.0, only matches releases of that package.

shellmayr and others added 5 commits September 25, 2026 10:09
Generic inbound filters can only match releases by glob, so a range such
as "every release below 2.0" is not expressible. Add a "version" op that
parses constraints like ">=1.2.0, <2.0.0" or "~>1.2" and compares them
against the version part of a Sentry release.

Versions order by semver precedence, so a pre-release ranks below its
final release and build codes are ignored. A release without a version,
such as a commit hash, never matches. Entries that do not parse are
skipped, like invalid glob patterns. Old Relays read the op as
unsupported, so a filter using it is inert there and other filters keep
working.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Keep only ">", ">=", "<", and "<=". A value without an operator, "=",
"!=", and "~>" are no longer parsed, so they are skipped like any
invalid entry.

Drop the semver crate and order versions with the release parser's
own components instead. This handles four components and compares
pre-release tags as text, which is how Sentry orders releases. Missing
components are zero, so ">1.2" matches "1.2.3", like Sentry's
release search.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Replace the dedicated "version" op with a release type on the data.
Getters return release fields as Val::Release, and the gt, gte, lt,
and lte conditions order that type by version instead of as text. The
eq and glob conditions keep treating a release as a string, so
existing release filters are unchanged.

Sentry can now emit {"op": "gte", "name": "event.release", "value":
"1.2.0"} without a new op in the DSL. A range is an "and" of two
conditions. Relays that predate this change compare the same JSON
lexicographically, which is accepted since managed Relays update within
days.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@shellmayr shellmayr changed the title feat(protocol): Add a version rule condition for release constraints feat(protocol): Compare releases by version in comparison conditions Sep 25, 2026
Keep the item getters, the sampling context, and the schema untouched.
The generic filter wrapper that already adds the envelope client IP
retypes the four release paths as Val::Release, so only generic
inbound filters compare releases by version.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@shellmayr shellmayr changed the title feat(protocol): Compare releases by version in comparison conditions feat(filters): Compare releases by version in generic inbound filters Sep 25, 2026
Move the version ordering into a Release type local to relay-filter,
and evaluate the gt, gte, lt, and lte conditions there. A string field
in a generic filter comparison is parsed as a release on both sides
and ordered by version; any other field compares as the condition does
itself. No release paths are listed, and relay-protocol is untouched.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@shellmayr shellmayr changed the title feat(filters): Compare releases by version in generic inbound filters feat(filters): Compare strings as release versions in generic inbound filters Sep 25, 2026
Replace the release parser with a Semver type built on the semver
crate. A release must carry a full semantic version, optionally behind
a Sentry package prefix such as "myapp@1.2.3". Ordering follows semver
precedence, so build metadata is ignored and pre-release identifiers
compare per the spec.

Add an integration test that filters events by a release range through
a processing Relay.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@shellmayr shellmayr changed the title feat(filters): Compare strings as release versions in generic inbound filters feat(filters): Compare releases as semantic versions in generic inbound filters Sep 28, 2026
Replace the semver interpretation of gt, gte, lt, and lte in generic
filters with a dedicated "semver" condition, like "glob" and "cidr".
The comparator is a field of the condition, so the author can validate
it and the version before emitting the condition:

  {"op": "semver", "name": "event.release",
   "comparator": "gte", "value": "1.2.0"}

Comparators are eq, gt, gte, lt, and lte. An unknown comparator or a
value that is not a semantic version makes the condition unsupported,
which relay_validate_rule_condition reports, and it never matches.

The filter crate needs no special evaluation anymore and goes back to
RuleCondition::matches.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@shellmayr shellmayr changed the title feat(filters): Compare releases as semantic versions in generic inbound filters feat(protocol): Add a semver rule condition Sep 29, 2026
shellmayr and others added 2 commits September 29, 2026 11:46
Test each layer for what only it owns. The Semver type covers parsing
and precedence. The condition covers a truth table over all comparators,
the inputs that never match, and the unknown comparator. One
integration test proves the filter end to end with two events.

Drop the filter crate test, since the crate has no production change,
and the roundtrip test, since the snapshot covers the serialized shape.
Reduce the Semver type to parsing and one comparison method.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@shellmayr shellmayr changed the title feat(protocol): Add a semver rule condition feat(generic-filters): Add a semver rule condition Sep 29, 2026
@shellmayr
shellmayr marked this pull request as ready for review September 29, 2026 10:27
@shellmayr
shellmayr requested a review from a team as a code owner September 29, 2026 10:27
@shellmayr
shellmayr requested a review from a team September 29, 2026 10:32
Comment thread relay-protocol/src/semver.rs Outdated
///
/// A release is either a version such as `1.2.3-rc.1+build` or a Sentry release with a package,
/// such as `myapp@1.2.3`. The version must be a full semantic version with three components.
pub(crate) struct Semver(Version);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we need another wrapper here?

There is already sentry_release_parser::Release, can we use that?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sentry_release_parser::Release afaik matches more as it allows the package specifiers. Depending whether that's wanted I think we either can go with sentry_release_parser::Release or just semver.

I almost assume that supporting the package specifiers is an upside. Though not sure how the matching for partial versions is. For example:

org.example.FooApp@1.0 >= 1.0 # is this true if the package is missing? Should it be?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Switched to using sentry_release_parser

Comment thread relay-protocol/src/condition.rs Outdated
/// The comparison to apply between the field and the value.
pub comparator: SemverComparator,
/// The semantic version to compare the field against.
pub value: String,

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As with IpNetworks, we should make value a parsed semver type (Version or whatever). This then requires Serialize + Deserialize, but it is better than parsing on every evaluation.

Comment thread relay-protocol/src/semver.rs Outdated
///
/// A release is either a version such as `1.2.3-rc.1+build` or a Sentry release with a package,
/// such as `myapp@1.2.3`. The version must be a full semantic version with three components.
pub(crate) struct Semver(Version);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This can be public as the module itself isn't public. We generally try to avoid these restricted visibility modifiers as pub(crate).

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changed

Comment thread relay-protocol/src/semver.rs Outdated
///
/// A release is either a version such as `1.2.3-rc.1+build` or a Sentry release with a package,
/// such as `myapp@1.2.3`. The version must be a full semantic version with three components.
pub(crate) struct Semver(Version);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sentry_release_parser::Release afaik matches more as it allows the package specifiers. Depending whether that's wanted I think we either can go with sentry_release_parser::Release or just semver.

I almost assume that supporting the package specifiers is an upside. Though not sure how the matching for partial versions is. For example:

org.example.FooApp@1.0 >= 1.0 # is this true if the package is missing? Should it be?

shellmayr and others added 3 commits September 29, 2026 14:51
Use sentry-release-parser instead of the semver crate, so the condition
reads releases the way Sentry does. Versions have one to four
components, and missing components are zero.

Define how packages compare. A value without a package matches releases
of every package. A value with a package only matches releases of that
package.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The module is private, so restricted visibility adds nothing.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Make the value of the semver condition a parsed type, like IpNetworks,
so the release is parsed when the condition is deserialized and not on
every evaluation.

A string without a version still deserializes and keeps its text, so
one bad condition cannot fail a project config. The condition reports
it as unsupported and never matches.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants