Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
108 changes: 51 additions & 57 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,126 +2,120 @@ name: CI

on:
push:
branches: [ main, develop ]
branches: [ main, develop, 'feature/**', 'fix/**', 'release/**' ]
pull_request:
branches: [ main ]
branches: [ main, develop ]

jobs:
test-android:
name: Test (Android/JVM)
runs-on: ubuntu-latest

# ──────────────────────────────────────────────────────────────────────────
lint:
name: Lint
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- name: Set up JDK 17
uses: actions/setup-java@v4
- uses: actions/setup-java@v4
with:
java-version: '17'
distribution: 'temurin'
- uses: gradle/actions/setup-gradle@v3
- name: Run Android Lint (krelay)
run: ./gradlew :krelay:lintDebug --no-daemon

- name: Setup Gradle
uses: gradle/actions/setup-gradle@v3
# ──────────────────────────────────────────────────────────────────────────
test-android:
name: Unit Tests (JVM)
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-java@v4
with:
gradle-version: wrapper

- name: Run Android unit tests
java-version: '17'
distribution: 'temurin'
- uses: gradle/actions/setup-gradle@v3
- name: Run krelay unit tests
run: ./gradlew :krelay:testDebugUnitTest --no-daemon

- name: Run common tests (JVM)
run: ./gradlew :krelay:jvmTest --no-daemon 2>/dev/null || ./gradlew :krelay:testReleaseUnitTest --no-daemon

- name: Upload test results
if: always()
uses: actions/upload-artifact@v4
with:
name: android-test-results
path: krelay/build/reports/tests/

# ──────────────────────────────────────────────────────────────────────────
test-ios:
name: Test (iOS)
name: Unit Tests (iOS Simulator)
runs-on: macos-14

steps:
- uses: actions/checkout@v4

- name: Set up JDK 17
uses: actions/setup-java@v4
- uses: actions/setup-java@v4
with:
java-version: '17'
distribution: 'temurin'

- name: Setup Gradle
uses: gradle/actions/setup-gradle@v3
with:
gradle-version: wrapper

- name: Run iOS simulator tests
- uses: gradle/actions/setup-gradle@v3
- name: Run krelay iOS simulator tests
run: ./gradlew :krelay:iosSimulatorArm64Test --no-daemon

- name: Upload test results
if: always()
uses: actions/upload-artifact@v4
with:
name: ios-test-results
path: krelay/build/reports/

# ──────────────────────────────────────────────────────────────────────────
build:
name: Build Library
name: Build (All Targets)
runs-on: macos-14
needs: [ test-android ]

needs: [ lint, test-android, test-ios ]
steps:
- uses: actions/checkout@v4

- name: Set up JDK 17
uses: actions/setup-java@v4
- uses: actions/setup-java@v4
with:
java-version: '17'
distribution: 'temurin'

- name: Setup Gradle
uses: gradle/actions/setup-gradle@v3
with:
gradle-version: wrapper

- name: Build all targets
- uses: gradle/actions/setup-gradle@v3
- name: Build krelay (all targets)
run: ./gradlew :krelay:assemble --no-daemon

- name: Build krelay-compose (all targets)
run: ./gradlew :krelay-compose:assemble --no-daemon
- name: Build composeApp demo (Android)
run: ./gradlew :composeApp:assembleDebug --no-daemon
- name: Link iOS framework (krelay)
run: ./gradlew :composeApp:linkDebugFrameworkIosSimulatorArm64 --no-daemon
- name: Generate Dokka docs
run: ./gradlew :krelay:dokkaHtml --no-daemon

run: ./gradlew :krelay:dokkaHtmlCustom --no-daemon
- name: Upload build artifacts
uses: actions/upload-artifact@v4
with:
name: krelay-artifacts
path: krelay/build/outputs/

# ──────────────────────────────────────────────────────────────────────────
publish-snapshot:
name: Publish Snapshot
runs-on: macos-14
needs: [ build ]
if: github.ref == 'refs/heads/main' && github.event_name == 'push'

steps:
- uses: actions/checkout@v4

- name: Set up JDK 17
uses: actions/setup-java@v4
- uses: actions/setup-java@v4
with:
java-version: '17'
distribution: 'temurin'

