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
3 changes: 3 additions & 0 deletions .fernignore
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,9 @@ gradlew
gradlew.bat
pom.xml
Makefile
mvnw
mvnw.cmd
.mvn/

# Documentation
README.md
Expand Down
3 changes: 2 additions & 1 deletion .github/workflows/release-please.yml
Original file line number Diff line number Diff line change
Expand Up @@ -108,4 +108,5 @@ jobs:
MAVEN_USERNAME: ${{ secrets.SONATYPE_USERNAME }}
MAVEN_PASSWORD: ${{ secrets.SONATYPE_PASSWORD }}
GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }}
run: mvn deploy -DskipTests -P release
# Maven 3.10 adds local metadata to the Central bundle, which Central rejects.
run: ./mvnw deploy -DskipTests -P release
66 changes: 66 additions & 0 deletions .github/workflows/retry-maven-central-publish.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
name: Retry Maven Central Publish

on:
workflow_dispatch:
inputs:
release_tag:
description: Release tag to republish (for example, v0.11.1)
required: true
type: string

permissions:
contents: read

jobs:
publish:
name: Publish to Maven Central
runs-on: ubuntu-latest

steps:
- name: Checkout retry tools
uses: actions/checkout@v4
with:
path: retry-tools
ref: ${{ github.event.repository.default_branch }}

- name: Checkout release tag
uses: actions/checkout@v4
with:
fetch-depth: 0
path: release
ref: ${{ inputs.release_tag }}

- name: Verify release tag
env:
RELEASE_TAG: ${{ inputs.release_tag }}
working-directory: release
run: |
version=$(jq -er '."."' .github/.release-please-manifest.json)
if [ "$RELEASE_TAG" != "v${version}" ]; then
echo "Release tag must be v${version}." >&2
exit 1
fi
if [ "$(git rev-parse HEAD)" != "$(git rev-parse "refs/tags/${RELEASE_TAG}^{commit}")" ]; then
echo "Checked out commit does not match ${RELEASE_TAG}." >&2
exit 1
fi

- name: Set up Java 17
uses: actions/setup-java@v4
with:
distribution: temurin
java-version: "17"
server-id: central
server-username: MAVEN_USERNAME
server-password: MAVEN_PASSWORD
gpg-private-key: ${{ secrets.GPG_PRIVATE_KEY }}
gpg-passphrase: GPG_PASSPHRASE

- name: Publish to Maven Central
env:
MAVEN_USERNAME: ${{ secrets.SONATYPE_USERNAME }}
MAVEN_PASSWORD: ${{ secrets.SONATYPE_PASSWORD }}
GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }}
# Maven 3.10 adds local metadata to the Central bundle, which Central rejects.
run: ../retry-tools/mvnw deploy -DskipTests -P release
working-directory: release
20 changes: 20 additions & 0 deletions .mvn/wrapper/maven-wrapper.properties
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
wrapperVersion=3.3.4
distributionType=only-script
distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.16/apache-maven-3.9.16-bin.zip
distributionSha256Sum=5af3b743dd8b876b5c45da33b676251e5f1687712644abb4ee519ca56e1d89ce
11 changes: 9 additions & 2 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ Current permanently frozen files:
- `src/main/java/com/deepgram/DeepgramClient.java`, `src/main/java/com/deepgram/AsyncDeepgramClient.java`, `src/main/java/com/deepgram/DeepgramClientBuilder.java`, `src/main/java/com/deepgram/AsyncDeepgramClientBuilder.java` - custom wrapper entrypoints that add Bearer auth, session ID support, and custom transport behavior on top of Fern's generated API client
- `src/main/java/com/deepgram/core/transport/` - hand-written transport abstraction
- `src/main/java/com/deepgram/resources/listen/v1/websocket/ListenV1ErrorException.java` - hand-written exception carrying a Listen V1 server `Error` (`ListenV1Error`) to the generic `onError` handler when no `onErrorMessage` handler is registered; thrown by the frozen listen v1 `V1WebSocketClient`. No Fern equivalent
- `build.gradle`, `settings.gradle`, `gradle/`, `gradlew`, `gradlew.bat`, `pom.xml`, `Makefile` - build and project configuration
- `build.gradle`, `settings.gradle`, `gradle/`, `gradlew`, `gradlew.bat`, `mvnw`, `mvnw.cmd`, `.mvn/`, `pom.xml`, `Makefile` - build and project configuration
- `README.md`, `CHANGELOG.md`, `CONTRIBUTING.md`, `LICENSE`, `docs/` - docs
- `src/test/` - manually maintained tests
- `examples/` - manually maintained examples
Expand All @@ -48,7 +48,7 @@ How to identify:

