Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(tracing): Allow manual creation and sending of spanless Transactions #631

Merged
merged 22 commits into from
Dec 23, 2021

Conversation

relaxolotl
Copy link
Contributor

@relaxolotl relaxolotl commented Dec 16, 2021

The proverbial "rest of the owl" PR. This hooks up everything so that it is possible to create and send a transaction, provided that the user is capable of manually finishing that transaction by threading it through everything it is meant to measure.

The main purpose of this is to get some sort of transaction showing up on sentry. Things not covered in this PR:

This is being submitted as a larger PR (in comparison to my previous ones) because much of the code relies on understanding a transaction's inner structure. I was hesitant to expose transaction_start nor transaction_finish in an incomplete state, but those two are important in providing context on what some of the functions here, such as prepare_transaction were doing.

relates to #601

picks up off of #630

@relaxolotl relaxolotl requested a review from a team December 16, 2021 08:32
Copy link
Member

@Swatinem Swatinem left a comment

Choose a reason for hiding this comment

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

looking good, though the decref of the primitive value technically hides a use after free ;-)
and I noted down some questions that need clearing up maybe.

include/sentry.h Outdated Show resolved Hide resolved
src/backends/sentry_backend_inproc.c Outdated Show resolved Hide resolved
examples/example.c Outdated Show resolved Hide resolved
src/sentry_core.c Outdated Show resolved Hide resolved
{
sentry_value_t tx = sentry_value_new_event();

// TODO: stuff transaction into the scope
Copy link
Member

Choose a reason for hiding this comment

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

shouldn’t API users do that themselves rather?

Copy link
Contributor

Choose a reason for hiding this comment

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

if we go with today's suggestion 3, then yes i think

Copy link
Contributor Author

Choose a reason for hiding this comment

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

no: the transaction which is occasionally referred to as as the root span, and is a superset of a span is managed by the native SDK. transactions tend to contain things that track all of the child spans that spawn off them, and notably have names which spans do not. in addition to that, i'm pretty sure that the native SDK is committing to only ever letting one transaction be active at once, which couples nicely with the way the scope is implemented and managed in this SDK.

spans, which are all children of a transaction will be managed by the end users.

that was my understanding of the discussion, and is subject to my shoddy short term memory. feel free to correct or comment on this conclusion.

src/sentry_core.c Outdated Show resolved Hide resolved
src/sentry_core.c Outdated Show resolved Hide resolved
sentry_value_set_by_key(tx, "status", sentry_value_new_string("ok"));

sentry_value_t trace_context = sentry__span_get_trace_context(tx);
sentry_value_t contexts = sentry_value_new_object();
Copy link
Member

Choose a reason for hiding this comment

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

are we absolutely sure there is no contexts yet on the transaction value?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

by this point, yes. unless a user deliberately tries to insert a context properly via the sentry_value API instead of using the provided setters for transactions in the public API, the context field should not exist on a transaction yet during this method.

src/sentry_core.c Outdated Show resolved Hide resolved
include/sentry.h Outdated
* or manually constructed by a user.
*/
SENTRY_EXPERIMENTAL_API sentry_value_t sentry_transaction_start(
sentry_value_t transaction);
Copy link
Contributor

Choose a reason for hiding this comment

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

after todays discussion are we going roll this function into sentry_value_new_transaction?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

i don't think we can/should roll this into sentry_value_new_transaction, unfortunately. i do think we may need to re-append _context to some of the methods if we're going ahead with this rename: a transaction context is at best a subset of what a full transaction contains, and some functions(/methods) on transactions would not work on a transaction context, and vice versa.

some examples:

  • transaction sampling: whether a transaction will be sent or not (i.e. its sampled field is true/false) needs to be decided upon during transaction start
    • this is an immutable internal field on a transaction
    • this is a mutable field on a transaction context
  • tags and data
    • transactions have these
    • transaction contexts do not

Copy link
Member

Choose a reason for hiding this comment

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

tags and data

  • transactions have these
  • transaction contexts do not

How would you end up adding these to a transaction then? Would they only end up on the transaction based on whatever is set on the scope?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

there should be functions to set data and tags on the Transaction itself, i.e. what's returned by sentry_transaction_start(). that's implemented in #626.

transaction_contexts on the other hand shouldn't have any of that information, nor should they offer the ability to set those. they're only really there so a user can set some values that are immutable after a Transaction has been started. to be fair, the name and operation on a transaction_context are exceptions to that rule, but it otherwise applies to everything else on a transaction_context.

src/sentry_core.c Outdated Show resolved Hide resolved
src/sentry_core.h Outdated Show resolved Hide resolved
{
sentry_value_t tx = sentry_value_new_event();

// TODO: stuff transaction into the scope
Copy link
Contributor

Choose a reason for hiding this comment

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

if we go with today's suggestion 3, then yes i think

src/sentry_core.c Outdated Show resolved Hide resolved
@relaxolotl relaxolotl force-pushed the tracing/the-rest branch 3 times, most recently from 997d0ef to 1f11180 Compare December 21, 2021 07:51
@relaxolotl
Copy link
Contributor Author

addressed most comments and left replies to some where a code change wasn't needed, should be good for re-review.

cc @flub @Swatinem

src/sentry_core.c Show resolved Hide resolved
src/sentry_core.c Outdated Show resolved Hide resolved
// valid value
sentry_value_t parent_span
= sentry_value_get_by_key_owned(tx_cxt, "parent_span_id");
if (sentry_value_get_length(parent_span) > 0) {
Copy link
Contributor

Choose a reason for hiding this comment

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

rather minor as the semantics end up being the same, but i was kind of expecting a !sentry_value_is_null(parent_span) here

Copy link
Contributor Author

Choose a reason for hiding this comment

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

yeah, i was really relying on the fact that .._get_length() handles nulls already here. a lot of the helper functions for sentry_value_t do so already, so i'm trying to remove any unnecessary null checks whereever i can as a result.

sentry_value_set_by_key(
tx, "span_id", sentry_value_get_by_key_owned(tx_cxt, "trace_id"));
sentry_value_set_by_key(
tx, "transaction", sentry_value_get_by_key_owned(tx_cxt, "name"));
Copy link
Contributor

Choose a reason for hiding this comment

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

fwiw these three may end up setting null values if the passed in transaction context was not correct. this is fine i think, there's little point in doing much validation here already.

Copy link
Contributor Author

@relaxolotl relaxolotl Dec 23, 2021

Choose a reason for hiding this comment

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

there's a future PR that validates the contents of these values later on in the transaction lifecycle: #633

include/sentry.h Show resolved Hide resolved
sentry_value_t transaction, const char *name);

/**
* Sets the `operation` of a Transaction.
* Sets the `operation` on a Transaction Context, which will be used in the
* Transaction constructed off of the context
*
* See https://develop.sentry.dev/sdk/performance/span-operations/ for
* conventions on `operation`s.
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
* conventions on `operation`s.
* conventions on `operation`s.
*
* Borrows `transaction`.

*
* When passed any value above 0, the Transaction will bypass all sampling
* options and always be sent to sentry. If passed 0, this Transaction and its
* child spans will never be sent to sentry.
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
* child spans will never be sent to sentry.
* child spans will never be sent to sentry.
*
* Borrows `transaction`.

src/sentry_core.c Outdated Show resolved Hide resolved
* Removes the sampled field on a Transaction Context, which will be used in the
* Transaction constructed off of the context.
*
* The Transaction will use the sampling rate as defined in `sentry_options`.
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
* The Transaction will use the sampling rate as defined in `sentry_options`.
* The Transaction will use the sampling rate as defined in `sentry_options`.
*
* Borrows `transaction`.

include/sentry.h Show resolved Hide resolved
src/sentry_core.c Show resolved Hide resolved
src/sentry_core.h Show resolved Hide resolved
Base automatically changed from tracing/base-envelope-support to master December 22, 2021 13:59
@codecov-commenter
Copy link

Codecov Report

Merging #631 (3e716bc) into master (438f38e) will increase coverage by 0.27%.
The diff coverage is 92.85%.

@@            Coverage Diff             @@
##           master     #631      +/-   ##
==========================================
+ Coverage   86.67%   86.95%   +0.27%     
==========================================
  Files          50       50              
  Lines        4084     4148      +64     
  Branches      869      872       +3     
==========================================
+ Hits         3540     3607      +67     
+ Misses        456      453       -3     
  Partials       88       88              

@relaxolotl
Copy link
Contributor Author

github is blocked on an explicit approval from arpad and i'd really prefer not to cheat the system, so 🙏 requesting a final OK on the changes here from @Swatinem

i'd like to also note that i've deliberately opted not to call out borrows on functions: i think the default assumption is that any passed in parameters are borrowed, and ownership theft is considered the exception. the documentation on existing public APIs support this in that they explicitly note when ownership is taken from the caller, but they do not go out of their way to mention when something is borrowed. cc @flub

Copy link
Member

@Swatinem Swatinem left a comment

Choose a reason for hiding this comment

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

lets unblock this then ;-)

@flub
Copy link
Contributor

flub commented Dec 23, 2021

i'd like to also note that i've deliberately opted not to call out borrows on functions: i think the default assumption is that any passed in parameters are borrowed, and ownership theft is considered the exception. the documentation on existing public APIs support this in that they explicitly note when ownership is taken from the caller, but they do not go out of their way to mention when something is borrowed. cc @flub

I'm not going to block on this, but I regret the choice, explicit is often nice. but let's merge this instead of bikeshedding over this.

@relaxolotl relaxolotl merged commit 418588e into master Dec 23, 2021
@relaxolotl relaxolotl deleted the tracing/the-rest branch December 23, 2021 17:10
nunomluz pushed a commit to mystaff/sentry-native that referenced this pull request Dec 24, 2021
* ci(codechecker): Workaround for code checker not building due to node issues (getsentry#615)

* meta: Update breakpad/crashpad to 2021-12-03 (getsentry#614)

* feat(tracing): Add config options (getsentry#613)

* fix: Correct changelog entry (getsentry#622)

* meta: Bump breakpad (getsentry#621)

* feat: Add internal UUID types (getsentry#616)

This adds in support for internal UUIDs needed by tracing,
such as the trace ID and the span ID.
 
The major difference between this and the "standard" UUID 
is that the hyphens are stripped during serialization. sentry 
appears to not consider the hyphenated representations of 
these UUIDs to be valid for certain fields in an event.

* meta: Update changelog (getsentry#625)

* release: 0.4.13

* feat(tracing): Groundwork to add tracing context to all events (getsentry#617)

This adds the appropriate stubs and fields to 
start storing spans on the (universal) scope. 
No actual logic has been added to actually 
support setting spans on the scope itself.

The focus of this is to begin including tracing 
info in the context on all events if there is a 
transaction set on the scope. It does this fairly 
naively right now as the tooling to merge 
`sentry_value_t`s are basically nonexistent.

* ci: Make integration tests capable of reading the non-backwards compatible version number for Big Sur (getsentry#627)

* feat(tracing): Basic transaction context creation (getsentry#619)

This adds in the ability to create and manipulate transaction contexts as defined in 
https://develop.sentry.dev/sdk/performance/#new-span-and-transaction-classes, 
under Transaction Interface.

Instead of defining several transaction constructor functions with varying names 
(since overloading doesn't exist), the decision has been made to have the user 
construct an "inactive" transaction which should be fed into the SDK's 
implementation of `start_transaction`. This follows an existing pattern in the SDK 
where exceptions, threads, messages, etc can be constructed but they must be 
explicitly added to an event to be sent to sentry.

* feat(tracing): Support basic sampling of transactions (getsentry#620)

If an event is a transaction, event flushing should determine 
discard or forward the transaction to sentry based on the
sample rate as configured in sentry options. Follows the 
sampling rules as defined in 
https://develop.sentry.dev/sdk/performance/#sampling-context.

This does not take into consideration parent sampling as 
that property is currently unimplemented on the transaction 
context.

* feat(tracing): Introduce a helper that identifies events that are transactions (getsentry#628)

* feat(tracing): Restrict `sentry_capture_event` so it only sends non-transaction events (getsentry#629)

Prevent the public API from being used to send transaction events
as another transaction-specific function is meant to be used to 
accomplish this.

* fix: Avoid deadlocks with uninitialized options (getsentry#639)

The `SENTRY_WITH_OPTIONS_MUT` was a footgun since it never unlocked when
the options were NULL (uninitialized).
This removes the macro and replaces its uses with explicit lock/unlock calls.

* feat(tracing): Add in basic Envelope support for Transactions (getsentry#630)

* feat(tracing): Allow manual creation and sending of spanless Transactions (getsentry#631)

* feat(tracing): Defer some transaction validation and allow creation of internal spans (getsentry#633)

Co-authored-by: relaxolotl <[email protected]>
Co-authored-by: Sebastian Zivota <[email protected]>
Co-authored-by: getsentry-bot <[email protected]>
Co-authored-by: Arpad Borsos <[email protected]>
Co-authored-by: Arpad Borsos <[email protected]>
nunomluz added a commit to mystaff/sentry-native that referenced this pull request Dec 27, 2021
)

* feat: Add support for Qt 6 (getsentry#509)

* fix: Windows SDK Compiler Warning (getsentry#511)

* fix: Validate tm put into strftime (getsentry#510)

* fix: Rewrite the Linux module finder (getsentry#431)

It now works in memory, without requiring to mmap the libraries again,
which should make it work correctly on android when loading libraries
directly from apk or appbundle files.

The new code will keep track of readable memory maps and will
translate read requests based on the offset of those memory maps,
making sure that we actually can read whatever we are trying to read.

* build: Avoid building all targets (getsentry#512)

It looks like cmake is broken and builds ALL the targets when the parallel option is specified first, lol

* fix: Update Crashpad to 2021-04-12 and fix macOS universal build (getsentry#513)

* feat: Invoke before_send hook when using Crashpad (getsentry#519)

* feat: Add more Event Payload convenience methods (getsentry#517)

Adds:
* `sentry_value_new_exception`
* `sentry_value_new_thread`
* `sentry_value_new_stacktrace`
* `sentry_event_add_exception`
* `sentry_event_add_thread`

Deprecates `sentry_event_value_add_stacktrace`

* feat: Introduce `sentry_close` (getsentry#518)

This replaces the former `sentry_shutdown`, which is being forwarded.

* meta: Prepare Changelog for upcoming release (getsentry#522)

* ref: Pass options to scope apply directly (getsentry#521)

* fix: Further clean up platform libraries for static linking (getsentry#523)

* fix: Better macOS availability checks (getsentry#524)

This should allow building on older macOS versions as well as
running on older versions by fixing the usage of __builtin_available,
and adding a different clock source for older macOS versions.

* release: 0.4.9

* fix: Avoid double-free on invalid DSN (getsentry#527)

* meta: Use correct libunwindstack commit

* fix: Allow for Unity builds (getsentry#536)

* ref: Add more testcases that trigger crashes in various ways (getsentry#538)

* ref(craft): Modernize Craft config (getsentry#543)

* fix: Update venv and test-discovery Makefile targets (getsentry#544)

* fix: Avoid recursion when using `sentry_reinstall_backend` (getsentry#548)

Previously, the `inproc` and `crashpad` (on linux) backends didn’t correctly reset their signal handlers when doing `reinstall_backend` (or multiple `init` calls for that matter).
This has led to an infinite loop generating crashes.

The fix now correctly unregisters the inproc/crashpad signal handlers, and adds an integration test using `reinstall_backend` to make sure we do not end up in an infinite loop.

Co-authored-by: Mischa Alff <[email protected]>

* fix: Address -Wundef warning for SENTRY_UNITTEST defines (getsentry#549)

* build: Set 32-bit option for compiling assembly as well (getsentry#550)

This fixes compilation of breakpad for 32-bit systems

* meta: Update break/crashpad to 2021-06-14 (getsentry#552)

* fix: Shorten/Split Locked sections to avoid deadlock (getsentry#551)

We have received a report that the `sentry_get_modules_list` on mac can deadlock
when other code concurrently does a `dlopen` and thus invokes the `add_image`
callback from a different thread.

We shorten/split the locked blocks in order to avoid holding a lock in the
`get_modules` function whenever the `add_image` function is being invoked possibly
from other threads.

* fix: Tighten Stack Usage (getsentry#553)

According to some docs, JVM/JNI stacks on Android can be as small as
32K, and our own sigaltstack is not much larger with 64K.
Make sure to avoid large stack allocations as much as possible.

We have especially seen the literal content of `/proc/self/maps` as well
as formatted addresses inside corrupted release/environment attributes,
which might point to overflows that write into a previously allocated
release/environment string.

* meta: Update Changelog (getsentry#556)

* release: 0.4.10

* reformat

* fix: Make Linux modulefinder/unwinder safer (getsentry#559)

This is using the `process_vm_read` call to safely poke at random memory. It also makes sure to shim the libc provided call with a direct syscall for older Android devices.

* docs: Try to better explain unwind API (getsentry#564)

* fix: Make Crashpad Backend respect max_breadcrumbs setting (getsentry#566)

* fix: Cancel slow winhttp requests on shutdown (getsentry#570)

Co-authored-by: Gerhard Herbert <[email protected]>

* fix: Properly close the background worker thread on timeout (getsentry#571)

* fix: Possible race conditions in init/close and sessions (getsentry#545)

* meta: Draft Changelog (getsentry#572)

* release: 0.4.11

* feat: Make shutdown timeout customizable (getsentry#577)

Co-authored-by: Andrei Muraru <[email protected]>

* CMake: Link to the CURL::libcurl target when available (getsentry#579)

Caters better for newer cmake versions.

* meta: Update crashpad to 2021-07-14 (getsentry#580)

* fix: Properly use `SENTRY_BUILD_RUNTIMESTATIC` for `sentry_fuzz_json` unit test (getsentry#583)

* meta: Update break/crashpad to 2021-07-28 (getsentry#584)

* release: 0.4.12

* fix: Increment CXX standard to 14 to allow crashpad build (getsentry#585)

Fixes getsentry#574

* meta: Bump python dependencies (getsentry#600)

The old version of pytest breaks with python 3.10 which changed a
little how code object internals work.  Since python 3.10 is now
released it starts being used in CI.

* fix: Ensure that a valid DSN has a public_key (getsentry#598)

* feat: AIX support (getsentry#593)

* CMake: Check whether libcurl was already found (getsentry#602)

Currently when there is any other project that brings libcurl as a dependency,
the build fails with “Could NOT find CURL (missing: CURL_LIBRARY CURL_INCLUDE_DIR)“,
even though libcurl has already added as CURL::libcurl library.

This patch adds a check for CURL_FOUND, to indicate that the library was already
found, if set by another project. It also skips the additional find_package()
step so it does not fail.

Signed-off-by: Ladislav Macoun <[email protected]>

* CMake: fix `SENTRY_BACKEND` defined in outer scope (getsentry#603)

* CMake: add ability to set solution folder name (getsentry#604)

* [pull] master from getsentry:master (#14)

* ci(codechecker): Workaround for code checker not building due to node issues (getsentry#615)

* meta: Update breakpad/crashpad to 2021-12-03 (getsentry#614)

* feat(tracing): Add config options (getsentry#613)

* fix: Correct changelog entry (getsentry#622)

* meta: Bump breakpad (getsentry#621)

* feat: Add internal UUID types (getsentry#616)

This adds in support for internal UUIDs needed by tracing,
such as the trace ID and the span ID.
 
The major difference between this and the "standard" UUID 
is that the hyphens are stripped during serialization. sentry 
appears to not consider the hyphenated representations of 
these UUIDs to be valid for certain fields in an event.

* meta: Update changelog (getsentry#625)

* release: 0.4.13

* feat(tracing): Groundwork to add tracing context to all events (getsentry#617)

This adds the appropriate stubs and fields to 
start storing spans on the (universal) scope. 
No actual logic has been added to actually 
support setting spans on the scope itself.

The focus of this is to begin including tracing 
info in the context on all events if there is a 
transaction set on the scope. It does this fairly 
naively right now as the tooling to merge 
`sentry_value_t`s are basically nonexistent.

* ci: Make integration tests capable of reading the non-backwards compatible version number for Big Sur (getsentry#627)

* feat(tracing): Basic transaction context creation (getsentry#619)

This adds in the ability to create and manipulate transaction contexts as defined in 
https://develop.sentry.dev/sdk/performance/#new-span-and-transaction-classes, 
under Transaction Interface.

Instead of defining several transaction constructor functions with varying names 
(since overloading doesn't exist), the decision has been made to have the user 
construct an "inactive" transaction which should be fed into the SDK's 
implementation of `start_transaction`. This follows an existing pattern in the SDK 
where exceptions, threads, messages, etc can be constructed but they must be 
explicitly added to an event to be sent to sentry.

* feat(tracing): Support basic sampling of transactions (getsentry#620)

If an event is a transaction, event flushing should determine 
discard or forward the transaction to sentry based on the
sample rate as configured in sentry options. Follows the 
sampling rules as defined in 
https://develop.sentry.dev/sdk/performance/#sampling-context.

This does not take into consideration parent sampling as 
that property is currently unimplemented on the transaction 
context.

* feat(tracing): Introduce a helper that identifies events that are transactions (getsentry#628)

* feat(tracing): Restrict `sentry_capture_event` so it only sends non-transaction events (getsentry#629)

Prevent the public API from being used to send transaction events
as another transaction-specific function is meant to be used to 
accomplish this.

* fix: Avoid deadlocks with uninitialized options (getsentry#639)

The `SENTRY_WITH_OPTIONS_MUT` was a footgun since it never unlocked when
the options were NULL (uninitialized).
This removes the macro and replaces its uses with explicit lock/unlock calls.

* feat(tracing): Add in basic Envelope support for Transactions (getsentry#630)

* feat(tracing): Allow manual creation and sending of spanless Transactions (getsentry#631)

* feat(tracing): Defer some transaction validation and allow creation of internal spans (getsentry#633)

Co-authored-by: relaxolotl <[email protected]>
Co-authored-by: Sebastian Zivota <[email protected]>
Co-authored-by: getsentry-bot <[email protected]>
Co-authored-by: Arpad Borsos <[email protected]>
Co-authored-by: Arpad Borsos <[email protected]>

Co-authored-by: Tor Arne Vestbø <[email protected]>
Co-authored-by: Arpad Borsos <[email protected]>
Co-authored-by: Luke Street <[email protected]>
Co-authored-by: getsentry-bot <[email protected]>
Co-authored-by: Sentry Bot <[email protected]>
Co-authored-by: Arpad Borsos <[email protected]>
Co-authored-by: bschatt <[email protected]>
Co-authored-by: Burak Yigit Kaya <[email protected]>
Co-authored-by: MikeRumplerSentry <[email protected]>
Co-authored-by: Mischa Alff <[email protected]>
Co-authored-by: Michał Janiszewski <[email protected]>
Co-authored-by: getsentry-bot <[email protected]>
Co-authored-by: Gerhard Herbert <[email protected]>
Co-authored-by: andrei-mu <[email protected]>
Co-authored-by: Andrei Muraru <[email protected]>
Co-authored-by: pastdue <[email protected]>
Co-authored-by: Roshan Padaki <[email protected]>
Co-authored-by: mjvankampen <[email protected]>
Co-authored-by: Floris Bruynooghe <[email protected]>
Co-authored-by: Calvin Buckley <[email protected]>
Co-authored-by: Ladislav <[email protected]>
Co-authored-by: Mikhail Paulyshka <[email protected]>
Co-authored-by: pull[bot] <39814207+pull[bot]@users.noreply.github.com>
Co-authored-by: relaxolotl <[email protected]>
Co-authored-by: Sebastian Zivota <[email protected]>
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.

5 participants