- name: Setup Gradle
uses: gradle/actions/setup-gradle@v3
with:
gradle-version: wrapper

- name: Publish to Maven Central Snapshots
- uses: gradle/actions/setup-gradle@v3
- name: Publish krelay to OSSRH
run: ./gradlew :krelay:publishAllPublicationsToOSSRHRepository --no-daemon
env:
OSSRH_USERNAME: ${{ secrets.OSSRH_USERNAME }}
OSSRH_PASSWORD: ${{ secrets.OSSRH_PASSWORD }}
SIGNING_KEY: ${{ secrets.SIGNING_KEY }}
SIGNING_PASSWORD: ${{ secrets.SIGNING_PASSWORD }}
continue-on-error: true
- name: Publish krelay-compose to OSSRH
run: ./gradlew :krelay-compose:publishAllPublicationsToOSSRHRepository --no-daemon
env:
OSSRH_USERNAME: ${{ secrets.OSSRH_USERNAME }}
OSSRH_PASSWORD: ${{ secrets.OSSRH_PASSWORD }}
SIGNING_KEY: ${{ secrets.SIGNING_KEY }}
SIGNING_PASSWORD: ${{ secrets.SIGNING_PASSWORD }}
continue-on-error: true
21 changes: 21 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,27 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

---

## [2.1.1] - 2026-04-06

### Added
- **Hardened Concurrency Safety**: Persisted dispatch is now fully atomic. The decision to enqueue (impl lookup + queue insertion) happens inside a single lock, eliminating the TOCTOU race where `register()` completing between the check and the enqueue would leave an action stranded. Persistence I/O (`save`/`remove`) is intentionally performed *outside* the lock so disk latency cannot block other threads.
- **Compose Module** (`krelay-compose`): `KRelayEffect<T>` and `rememberKRelayImpl<T>` are now published as a standalone `dev.brewkits:krelay-compose` artifact. The module uses `api(project(":krelay"))` so all core types are automatically visible to consumers.
- **Enhanced iOS Registration Safety**: `registerFeature` helper now validates at runtime that the provided implementation conforms to the target interface, crashing early in debug mode and logging a clear warning in release mode. Prevents silent failures from KClass mismatches.
- **ProGuard/R8-safe Persistence API**: `registerActionFactory` and `dispatchPersisted` now require an explicit stable `featureKey: String`. Class simple names can be obfuscated by R8; explicit keys survive minification. Old overloads deprecated with `replaceWith` guidance.
- **Testability via `KRelayInstance` interface**: Persistence and dispatch methods are now declared on the `KRelayInstance` interface, allowing test doubles to implement it directly without casting to `KRelayInstanceImpl`.
- **Binary Compatibility**: Compiled with **Kotlin 2.1.0 (Stable)** for maximum consumer compatibility.

### Fixed
- **Thread-safe `KRelayMetrics`**: All `record*`, `get*`, and `getAllMetrics()` operations are now protected by an internal lock, preventing data races in concurrent apps.
- **`getMetricsInternal` stub**: iOS Swift interop helper now returns real metrics from `KRelayMetrics` instead of an empty map.
- **Priority Eviction Logic**: When the queue is full, KRelay now evicts the **lowest-priority** action (not just the oldest FIFO action), correctly honouring the `ActionPriority` attribute.
- **Compose overwrite warning noise**: `register()` now only logs the overwrite warning when the incoming implementation is a *different class* from the existing one. Same-class replacements (e.g. Activity recreated by the Compose lifecycle) are expected and are no longer logged.
- **`restorePersistedActions` I/O inside lock**: Persistence I/O (`loadAll`/`remove`) is now performed outside the instance lock; all in-memory mutations happen in a single `lock.withLock` block. This removes the risk of disk latency blocking the dispatch path.
- **Identity-aware `unregister`**: Passing an `impl` to `unregister()` now only removes the registration if the stored reference is the same object, preventing a recomposing Compose screen from accidentally clearing a newer registration.
- **`VoyagerDemo` GC bug**: `VoyagerNavigationImpl` is now held by `remember(navigator)` outside the `DisposableEffect`, preventing the Kotlin/Native GC from reclaiming it before the first dispatch.

---

## [2.1.0] - 2026-03-16

### Added
Expand Down
Loading
Loading