Current temporarily frozen files:

- `src/main/java/com/deepgram/core/ClientOptions.java` - preserves release-please version markers, correct SDK header constants, and the accurate `close()` lifecycle Javadoc that Fern currently overwrites; use the standard `.bak` swap/restore workflow during regen review. Since generator 4.18.0 Fern emits a `getSdkVersion()` helper reading `Package.getImplementationVersion()` instead of a literal. That *does* resolve in the published artifact (CI publishes via `mvn deploy -P release`, and `pom.xml`'s maven-jar-plugin sets `addDefaultImplementationEntries=true`, so the JAR manifest carries `Implementation-Version`), but it resolves to `null` under Gradle and in tests, where it silently falls back to a hardcoded literal the generator does not keep current. We keep the explicit literals because they are correct in every context and because `.github/release-please-config.json` already lists this file in `extra-files`, so release-please bumps it alongside `pom.xml`, `build.gradle`, and `.fern/metadata.json`. Fern also emits `User-Agent` with a `com.deepgram.` prefix while leaving `X-Fern-SDK-Name` on the `com.deepgram:` Maven-coordinate form; we keep both on the colon form. Fern 4.22.1 tracks and closes child WebSockets before shutting down SDK-owned OkHttp resources; the permanent custom client wrappers inherit it, so do not add wrapper-local lifecycle code and retain Fern's lifecycle implementation during reconciliation.
- `src/main/java/com/deepgram/core/ClientOptions.java` - preserves release-please version markers, correct SDK header constants, and the accurate `close()` lifecycle Javadoc that Fern currently overwrites; use the standard `.bak` swap/restore workflow during regen review. Since generator 4.18.0 Fern emits a `getSdkVersion()` helper reading `Package.getImplementationVersion()` instead of a literal. That *does* resolve in the published artifact (CI publishes via `./mvnw deploy -P release`, and `pom.xml`'s maven-jar-plugin sets `addDefaultImplementationEntries=true`, so the JAR manifest carries `Implementation-Version`), but it resolves to `null` under Gradle and in tests, where it silently falls back to a hardcoded literal the generator does not keep current. We keep the explicit literals because they are correct in every context and because `.github/release-please-config.json` already lists this file in `extra-files`, so release-please bumps it alongside `pom.xml`, `build.gradle`, and `.fern/metadata.json`. Fern also emits `User-Agent` with a `com.deepgram.` prefix while leaving `X-Fern-SDK-Name` on the `com.deepgram:` Maven-coordinate form; we keep both on the colon form. Fern 4.22.1 tracks and closes child WebSockets before shutting down SDK-owned OkHttp resources; the permanent custom client wrappers inherit it, so do not add wrapper-local lifecycle code and retain Fern's lifecycle implementation during reconciliation.
- `src/main/java/com/deepgram/core/ReconnectingWebSocketListener.java` - carries bug fixes for `maxRetries(0)` semantics ("connect once, don't retry") and a configurable `connectionTimeoutMs` field (was hardcoded 4000ms), plus an `applyOptionsOverride(...)` hook used by `TransportWebSocketFactory` to apply per-transport reconnect policy; pull this back out once the fixes are upstreamed into the Fern generator. Use the standard `.bak` swap/restore workflow during regen review.
- `src/main/java/com/deepgram/resources/speak/v2/websocket/V2WebSocketClient.java` and `src/main/java/com/deepgram/resources/listen/v2/websocket/V2WebSocketClient.java` - forward-compat patch (both clients). Fern's generated `handleIncomingMessage` dispatcher routes any unrecognized message type to `onError` with "Update your SDK version...", which makes a benign new server control frame look fatal to a deployed client. Patched so the unrecognized-type branch is a no-op — the raw frame is already delivered via `onMessage(String)` earlier in the method, so consumers still see it. Mirrors the JS/Python SDKs' forward-compat behavior and is regression-guarded by `src/test/java/com/deepgram/SpeakV2ForwardCompatTest.java` and `src/test/java/com/deepgram/ListenV2ForwardCompatTest.java`. These two clients also carry the streaming query-param patches described in the next entry. Use the standard `.bak` swap/restore workflow during regen review; re-apply the no-op to both after regen, and unfreeze once the generator stops treating unknown frames as errors.
- `src/main/java/com/deepgram/resources/listen/v1/websocket/V1WebSocketClient.java` and `src/main/java/com/deepgram/resources/speak/v1/websocket/V1WebSocketClient.java` (and the v2 clients above) - streaming query-param patches on the generated `connect()` builders. Two fixes: (1) multi-value serialization — array-valued params (listen: `keyterm`, `keywords`, `replace`, `search`, `tag`, `extra`, `language_hint`; speak: `tag`) were serialized with `String.valueOf(union.get())`, collapsing a `List` into one param (`keyterm=[a, b]`) instead of repeats (`keyterm=a&keyterm=b`); (2) an `additionalProperties` escape hatch — the builder exposes `additionalProperty(key, value)` for unmodeled params (e.g. `no_delay`) but `connect()` never emitted them to the URL. Both patched to route through `QueryStringMapper(arraysAsRepeats=true)`, matching the REST path. Listen V1 also forwards a parsed server `Error` to `onError` as a `ListenV1ErrorException` (carrying the typed `ListenV1Error`) when no `onErrorMessage` handler is registered, preserving the prior generic error-handler behavior. Use the standard `.bak` swap/restore workflow during regen review; re-apply after regen and unfreeze once the generator emits array params as repeats and serializes `additionalProperties` on the WS `connect()` path (tracked as an upstream Fern request).
Expand Down Expand Up @@ -88,3 +88,10 @@ The `.bak` files are our manually patched versions protected by `.fernignore`. T
Ignore `core/Nullable.java` and `core/NullableNonemptyFilter.java` (Jackson utility classes, not message types). Anything else in the list needs the `hashCode()` patch, a `.fernignore` entry, an entry in the list above, and a `RegenTypesTest.FieldsLessMessageContract` assertion. `ListenV2ForceEndTurn` reached `main` unpatched this way.
7. Run checks (`./gradlew spotlessCheck test compileExamples`) to verify. `test` covers unit/wire tests including the README snippet compilation; `compileExamples` separately compiles the hand-maintained `examples/` directory and catches stale API call sites that `test` alone would miss. **`spotlessCheck` is required** — CI runs it as a separate job, so `test compileExamples` alone can pass locally while CI fails. Re-applying patches by hand almost always trips it: inserted comments and renamed types change line lengths, which changes how the formatter wraps chained calls. Run `./gradlew spotlessApply` to fix, then re-check.
8. Commit as `chore: re-apply manual patches after regen` and push.

## Releasing to Maven Central

release-please tags the release, then the `publish` job in `.github/workflows/release-please.yml` runs `./mvnw deploy -DskipTests -P release`. The Maven Wrapper (`mvnw`, `.mvn/wrapper/maven-wrapper.properties`) pins Maven 3.9.16 so the build does not depend on the runner's Maven.

- **Do not bump the wrapper to Maven 3.10+** until the Central publishing flow handles it. Maven 3.10 adds `maven-metadata-local.xml` to the Central bundle, and Central rejects the deployment (this blocked 0.11.1 on 2026-10-12). Upgrading `central-publishing-maven-plugin` past 0.7.0 may fix this; verify the bundle contents with `./mvnw deploy -DskipTests -P release -Dgpg.skip=true -DskipPublishing=true` (then inspect `target/central-publishing/central-bundle.zip`) before changing the pin.
- **If the Central publish fails after release-please has tagged**, fix the cause on `main`, then run the **Retry Maven Central Publish** workflow (`.github/workflows/retry-maven-central-publish.yml`) with the release tag (for example, `v0.11.1`). It builds the tagged commit with the wrapper from `main`, and refuses tags that don't match the version in `.github/.release-please-manifest.json`. Maven Central versions are immutable, so a version that already published cannot be re-uploaded.
Loading
Loading