Skip to content

feat: per-hook activity spans with method names#5159

Merged
thomhurst merged 3 commits intomainfrom
feat/per-hook-activity-spans
Mar 14, 2026
Merged

feat: per-hook activity spans with method names#5159
thomhurst merged 3 commits intomainfrom
feat/per-hook-activity-spans

Conversation

@thomhurst
Copy link
Owner

Summary

  • Each hook method now gets its own dedicated OpenTelemetry activity/span named "hook: ClassName.MethodName(params)" instead of a single generic "hook: BeforeTest" / "hook: AfterTest" wrapping all hooks
  • Introduced NamedHookDelegate<TContext> to carry hook names through the delegate pipeline from HookDelegateBuilder to HookExecutor
  • Applies to all hook levels: test session, assembly, class, and test hooks

Before

test case
  └─ hook: BeforeTest
  └─ test body
  └─ hook: AfterTest

After

test case
  └─ hook: MySetup.Initialize(context)
  └─ hook: BaseClass.Setup(context)
  └─ test body
  └─ hook: MyClass.Cleanup(context)
  └─ hook: GlobalHooks.AfterEvery(context)

Test plan

  • Verify build passes across all target frameworks
  • Run tests with an OpenTelemetry collector to confirm individual hook spans appear with correct names
  • Verify session/assembly/class-level hooks also produce named spans

… tracing

Instead of generic "hook: BeforeTest" / "hook: AfterTest" spans that wrap
all hooks, each hook method now gets its own dedicated span named
"hook: ClassName.MethodName(params)" for precise timing and identification.
Copy link
Contributor

@claude claude bot left a comment

Choose a reason for hiding this comment

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

Code Review: feat/per-hook-activity-spans

Great feature — granular per-hook spans are significantly more useful for debugging slow/failing hooks in distributed traces. The overall design is solid and the centralization of activity lifecycle in HookExecutor.ExecuteHookWithActivityAsync is the right call.


Issue 1 (Important): Unsafe cast in SortAndAddHooks — AOT risk

HookDelegateBuilder.cs, ~line 144–157

private static void SortAndAddHooks<TDelegate>(
    List<NamedHookDelegate<TestContext>> target,
    List<(int order, int registrationIndex, TDelegate hook)> hooks)
    where TDelegate : struct
{
    ...
    target.Add((NamedHookDelegate<TestContext>)(object)hook); // ⚠️ boxing struct through object
}

This is always called where TDelegate = NamedHookDelegate<TestContext>, making the generic parameter fake. Boxing a struct through object to cast it is an AOT red flag — under NativeAOT, generic instantiations over value types have strict requirements, and this double-cast bypasses compile-time verification.

Recommended fix: Drop the generic entirely, since it is never actually generic:

private static void SortAndAddHooks(
    List<NamedHookDelegate<TestContext>> target,
    List<(int order, int registrationIndex, NamedHookDelegate<TestContext> hook)> hooks)
{
    hooks.Sort(...);
    foreach (var (_, _, hook) in hooks)
        target.Add(hook);
}

This mirrors SortAndAddClassHooks which is already correctly typed and cast-free. Unifying the two also removes the confusing asymmetry.


Issue 2 (Minor): Assembly hook sorting drops registrationIndex — non-deterministic ordering

HookDelegateBuilder.cs, BuildBeforeAssemblyHooksAsync / BuildAfterAssemblyHooksAsync

return allHooks
    .OrderBy(h => h.order)  // ← missing .ThenBy(h => h.registrationIndex)
    .Select(h => h.hook)
    .ToList();

Every other build path (including BuildGlobalHooksAsync) uses a secondary sort on registrationIndex:

.OrderBy(static h => h.order)
.ThenBy(static h => h.registrationIndex)

