From 62e0b65f784ce5eda16816c3c64e8bf25f7cb37c Mon Sep 17 00:00:00 2001
From: "stainless-app[bot]"
<142633134+stainless-app[bot]@users.noreply.github.com>
Date: Wed, 14 Jan 2026 21:28:19 +0000
Subject: [PATCH 01/11] codegen metadata
---
.stats.yml | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/.stats.yml b/.stats.yml
index 92aa691..98ae2b6 100644
--- a/.stats.yml
+++ b/.stats.yml
@@ -1,4 +1,4 @@
configured_endpoints: 78
-openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/courier%2Fcourier-e3e54d99e2a73fd87519270f2685131050d342e86a4e96130247b854deae5c20.yml
-openapi_spec_hash: 897a3fbee24f24d021d6af0df480220c
-config_hash: 66a5c28bb74d78454456d9ce7d1c0a0c
+openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/courier%2Fcourier-9a543994a29d199daec5228c34a90caf017db9a1084289f58645c6849b606940.yml
+openapi_spec_hash: f2f64858daadb0a1978b0e4a4d8ed149
+config_hash: ba6cf7f4dbdf6b9d03c6962dd1770569
From e1937aba76551a3e486cb8961c3f1e0981d9a57f Mon Sep 17 00:00:00 2001
From: "stainless-app[bot]"
<142633134+stainless-app[bot]@users.noreply.github.com>
Date: Fri, 16 Jan 2026 03:55:23 +0000
Subject: [PATCH 02/11] chore(internal): clean up maven repo artifact script
and add html documentation to repo root
---
scripts/upload-artifacts | 44 +++++++++++++++++++++++++++++++++++++---
1 file changed, 41 insertions(+), 3 deletions(-)
diff --git a/scripts/upload-artifacts b/scripts/upload-artifacts
index 729e6f2..df0c8d9 100755
--- a/scripts/upload-artifacts
+++ b/scripts/upload-artifacts
@@ -7,6 +7,8 @@ GREEN='\033[32m'
RED='\033[31m'
NC='\033[0m' # No Color
+MAVEN_REPO_PATH="./build/local-maven-repo"
+
log_error() {
local msg="$1"
local headers="$2"
@@ -24,7 +26,7 @@ upload_file() {
if [ -f "$file_name" ]; then
echo -e "${GREEN}Processing file: $file_name${NC}"
- pkg_file_name="mvn${file_name#./build/local-maven-repo}"
+ pkg_file_name="mvn${file_name#"${MAVEN_REPO_PATH}"}"
# Get signed URL for uploading artifact file
signed_url_response=$(curl -X POST -G "$URL" \
@@ -47,6 +49,7 @@ upload_file() {
md5|sha1|sha256|sha512) content_type="text/plain" ;;
module) content_type="application/json" ;;
pom|xml) content_type="application/xml" ;;
+ html) content_type="text/html" ;;
*) content_type="application/octet-stream" ;;
esac
@@ -81,6 +84,41 @@ walk_tree() {
done
}
+generate_instructions() {
+ cat << EOF > "$MAVEN_REPO_PATH/index.html"
+
+
+
+ Maven Repo
+
+
+ Stainless SDK Maven Repository
+ This is the Maven repository for your Stainless Java SDK build.
+
+ Directions
+ To use the uploaded Maven repository, add the following to your project's pom.xml:
+ <repositories>
+ <repository>
+ <id>stainless-sdk-repo</id>
+ <url>https://pkg.stainless.com/s/${PROJECT}/${SHA}/mvn</url>
+ </repository>
+</repositories>
+
+ If you're using Gradle, add the following to your build.gradle file:
+ repositories {
+ maven {
+ url 'https://pkg.stainless.com/s/${PROJECT}/${SHA}/mvn'
+ }
+}
+
+
+EOF
+ upload_file "${MAVEN_REPO_PATH}/index.html"
+
+ echo "Configure maven or gradle to use the repo located at 'https://pkg.stainless.com/s/${PROJECT}/${SHA}/mvn'"
+ echo "For more details, see the directions in https://pkg.stainless.com/s/${PROJECT}/${SHA}/mvn/index.html"
+}
+
cd "$(dirname "$0")/.."
echo "::group::Creating local Maven content"
@@ -88,9 +126,9 @@ echo "::group::Creating local Maven content"
echo "::endgroup::"
echo "::group::Uploading to pkg.stainless.com"
-walk_tree "./build/local-maven-repo"
+walk_tree "$MAVEN_REPO_PATH"
echo "::endgroup::"
echo "::group::Generating instructions"
-echo "Configure maven or gradle to use the repo located at 'https://pkg.stainless.com/s/${PROJECT}/${SHA}/mvn'"
+generate_instructions
echo "::endgroup::"
From fa50329d5f5e1cd2c555d5deb71b4dd4d8a57c4a Mon Sep 17 00:00:00 2001
From: "stainless-app[bot]"
<142633134+stainless-app[bot]@users.noreply.github.com>
Date: Sat, 17 Jan 2026 04:37:45 +0000
Subject: [PATCH 03/11] chore: test on Jackson 2.14.0 to avoid encountering
FasterXML/jackson-databind#3240 in tests fix: date time deserialization
leniency
---
README.md | 2 ++
courier-java-core/build.gradle.kts | 18 +++++-----
.../kotlin/com/courier/core/ObjectMappers.kt | 33 ++++++++++++-------
.../courier/models/MessageRoutingChannel.kt | 2 +-
.../models/UserProfileFirebaseToken.kt | 2 +-
.../notifications/NotificationGetContent.kt | 2 +-
.../courier/models/send/SendMessageParams.kt | 2 +-
.../courier/models/users/tokens/UserToken.kt | 2 +-
.../com/courier/core/ObjectMappersTest.kt | 16 +++------
.../models/MessageRoutingChannelTest.kt | 17 +++++++---
.../models/UserProfileFirebaseTokenTest.kt | 17 +++++++---
courier-java-proguard-test/build.gradle.kts | 2 +-
12 files changed, 69 insertions(+), 46 deletions(-)
diff --git a/README.md b/README.md
index 47bcf43a..034b735 100644
--- a/README.md
+++ b/README.md
@@ -310,6 +310,8 @@ If the SDK threw an exception, but you're _certain_ the version is compatible, t
> [!CAUTION]
> We make no guarantee that the SDK works correctly when the Jackson version check is disabled.
+Also note that there are bugs in older Jackson versions that can affect the SDK. We don't work around all Jackson bugs ([example](https://github.com/FasterXML/jackson-databind/issues/3240)) and expect users to upgrade Jackson for those instead.
+
## Network options
### Retries
diff --git a/courier-java-core/build.gradle.kts b/courier-java-core/build.gradle.kts
index d31e4b1..3396f58 100644
--- a/courier-java-core/build.gradle.kts
+++ b/courier-java-core/build.gradle.kts
@@ -5,14 +5,16 @@ plugins {
configurations.all {
resolutionStrategy {
- // Compile and test against a lower Jackson version to ensure we're compatible with it.
- // We publish with a higher version (see below) to ensure users depend on a secure version by default.
- force("com.fasterxml.jackson.core:jackson-core:2.13.4")
- force("com.fasterxml.jackson.core:jackson-databind:2.13.4")
- force("com.fasterxml.jackson.core:jackson-annotations:2.13.4")
- force("com.fasterxml.jackson.datatype:jackson-datatype-jdk8:2.13.4")
- force("com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.13.4")
- force("com.fasterxml.jackson.module:jackson-module-kotlin:2.13.4")
+ // Compile and test against a lower Jackson version to ensure we're compatible with it. Note that
+ // we generally support 2.13.4, but test against 2.14.0 because 2.13.4 has some annoying (but
+ // niche) bugs (users should upgrade if they encounter them). We publish with a higher version
+ // (see below) to ensure users depend on a secure version by default.
+ force("com.fasterxml.jackson.core:jackson-core:2.14.0")
+ force("com.fasterxml.jackson.core:jackson-databind:2.14.0")
+ force("com.fasterxml.jackson.core:jackson-annotations:2.14.0")
+ force("com.fasterxml.jackson.datatype:jackson-datatype-jdk8:2.14.0")
+ force("com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.14.0")
+ force("com.fasterxml.jackson.module:jackson-module-kotlin:2.14.0")
}
}
diff --git a/courier-java-core/src/main/kotlin/com/courier/core/ObjectMappers.kt b/courier-java-core/src/main/kotlin/com/courier/core/ObjectMappers.kt
index a2cc05b..3f2ec81 100644
--- a/courier-java-core/src/main/kotlin/com/courier/core/ObjectMappers.kt
+++ b/courier-java-core/src/main/kotlin/com/courier/core/ObjectMappers.kt
@@ -24,6 +24,7 @@ import java.io.InputStream
import java.time.DateTimeException
import java.time.LocalDate
import java.time.LocalDateTime
+import java.time.OffsetDateTime
import java.time.ZonedDateTime
import java.time.format.DateTimeFormatter
import java.time.temporal.ChronoField
@@ -36,7 +37,7 @@ fun jsonMapper(): JsonMapper =
.addModule(
SimpleModule()
.addSerializer(InputStreamSerializer)
- .addDeserializer(LocalDateTime::class.java, LenientLocalDateTimeDeserializer())
+ .addDeserializer(OffsetDateTime::class.java, LenientOffsetDateTimeDeserializer())
)
.withCoercionConfig(LogicalType.Boolean) {
it.setCoercion(CoercionInputShape.Integer, CoercionAction.Fail)
@@ -64,6 +65,12 @@ fun jsonMapper(): JsonMapper =
.setCoercion(CoercionInputShape.Array, CoercionAction.Fail)
.setCoercion(CoercionInputShape.Object, CoercionAction.Fail)
}
+ .withCoercionConfig(LogicalType.DateTime) {
+ it.setCoercion(CoercionInputShape.Integer, CoercionAction.Fail)
+ .setCoercion(CoercionInputShape.Float, CoercionAction.Fail)
+ .setCoercion(CoercionInputShape.Array, CoercionAction.Fail)
+ .setCoercion(CoercionInputShape.Object, CoercionAction.Fail)
+ }
.withCoercionConfig(LogicalType.Array) {
it.setCoercion(CoercionInputShape.Boolean, CoercionAction.Fail)
.setCoercion(CoercionInputShape.Integer, CoercionAction.Fail)
@@ -124,10 +131,10 @@ private object InputStreamSerializer : BaseSerializer(InputStream::
}
/**
- * A deserializer that can deserialize [LocalDateTime] from datetimes, dates, and zoned datetimes.
+ * A deserializer that can deserialize [OffsetDateTime] from datetimes, dates, and zoned datetimes.
*/
-private class LenientLocalDateTimeDeserializer :
- StdDeserializer(LocalDateTime::class.java) {
+private class LenientOffsetDateTimeDeserializer :
+ StdDeserializer(OffsetDateTime::class.java) {
companion object {
@@ -141,7 +148,7 @@ private class LenientLocalDateTimeDeserializer :
override fun logicalType(): LogicalType = LogicalType.DateTime
- override fun deserialize(p: JsonParser, context: DeserializationContext?): LocalDateTime {
+ override fun deserialize(p: JsonParser, context: DeserializationContext): OffsetDateTime {
val exceptions = mutableListOf()
for (formatter in DATE_TIME_FORMATTERS) {
@@ -149,18 +156,20 @@ private class LenientLocalDateTimeDeserializer :
val temporal = formatter.parse(p.text)
return when {
- !temporal.isSupported(ChronoField.HOUR_OF_DAY) ->
- LocalDate.from(temporal).atStartOfDay()
- !temporal.isSupported(ChronoField.OFFSET_SECONDS) ->
- LocalDateTime.from(temporal)
- else -> ZonedDateTime.from(temporal).toLocalDateTime()
- }
+ !temporal.isSupported(ChronoField.HOUR_OF_DAY) ->
+ LocalDate.from(temporal).atStartOfDay()
+ !temporal.isSupported(ChronoField.OFFSET_SECONDS) ->
+ LocalDateTime.from(temporal)
+ else -> ZonedDateTime.from(temporal).toLocalDateTime()
+ }
+ .atZone(context.timeZone.toZoneId())
+ .toOffsetDateTime()
} catch (e: DateTimeException) {
exceptions.add(e)
}
}
- throw JsonParseException(p, "Cannot parse `LocalDateTime` from value: ${p.text}").apply {
+ throw JsonParseException(p, "Cannot parse `OffsetDateTime` from value: ${p.text}").apply {
exceptions.forEach { addSuppressed(it) }
}
}
diff --git a/courier-java-core/src/main/kotlin/com/courier/models/MessageRoutingChannel.kt b/courier-java-core/src/main/kotlin/com/courier/models/MessageRoutingChannel.kt
index d578d83..1a6a935 100644
--- a/courier-java-core/src/main/kotlin/com/courier/models/MessageRoutingChannel.kt
+++ b/courier-java-core/src/main/kotlin/com/courier/models/MessageRoutingChannel.kt
@@ -167,7 +167,7 @@ private constructor(
.toList()
return when (bestMatches.size) {
// This can happen if what we're deserializing is completely incompatible with all
- // the possible variants (e.g. deserializing from array).
+ // the possible variants (e.g. deserializing from boolean).
0 -> MessageRoutingChannel(_json = json)
1 -> bestMatches.single()
// If there's more than one match with the highest validity, then use the first
diff --git a/courier-java-core/src/main/kotlin/com/courier/models/UserProfileFirebaseToken.kt b/courier-java-core/src/main/kotlin/com/courier/models/UserProfileFirebaseToken.kt
index 3f82532..0beef44 100644
--- a/courier-java-core/src/main/kotlin/com/courier/models/UserProfileFirebaseToken.kt
+++ b/courier-java-core/src/main/kotlin/com/courier/models/UserProfileFirebaseToken.kt
@@ -165,7 +165,7 @@ private constructor(
.toList()
return when (bestMatches.size) {
// This can happen if what we're deserializing is completely incompatible with all
- // the possible variants (e.g. deserializing from object).
+ // the possible variants (e.g. deserializing from boolean).
0 -> UserProfileFirebaseToken(_json = json)
1 -> bestMatches.single()
// If there's more than one match with the highest validity, then use the first
diff --git a/courier-java-core/src/main/kotlin/com/courier/models/notifications/NotificationGetContent.kt b/courier-java-core/src/main/kotlin/com/courier/models/notifications/NotificationGetContent.kt
index d58e0f1..d436598 100644
--- a/courier-java-core/src/main/kotlin/com/courier/models/notifications/NotificationGetContent.kt
+++ b/courier-java-core/src/main/kotlin/com/courier/models/notifications/NotificationGetContent.kt
@@ -940,7 +940,7 @@ private constructor(
.toList()
return when (bestMatches.size) {
// This can happen if what we're deserializing is completely incompatible
- // with all the possible variants (e.g. deserializing from array).
+ // with all the possible variants (e.g. deserializing from boolean).
0 -> Content(_json = json)
1 -> bestMatches.single()
// If there's more than one match with the highest validity, then use the
diff --git a/courier-java-core/src/main/kotlin/com/courier/models/send/SendMessageParams.kt b/courier-java-core/src/main/kotlin/com/courier/models/send/SendMessageParams.kt
index 23beed9..80bfc66 100644
--- a/courier-java-core/src/main/kotlin/com/courier/models/send/SendMessageParams.kt
+++ b/courier-java-core/src/main/kotlin/com/courier/models/send/SendMessageParams.kt
@@ -2089,7 +2089,7 @@ private constructor(
return when (bestMatches.size) {
// This can happen if what we're deserializing is completely
// incompatible with all the possible variants (e.g. deserializing from
- // object).
+ // boolean).
0 -> ExpiresIn(_json = json)
1 -> bestMatches.single()
// If there's more than one match with the highest validity, then use
diff --git a/courier-java-core/src/main/kotlin/com/courier/models/users/tokens/UserToken.kt b/courier-java-core/src/main/kotlin/com/courier/models/users/tokens/UserToken.kt
index 038730e..4df0674 100644
--- a/courier-java-core/src/main/kotlin/com/courier/models/users/tokens/UserToken.kt
+++ b/courier-java-core/src/main/kotlin/com/courier/models/users/tokens/UserToken.kt
@@ -983,7 +983,7 @@ private constructor(
.toList()
return when (bestMatches.size) {
// This can happen if what we're deserializing is completely incompatible with
- // all the possible variants (e.g. deserializing from object).
+ // all the possible variants (e.g. deserializing from integer).
0 -> ExpiryDate(_json = json)
1 -> bestMatches.single()
// If there's more than one match with the highest validity, then use the first
diff --git a/courier-java-core/src/test/kotlin/com/courier/core/ObjectMappersTest.kt b/courier-java-core/src/test/kotlin/com/courier/core/ObjectMappersTest.kt
index 027efee..1db077e 100644
--- a/courier-java-core/src/test/kotlin/com/courier/core/ObjectMappersTest.kt
+++ b/courier-java-core/src/test/kotlin/com/courier/core/ObjectMappersTest.kt
@@ -3,7 +3,7 @@ package com.courier.core
import com.fasterxml.jackson.annotation.JsonProperty
import com.fasterxml.jackson.databind.exc.MismatchedInputException
import com.fasterxml.jackson.module.kotlin.readValue
-import java.time.LocalDateTime
+import java.time.OffsetDateTime
import kotlin.reflect.KClass
import org.assertj.core.api.Assertions.assertThat
import org.assertj.core.api.Assertions.catchThrowable
@@ -58,14 +58,6 @@ internal class ObjectMappersTest {
LONG to DOUBLE,
LONG to INTEGER,
CLASS to MAP,
- // These aren't actually valid, but coercion configs don't work for String until
- // v2.14.0: https://github.com/FasterXML/jackson-databind/issues/3240
- // We currently test on v2.13.4.
- BOOLEAN to STRING,
- FLOAT to STRING,
- DOUBLE to STRING,
- INTEGER to STRING,
- LONG to STRING,
)
}
}
@@ -84,7 +76,7 @@ internal class ObjectMappersTest {
}
}
- enum class LenientLocalDateTimeTestCase(val string: String) {
+ enum class LenientOffsetDateTimeTestCase(val string: String) {
DATE("1998-04-21"),
DATE_TIME("1998-04-21T04:00:00"),
ZONED_DATE_TIME_1("1998-04-21T04:00:00+03:00"),
@@ -93,10 +85,10 @@ internal class ObjectMappersTest {
@ParameterizedTest
@EnumSource
- fun readLocalDateTime_lenient(testCase: LenientLocalDateTimeTestCase) {
+ fun readOffsetDateTime_lenient(testCase: LenientOffsetDateTimeTestCase) {
val jsonMapper = jsonMapper()
val json = jsonMapper.writeValueAsString(testCase.string)
- assertDoesNotThrow { jsonMapper().readValue(json) }
+ assertDoesNotThrow { jsonMapper().readValue(json) }
}
}
diff --git a/courier-java-core/src/test/kotlin/com/courier/models/MessageRoutingChannelTest.kt b/courier-java-core/src/test/kotlin/com/courier/models/MessageRoutingChannelTest.kt
index fcfc0fe..767ec93 100644
--- a/courier-java-core/src/test/kotlin/com/courier/models/MessageRoutingChannelTest.kt
+++ b/courier-java-core/src/test/kotlin/com/courier/models/MessageRoutingChannelTest.kt
@@ -9,6 +9,8 @@ import com.fasterxml.jackson.module.kotlin.jacksonTypeRef
import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.assertThrows
+import org.junit.jupiter.params.ParameterizedTest
+import org.junit.jupiter.params.provider.EnumSource
internal class MessageRoutingChannelTest {
@@ -67,11 +69,18 @@ internal class MessageRoutingChannelTest {
assertThat(roundtrippedMessageRoutingChannel).isEqualTo(messageRoutingChannel)
}
- @Test
- fun incompatibleJsonShapeDeserializesToUnknown() {
- val value = JsonValue.from(listOf("invalid", "array"))
+ enum class IncompatibleJsonShapeTestCase(val value: JsonValue) {
+ BOOLEAN(JsonValue.from(false)),
+ INTEGER(JsonValue.from(-1)),
+ FLOAT(JsonValue.from(3.14)),
+ ARRAY(JsonValue.from(listOf("invalid", "array"))),
+ }
+
+ @ParameterizedTest
+ @EnumSource
+ fun incompatibleJsonShapeDeserializesToUnknown(testCase: IncompatibleJsonShapeTestCase) {
val messageRoutingChannel =
- jsonMapper().convertValue(value, jacksonTypeRef())
+ jsonMapper().convertValue(testCase.value, jacksonTypeRef())
val e = assertThrows { messageRoutingChannel.validate() }
assertThat(e).hasMessageStartingWith("Unknown ")
diff --git a/courier-java-core/src/test/kotlin/com/courier/models/UserProfileFirebaseTokenTest.kt b/courier-java-core/src/test/kotlin/com/courier/models/UserProfileFirebaseTokenTest.kt
index 9966066..61b8e98 100644
--- a/courier-java-core/src/test/kotlin/com/courier/models/UserProfileFirebaseTokenTest.kt
+++ b/courier-java-core/src/test/kotlin/com/courier/models/UserProfileFirebaseTokenTest.kt
@@ -9,6 +9,8 @@ import com.fasterxml.jackson.module.kotlin.jacksonTypeRef
import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.assertThrows
+import org.junit.jupiter.params.ParameterizedTest
+import org.junit.jupiter.params.provider.EnumSource
internal class UserProfileFirebaseTokenTest {
@@ -60,11 +62,18 @@ internal class UserProfileFirebaseTokenTest {
assertThat(roundtrippedUserProfileFirebaseToken).isEqualTo(userProfileFirebaseToken)
}
- @Test
- fun incompatibleJsonShapeDeserializesToUnknown() {
- val value = JsonValue.from(mapOf("invalid" to "object"))
+ enum class IncompatibleJsonShapeTestCase(val value: JsonValue) {
+ BOOLEAN(JsonValue.from(false)),
+ INTEGER(JsonValue.from(-1)),
+ FLOAT(JsonValue.from(3.14)),
+ OBJECT(JsonValue.from(mapOf("invalid" to "object"))),
+ }
+
+ @ParameterizedTest
+ @EnumSource
+ fun incompatibleJsonShapeDeserializesToUnknown(testCase: IncompatibleJsonShapeTestCase) {
val userProfileFirebaseToken =
- jsonMapper().convertValue(value, jacksonTypeRef())
+ jsonMapper().convertValue(testCase.value, jacksonTypeRef())
val e = assertThrows { userProfileFirebaseToken.validate() }
assertThat(e).hasMessageStartingWith("Unknown ")
diff --git a/courier-java-proguard-test/build.gradle.kts b/courier-java-proguard-test/build.gradle.kts
index 9d26310..0d3c8c1 100644
--- a/courier-java-proguard-test/build.gradle.kts
+++ b/courier-java-proguard-test/build.gradle.kts
@@ -19,7 +19,7 @@ dependencies {
testImplementation(kotlin("test"))
testImplementation("org.junit.jupiter:junit-jupiter-api:5.9.3")
testImplementation("org.assertj:assertj-core:3.25.3")
- testImplementation("com.fasterxml.jackson.module:jackson-module-kotlin:2.13.4")
+ testImplementation("com.fasterxml.jackson.module:jackson-module-kotlin:2.14.0")
}
tasks.shadowJar {
From af0c83b582d3d26589a0f1176405a365df1e26b5 Mon Sep 17 00:00:00 2001
From: "stainless-app[bot]"
<142633134+stainless-app[bot]@users.noreply.github.com>
Date: Sat, 17 Jan 2026 04:41:50 +0000
Subject: [PATCH 04/11] chore(internal): improve maven repo docs
---
scripts/upload-artifacts | 7 ++++++-
1 file changed, 6 insertions(+), 1 deletion(-)
diff --git a/scripts/upload-artifacts b/scripts/upload-artifacts
index df0c8d9..548d152 100755
--- a/scripts/upload-artifacts
+++ b/scripts/upload-artifacts
@@ -56,12 +56,13 @@ upload_file() {
# Upload file
upload_response=$(curl -v -X PUT \
--retry 5 \
+ --retry-all-errors \
-D "$tmp_headers" \
-H "Content-Type: $content_type" \
--data-binary "@${file_name}" "$signed_url" 2>&1)
if ! echo "$upload_response" | grep -q "HTTP/[0-9.]* 200"; then
- log_error "Failed upload artifact file" "$tmp_headers" "$upload_response"
+ log_error "Failed to upload artifact file" "$tmp_headers" "$upload_response"
fi
# Insert small throttle to reduce rate limiting risk
@@ -110,6 +111,10 @@ generate_instructions() {
url 'https://pkg.stainless.com/s/${PROJECT}/${SHA}/mvn'
}
}
+
+ Once you've added the repository, you can include dependencies from it as usual. See your
+ project README
+ for more details.