diff --git a/.github/workflows/manual-release.yml b/.github/workflows/manual-release.yml new file mode 100644 index 0000000..f50e69b --- /dev/null +++ b/.github/workflows/manual-release.yml @@ -0,0 +1,20 @@ +name: Manual Release +# Triggers a merge from main->release, which will then trigger a release +# from the release branch. +on: + # Allows you to run this workflow manually from the Actions tab + workflow_dispatch: + +jobs: + merge-to-release-branch: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + + - name: Merge main -> release + uses: devmasx/merge-branch@master + with: + type: now + from_branch: main + target_branch: release + github_token: ${{ secrets.MOMENTO_MACHINE_USER_GITHUB_TOKEN }} diff --git a/.github/workflows/on-push-to-release-branch.yml b/.github/workflows/on-push-to-release-branch.yml new file mode 100644 index 0000000..2b1feb0 --- /dev/null +++ b/.github/workflows/on-push-to-release-branch.yml @@ -0,0 +1,58 @@ +name: On push to release branch + +on: + push: + branches: [release] + +jobs: + release: + runs-on: ubuntu-latest + outputs: + version: ${{ steps.release.outputs.release }} + steps: + - uses: actions/checkout@v3 + - name: Set release + id: semrel + uses: go-semantic-release/action@v1 + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + allow-initial-development-versions: true + force-bump-patch-version: true + # For whatever reason, this silly tool won't let you do releases from branches + # other than the default branch unless you pass this flag, which doesn't seem + # to actually have anything to do with CI: + # https://github.com/go-semantic-release/semantic-release/blob/master/cmd/semantic-release/main.go#L173-L194 + # https://github.com/go-semantic-release/condition-github/blob/4c8af3fc516151423fff2f77eb08bf7082570676/pkg/condition/github.go#L42-L44 + custom-arguments: '--no-ci' + + - name: Output release + id: release + run: echo "release=${{ steps.semrel.outputs.version }}" >> $GITHUB_OUTPUT + + publish: + runs-on: ubuntu-latest + + steps: + - name: Checkout project + uses: actions/checkout@v3 + + - name: Set up JDK 17 + uses: actions/setup-java@v3 + with: + java-version: 17 + distribution: 'corretto' + + - name: Build project + run: make build + + - name: Publish to sonatype + env: + SONATYPE_SIGNING_KEY: ${{ secrets.SONATYPE_SIGNING_KEY }} + SONATYPE_SIGNING_KEY_PASSWORD: ${{ secrets.SONATYPE_SIGNING_KEY_PASSWORD }} + SONATYPE_USERNAME: ${{ secrets.SONATYPE_USERNAME }} + SONATYPE_PASSWORD: ${{ secrets.SONATYPE_PASSWORD }} + ORG_GRADLE_PROJECT_version: ${{ needs.release.outputs.version }} + uses: gradle/gradle-build-action@v2 + with: + # Note that this will require manual release from Sonatype + arguments: publishToSonatype closeSonatypeStagingRepository diff --git a/build.gradle.kts b/build.gradle.kts index 583fa0e..df54fef 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,6 +1,11 @@ plugins { id("java") id("com.diffplug.spotless") version "5.15.1" + + // Publishing plugins + id("maven-publish") + id("signing") + id("io.github.gradle-nexus.publish-plugin") version "1.3.0" } group = "software.momento.java" @@ -10,6 +15,15 @@ repositories { mavenCentral() } +java { + sourceCompatibility = JavaVersion.VERSION_17 + targetCompatibility = JavaVersion.VERSION_17 + + // For publishing + withSourcesJar() + withJavadocJar() +} + dependencies { implementation("io.lettuce:lettuce-core:6.4.0.RELEASE") implementation("software.momento.java:sdk:1.15.0") @@ -35,3 +49,66 @@ spotless { googleJavaFormat("1.11.0") } } + +publishing { + publications { + create("mavenJava") { + from(components["java"]) + groupId = group.toString() + artifactId = "momento-lettuce" + version = project.version.toString() + + pom { + name.set("Momento Lettuce Compatibility Client") + description.set("Momento-backed implementation of the Lettuce Redis client") + url.set("https://github.com/momentohq/momento-java-lettuce-client") + licenses { + license { + name.set("The Apache License, Version 2.0") + url.set("https://www.apache.org/licenses/LICENSE-2.0.txt") + } + } + developers { + developer { + id.set("momento") + name.set("Momento") + organization.set("Momento") + email.set("eng-deveco@momentohq.com") + } + } + scm { + connection.set("scm:git:git://github.com/momentohq/momento-java-lettuce-client.git") + developerConnection.set("scm:git:git@github.com:momentohq/momento-java-lettuce-client.git") + url.set("https://github.com/momentohq/momento-java-lettuce-client") + } + } + } + } +} + +// Signing and Nexus publishing setup +val signingKey: String? = System.getenv("SONATYPE_SIGNING_KEY") +val signingPassword: String? = System.getenv("SONATYPE_SIGNING_KEY_PASSWORD") + +if (signingKey != null && signingPassword != null) { + signing { + useInMemoryPgpKeys(signingKey, signingPassword) + sign(publishing.publications["mavenJava"]) + } +} + +val sonatypeUsername: String? = System.getenv("SONATYPE_USERNAME") +val sonatypePassword: String? = System.getenv("SONATYPE_PASSWORD") + +if (sonatypeUsername != null && sonatypePassword != null) { + nexusPublishing { + repositories { + sonatype { + nexusUrl.set(uri("https://s01.oss.sonatype.org/service/local/")) + snapshotRepositoryUrl.set(uri("https://s01.oss.sonatype.org/content/repositories/snapshots/")) + username.set(sonatypeUsername) + password.set(sonatypePassword) + } + } + } +} diff --git a/src/main/java/momento/lettuce/MomentoRedisReactiveClient.java b/src/main/java/momento/lettuce/MomentoRedisReactiveClient.java index f5694d6..669a161 100644 --- a/src/main/java/momento/lettuce/MomentoRedisReactiveClient.java +++ b/src/main/java/momento/lettuce/MomentoRedisReactiveClient.java @@ -101,6 +101,12 @@ import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; +/** + * A Redis client that wraps a {@link CacheClient} and provides reactive commands. + * + * @param Key type. + * @param Value type. + */ public class MomentoRedisReactiveClient implements RedisReactiveCommands, MomentoRedisReactiveCommands { private final CacheClient client; @@ -108,6 +114,13 @@ public class MomentoRedisReactiveClient private final RedisCodecByteArrayConverter codec; private volatile EventExecutorGroup scheduler; + /** + * Creates a new {@link MomentoRedisReactiveClient}. + * + * @param client The cache client. + * @param cacheName The name of the cache to store data in. + * @param codec The codec to use for serializing and deserializing keys and values. + */ public MomentoRedisReactiveClient(CacheClient client, String cacheName, RedisCodec codec) { this.client = client; this.cacheName = cacheName; @@ -115,11 +128,30 @@ public MomentoRedisReactiveClient(CacheClient client, String cacheName, RedisCod this.scheduler = ImmediateEventExecutor.INSTANCE; } + /** + * Instantiates a new {@link MomentoRedisReactiveClient} with the provided {@link CacheClient} and + * cache name. + * + * @param client The cache client. + * @param cacheName The name of the cache to store data in. + * @param codec The codec to use for serializing and deserializing keys and values. + * @return A new {@link MomentoRedisReactiveClient}. + * @param Key type. + * @param Value type. + */ public static MomentoRedisReactiveClient create( CacheClient client, String cacheName, RedisCodec codec) { return new MomentoRedisReactiveClient<>(client, cacheName, codec); } + /** + * Instantiates a new {@link MomentoRedisReactiveClient} with the provided {@link CacheClient} and + * cache name. + * + * @param client The cache client. + * @param cacheName The name of the cache to store data in. + * @return A new {@link MomentoRedisReactiveClient}. + */ public static MomentoRedisReactiveClient create( CacheClient client, String cacheName) { return new MomentoRedisReactiveClient<>(client, cacheName, StringCodec.UTF8); diff --git a/src/main/java/momento/lettuce/utils/ExpireCondition.java b/src/main/java/momento/lettuce/utils/ExpireCondition.java index 21cda36..05667c3 100644 --- a/src/main/java/momento/lettuce/utils/ExpireCondition.java +++ b/src/main/java/momento/lettuce/utils/ExpireCondition.java @@ -17,6 +17,15 @@ public class ExpireCondition { private final boolean requiresLessThan; private final boolean requiresGreaterThan; + /** + * Creates a new {@link ExpireCondition}. + * + * @param requiresExistingExpiry Whether the operation requires an existing expiry. + * @param requiresNoExpiry Whether the operation requires no expiry set. + * @param requiresLessThan Whether the operation requires the provided expiry to be less than the + * @param requiresGreaterThan Whether the operation requires the provided expiry to be greater + * than + */ public ExpireCondition( boolean requiresExistingExpiry, boolean requiresNoExpiry, diff --git a/src/main/java/momento/lettuce/utils/RangeUtils.java b/src/main/java/momento/lettuce/utils/RangeUtils.java index a0dc776..626d96f 100644 --- a/src/main/java/momento/lettuce/utils/RangeUtils.java +++ b/src/main/java/momento/lettuce/utils/RangeUtils.java @@ -1,5 +1,6 @@ package momento.lettuce.utils; +/** Provides utility methods for working with ranges. */ public class RangeUtils { /** * Adjusts the end range from inclusive to exclusive. diff --git a/src/main/java/momento/lettuce/utils/RedisResponse.java b/src/main/java/momento/lettuce/utils/RedisResponse.java index 6c08382..e48ee08 100644 --- a/src/main/java/momento/lettuce/utils/RedisResponse.java +++ b/src/main/java/momento/lettuce/utils/RedisResponse.java @@ -1,5 +1,7 @@ package momento.lettuce.utils; +/** Represents the response from Redis. */ public class RedisResponse { + /** The response from Redis when a command is successful. */ public static final String OK = "OK"; } diff --git a/src/main/java/momento/lettuce/utils/ValidatorUtils.java b/src/main/java/momento/lettuce/utils/ValidatorUtils.java index 6e91658..b5208bd 100644 --- a/src/main/java/momento/lettuce/utils/ValidatorUtils.java +++ b/src/main/java/momento/lettuce/utils/ValidatorUtils.java @@ -1,6 +1,13 @@ package momento.lettuce.utils; +/** Provides utility methods for validating arguments. */ public class ValidatorUtils { + /** + * Ensures that the provided value is within the range of an integer. + * + * @param value The value to check. + * @param argumentName The name of the argument. + */ public static void ensureInIntegerRange(long value, String argumentName) { if (value < Integer.MIN_VALUE || value > Integer.MAX_VALUE) { throw MomentoToLettuceExceptionMapper.createIntegerOutOfRangeException(argumentName, value);