Without the ThenBy, assembly hooks registered at the same Order value will have non-deterministic execution order across runs (LINQ's OrderBy is stable per-run but the input order could vary). This is likely a latent bug carried in from before this PR, but since you're touching these methods it's worth fixing now.


Issue 3 (Minor): CreateXXXHookDelegateAsync should return ValueTask<> not Task<>

Per project rules, ValueTask should be used for operations that are often synchronous. ProcessHookRegistrationAsync returns immediately on the fast path (cached hook — !_processedHooks.TryAdd is false). This means most call sites after warm-up are effectively synchronous, and wrapping them in Task<> allocates a state machine unnecessarily.

// Before
private async Task<NamedHookDelegate<TestContext>> CreateInstanceHookDelegateAsync(...)

// After
private async ValueTask<NamedHookDelegate<TestContext>> CreateInstanceHookDelegateAsync(...)

(Same for CreateStaticHookDelegateAsync, CreateClassHookDelegateAsync, etc.)

The callers (BuildGlobalHooksAsync's createDelegate parameter) would need Func<THookMethod, ValueTask<NamedHookDelegate<TContext>>> — a small but consistent change.


Issue 4 (Nitpick): Array.Empty<Exception>() vs [] inconsistency

HookExecutor.cs, ExecuteAfterTestHooksAsync

return exceptions == null ? Array.Empty<Exception>() : exceptions;

The rest of the file uses []. This is a trivial style inconsistency but the modern form is cleaner:

return exceptions ?? [];

What's working well

  • NamedHookDelegate<TContext> as readonly record struct — value semantics with zero heap allocation for the wrapper itself. Correct choice for a hot path.
  • #if NET / #else dual implementation of ExecuteHookWithActivityAsync — clean gating with no runtime overhead on older targets.
  • BuildGlobalHooksAsync generic helper — eliminates substantial duplication across 10 global hook variants.
  • Removing Activity management from TestExecutor — the 55-line reduction is exactly the right kind of cleanup; TestExecutor had no business knowing about spans.
  • Per-hook span naming (hook: ClassName.MethodName(params)) — the before/after semantics are now actually traceable in distributed tracing tools.

Overall this is a clean, well-scoped improvement. The unsafe cast in SortAndAddHooks is the most important thing to fix before merging given the AOT compatibility requirement.

- Pre-compute activity span name in NamedHookDelegate to avoid
  per-invocation string allocation on the hot path
- Unify SortAndAddHooks/SortAndAddClassHooks into single generic method,
  removing unsafe boxing cast through object
- Collapse five identical CreateXxxHookDelegateAsync methods into one
  generic CreateStaticHookDelegateAsync<TContext>
- Reuse already-fetched context variables in hook loops instead of
  redundant GetOrCreate calls
Copy link
Contributor

@claude claude bot left a comment

Choose a reason for hiding this comment

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

Code Review: feat/per-hook-activity-spans

This is a well-executed PR that improves OpenTelemetry observability and significantly reduces code complexity at the same time.

What's good

**** is a minimal, well-chosen abstraction. Pre-computing in the constructor avoids a string allocation on every hook invocation, which aligns with the Performance First principle. Using a readonly record struct is idiomatic for this kind of lightweight carrier type.

The consolidation of five near-identical CreateXxxHookDelegateAsync methods into one generic CreateStaticHookDelegateAsync<TContext> is a meaningful reduction. The five methods were byte-for-byte identical except for the type parameter — this was exactly the kind of duplication that warranted a generic.

Removing the unsafe boxing cast in the old SortAndAddHooks:

// Old: unsafe cast through object
target.Add((Func<TestContext, CancellationToken, Task>)(object)hook);

This was a latent type-safety hazard. The new unified SortAndAddHooks<TContext> is clean and type-safe.

Moving GetOrCreateAssemblyContext / GetOrCreateClassContext outside the foreach loops is a nice micro-optimisation — same context object every iteration, no need to look it up repeatedly.

The #if NET split on ExecuteHookWithActivityAsync is consistent with the existing pattern in this codebase and correctly means non-NET targets get zero overhead.


One observation worth considering

The Name property on NamedHookDelegate<TContext> is set at construction time but never read by any consumer after that — only ActivityName and Invoke are accessed in HookExecutor. The Name field currently exists solely to build ActivityName in the convenience constructor.

// NamedHookDelegate: Name is used only to build ActivityName
public NamedHookDelegate(string name, Func<TContext, CancellationToken, Task> invoke)
    : this(name, $"hook: {name}", invoke) { }

This isn't a bug — it's harmless and keeping Name gives a clean escape hatch for future logging or error messages. But if you want to be strict about YAGNI, the struct could drop Name entirely and just take activityName directly. Worth a moment's thought, though I wouldn't block on it.


Behavioral note (not a problem, just worth documenting)

The old code had an explicit ActivityStatusCode.Error set on the aggregate "hook: AfterTest" span when hookExceptions.Count > 0. That aggregate span is gone. The new per-hook spans correctly record exceptions via the catch block in ExecuteHookWithActivityAsync — but only for hooks that throw. Since ExecuteAfterTestHooksAsync catches exceptions per hook internally, each failing hook's span will still be marked as failed via TUnitActivitySource.RecordException. The net result is actually better observability (you can see which hook failed rather than just that some after-hook failed), but if anyone was relying on the aggregate span for alerting, that span no longer exists.


Summary

Net -141 lines of code, better observability, eliminated an unsafe cast, and no meaningful trade-offs. Clean PR.

Copy link
Contributor

@claude claude bot left a comment

Choose a reason for hiding this comment

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

Code Review: feat/per-hook-activity-spans

This is a well-executed PR that improves OpenTelemetry observability and significantly reduces code complexity at the same time.

What's good

NamedHookDelegate<TContext> is a minimal, well-chosen abstraction. Pre-computing ActivityName in the constructor avoids a string allocation on every hook invocation, which aligns with the Performance First principle. Using a readonly record struct is idiomatic for this kind of lightweight carrier type.

The consolidation of five near-identical CreateXxxHookDelegateAsync methods into one generic CreateStaticHookDelegateAsync<TContext> is a meaningful reduction. The five methods were byte-for-byte identical except for the type parameter — this was exactly the kind of duplication that warranted a generic.

Removing the unsafe boxing cast in the old SortAndAddHooks:

// Old: unsafe cast through object
target.Add((Func<TestContext, CancellationToken, Task>)(object)hook);

This was a latent type-safety hazard. The new unified SortAndAddHooks<TContext> is clean and type-safe.

Moving GetOrCreateAssemblyContext / GetOrCreateClassContext outside the foreach loops is a nice micro-optimisation — same context object every iteration, no need to look it up repeatedly.

The #if NET split on ExecuteHookWithActivityAsync is consistent with the existing pattern in this codebase and correctly means non-NET targets get zero overhead.


One observation worth considering

The Name property on NamedHookDelegate<TContext> is set at construction time but never read by any consumer after that — only ActivityName and Invoke are accessed in HookExecutor. The Name field currently exists solely to build ActivityName in the convenience constructor.

This isn't a bug — it's harmless and keeping Name gives a clean escape hatch for future logging or error messages. But if you want to be strict about YAGNI, the struct could drop Name entirely and just take activityName directly. Worth a moment's thought, though I wouldn't block on it.


Behavioral note (not a problem, just worth documenting)

The old code had an explicit ActivityStatusCode.Error set on the aggregate "hook: AfterTest" span when hookExceptions.Count > 0. That aggregate span is gone. The new per-hook spans correctly record exceptions via the catch block in ExecuteHookWithActivityAsync — but only for hooks that throw. Since ExecuteAfterTestHooksAsync catches exceptions per hook internally, each failing hook's span will still be marked as failed via TUnitActivitySource.RecordException. The net result is actually better observability (you can see which hook failed rather than just that some after-hook failed), but if anyone was relying on the aggregate span for alerting, that span no longer exists.


Summary

Net -141 lines of code, better observability, eliminated an unsafe cast, and no meaningful trade-offs. Clean PR.

- Add missing .ThenBy(registrationIndex) to assembly hook sorting
  for deterministic ordering when hooks share the same Order value
- Change CreateInstanceHookDelegateAsync and CreateStaticHookDelegateAsync
  to return ValueTask<> instead of Task<> to avoid state machine
  allocation on the fast path (cached hooks)
- Replace Array.Empty<Exception>() with [] for style consistency
Copy link
Contributor

@claude claude bot left a comment

Choose a reason for hiding this comment

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

Code Review: feat/per-hook-activity-spans

This is a well-executed refactor. The feature goal (per-hook named spans) is achieved cleanly, and the implementation brings some meaningful secondary improvements along the way. A few things worth calling out:


Positive changes

Context acquisition moved outside the loop (HookExecutor.cs)
Previously GetOrCreateAssemblyContext(assembly) and GetOrCreateClassContext(testClass) were called on every loop iteration inside ExecuteAfterAssemblyHooksAsync / ExecuteAfterClassHooksAsync. The new code calls them once before the loop. This is a subtle correctness improvement — previously if the context factory had any side-effects or returned different instances, loop iterations could diverge. Now it's consistent.

Delegate factory unification
Removing five separate CreateXxxHookDelegateAsync methods in favour of a single generic CreateStaticHookDelegateAsync<TContext> is a great simplification. The old specializations were pure boilerplate — all structurally identical.

NamedHookDelegate pre-computes ActivityName
Storing "hook: {Name}" at construction time rather than formatting on every hook invocation is the right trade-off for a hot path. Good call.

exceptions ?? [] cleanup — clean modern C# idiom over Array.Empty<Exception>().


Issues to investigate

1. Dead #else branch in ExecuteHookWithActivityAsync

#if NET
    private static async ValueTask ExecuteHookWithActivityAsync<TContext>(...) { ... }
#else
    private static async ValueTask ExecuteHookWithActivityAsync<TContext>(...)
    {
        await hook.Invoke(context, cancellationToken).ConfigureAwait(false);
    }
#endif

TUnit targets net8.0;net9.0;net10.0 — all of which define NET. The #else branch will never be compiled under the current build matrix. The original TestExecutor.cs used #if NET guards too, but in this case since you're wrapping the entire method, consider just removing the #else branch entirely and leaving only the #if NET path, or removing the conditional entirely if there's no plan to ever support non-NET targets.

2. NamedHookDelegate — consider whether ActivityName is worth its own property

The struct currently stores both Name and ActivityName (always $"hook: {Name}"). If Name is only ever used for logging/display and ActivityName for span creation, having two separate string properties on the struct is fine. But if there are future cases where the activity name might differ from "hook: " + name, the dual-property design makes that easy. The secondary constructor that defaults ActivityName is a nice safety valve. No change needed — just confirming the design intent is sound.

3. Error status on after-test hook failures (confirmed non-regression ✅)

The old TestExecutor.cs code explicitly called afterTestActivity.SetStatus(ActivityStatusCode.Error) when hookExceptions.Count > 0. Checking TUnitActivitySource.RecordException: it calls activity.SetStatus(ActivityStatusCode.Error, exception.Message) internally, so the new per-hook catch block in ExecuteHookWithActivityAsync correctly propagates error status. No regression here — if anything the new per-hook spans give more precise error attribution.


Minor

  • The BuildGlobalBeforeTestSessionHooksAsync and similar methods previously called CreateTestSessionHookDelegateAsync (a distinct method) but now call CreateStaticHookDelegateAsync. Worth confirming the removed specializations didn't have any unique logic (e.g. extra processing steps) — from the diff they look identical, but a quick grep to confirm no behavioural difference is worthwhile before merging.

Summary

The refactor cleanly threads hook names through the pipeline without exposing them to public API surfaces, and NamedHookDelegate<TContext> is a minimal, appropriately-scoped abstraction. The per-hook span granularity is a genuine observability improvement. The dead #else branch is the only thing I'd clean up before merging.

@thomhurst
Copy link
Owner Author

Thanks for the reviews! Here's what was addressed across the last two commits:

Already fixed before the reviews (simplification commit):

  • Unsafe boxing cast in SortAndAddHooks — unified into single generic SortAndAddHooks<TContext>
  • Five duplicate CreateXxxHookDelegateAsync → single generic CreateStaticHookDelegateAsync<TContext>
  • Redundant GetOrCreateAssemblyContext/GetOrCreateClassContext calls moved outside loops
  • Pre-computed ActivityName in NamedHookDelegate to avoid per-invocation string allocation

Fixed in response to review feedback:

  • Added missing .ThenBy(registrationIndex) to assembly hook sorting (latent bug fix)
  • Changed Task<>ValueTask<> for create delegate methods
  • Array.Empty<Exception>()[] for consistency

Kept as-is (with reasoning):

  • Name property on NamedHookDelegate — kept as escape hatch for future logging/error messages
  • #else branch in ExecuteHookWithActivityAsyncnot dead code; Library.props targets netstandard2.0;net8.0;net9.0;net10.0, so the #else branch compiles for netstandard2.0 where NET is not defined
  • Removed specializations (CreateTestSessionHookDelegateAsync, etc.) had no unique logic — confirmed identical to the generic version

This was referenced Mar 23, 2026
github-actions bot pushed a commit to IntelliTect/CodingGuidelines that referenced this pull request Mar 23, 2026
[//]: # (dependabot-start)
⚠️  **Dependabot is rebasing this PR** ⚠️ 

Rebasing might not happen immediately, so don't worry if this takes some
time.

Note: if you make any changes to this PR yourself, they will take
precedence over the rebase.

---

[//]: # (dependabot-end)

Updated [TUnit.Core](https://github.com/thomhurst/TUnit) from 1.19.57 to
1.21.6.

<details>
<summary>Release notes</summary>

_Sourced from [TUnit.Core's
releases](https://github.com/thomhurst/TUnit/releases)._

## 1.21.6

<!-- Release notes generated using configuration in .github/release.yml
at v1.21.6 -->

## What's Changed
### Other Changes
* perf: replace object locks with Lock type for efficient
synchronization by @​thomhurst in
thomhurst/TUnit#5219
* perf: parallelize test metadata collection for source-generated tests
by @​thomhurst in thomhurst/TUnit#5221
* perf: use GetOrAdd args overload to eliminate closure allocations in
event receivers by @​thomhurst in
thomhurst/TUnit#5222
* perf: self-contained TestEntry<T> with consolidated switch invokers
eliminates per-test JIT by @​thomhurst in
thomhurst/TUnit#5223
### Dependencies
* chore(deps): update tunit to 1.21.0 by @​thomhurst in
thomhurst/TUnit#5220


**Full Changelog**:
thomhurst/TUnit@v1.21.0...v1.21.6

## 1.21.0

<!-- Release notes generated using configuration in .github/release.yml
at v1.21.0 -->

## What's Changed
### Other Changes
* perf: reduce ConcurrentDictionary closure allocations in hot paths by
@​thomhurst in thomhurst/TUnit#5210
* perf: reduce async state machine overhead in test execution pipeline
by @​thomhurst in thomhurst/TUnit#5214
* perf: reduce allocations in EventReceiverOrchestrator and
TestContextExtensions by @​thomhurst in
thomhurst/TUnit#5212
* perf: skip timeout machinery when no timeout configured by @​thomhurst
in thomhurst/TUnit#5211
* perf: reduce allocations and lock contention in ObjectTracker by
@​thomhurst in thomhurst/TUnit#5213
* Feat/numeric tolerance by @​agray in
thomhurst/TUnit#5110
* perf: remove unnecessary lock in ObjectTracker.TrackObjects by
@​thomhurst in thomhurst/TUnit#5217
* perf: eliminate async state machine in
TestCoordinator.ExecuteTestAsync by @​thomhurst in
thomhurst/TUnit#5216
* perf: eliminate LINQ allocation in ObjectTracker.UntrackObjectsAsync
by @​thomhurst in thomhurst/TUnit#5215
* perf: consolidate module initializers into single .cctor via partial
class by @​thomhurst in thomhurst/TUnit#5218
### Dependencies
* chore(deps): update tunit to 1.20.0 by @​thomhurst in
thomhurst/TUnit#5205
* chore(deps): update dependency nunit3testadapter to 6.2.0 by
@​thomhurst in thomhurst/TUnit#5206
* chore(deps): update dependency cliwrap to 3.10.1 by @​thomhurst in
thomhurst/TUnit#5207


**Full Changelog**:
thomhurst/TUnit@v1.20.0...v1.21.0

## 1.20.0

<!-- Release notes generated using configuration in .github/release.yml
at v1.20.0 -->

## What's Changed
### Other Changes
* Fix inverted colors in HTML report ring chart due to locale-dependent
decimal formatting by @​Copilot in
thomhurst/TUnit#5185
* Fix nullable warnings when using Member() on nullable properties by
@​Copilot in thomhurst/TUnit#5191
* Add CS8629 suppression and member access expression matching to
IsNotNullAssertionSuppressor by @​Copilot in
thomhurst/TUnit#5201
* feat: add ConfigureAppHost hook to AspireFixture by @​thomhurst in
thomhurst/TUnit#5202
* Fix ConfigureTestConfiguration being invoked twice by @​thomhurst in
thomhurst/TUnit#5203
* Add IsEquivalentTo assertion for Memory<T> and ReadOnlyMemory<T> by
@​thomhurst in thomhurst/TUnit#5204
### Dependencies
* chore(deps): update dependency gitversion.tool to v6.6.2 by
@​thomhurst in thomhurst/TUnit#5181
* chore(deps): update dependency gitversion.msbuild to 6.6.2 by
@​thomhurst in thomhurst/TUnit#5180
* chore(deps): update tunit to 1.19.74 by @​thomhurst in
thomhurst/TUnit#5179
* chore(deps): update verify to 31.13.3 by @​thomhurst in
thomhurst/TUnit#5182
* chore(deps): update verify to 31.13.5 by @​thomhurst in
thomhurst/TUnit#5183
* chore(deps): update aspire to 13.1.3 by @​thomhurst in
thomhurst/TUnit#5189
* chore(deps): update dependency stackexchange.redis to 2.12.4 by
@​thomhurst in thomhurst/TUnit#5193
* chore(deps): update microsoft/setup-msbuild action to v3 by
@​thomhurst in thomhurst/TUnit#5197


**Full Changelog**:
thomhurst/TUnit@v1.19.74...v1.20.0

## 1.19.74

<!-- Release notes generated using configuration in .github/release.yml
at v1.19.74 -->

## What's Changed
### Other Changes
* feat: per-hook activity spans with method names by @​thomhurst in
thomhurst/TUnit#5159
* fix: add tooltip to truncated span names in HTML report by @​thomhurst
in thomhurst/TUnit#5164
* Use enum names instead of numeric values in test display names by
@​Copilot in thomhurst/TUnit#5178
* fix: resolve CS8920 when mocking interfaces whose members return
static-abstract interfaces by @​lucaxchaves in
thomhurst/TUnit#5154
### Dependencies
* chore(deps): update tunit to 1.19.57 by @​thomhurst in
thomhurst/TUnit#5157
* chore(deps): update dependency gitversion.msbuild to 6.6.1 by
@​thomhurst in thomhurst/TUnit#5160
* chore(deps): update dependency gitversion.tool to v6.6.1 by
@​thomhurst in thomhurst/TUnit#5161
* chore(deps): update dependency polyfill to 9.20.0 by @​thomhurst in
thomhurst/TUnit#5163
* chore(deps): update dependency polyfill to 9.20.0 by @​thomhurst in
thomhurst/TUnit#5162
* chore(deps): update dependency polyfill to 9.21.0 by @​thomhurst in
thomhurst/TUnit#5166
* chore(deps): update dependency polyfill to 9.21.0 by @​thomhurst in
thomhurst/TUnit#5167
* chore(deps): update dependency polyfill to 9.22.0 by @​thomhurst in
thomhurst/TUnit#5168
* chore(deps): update dependency polyfill to 9.22.0 by @​thomhurst in
thomhurst/TUnit#5169
* chore(deps): update dependency coverlet.collector to 8.0.1 by
@​thomhurst in thomhurst/TUnit#5177

## New Contributors
* @​lucaxchaves made their first contribution in
thomhurst/TUnit#5154

**Full Changelog**:
thomhurst/TUnit@v1.19.57...v1.19.74

Commits viewable in [compare
view](thomhurst/TUnit@v1.19.57...v1.21.6).
</details>

[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=TUnit.Core&package-manager=nuget&previous-version=1.19.57&new-version=1.21.6)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
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.

1 participant