Skip to content

Add :rules-engine-internal skeleton module#3478

Merged
ajpallares merged 22 commits into
mainfrom
pallares/rules-engine-skeleton
May 28, 2026
Merged

Add :rules-engine-internal skeleton module#3478
ajpallares merged 22 commits into
mainfrom
pallares/rules-engine-skeleton

Conversation

@ajpallares

@ajpallares ajpallares commented May 14, 2026

Copy link
Copy Markdown
Member

Checklist

  • If applicable, unit tests

Motivation

Resolves SDK-4336

Adds a :rules-engine-internal module so the upcoming JSON Logic engine can ship as a separate library. Sibling iOS PR: RevenueCat/purchases-ios#6787.

Description

  • Skeleton module with an internal RulesEngine namespace, smoke test, and Maven publishing deferred until the engine has a consumer (#3488).
  • Module and artifact named rules-engine-internal / purchases-rules-engine-internal to make it clear that the API is unstable and not meant to be reached for directly, mirroring iOS' RulesEngineInternal.
  • Metalava and Dokka are also skipped for this module since it has no public-facing API for third-party developers.
  • New test-rules-engine-internal CircleCI job runs :rules-engine-internal:testDefaultsDebugUnitTest.

Note

Low Risk
Scaffolding-only module and build/CI wiring with no runtime behavior or public API surface changes to the main SDK.

Overview
Adds a new :rules-engine-internal Android library (artifact purchases-rules-engine-internal) as a placeholder for the upcoming JSON Logic rules engine, aligned with iOS’ internal rules module naming.

The module only exposes an internal RulesEngine namespace plus a smoke unit test. Maven publishing, Metalava, and Dokka are explicitly skipped for this path until there is a real consumer; Dokka application is refactored behind configureDokka() on the public-library convention plugin.

CircleCI gains test-rules-engine-internal (:rules-engine-internal:testDefaultsDebugUnitTest), wired into the main workflow and the release approval gate.

Reviewed by Cursor Bugbot for commit 2d7bf9d. Bugbot is set up for automated code reviews on this repo. Configure here.

Sets up the plumbing for an internal rules-engine module that the SDK can
depend on without coupling to :purchases or :ui:revenuecatui. Includes:

- New :rules-engine Gradle module using the existing
  `revenuecat-public-library` convention plugin (Metalava, Dokka, Kover,
  Vanniktech publish, baseline profile, explicit-API mode).
- Single-flavor (`apis { defaults }`); no `billingclient` dimension since
  the rules engine has no Billing Client dependency. Publishes a single
  `purchases-rules-engine` artifact instead of a bc7/bc8 split.
- Module-scoped `mavenPublishing.configure(AndroidSingleVariantLibrary("defaultsRelease"))`
  override so the global `ANDROID_VARIANT_TO_PUBLISH=defaultsBc8Release`
  default doesn't apply here.
- Placeholder `RulesEngine` Kotlin object plus a smoke test so CI exercises
  the module from day one. No actual rules logic yet.
- BOM constraint added so consumers using the BOM get an aligned version.

Co-authored-by: Cursor <cursoragent@cursor.com>
ajpallares and others added 2 commits May 14, 2026 11:36
Every public declaration in this module is intended to be visible only
to the rest of the SDK (`:purchases`, `:ui:revenuecatui`, hybrid
bridges), not to app developers, so put the opt-in gate in place from
day one instead of bolting it on later.

Sibling annotation, not the existing one
----------------------------------------

`@InternalRevenueCatAPI` lives in `:purchases` and we deliberately
keep `:rules-engine` standalone (no dependency on `:purchases`), so
this defines a parallel `@InternalRulesEngineAPI` in
`com.revenuecat.purchases.rules` with identical
`@RequiresOptIn(level=ERROR)` semantics. Two annotations doing the
same job is mildly redundant but unambiguous in the IDE and avoids
coupling the two modules just for an annotation.

Changes
-------

- New `InternalRulesEngineAPI.kt` mirroring the shape of
  `:purchases`'s `InternalRevenueCatAPI`.
- `RulesEngine` object annotated with `@InternalRulesEngineAPI`.
- Test class opts in with `@OptIn(InternalRulesEngineAPI::class)`.
- Metalava configured (per-module) to add
  `com.revenuecat.purchases.rules.InternalRulesEngineAPI` to the
  hidden-annotations list, on top of the
  `com.revenuecat.purchases.InternalRevenueCatAPI` entry already added
  by the `revenuecat-public-library` convention plugin.
- `api.txt` regenerated: `RulesEngine` is now hidden; the annotation
  itself remains public so consumers can opt in.

Verified
--------

- `./gradlew :rules-engine:testDefaultsDebugUnitTest` ✔
- `./gradlew :rules-engine:metalavaCheckCompatibilityDefaultsRelease` ✔
- `./gradlew detektAll` ✔

Co-authored-by: Cursor <cursoragent@cursor.com>
Drop the explainer comment above the `mavenPublishing.configure(...)`
override in `rules-engine/build.gradle.kts` and the doc on
`InternalRulesEngineAPI`. The behavior is self-evident from the code
and the annotation already carries a `@RequiresOptIn` message.

Co-authored-by: Cursor <cursoragent@cursor.com>
@codecov

codecov Bot commented May 14, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 79.91%. Comparing base (61618ee) to head (2d7bf9d).

Additional details and impacted files
@@           Coverage Diff           @@
##             main    #3478   +/-   ##
=======================================
  Coverage   79.91%   79.91%           
=======================================
  Files         369      369           
  Lines       14934    14934           
  Branches     2058     2058           
=======================================
  Hits        11934    11934           
  Misses       2163     2163           
  Partials      837      837           

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

ajpallares and others added 4 commits May 14, 2026 18:28
- Gate the `mavenPublishing { ... }` configuration on the
  `com.vanniktech.maven.publish` plugin actually being applied.
  `ConfigureConditionalPublishing` in the convention plugin skips the
  publish plugin when `ANDROID_VARIANT_TO_PUBLISH` contains
  `customEntitlementComputation` (which `:rules-engine` doesn't have),
  so the unconditional block was breaking
  `./gradlew :purchases:publish -PANDROID_VARIANT_TO_PUBLISH=customEntitlementComputationBc8Release`
  — i.e. the "Deploying Custom Entitlements Computation version"
  fastlane step — with `Unresolved reference: mavenPublishing` while
  configuring `:rules-engine`.

- Suppress `:rules-engine` from `dokkaHtmlMultiModule`. The existing
  `HideInternalRevenueCatAPIPlugin` is hardcoded to
  `com.revenuecat.purchases.InternalRevenueCatAPI` and only applied to
  `:purchases`, so without this `RulesEngine` and `InternalRulesEngineAPI`
  would leak into the published docs at `docs/{version}/`. Every public
  symbol in this module is gated by `@InternalRulesEngineAPI`, so a
  dedicated docs page would be empty anyway — suppressing the module
  is simpler than generalizing the hide-plugin.

- Add `:rules-engine:metalavaGenerateSignatureDefaultsRelease` to
  `scripts/api-dump.sh`. The script previously only invoked the
  `Bc8`/`Bc7`/`customEntitlement` task names, so `:rules-engine`
  (single-flavor, no `billingclient` dimension) was never regenerated,
  meaning the committed `rules-engine/api.txt` couldn't act as a
  tripwire via `scripts/api-check.sh`. With this change a leaked
  non-internal API would show up as a diff in CI.

Verified:
- `:purchases:publish --dry-run -PANDROID_VARIANT_TO_PUBLISH=customEntitlementComputationBc8Release` now only fails on the unrelated `mavenCentralUsername`/`Password` credentials error.
- `:rules-engine:dokkaHtmlPartial` reports `Exiting Generation: Nothing to document`.
- `scripts/api-dump.sh` runs the rules-engine task and `api.txt` round-trips clean.
- `:rules-engine:testDefaultsReleaseUnitTest`, `:rules-engine:metalavaCheckCompatibilityDefaultsRelease`, and `detektAll` all pass.

Co-authored-by: Cursor <cursoragent@cursor.com>
The module is currently a skeleton with no functionality and no
consumers, so publishing `purchases-rules-engine` would ship an empty
artifact whose Maven Central version we'd then be on the hook to keep
publishing forever. Defer the publishing wiring until the JSON Logic
engine lands.

- Short-circuit `:rules-engine` in `ConfigureConditionalPublishing` so
  `com.vanniktech.maven.publish` is never applied to it. The module
  still compiles, gets detekt'd, runs tests, and is dokka-suppressed on
  every PR — there's just no AAR pushed to Sonatype.
- Drop the `mavenPublishing { configure(AndroidSingleVariantLibrary(…)) }`
  block (and its imports) from `rules-engine/build.gradle.kts`. With the
  publish plugin no longer applied, it's unreachable.
- Remove `api(project(":rules-engine"))` from `:bom` so consumers
  exploring the BoM don't see a real-looking `purchases-rules-engine`
  they could pull in and get nothing.
- Drop the `:rules-engine:metalavaGenerateSignatureDefaultsRelease`
  entry from `scripts/api-dump.sh`. It will be re-added in the same
  follow-up PR that flips publishing back on, to keep all the "publish
  wiring" in one switch-flip.

Follow-up PR (alongside the first real consumer of `RulesEngine`):
revert the `:rules-engine` short-circuit, restore the
`mavenPublishing { … }` block (gated with `plugins.withId(...)` so it
doesn't break CE deploys), restore the `:bom` entry, and re-add the
api-dump invocation.

Verified:
- `:purchases:publish --dry-run -PANDROID_VARIANT_TO_PUBLISH=customEntitlementComputationBc8Release` only fails on the unrelated `mavenCentralUsername`/`Password` credentials error — `:rules-engine` configures cleanly.
- `:rules-engine:tasks --all` lists no Maven publish tasks (only the unrelated `prepareLintJarForPublish` Android Lint internal).
- `:bom:tasks` resolves without `:rules-engine`.
- `:rules-engine:testDefaultsReleaseUnitTest`, `:rules-engine:metalavaCheckCompatibilityDefaultsRelease`, `:rules-engine:dokkaHtmlPartial`, and `detektAll` all pass.
- `scripts/api-dump.sh` leaves all `api*.txt` files unchanged.

Co-authored-by: Cursor <cursoragent@cursor.com>
Keep this PR minimal: an internal kotlin android library that
compiles, tests, and gets detekt'd. Everything that's only relevant
when the module ships an artifact lives in a separate draft PR:

- Drop the module-level `metalava { … }` block (and the committed
  `api.txt` baseline). Without a CI step regenerating it, the file
  was just static; we'll re-add both — and wire them into
  `scripts/api-dump.sh` — once publishing flips on.
- Drop the `dokkaHtmlPartial` suppression. The module would only show
  up in `dokkaHtmlMultiModule` output once it's published, so there's
  nothing to hide today.

Replace the two trailing comments with a single short pointer to the
follow-up PR and the `ConfigureConditionalPublishing` short-circuit.

Co-authored-by: Cursor <cursoragent@cursor.com>
ajpallares and others added 4 commits May 14, 2026 20:27
`metalava { hiddenAnnotations.add(InternalRulesEngineAPI) }` + a
committed `api.txt` are standard hygiene for every module that uses
`revenuecat.public.library`. The CocoaPods/Maven publishing wiring is
the only thing that's truly "exists but unwired" in this PR and that
already moved to its own draft.

Keeping the metalava config here also avoids a duplicate `metalava {}`
block landing in both the distribution PR (which adds the
`api-dump.sh` entry to enforce drift) and the enforce-internal-api PR
(which uses metalava to verify nothing leaks outside
`@InternalRulesEngineAPI`).

The `api.txt` file is not yet regenerated by CI in this PR — that
follow-up lives in the distribution draft PR.

Co-authored-by: Cursor <cursoragent@cursor.com>
The comment described what `ConfigureConditionalPublishing` already
explains via its own short-circuit comment, so it was duplicating
context.

Co-authored-by: Cursor <cursoragent@cursor.com>
`aed6e2f88` committed an `api.txt` for parity with other public-library
convention-plugin modules, but the immediate follow-up #3480
(no-public-apis enforcement) replaces that baseline with an explicit
annotate-or-fail check that doesn't keep a file in source. Committing
the baseline here just to delete it there is churn, so:

- Drop `rules-engine/api.txt`.
- Set `enforceCheck = false` so the standard `check` task no longer runs
  `metalavaCheckCompatibility*` against a missing baseline.
- Redirect `metalavaGenerateSignature*` output to `build/api-dump.txt`
  so local spot-checks don't drop a file at the module root.
- Keep `hiddenAnnotations.add(...InternalRulesEngineAPI)` so the
  generated dump filters gated symbols.
- Update the trailing comment to call out both follow-up PRs
  (publishing/distribution + #3480 enforcement).

Co-authored-by: Cursor <cursoragent@cursor.com>
Removes the trailing block describing the publishing/distribution and
no-public-APIs enforcement follow-ups. The comment isn't load-bearing —
the PR descriptions and split branches already make this clear, so it
just adds noise to the build script.

Co-authored-by: Cursor <cursoragent@cursor.com>
@ajpallares ajpallares marked this pull request as ready for review May 16, 2026 10:32
@ajpallares ajpallares requested a review from a team as a code owner May 16, 2026 10:32

@tonidero tonidero left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

It's good in general! Just a thought about whether we need the new annotation... Lmk what you think!

* API. Annotate every new public declaration in this module with
* [InternalRulesEngineAPI].
*/
@InternalRulesEngineAPI

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Hmm do we need this annotation? I guess someone could depend on the rules engine module directly, but then, I think it's safe to say they are shooting themselves in the foot. We could name the artifact something like rules-engine-internal or something to make it clearer.

I'm just a bit reticent to have to add this to all API, since it could get annoying/noisy. And as long as we use this module as implementation and not api, these APIs shouldn't be exposed to our customers.

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.

100%. We don't have to, really. Definitely, it would be simpler for us not to have to deal with this annotation at all.

And as long as we use this module as implementation and not api

This, I think we should enforce somehow, to avoid using some of these as api by mistake. WDYT?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Hmm indeed... not sure of ways to enforce this, other than by using some danger rule or something like that... But yeah, I would say not super critical. I think we should always be very careful with anything we use api with (since it ends up being public API.

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.

True, true. In iOS, I did come up with a very simple lint rule that indirectly prevents us from doing it (RevenueCat/purchases-ios#6788). But I agree, it's not super critical and not a blocker at all 👍

ajpallares and others added 3 commits May 21, 2026 18:36
Mirrors the iOS counterpart shift on PR #6787 (commit `931f36373`):
the per-declaration opt-in marker is dropped in favor of a structural
guarantee. On Android the structural guarantee was already in place —
`:rules-engine` is consumed by `:purchases` / `:ui:revenuecatui` as an
`implementation` dependency, which keeps every symbol off the SDK's
transitive compile classpath, so third-party consumers never see the
declarations regardless of their visibility modifier. The annotation
was therefore signaling intent rather than enforcing it.

Removing it cleans up:

- `InternalRulesEngineAPI.kt` (the `@RequiresOptIn` annotation) is
  deleted outright.
- `RulesEngine` becomes a plain `public object`. Doc comment trimmed
  to point readers at the structural-via-`implementation` guarantee
  rather than at the now-gone annotation.
- `RulesEngineTest` drops its `@OptIn(InternalRulesEngineAPI::class)`.
- `rules-engine/build.gradle.kts` no longer needs the `metalava`
  block. With no annotation to hide, the convention plugin's defaults
  produce a committed `rules-engine/api.txt` at the module root with
  `enforceCheck` on — same shape as `:feature:amazon` and the rest of
  the published modules. This also removes the `build/api-dump.txt`
  spot-check redirection.
- `rules-engine/api.txt` baseline is committed for the first time.
  Skeleton surface is exactly `RulesEngine` (the namespace object).

The follow-up commit on PR #3480 will drop
`scripts/check-rules-engine-internal-only.sh` (its premise — "api
dump must contain only the annotation declaration" — no longer
holds) and the corresponding CircleCI step. PR #3480 itself ends up
empty as a result; iOS `4c32721b8` made the same call and rescoped
its enforcement PR.

No platform-specific Detekt equivalent of iOS's
`no_leaking_rules_engine_import` SwiftLint rule is added at this
time. Per-module `implementation` dependencies plus Kotlin's
`internal` visibility on every non-namespace declaration cover the
intended surface for now.

Verified: `:rules-engine:testDefaultsDebugUnitTest`, `detektAll`,
`metalavaGenerateSignatureDefaultsRelease`, and
`metalavaCheckDefaultsRelease` all green.

Co-authored-by: Cursor <cursoragent@cursor.com>
Clears the public API dump; RulesEngine will become public once wired
into the SDK surface.

Co-authored-by: Cursor <cursoragent@cursor.com>
@ajpallares ajpallares changed the title Add :rules-engine skeleton module Add :rules-engine-internal skeleton module May 27, 2026
ajpallares and others added 4 commits May 27, 2026 20:10
Mirror iOS RulesEngineInternal naming and add a dedicated CircleCI job
so the single-flavor module's smoke test runs outside the bc7/bc8 matrix.

Co-authored-by: Cursor <cursoragent@cursor.com>
Module is SDK-internal by design and exposes no public API to track,
so the metalava plugin would only generate a permanently empty api.txt.
Drop the empty signature file and tighten RulesEngine's KDoc.

Co-authored-by: Cursor <cursoragent@cursor.com>
…, exclude from Dokka

- Trim the temporary publishing-skip comment to the essentials.
- Drop redundant `testOptions { unitTests.isIncludeAndroidResources = true }` (already set by `configureAndroidLibrary`).
- Drop `--parallel` from the new CI job (no-op for a single qualified task).
- Exclude `:rules-engine-internal` from Dokka so it doesn't ship an empty page in the public docs.

Co-authored-by: Cursor <cursoragent@cursor.com>
@ajpallares ajpallares requested a review from tonidero May 27, 2026 20:36
@ajpallares ajpallares requested a review from a team May 27, 2026 20:36

@tonidero tonidero left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Very nice!! Thanks for adding this!

internal fun Project.configureConditionalPublishing() {
// `:rules-engine-internal` is a skeleton with no functionality and no consumers yet,
// so we deliberately don't publish `purchases-rules-engine-internal` to Maven Central.
if (project.path == ":rules-engine-internal") return

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I think at some point (once we start using it) we will need to publish this module to maven as well... But we can do that later

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.

That's right. That I will add in #3488 (draft for now)

*/
internal fun Project.configureDokka() {
// Skip Dokka for `:rules-engine-internal` since it's SDK-internal by design.
if (project.path == ":rules-engine-internal") return

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Could be interesting to have a list of "internal" modules that can be skipped for this and metalava... But considering we only have one right now, we can keep this and add that later when needed :)

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.

Good idea. But I agree 👍

Comment thread scripts/api-dump.sh Outdated

# Note: `:rules-engine-internal` is SDK-internal by design and opts out of
# Metalava entirely (see `ConfigureMetalava.kt`), so it has no signature
# generation task to invoke from this script.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Hmm I don't think this comment is needed IMO... but FFTI

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.

True. Removed in a196f02

ajpallares and others added 2 commits May 28, 2026 16:24
@ajpallares ajpallares enabled auto-merge May 28, 2026 14:25
@ajpallares ajpallares added this pull request to the merge queue May 28, 2026
@github-merge-queue github-merge-queue Bot removed this pull request from the merge queue due to failed status checks May 28, 2026
ajpallares added a commit that referenced this pull request May 28, 2026
Make evaluateValue internal, add warn tag parameter with a default, expose setLogger instead of a public logger property, and remove api.txt left over from a pre-#3478 merge (Metalava is already disabled for this module).

Co-authored-by: Cursor <cursoragent@cursor.com>
@ajpallares ajpallares added this pull request to the merge queue May 28, 2026
Merged via the queue into main with commit a0b14f6 May 28, 2026
39 checks passed
@ajpallares ajpallares deleted the pallares/rules-engine-skeleton branch May 28, 2026 15:50
matteinn pushed a commit to matteinn/purchases-android that referenced this pull request Jun 5, 2026
**This is an automatic release.**

## RevenueCat SDK
### ✨ New Features
* Add presented offering context to custom paywall events (RevenueCat#3424) via
Rick (@rickvdl)
* Add Workflows list endpoint (RevenueCat#3509) via Cesar de la Vega (@vegaro)

## RevenueCatUI SDK
### Paywalls_v2
#### 🐞 Bugfixes
* Fix 1px seam between sliding multipage paywall pages (RevenueCat#3526) via Cesar
de la Vega (@vegaro)

### 🔄 Other Changes
* refactor: extract Offering.presentedOfferingContext() helper and apply
across SDK (RevenueCat#3513) via Rick (@rickvdl)
* Add JSON Logic string + array operators (RevenueCat#3485) via Antonio Pallares
(@ajpallares)
* Add ForbiddenPublicSealedClass detekt rule (RevenueCat#3503) via Toni Rico
(@tonidero)
* Update baseline profiles (RevenueCat#3519) via RevenueCat Git Bot (@RCGitBot)
* build(deps): bump fastlane-plugin-revenuecat_internal from `af7bb5c`
to `ce6a7ef` (RevenueCat#3515) via dependabot[bot] (@dependabot[bot])
* Add JSON Logic comparison operators (<, <=, >, >=) (RevenueCat#3484) via Antonio
Pallares (@ajpallares)
* Add JSON Logic arithmetic operators (+, -, *, /, %) (RevenueCat#3483) via
Antonio Pallares (@ajpallares)
* Add WorkflowEvent model and backend serialization (RevenueCat#3486) via Cesar de
la Vega (@vegaro)
* RulesEngine: add JSON Logic predicate evaluator (RevenueCat#3482) via Antonio
Pallares (@ajpallares)
* Add :rules-engine-internal skeleton module (RevenueCat#3478) via Antonio
Pallares (@ajpallares)

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Low Risk**
> Version bump and changelog/docs/CI path updates only; no application
logic changes in the diff.
> 
> **Overview**
> This **automatic release** finalizes **Android SDK 10.8.0** by
replacing **`10.8.0-SNAPSHOT`** with **`10.8.0`** across versioning
(`gradle.properties`, `.version`, `Config.frameworkVersion`), sample
apps, and changelog files.
> 
> Release notes for **10.8.0** are recorded in **`CHANGELOG.md`** /
**`CHANGELOG.latest.md`** (workflows list API, paywall offering context
on custom events, multipage paywall seam fix, rules-engine/JSON Logic
work, etc.). **Docs publishing** now targets **`10.8.0`** on S3, and
**`docs/index.html`** redirects to the new doc URL.
> 
> There are **no functional code changes** in this diff beyond version
strings and release metadata.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
c3048b8. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants