Skip to content
Open
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
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
13 changes: 6 additions & 7 deletions .claude/skills/uts-to-kotlin/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -676,13 +676,12 @@ Recognise a **proxy** spec by a reference to `create_proxy_session()`, proxy `ru

A **direct-sandbox** spec (no `create_proxy_session`, no rules — just happy-path interop against `nonprod:sandbox`) uses the same `SandboxApp` provisioning and the same `runTest` / `@BeforeAll`+`runBlocking` lifecycle as a proxy test, but **drops all proxy wiring**: no `ProxyManager.ensureProxy()`, no `ProxySession`, no `connectThroughProxy`. The client connects straight to the sandbox host. `ChannelHistoryTest` (realtime) and `ObjectsLifecycleTest` (liveobjects) are the reference examples — read one before translating a direct-sandbox spec.

**Client wiring** — point both transports at the sandbox host (explicit hosts auto-disable fallback hosts, so no `fallbackHosts`):
**Client wiring** — point the client at the sandbox endpoint:

```kotlin
private fun newClient(useBinaryProtocol: Boolean): AblyRealtime = TestRealtimeClient {
key = app.defaultKey
realtimeHost = SandboxApp.sandboxHost // sandbox.realtime.ably-nonprod.net
restHost = SandboxApp.sandboxHost
endpoint = SandboxApp.sandboxEndpoint // nonprod:sandbox
this.useBinaryProtocol = useBinaryProtocol
autoConnect = false
}
Expand Down Expand Up @@ -717,7 +716,7 @@ Import `org.junit.jupiter.params.ParameterizedTest` and `org.junit.jupiter.param
| Pseudocode | Kotlin |
|---|---|
| `AWAIT channel.attach()` | `channel.attach()` then `awaitChannelState(channel, ChannelState.attached, 10.seconds)` |
| `AWAIT channel.publish(name, data)` (await the ack) | wrap the **non-deprecated** `publish(name, data, Callback<PublishResult>)` overload in `suspendCancellableCoroutine` — resume on `onSuccess`, fail on `onError` (the `CompletionListener` overload is deprecated) |
| `AWAIT channel.publish(name, data)` (await the ack) | wrap the `publish(name, data, Callback<PublishResult>)` overload in `suspendCancellableCoroutine` — resume on `onSuccess`, fail on `onError` |
| `poll_until(() => AWAIT channel.history().items.length == N, …)` | `pollUntil(10.seconds, 500.milliseconds) { channel.history(null).items().size == N }` (`history()` is a blocking REST call; `null` = no params) |

Use generous timeouts (10–30s) — real network is involved. Everything else is the shared foundation described at the top of this section; a direct-sandbox test just skips the proxy-only subsections (`ProxySession`, rule factories, the event log).
Expand All @@ -728,7 +727,7 @@ Three helpers live under `uts/src/main/kotlin/io/ably/lib/uts/infra/integration/

- **`ProxyManager`** (`infra/integration/proxy/ProxyManager.kt`, package `io.ably.lib.uts.infra.integration.proxy`) — downloads/starts the shared `uts-proxy` process. Call `ProxyManager.ensureProxy()` once per suite in setup.
- **`ProxySession`** (`infra/integration/proxy/ProxySession.kt`, same package) — one programmable session wrapping the proxy control API; also defines the `connectThroughProxy` extension and the rule-builder helpers.
- **`SandboxApp`** (`infra/integration/SandboxApp.kt`, package `io.ably.lib.uts.infra.integration`) — provisions/deletes a sandbox test app from the shared `test-app-setup.json` in ably-common. `SandboxApp.create()` returns a `SandboxApp` with `appId`, `defaultKey`, and `keys` (`defaultKey` is a full-capability `appId.keyId:keySecret`); `app.delete()` tears it down. Provision in suite setup, delete in teardown. Also owns the single upstream sandbox host constant `SandboxApp.sandboxHost` (`sandbox.realtime.ably-nonprod.net`, the resolved `nonprod:sandbox` endpoint) — the default target of every `ProxySession` (both `realtimeHost` and `restHost`), and what direct-sandbox clients set `realtimeHost` / `restHost` from.
- **`SandboxApp`** (`infra/integration/SandboxApp.kt`, package `io.ably.lib.uts.infra.integration`) — provisions/deletes a sandbox test app from the shared `test-app-setup.json` in ably-common. `SandboxApp.create()` returns a `SandboxApp` with `appId`, `defaultKey`, and `keys` (`defaultKey` is a full-capability `appId.keyId:keySecret`); `app.delete()` tears it down. Provision in suite setup, delete in teardown. Also owns `SandboxApp.sandboxEndpoint` (`nonprod:sandbox`), which direct-sandbox clients set as their `endpoint`, and `SandboxApp.sandboxHost` (`sandbox.realtime.ably-nonprod.net`, the hostname that endpoint resolves to) — the default upstream target of every `ProxySession`.

Import what the tier needs: a **direct-sandbox** test imports `io.ably.lib.uts.infra.integration.SandboxApp` plus `io.ably.lib.uts.infra.unit.TestRealtimeClient` and `io.ably.lib.uts.infra.{awaitState, pollUntil}`; a **proxy** test additionally imports `io.ably.lib.uts.infra.integration.proxy.{ProxyManager, ProxySession, connectThroughProxy}`.

Expand Down Expand Up @@ -799,11 +798,11 @@ val client = TestRealtimeClient {
}
```

ably-java has **no `endpoint` ClientOptions field**; `connectThroughProxy` sets the discrete host fields for you:
`connectThroughProxy` sets the proxy-def options for you:

| Proxy-def option | What `connectThroughProxy` sets |
|---|---|
| `endpoint: "localhost"` | `realtimeHost` **and** `restHost` = `session.proxyHost` (`"localhost"`) |
| `endpoint: "localhost"` | `endpoint = session.proxyHost` (`"localhost"`) |
| `port: proxy_port` | `port = session.proxyPort` |
| `tls: false` | `tls = false` |
| `useBinaryProtocol: false` | already the `ClientOptionsBuilder` default — left untouched |
Expand Down
7 changes: 3 additions & 4 deletions .claude/skills/uts-to-kotlin/references/objects-mapping.md
Original file line number Diff line number Diff line change
Expand Up @@ -623,10 +623,9 @@ REST provisioning" convention survives even though the module's internals are vi
> envelope on the legacy `sandbox-rest.ably.io` host; both were aligned upstream — to this V2 shape and to
> the canonical nonprod sandbox host `sandbox.realtime.ably-nonprod.net` — in ably/specification#497.
>
> **Sandbox host.** `provisionObjectsViaRest` sets `restHost = SandboxApp.sandboxHost`
> (`sandbox.realtime.ably-nonprod.net`) — the same nonprod host `SandboxApp` and the realtime clients use,
> **not** `environment="sandbox"` (which resolves to the legacy `sandbox-rest.ably.io`, and
> can't be combined with `restHost` per `Hosts.java` TO3k2/TO3k3). The REST call hits the live sandbox
> **Sandbox host.** `provisionObjectsViaRest` sets `endpoint = SandboxApp.sandboxEndpoint`
> (`nonprod:sandbox`, resolving to `sandbox.realtime.ably-nonprod.net`) — the same endpoint `SandboxApp`
> and the realtime clients use. The REST call hits the live sandbox
> today, and the realtime client observes the provisioned data through the SDK's OBJECT_SYNC +
> `RealtimeObject.get()` (both implemented).

Expand Down
29 changes: 28 additions & 1 deletion .github/workflows/check.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,4 +23,31 @@ jobs:
distribution: 'temurin'
- name: Set up Gradle
uses: gradle/actions/setup-gradle@d9c87d481d55275bb5441eef3fe0e46805f9ef70 # v3
- run: ./gradlew checkWithCodenarc checkstyleMain checkstyleTest runUnitTests runLiveObjectsUnitTests :java:runUtsUnitTests :uts:runUtsUnitTests
- run: ./gradlew checkWithCodenarc checkstyleMain checkstyleTest runUnitTests runLiveObjectsUnitTests :core:runUtsUnitTests :uts:runUtsUnitTests

# A second UTS leg through the server door's builders (see the uts.side handling in
# uts/.../ClientFactories.kt): the builders stamp a side-declaring agent entry and pass
# everything else through, so conformance must be identical on both legs; SideModesTest
# fails a leg whose stamp does not match. There is no device leg on the JVM — the device
# door is an Android artifact, covered by the instrumentation tests in emulate.yml.
- run: ./gradlew :uts:runUtsUnitTests -Duts.side=server

# Continuously proves the release pre-flight and that every published module
# builds a publishable artifact set, so version/coordinate regressions surface
# on PRs rather than on release day.
release-dry-run:
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3
with:
persist-credentials: false
- name: Set up the JDK
uses: actions/setup-java@17f84c3641ba7b8f6deff6309fc4c864478f5d62 # v3
with:
java-version: '17'
distribution: 'temurin'
- name: Set up Gradle
uses: gradle/actions/setup-gradle@d9c87d481d55275bb5441eef3fe0e46805f9ef70 # v3
- run: ./gradlew verifyReleaseArtifacts publishToMavenLocal
8 changes: 4 additions & 4 deletions .github/workflows/emulate.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ jobs:
strategy:
fail-fast: false
matrix:
android-api-level: [ 21, 24, 29, 35 ]
android-api-level: [ 24, 29, 35 ]

steps:
- name: checkout
Expand Down Expand Up @@ -50,10 +50,10 @@ jobs:
arch: ${{ steps.get-avd-arch.outputs.arch }}
target: default
# Print emulator logs if tests fail
script: ./gradlew :android:connectedAndroidTest ${{ matrix.android-api-level == 19 && '-PhttpURLConnection' || '' }} || (adb logcat -d System.out:I && exit 1)
script: ./gradlew :core-android:connectedAndroidTest :device:connectedAndroidTest ${{ matrix.android-api-level == 19 && '-PhttpURLConnection' || '' }} || (adb logcat -d System.out:I && exit 1)

- uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
if: always()
with:
name: android-build-reports-${{ matrix.android-api-level }}
path: android/build/reports/
name: core-android-build-reports-${{ matrix.android-api-level }}
path: core-android/build/reports/
22 changes: 13 additions & 9 deletions .github/workflows/integration-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,13 +27,13 @@ jobs:
- name: Set up Gradle
uses: gradle/actions/setup-gradle@d9c87d481d55275bb5441eef3fe0e46805f9ef70 # v3

- run: ./gradlew :java:testRestSuite -PhttpURLConnection
- run: ./gradlew :core:testHttpSuite -PhttpURLConnection

- uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
if: always()
with:
name: java-build-reports-rest
path: java/build/reports/
name: core-build-reports-rest
path: core/build/reports/

check-realtime-httpurlconnection:
runs-on: ubuntu-latest
Expand All @@ -54,13 +54,13 @@ jobs:
- name: Set up Gradle
uses: gradle/actions/setup-gradle@d9c87d481d55275bb5441eef3fe0e46805f9ef70 # v3

- run: ./gradlew :java:testRealtimeSuite -PhttpURLConnection
- run: ./gradlew :core:testRealtimeSuite -PhttpURLConnection

- uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
if: always()
with:
name: java-build-reports-realtime
path: java/build/reports/
name: core-build-reports-realtime
path: core/build/reports/
check-rest-okhttp:
runs-on: ubuntu-latest
permissions:
Expand All @@ -80,7 +80,7 @@ jobs:
- name: Set up Gradle
uses: gradle/actions/setup-gradle@d9c87d481d55275bb5441eef3fe0e46805f9ef70 # v3

- run: ./gradlew :java:testRestSuite
- run: ./gradlew :core:testHttpSuite

check-realtime-okhttp:
runs-on: ubuntu-latest
Expand All @@ -101,7 +101,7 @@ jobs:
- name: Set up Gradle
uses: gradle/actions/setup-gradle@d9c87d481d55275bb5441eef3fe0e46805f9ef70 # v3

- run: ./gradlew :java:testRealtimeSuite
- run: ./gradlew :core:testRealtimeSuite

check-liveobjects:
runs-on: ubuntu-latest
Expand Down Expand Up @@ -143,4 +143,8 @@ jobs:
- name: Set up Gradle
uses: gradle/actions/setup-gradle@d9c87d481d55275bb5441eef3fe0e46805f9ef70 # v3

- run: ./gradlew :java:runUtsIntegrationTests :uts:runUtsIntegrationTests
- run: ./gradlew :core:runUtsIntegrationTests :uts:runUtsIntegrationTests

# A second leg through the server door's builders — see the uts.side handling in
# uts/.../ClientFactories.kt and the matching leg in check.yml.
- run: ./gradlew :uts:runUtsIntegrationTests -Duts.side=server
2 changes: 1 addition & 1 deletion .github/workflows/javadoc.yml
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,6 @@ jobs:
- name: Upload Documentation
uses: ably/sdk-upload-action@4e694297f208b72b5a9f6b1248a1556f19f821d6 # v2
with:
sourcePath: java/build/docs/javadoc
sourcePath: core/build/docs/javadoc

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.

[MAJOR] javadoc.yml assumes the old per-repo IAM role — docs upload fails silently after the rename

Before running ably/sdk-upload-action on this sourcePath, javadoc.yml assumes role/ably-sdk-builds-ably-java (line 24). This repo is renamed to ably-pubsub-java under the PubSub device/server split rollout (PDR-091b2), and the rollout explicitly warns that these per-repo OIDC/IAM bindings break on rename: the role's trust policy no longer matches the workflow's OIDC subject, so the docs upload starts failing silently on the next push to main. The IAM role (name and trust-policy repo binding) needs re-provisioning for ably/ably-pubsub-java, and the ARN on line 24 updated, in the same pass as the rename.

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.

[MAJOR] Docs published only for the internal core; the public doors get no docs site

This uploads only core/build/docs/javadoc, and the device module publishes an empty javadoc jar. Under the PubSub device/server split rollout (PDR-091b2), core is the internal-only artifact and PubSubDevice/PubSubServer are the recommended public entry points — so as configured, the docs site documents the one artifact users must not depend on and omits the public API. Please generate and publish docs for the device and server modules (in addition to, or instead of, core).

githubToken: ${{ secrets.GITHUB_TOKEN }}
artifactName: javadoc
6 changes: 6 additions & 0 deletions .github/workflows/release.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,12 @@ jobs:
- name: Set up Gradle
uses: gradle/actions/setup-gradle@d9c87d481d55275bb5441eef3fe0e46805f9ef70 # v3

# Fails before anything is uploaded if the artifact set, group or lockstep
# version drifts (core, core-android, device and server release
# together on the same version; partial release must be impossible).
- name: Release pre-flight

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.

[CRITICAL] release.yaml silently no-ops after the repo rename

release.yaml guards the release job with if: github.repository == 'ably/ably-java' (line 9), but this repository is now ably/ably-pubsub-java (as settings.gradle.kts's rootProject.name already reflects, per the PubSub device/server split rollout, PDR-091b2). With the old name in the guard the condition is false, the job is skipped, and the workflow reports success having published nothing — releases silently stop. Update the guard to ably/ably-pubsub-java, keeping the fork-protection intent, and sweep for any other github.repository guards. (Anchored on the added pre-flight step; the guard itself is on line 9, outside this PR's hunks.)

run: ./gradlew verifyReleaseArtifacts

- name: Publish and release to Maven Central
run: ./gradlew publishAndReleaseToMavenCentral
env:
Expand Down
Loading
Loading