From d8664e6be170d9f333b46ecf425cd1d25be17e34 Mon Sep 17 00:00:00 2001 From: PJ Fanning Date: Tue, 23 Jun 2026 20:24:56 +0100 Subject: [PATCH 1/5] use Locale.ROOT for toLowerCase/toUpperCase (#1086) (cherry picked from commit 7def2274d7a0fb0f778ed80b791b8b4d51f834e6) --- .../pekko/http/impl/engine/http2/Http2Blueprint.scala | 6 +++--- .../pekko/http/impl/settings/ParserSettingsImpl.scala | 3 ++- .../org/apache/pekko/http/javadsl/ConnectHttp.scala | 8 ++++---- .../pekko/http/javadsl/settings/ParserSettings.scala | 3 ++- .../org/apache/pekko/http/scaladsl/model/ErrorInfo.scala | 6 ++++-- .../apache/pekko/http/scaladsl/model/HttpHeader.scala | 3 ++- .../org/apache/pekko/http/scaladsl/model/MediaType.scala | 9 +++++---- .../scala/org/apache/pekko/http/scaladsl/model/Uri.scala | 3 ++- .../pekko/http/scaladsl/settings/ParserSettings.scala | 3 ++- .../scaladsl/server/directives/HeaderDirectives.scala | 5 +++-- .../scaladsl/server/directives/MethodDirectives.scala | 4 +++- .../PredefinedFromStringUnmarshallers.scala | 3 ++- 12 files changed, 34 insertions(+), 22 deletions(-) diff --git a/http-core/src/main/scala/org/apache/pekko/http/impl/engine/http2/Http2Blueprint.scala b/http-core/src/main/scala/org/apache/pekko/http/impl/engine/http2/Http2Blueprint.scala index 3b14c0b86b..f732085b05 100644 --- a/http-core/src/main/scala/org/apache/pekko/http/impl/engine/http2/Http2Blueprint.scala +++ b/http-core/src/main/scala/org/apache/pekko/http/impl/engine/http2/Http2Blueprint.scala @@ -38,8 +38,8 @@ import pekko.http.scaladsl.settings.{ import pekko.stream.{ BidiShape, Graph, StreamTcpException, ThrottleMode } import pekko.stream.TLSProtocol._ import pekko.stream.scaladsl.{ BidiFlow, Flow, Keep, Source } -import pekko.util.ByteString -import pekko.util.OptionVal +import pekko.util.{ ByteString, OptionVal } +import pekko.util.Helpers.toRootLowerCase import scala.concurrent.duration.{ Duration, FiniteDuration } import scala.concurrent.{ ExecutionContext, Future } @@ -226,7 +226,7 @@ private[http] object Http2Blueprint { } private[http2] def frameTypeAliasToFrameTypeName(frameType: String): Option[String] = { - frameType.toLowerCase match { + toRootLowerCase(frameType) match { case "reset" => Some("RstStreamFrame") case "headers" => Some("HeadersFrame") case "continuation" => Some("ContinuationFrame") diff --git a/http-core/src/main/scala/org/apache/pekko/http/impl/settings/ParserSettingsImpl.scala b/http-core/src/main/scala/org/apache/pekko/http/impl/settings/ParserSettingsImpl.scala index c8999d0ce3..3a47781d17 100644 --- a/http-core/src/main/scala/org/apache/pekko/http/impl/settings/ParserSettingsImpl.scala +++ b/http-core/src/main/scala/org/apache/pekko/http/impl/settings/ParserSettingsImpl.scala @@ -23,6 +23,7 @@ import pekko.http.scaladsl.settings.ParserSettings.{ IllegalResponseHeaderValueProcessingMode } import pekko.util.ConstantFun +import pekko.util.Helpers.toRootLowerCase import com.typesafe.config.Config import scala.collection.JavaConverters._ @@ -115,7 +116,7 @@ object ParserSettingsImpl extends SettingsCompanionImpl[ParserSettingsImpl]("pek Uri.ParsingMode(c.getString("uri-parsing-mode")), CookieParsingMode(c.getString("cookie-parsing-mode")), c.getBoolean("illegal-header-warnings"), - c.getStringList("ignore-illegal-header-for").asScala.map(_.toLowerCase).toSet, + c.getStringList("ignore-illegal-header-for").asScala.map(toRootLowerCase).toSet, ErrorLoggingVerbosity(c.getString("error-logging-verbosity")), IllegalResponseHeaderNameProcessingMode(c.getString("illegal-response-header-name-processing-mode")), IllegalResponseHeaderValueProcessingMode(c.getString("illegal-response-header-value-processing-mode")), diff --git a/http-core/src/main/scala/org/apache/pekko/http/javadsl/ConnectHttp.scala b/http-core/src/main/scala/org/apache/pekko/http/javadsl/ConnectHttp.scala index 7191d01827..27d3bc9990 100644 --- a/http-core/src/main/scala/org/apache/pekko/http/javadsl/ConnectHttp.scala +++ b/http-core/src/main/scala/org/apache/pekko/http/javadsl/ConnectHttp.scala @@ -13,12 +13,12 @@ package org.apache.pekko.http.javadsl -import java.util.Locale import java.util.Optional import org.apache.pekko import pekko.annotation.{ DoNotInherit, InternalApi } import pekko.http.javadsl.model.Uri +import pekko.util.Helpers.toRootLowerCase import pekko.util.OptionConverters._ @DoNotInherit @@ -71,7 +71,7 @@ object ConnectHttp { } private def toHost(uriHost: Uri, port: Int): ConnectHttp = { - val s = uriHost.scheme.toLowerCase(Locale.ROOT) + val s = toRootLowerCase(uriHost.scheme) if (s == "https") new ConnectHttpsImpl(uriHost.host.address, effectivePort(s, port), context = Optional.empty()) else new ConnectHttpImpl(uriHost.host.address, effectivePort(s, port)) } @@ -114,7 +114,7 @@ object ConnectHttp { } private def toHostHttps(uriHost: Uri, port: Int): ConnectWithHttps = { - val s = uriHost.scheme.toLowerCase(Locale.ROOT) + val s = toRootLowerCase(uriHost.scheme) require(s == "" || s == "https", "toHostHttps used with non https scheme! Was: " + uriHost) new ConnectHttpsImpl(uriHost.host.address, effectivePort("https", port), context = Optional.empty()) } @@ -125,7 +125,7 @@ object ConnectHttp { } private def effectivePort(scheme: String, port: Int): Int = { - val s = scheme.toLowerCase(Locale.ROOT) + val s = toRootLowerCase(scheme) if (port >= 0) port else if (s == "https" || s == "wss") 443 else if (s == "http" || s == "ws") 80 diff --git a/http-core/src/main/scala/org/apache/pekko/http/javadsl/settings/ParserSettings.scala b/http-core/src/main/scala/org/apache/pekko/http/javadsl/settings/ParserSettings.scala index 6eaa2adedb..2b0b3240c4 100644 --- a/http-core/src/main/scala/org/apache/pekko/http/javadsl/settings/ParserSettings.scala +++ b/http-core/src/main/scala/org/apache/pekko/http/javadsl/settings/ParserSettings.scala @@ -23,6 +23,7 @@ import java.{ util => ju } import pekko.annotation.DoNotInherit import pekko.http.impl.util.JavaMapping.Implicits._ +import pekko.util.Helpers.toRootLowerCase import scala.annotation.varargs import scala.collection.JavaConverters._ @@ -90,7 +91,7 @@ abstract class ParserSettings private[pekko] () extends BodyPartParser.Settings self.copy(includeSslSessionAttribute = newValue) def withModeledHeaderParsing(newValue: Boolean): ParserSettings = self.copy(modeledHeaderParsing = newValue) def withIgnoreIllegalHeaderFor(newValue: List[String]): ParserSettings = - self.copy(ignoreIllegalHeaderFor = newValue.map(_.toLowerCase).toSet) + self.copy(ignoreIllegalHeaderFor = newValue.map(toRootLowerCase).toSet) // special --- diff --git a/http-core/src/main/scala/org/apache/pekko/http/scaladsl/model/ErrorInfo.scala b/http-core/src/main/scala/org/apache/pekko/http/scaladsl/model/ErrorInfo.scala index 5d73947b54..2b552fe42a 100644 --- a/http-core/src/main/scala/org/apache/pekko/http/scaladsl/model/ErrorInfo.scala +++ b/http-core/src/main/scala/org/apache/pekko/http/scaladsl/model/ErrorInfo.scala @@ -14,7 +14,9 @@ package org.apache.pekko.http.scaladsl.model import StatusCodes.ClientError -import org.apache.pekko.annotation.InternalApi +import org.apache.pekko +import pekko.annotation.InternalApi +import pekko.util.Helpers.toRootLowerCase /** * Two-level model of error information. @@ -28,7 +30,7 @@ final class ErrorInfo( val errorHeaderName: String = "") extends scala.Product with scala.Equals with java.io.Serializable { def withSummary(newSummary: String) = copy(summary = newSummary) def withSummaryPrepended(prefix: String) = withSummary(if (summary.isEmpty) prefix else prefix + ": " + summary) - def withErrorHeaderName(headerName: String) = new ErrorInfo(summary, detail, headerName.toLowerCase) + def withErrorHeaderName(headerName: String) = new ErrorInfo(summary, detail, toRootLowerCase(headerName)) def withFallbackSummary(fallbackSummary: String) = if (summary.isEmpty) withSummary(fallbackSummary) else this def formatPretty = if (summary.isEmpty) detail else if (detail.isEmpty) summary else summary + ": " + detail def format(withDetail: Boolean): String = if (withDetail) formatPretty else summary diff --git a/http-core/src/main/scala/org/apache/pekko/http/scaladsl/model/HttpHeader.scala b/http-core/src/main/scala/org/apache/pekko/http/scaladsl/model/HttpHeader.scala index 0434b557de..6a2a47a027 100644 --- a/http-core/src/main/scala/org/apache/pekko/http/scaladsl/model/HttpHeader.scala +++ b/http-core/src/main/scala/org/apache/pekko/http/scaladsl/model/HttpHeader.scala @@ -23,6 +23,7 @@ import pekko.http.impl.model.parser.{ CharacterClasses, HeaderParser } import pekko.http.javadsl.{ model => jm } import pekko.http.scaladsl.model.headers._ import pekko.util.OptionVal +import pekko.util.Helpers.toRootLowerCase import scala.collection.immutable @@ -91,7 +92,7 @@ object HttpHeader { val parser = new HeaderParser(value, settings) parser.`header-field-value`.run() match { case Success(preProcessedValue) => - HeaderParser.parseFull(name.toLowerCase, preProcessedValue, settings) match { + HeaderParser.parseFull(toRootLowerCase(name), preProcessedValue, settings) match { case HeaderParser.Success(header) => ParsingResult.Ok(header, Nil) case HeaderParser.Failure(info) => val errors = info.withSummaryPrepended(s"Illegal HTTP header '$name'") :: Nil diff --git a/http-core/src/main/scala/org/apache/pekko/http/scaladsl/model/MediaType.scala b/http-core/src/main/scala/org/apache/pekko/http/scaladsl/model/MediaType.scala index 61643de19c..7eef53b92b 100644 --- a/http-core/src/main/scala/org/apache/pekko/http/scaladsl/model/MediaType.scala +++ b/http-core/src/main/scala/org/apache/pekko/http/scaladsl/model/MediaType.scala @@ -18,6 +18,7 @@ import pekko.annotation.DoNotInherit import pekko.http.impl.util._ import pekko.http.javadsl.{ model => jm } import pekko.http.impl.util.JavaMapping.Implicits._ +import pekko.util.Helpers.toRootLowerCase /** * A MediaType describes the type of the content of an HTTP message entity. @@ -74,7 +75,7 @@ sealed abstract class MediaType(_mainType: String, _subType: String) extends jm. case _ => false } - override def hashCode(): Int = value.toLowerCase.hashCode + override def hashCode(): Int = toRootLowerCase(value).hashCode /** * JAVA API @@ -327,12 +328,12 @@ object MediaTypes extends ObjectRegistry[(String, String), MediaType] { private[this] var extensionMap = Map.empty[String, MediaType] - def forExtensionOption(ext: String): Option[MediaType] = extensionMap.get(ext.toLowerCase) - def forExtension(ext: String): MediaType = extensionMap.getOrElse(ext.toLowerCase, `application/octet-stream`) + def forExtensionOption(ext: String): Option[MediaType] = extensionMap.get(toRootLowerCase(ext)) + def forExtension(ext: String): MediaType = extensionMap.getOrElse(toRootLowerCase(ext), `application/octet-stream`) private def registerFileExtensions[T <: MediaType](mediaType: T): T = { mediaType.fileExtensions.foreach { ext => - val lcExt = ext.toLowerCase + val lcExt = toRootLowerCase(ext) require(!extensionMap.contains(lcExt), s"Extension '$ext' clash: media-types '${extensionMap(lcExt)}' and '$mediaType'") extensionMap = extensionMap.updated(lcExt, mediaType) diff --git a/http-core/src/main/scala/org/apache/pekko/http/scaladsl/model/Uri.scala b/http-core/src/main/scala/org/apache/pekko/http/scaladsl/model/Uri.scala index 72e4e7f9d0..135ac4cf95 100644 --- a/http-core/src/main/scala/org/apache/pekko/http/scaladsl/model/Uri.scala +++ b/http-core/src/main/scala/org/apache/pekko/http/scaladsl/model/Uri.scala @@ -28,6 +28,7 @@ import pekko.http.javadsl.{ model => jm } import pekko.http.impl.model.parser.UriParser import pekko.http.impl.model.parser.CharacterClasses._ import pekko.http.impl.util._ +import pekko.util.Helpers.toRootLowerCase import Uri._ /** @@ -868,7 +869,7 @@ object Uri { } else if (allLower) -1 else -2 verify() match { - case -2 => scheme.toLowerCase + case -2 => toRootLowerCase(scheme) case -1 => scheme case ix => fail(s"Invalid URI scheme, unexpected character at pos $ix ('${scheme.charAt(ix)}')") } diff --git a/http-core/src/main/scala/org/apache/pekko/http/scaladsl/settings/ParserSettings.scala b/http-core/src/main/scala/org/apache/pekko/http/scaladsl/settings/ParserSettings.scala index 82b919b989..980d44c9a3 100644 --- a/http-core/src/main/scala/org/apache/pekko/http/scaladsl/settings/ParserSettings.scala +++ b/http-core/src/main/scala/org/apache/pekko/http/scaladsl/settings/ParserSettings.scala @@ -25,6 +25,7 @@ import pekko.http.impl.util._ import pekko.http.javadsl.model import pekko.http.scaladsl.model._ import pekko.http.scaladsl.{ settings => js } +import pekko.util.Helpers.toRootLowerCase import pekko.util.OptionConverters._ import com.typesafe.config.Config @@ -123,7 +124,7 @@ abstract class ParserSettings private[pekko] () extends pekko.http.javadsl.setti self.copy(includeSslSessionAttribute = newValue) override def withModeledHeaderParsing(newValue: Boolean): ParserSettings = self.copy(modeledHeaderParsing = newValue) override def withIgnoreIllegalHeaderFor(newValue: List[String]): ParserSettings = - self.copy(ignoreIllegalHeaderFor = newValue.map(_.toLowerCase).toSet) + self.copy(ignoreIllegalHeaderFor = newValue.map(toRootLowerCase).toSet) // overloads for idiomatic Scala use def withUriParsingMode(newValue: Uri.ParsingMode): ParserSettings = self.copy(uriParsingMode = newValue) diff --git a/http/src/main/scala/org/apache/pekko/http/scaladsl/server/directives/HeaderDirectives.scala b/http/src/main/scala/org/apache/pekko/http/scaladsl/server/directives/HeaderDirectives.scala index 54c4d9fa15..29d94b920c 100644 --- a/http/src/main/scala/org/apache/pekko/http/scaladsl/server/directives/HeaderDirectives.scala +++ b/http/src/main/scala/org/apache/pekko/http/scaladsl/server/directives/HeaderDirectives.scala @@ -18,6 +18,7 @@ import org.apache.pekko import pekko.http.impl.util._ import pekko.http.scaladsl.model._ import pekko.http.scaladsl.model.headers._ +import pekko.util.Helpers.toRootLowerCase import scala.reflect.ClassTag import scala.util.control.NonFatal @@ -92,7 +93,7 @@ trait HeaderDirectives { * @group header */ def headerValueByName(headerName: String): Directive1[String] = - headerValue(optionalValue(headerName.toLowerCase)) | reject(MissingHeaderRejection(headerName)) + headerValue(optionalValue(toRootLowerCase(headerName))) | reject(MissingHeaderRejection(headerName)) /** * Extracts the first HTTP request header of the given type. @@ -147,7 +148,7 @@ trait HeaderDirectives { * @group header */ def optionalHeaderValueByName(headerName: String): Directive1[Option[String]] = { - val lowerCaseName = headerName.toRootLowerCase + val lowerCaseName = toRootLowerCase(headerName) extract(_.request.headers.collectFirst { case h: HttpHeader if h.is(lowerCaseName) => h.value }) diff --git a/http/src/main/scala/org/apache/pekko/http/scaladsl/server/directives/MethodDirectives.scala b/http/src/main/scala/org/apache/pekko/http/scaladsl/server/directives/MethodDirectives.scala index 7edf7c6e01..3d609d957f 100644 --- a/http/src/main/scala/org/apache/pekko/http/scaladsl/server/directives/MethodDirectives.scala +++ b/http/src/main/scala/org/apache/pekko/http/scaladsl/server/directives/MethodDirectives.scala @@ -14,6 +14,8 @@ package org.apache.pekko.http.scaladsl.server package directives +import java.util.Locale + import org.apache.pekko import pekko.http.scaladsl.model.{ HttpMethod, StatusCodes } import pekko.http.scaladsl.model.HttpMethods._ @@ -111,7 +113,7 @@ trait MethodDirectives { def overrideMethodWithParameter(paramName: String): Directive0 = parameter(paramName.optional).flatMap { case Some(method) => - getForKey(method.toUpperCase) match { + getForKey(method.toUpperCase(Locale.ROOT)) match { case Some(m) => mapRequest(_.withMethod(m)) case _ => complete(StatusCodes.NotImplemented) } diff --git a/http/src/main/scala/org/apache/pekko/http/scaladsl/unmarshalling/PredefinedFromStringUnmarshallers.scala b/http/src/main/scala/org/apache/pekko/http/scaladsl/unmarshalling/PredefinedFromStringUnmarshallers.scala index a865fcedb2..e18eab4bbb 100755 --- a/http/src/main/scala/org/apache/pekko/http/scaladsl/unmarshalling/PredefinedFromStringUnmarshallers.scala +++ b/http/src/main/scala/org/apache/pekko/http/scaladsl/unmarshalling/PredefinedFromStringUnmarshallers.scala @@ -19,6 +19,7 @@ import scala.collection.immutable import org.apache.pekko import pekko.http.scaladsl.util.FastFuture import pekko.util.ByteString +import pekko.util.Helpers.toRootLowerCase trait PredefinedFromStringUnmarshallers { @@ -48,7 +49,7 @@ trait PredefinedFromStringUnmarshallers { implicit val booleanFromStringUnmarshaller: Unmarshaller[String, Boolean] = Unmarshaller.strict[String, Boolean] { string => - string.toLowerCase match { + toRootLowerCase(string) match { case "true" | "yes" | "on" | "1" => true case "false" | "no" | "off" | "0" => false case "" => throw Unmarshaller.NoContentException From 681c7da493a31c7a395ff33a7568493a458d4964 Mon Sep 17 00:00:00 2001 From: PJ Fanning Date: Mon, 24 Aug 2026 12:14:27 +0100 Subject: [PATCH 2/5] test: add turkish-i regression tests for Locale.ROOT lowercasing Motivation: The backported Locale.ROOT change has no test coverage beyond the existing HttpCharsets case, so a regression would go unnoticed. Modification: Extend TurkishISpec with cases that run under a tr-TR default locale and cover `HttpHeader.parse`, `Uri` scheme normalization, `MediaTypes.forExtension` and `ErrorInfo.withErrorHeaderName`. Result: The header-name, scheme, file-extension and error-header-name paths are pinned against locale sensitive lowercasing. Tests: - sbt "http-core / Test / testOnly org.apache.pekko.http.scaladsl.model.TurkishISpec" - 5 passed; all 4 new cases fail against 1.4.x without the backported commit - scalafmt --mode diff-ref=upstream/1.4.x - clean References: Refs #1086 --- .../http/scaladsl/model/TurkishISpec.scala | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/http-core/src/test/scala/org/apache/pekko/http/scaladsl/model/TurkishISpec.scala b/http-core/src/test/scala/org/apache/pekko/http/scaladsl/model/TurkishISpec.scala index 58d6a89f69..76f9fdede6 100644 --- a/http-core/src/test/scala/org/apache/pekko/http/scaladsl/model/TurkishISpec.scala +++ b/http-core/src/test/scala/org/apache/pekko/http/scaladsl/model/TurkishISpec.scala @@ -40,5 +40,34 @@ class TurkishISpec extends AnyWordSpec with Matchers { Locale.setDefault(previousLocale) } } + + "parse a header name containing a capital I in the turkish locale" in withTurkishLocale { + HttpHeader.parse("If-Match", "\"xyzzy\"") match { + case HttpHeader.ParsingResult.Ok(header, Nil) => header shouldBe a[headers.`If-Match`] + case other => fail(s"Expected a modelled If-Match header but got $other") + } + } + + "normalize a uri scheme containing a capital I in the turkish locale" in withTurkishLocale { + Uri(scheme = "IPP", authority = Uri.Authority(Uri.Host("example.com"))).scheme shouldEqual "ipp" + } + + "resolve a media type for an upper case file extension in the turkish locale" in withTurkishLocale { + MediaTypes.forExtension("TIFF") shouldEqual MediaTypes.`image/tiff` + } + + "lowercase an error header name in the turkish locale" in withTurkishLocale { + ErrorInfo("summary", "detail").withErrorHeaderName("If-Match").errorHeaderName shouldEqual "if-match" + } + } + + private def withTurkishLocale(body: => Any): Unit = { + val previousLocale = Locale.getDefault + try { + Locale.setDefault(new Locale("tr", "TR")) + body + } finally { + Locale.setDefault(previousLocale) + } } } From ff30a3f7d921bbdb32a45f7fb5d4f3a0ed42e268 Mon Sep 17 00:00:00 2001 From: PJ Fanning Date: Mon, 24 Aug 2026 12:31:43 +0100 Subject: [PATCH 3/5] ci: bump cache actions to the versions used on main Motivation: The 1.4.x workflows pin coursier/cache-action v8.1.0 (and v6 in two of the docs workflows) and actions/cache v5.0.4. Workflow runs on this branch end in startup_failure before any job is created, while main, which pins newer versions of both actions, runs fine. Modification: Pin coursier/cache-action to 95e5b1029b6b86e7bac033ee44a0697d8a527d2d (v8.1.1) and actions/cache to 55cc8345863c7cc4c66a329aec7e433d2d1c52a9 (v6.1.0) in every workflow, matching main. Result: CI on 1.4.x branches starts and runs as it does on main. Tests: - Not run - CI configuration only; verified by the workflow runs on this PR References: Refs #1224 --- .github/workflows/headers.yml | 2 +- .github/workflows/link-validator.yml | 2 +- .github/workflows/nightly.yml | 4 ++-- .github/workflows/publish-1.0-docs.yml | 2 +- .github/workflows/publish-1.0-snapshots.yml | 2 +- .github/workflows/publish-1.1-docs.yml | 2 +- .github/workflows/publish-1.1-snapshots.yml | 2 +- .github/workflows/publish-1.2-docs.yml | 2 +- .github/workflows/publish-1.3-docs.yml | 2 +- .github/workflows/publish.yml | 4 ++-- .github/workflows/validate-and-test.yml | 8 ++++---- 11 files changed, 16 insertions(+), 16 deletions(-) diff --git a/.github/workflows/headers.yml b/.github/workflows/headers.yml index e152f1f11a..6c7ce2efbf 100644 --- a/.github/workflows/headers.yml +++ b/.github/workflows/headers.yml @@ -40,7 +40,7 @@ jobs: uses: sbt/setup-sbt@66fb4376e81982c7d92a4074170846fff88e2e30 # v1.5.0 - name: Cache Coursier cache - uses: coursier/cache-action@90c37294538be80a558fd665531fcdc2b467b475 # v8.1.0 + uses: coursier/cache-action@95e5b1029b6b86e7bac033ee44a0697d8a527d2d # v8.1.1 - name: Enable jvm-opts run: cp .jvmopts-ci .jvmopts diff --git a/.github/workflows/link-validator.yml b/.github/workflows/link-validator.yml index 6a377689db..80d668d812 100644 --- a/.github/workflows/link-validator.yml +++ b/.github/workflows/link-validator.yml @@ -41,7 +41,7 @@ jobs: uses: sbt/setup-sbt@66fb4376e81982c7d92a4074170846fff88e2e30 # v1.5.0 - name: Cache Coursier cache - uses: coursier/cache-action@90c37294538be80a558fd665531fcdc2b467b475 # v8.1.0 + uses: coursier/cache-action@95e5b1029b6b86e7bac033ee44a0697d8a527d2d # v8.1.1 - name: Setup Coursier uses: coursier/setup-action@fd1707a76b027efdfb66ca79318b4d29b72e5a02 # v3.0.0 diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml index d5e49b1233..018a6d49f6 100644 --- a/.github/workflows/nightly.yml +++ b/.github/workflows/nightly.yml @@ -41,10 +41,10 @@ jobs: uses: sbt/setup-sbt@66fb4376e81982c7d92a4074170846fff88e2e30 # v1.5.0 - name: Cache Coursier cache - uses: coursier/cache-action@90c37294538be80a558fd665531fcdc2b467b475 # v8.1.0 + uses: coursier/cache-action@95e5b1029b6b86e7bac033ee44a0697d8a527d2d # v8.1.1 - name: Cache Build Target - uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4 + uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 with: path: project/**/target key: build-target-${{ hashFiles('**/*.sbt', 'project/build.properties', 'project/**/*.scala') }} diff --git a/.github/workflows/publish-1.0-docs.yml b/.github/workflows/publish-1.0-docs.yml index f6f04988a1..69e7547afd 100644 --- a/.github/workflows/publish-1.0-docs.yml +++ b/.github/workflows/publish-1.0-docs.yml @@ -43,7 +43,7 @@ jobs: uses: sbt/setup-sbt@66fb4376e81982c7d92a4074170846fff88e2e30 # v1.5.0 - name: Cache Coursier cache - uses: coursier/cache-action@90c37294538be80a558fd665531fcdc2b467b475 # v8.1.0 + uses: coursier/cache-action@95e5b1029b6b86e7bac033ee44a0697d8a527d2d # v8.1.1 - name: Build Documentation run: |- diff --git a/.github/workflows/publish-1.0-snapshots.yml b/.github/workflows/publish-1.0-snapshots.yml index 5d96cd06a5..6c7745bc49 100644 --- a/.github/workflows/publish-1.0-snapshots.yml +++ b/.github/workflows/publish-1.0-snapshots.yml @@ -36,7 +36,7 @@ jobs: uses: sbt/setup-sbt@66fb4376e81982c7d92a4074170846fff88e2e30 # v1.5.0 - name: Cache Coursier cache - uses: coursier/cache-action@90c37294538be80a558fd665531fcdc2b467b475 # v8.1.0 + uses: coursier/cache-action@95e5b1029b6b86e7bac033ee44a0697d8a527d2d # v8.1.1 - name: Install graphviz run: sudo apt-get install -y graphviz diff --git a/.github/workflows/publish-1.1-docs.yml b/.github/workflows/publish-1.1-docs.yml index 2f8c0f76a4..aec148b0a0 100644 --- a/.github/workflows/publish-1.1-docs.yml +++ b/.github/workflows/publish-1.1-docs.yml @@ -43,7 +43,7 @@ jobs: uses: sbt/setup-sbt@66fb4376e81982c7d92a4074170846fff88e2e30 # v1.5.0 - name: Cache Coursier cache - uses: coursier/cache-action@90c37294538be80a558fd665531fcdc2b467b475 # v8.1.0 + uses: coursier/cache-action@95e5b1029b6b86e7bac033ee44a0697d8a527d2d # v8.1.1 - name: Build Documentation run: |- diff --git a/.github/workflows/publish-1.1-snapshots.yml b/.github/workflows/publish-1.1-snapshots.yml index b5e8e689ef..74cdbccbc9 100644 --- a/.github/workflows/publish-1.1-snapshots.yml +++ b/.github/workflows/publish-1.1-snapshots.yml @@ -36,7 +36,7 @@ jobs: uses: sbt/setup-sbt@66fb4376e81982c7d92a4074170846fff88e2e30 # v1.5.0 - name: Cache Coursier cache - uses: coursier/cache-action@90c37294538be80a558fd665531fcdc2b467b475 # v8.1.0 + uses: coursier/cache-action@95e5b1029b6b86e7bac033ee44a0697d8a527d2d # v8.1.1 - name: Install graphviz run: sudo apt-get install -y graphviz diff --git a/.github/workflows/publish-1.2-docs.yml b/.github/workflows/publish-1.2-docs.yml index 882394b274..5ee98e7626 100644 --- a/.github/workflows/publish-1.2-docs.yml +++ b/.github/workflows/publish-1.2-docs.yml @@ -42,7 +42,7 @@ jobs: uses: sbt/setup-sbt@v1 - name: Cache Coursier cache - uses: coursier/cache-action@v6 + uses: coursier/cache-action@95e5b1029b6b86e7bac033ee44a0697d8a527d2d # v8.1.1 - name: Build Documentation run: |- diff --git a/.github/workflows/publish-1.3-docs.yml b/.github/workflows/publish-1.3-docs.yml index d3e36c9490..ca4caf397b 100644 --- a/.github/workflows/publish-1.3-docs.yml +++ b/.github/workflows/publish-1.3-docs.yml @@ -43,7 +43,7 @@ jobs: uses: sbt/setup-sbt@6c68d2fe8dfbc0a0534d70101baa2e0420e1a506 # v1.1.9 - name: Cache Coursier cache - uses: coursier/cache-action@v6 + uses: coursier/cache-action@95e5b1029b6b86e7bac033ee44a0697d8a527d2d # v8.1.1 - name: Build Documentation run: |- diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 8332f83328..cb2cf693e7 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -44,7 +44,7 @@ jobs: uses: sbt/setup-sbt@66fb4376e81982c7d92a4074170846fff88e2e30 # v1.5.0 - name: Cache Coursier cache - uses: coursier/cache-action@90c37294538be80a558fd665531fcdc2b467b475 # v8.1.0 + uses: coursier/cache-action@95e5b1029b6b86e7bac033ee44a0697d8a527d2d # v8.1.1 - name: Install graphviz run: sudo apt-get install -y graphviz @@ -77,7 +77,7 @@ jobs: uses: sbt/setup-sbt@66fb4376e81982c7d92a4074170846fff88e2e30 # v1.5.0 - name: Cache Coursier cache - uses: coursier/cache-action@90c37294538be80a558fd665531fcdc2b467b475 # v8.1.0 + uses: coursier/cache-action@95e5b1029b6b86e7bac033ee44a0697d8a527d2d # v8.1.1 - name: Build Documentation run: |- diff --git a/.github/workflows/validate-and-test.yml b/.github/workflows/validate-and-test.yml index fe48f6304f..e63283fa52 100644 --- a/.github/workflows/validate-and-test.yml +++ b/.github/workflows/validate-and-test.yml @@ -41,10 +41,10 @@ jobs: uses: sbt/setup-sbt@66fb4376e81982c7d92a4074170846fff88e2e30 # v1.5.0 - name: Cache Coursier cache - uses: coursier/cache-action@90c37294538be80a558fd665531fcdc2b467b475 # v8.1.0 + uses: coursier/cache-action@95e5b1029b6b86e7bac033ee44a0697d8a527d2d # v8.1.1 - name: Cache Build Target - uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4 + uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 with: path: project/**/target key: build-target-${{ hashFiles('**/*.sbt', 'project/build.properties', 'project/**/*.scala') }} @@ -89,10 +89,10 @@ jobs: uses: sbt/setup-sbt@66fb4376e81982c7d92a4074170846fff88e2e30 # v1.5.0 - name: Cache Coursier cache - uses: coursier/cache-action@90c37294538be80a558fd665531fcdc2b467b475 # v8.1.0 + uses: coursier/cache-action@95e5b1029b6b86e7bac033ee44a0697d8a527d2d # v8.1.1 - name: Cache Build Target - uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4 + uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 with: path: project/**/target key: build-target-${{ hashFiles('**/*.sbt', 'project/build.properties', 'project/**/*.scala') }} From d7258adf09b1022a1f8a274e65f7d6c6240605a9 Mon Sep 17 00:00:00 2001 From: PJ Fanning Date: Mon, 24 Aug 2026 12:18:44 +0100 Subject: [PATCH 4/5] fix: use root locale when looking up a response header by name in RouteTest (#1223) Motivation: `RouteTest#header(name: String)` lowercased the given header name with the JVM default locale before comparing it to `HttpHeader#lowercaseName`, which is built with `toRootLowerCase`. Under a Turkish locale `"If-Match".toLowerCase` yields a dotless i, so the lookup silently returned `None` for any header name containing a capital `I`. The javadsl equivalent (`TestRouteResult`) already uses `toRootLowerCase`. Modification: Use `pekko.util.Helpers.toRootLowerCase` in `RouteTest#header(name)`. Result: Header lookup by name in the Scala testkit is locale independent and matches the javadsl behaviour. Tests: - sbt "http-testkit / Test / testOnly org.apache.pekko.http.scaladsl.testkit.ScalatestRouteTestSpec" - passes with the fix; the new test fails without it - scalafmt --mode diff-ref=origin/main - clean - sbt +mimaReportBinaryIssues - not run; method body change only, no API or binary shape change References: None - found while auditing the code base for locale sensitive lowercasing (cherry picked from commit 08e174c4ce2a1bf6c8371bf75e572093840bbd54) --- .../http/scaladsl/testkit/RouteTest.scala | 3 ++- .../testkit/ScalatestRouteTestSpec.scala | 21 +++++++++++++++++++ 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/http-testkit/src/main/scala/org/apache/pekko/http/scaladsl/testkit/RouteTest.scala b/http-testkit/src/main/scala/org/apache/pekko/http/scaladsl/testkit/RouteTest.scala index c33ca493a8..bf961219f8 100644 --- a/http-testkit/src/main/scala/org/apache/pekko/http/scaladsl/testkit/RouteTest.scala +++ b/http-testkit/src/main/scala/org/apache/pekko/http/scaladsl/testkit/RouteTest.scala @@ -30,6 +30,7 @@ import pekko.stream.{ Materializer, SystemMaterializer } import pekko.stream.scaladsl.Source import pekko.testkit.TestKit import pekko.util.ConstantFun +import pekko.util.Helpers.toRootLowerCase import com.typesafe.config.{ Config, ConfigFactory } import scala.collection.immutable @@ -95,7 +96,7 @@ trait RouteTest extends RequestBuilding with WSTestRequestBuilding with RouteTes def charset: HttpCharset = charsetOption.getOrElse(sys.error("Binary entity does not have charset")) def headers: immutable.Seq[HttpHeader] = rawResponse.headers def header[T >: Null <: HttpHeader: ClassTag]: Option[T] = rawResponse.header[T](implicitly[ClassTag[T]]) - def header(name: String): Option[HttpHeader] = rawResponse.headers.find(_.is(name.toLowerCase)) + def header(name: String): Option[HttpHeader] = rawResponse.headers.find(_.is(toRootLowerCase(name))) def status: StatusCode = rawResponse.status def closingExtension: String = chunks.lastOption match { diff --git a/http-testkit/src/test/scala/org/apache/pekko/http/scaladsl/testkit/ScalatestRouteTestSpec.scala b/http-testkit/src/test/scala/org/apache/pekko/http/scaladsl/testkit/ScalatestRouteTestSpec.scala index 63eb780edb..4ef079b49d 100644 --- a/http-testkit/src/test/scala/org/apache/pekko/http/scaladsl/testkit/ScalatestRouteTestSpec.scala +++ b/http-testkit/src/test/scala/org/apache/pekko/http/scaladsl/testkit/ScalatestRouteTestSpec.scala @@ -13,6 +13,8 @@ package org.apache.pekko.http.scaladsl.testkit +import java.util.Locale + import scala.concurrent.duration._ import org.apache.pekko import pekko.testkit._ @@ -57,6 +59,25 @@ class ScalatestRouteTestSpec extends AnyFreeSpec with Matchers with ScalatestRou } } + "a header lookup by name that is unaffected by the turkish-i problem" in { + val previousLocale = Locale.getDefault + try { + Locale.setDefault(new Locale("tr", "TR")) + // in the turkish locale 'I'.toLowerCase is a dotless i, so a default-locale + // lowercasing of 'If-Match' would not match the header's lowercaseName + val ifMatchHeader = RawHeader("If-Match", "\"xyzzy\"") + Get() ~> { + respondWithHeader(ifMatchHeader) { + complete("abc") + } + } ~> check { + header("If-Match") shouldEqual Some(ifMatchHeader) + } + } finally { + Locale.setDefault(previousLocale) + } + } + "a test using ~!> and some checks" in { // raw here, should have been parsed into modelled header when going through an actual server when using `~!>` val extraHeader = RawHeader("X-Forwarded-Proto", "abc") From ed09e7449f815f876d0d54c47579016e0678a380 Mon Sep 17 00:00:00 2001 From: PJ Fanning Date: Mon, 24 Aug 2026 12:42:29 +0100 Subject: [PATCH 5/5] ci: bump sbt/setup-sbt to v1.5.7 Motivation: Jobs on this branch fail during "Set up job" with The action carabiner-dev/actions/install/ampel@2a11d59 is not allowed in apache/pekko-http because all actions must be from a repository owned by your enterprise, created by GitHub, or match one of the patterns: ... sbt/setup-sbt v1.5.0 pulls that action in transitively and it is not on the ASF allowlist. main pins v1.5.7 and its jobs run. Modification: Pin sbt/setup-sbt to 8feba82adc7f01ddcf8165b86f778bdb5b82cebc (v1.5.7) in every workflow, matching main. Two docs workflows were still on the floating v1 tag and on v1.1.9. Result: Jobs get past action setup on 1.4.x branches. Tests: - Not run - CI configuration only; verified by the workflow runs on this PR References: Refs #1224 --- .github/workflows/dependency-graph.yml | 2 +- .github/workflows/headers.yml | 2 +- .github/workflows/link-validator.yml | 2 +- .github/workflows/nightly.yml | 2 +- .github/workflows/publish-1.0-docs.yml | 2 +- .github/workflows/publish-1.0-snapshots.yml | 2 +- .github/workflows/publish-1.1-docs.yml | 2 +- .github/workflows/publish-1.1-snapshots.yml | 2 +- .github/workflows/publish-1.2-docs.yml | 2 +- .github/workflows/publish-1.3-docs.yml | 2 +- .github/workflows/publish.yml | 4 ++-- .github/workflows/stage-release-candidate.yml | 2 +- .github/workflows/validate-and-test.yml | 4 ++-- 13 files changed, 15 insertions(+), 15 deletions(-) diff --git a/.github/workflows/dependency-graph.yml b/.github/workflows/dependency-graph.yml index 46d7f5c408..eb87131010 100644 --- a/.github/workflows/dependency-graph.yml +++ b/.github/workflows/dependency-graph.yml @@ -28,7 +28,7 @@ jobs: steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: Install sbt - uses: sbt/setup-sbt@66fb4376e81982c7d92a4074170846fff88e2e30 # v1.5.0 + uses: sbt/setup-sbt@8feba82adc7f01ddcf8165b86f778bdb5b82cebc # v1.5.7 - uses: scalacenter/sbt-dependency-submission@f43202114d7522a4b233e052f82c2eea8d658134 # v3.2.1 with: modules-ignore: pekko-http-tests_3 pekko-http-docs_3 diff --git a/.github/workflows/headers.yml b/.github/workflows/headers.yml index 6c7ce2efbf..127f62627d 100644 --- a/.github/workflows/headers.yml +++ b/.github/workflows/headers.yml @@ -37,7 +37,7 @@ jobs: java-version: 8 - name: Install sbt - uses: sbt/setup-sbt@66fb4376e81982c7d92a4074170846fff88e2e30 # v1.5.0 + uses: sbt/setup-sbt@8feba82adc7f01ddcf8165b86f778bdb5b82cebc # v1.5.7 - name: Cache Coursier cache uses: coursier/cache-action@95e5b1029b6b86e7bac033ee44a0697d8a527d2d # v8.1.1 diff --git a/.github/workflows/link-validator.yml b/.github/workflows/link-validator.yml index 80d668d812..ae383c0647 100644 --- a/.github/workflows/link-validator.yml +++ b/.github/workflows/link-validator.yml @@ -38,7 +38,7 @@ jobs: java-version: 8 - name: Install sbt - uses: sbt/setup-sbt@66fb4376e81982c7d92a4074170846fff88e2e30 # v1.5.0 + uses: sbt/setup-sbt@8feba82adc7f01ddcf8165b86f778bdb5b82cebc # v1.5.7 - name: Cache Coursier cache uses: coursier/cache-action@95e5b1029b6b86e7bac033ee44a0697d8a527d2d # v8.1.1 diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml index 018a6d49f6..7651389765 100644 --- a/.github/workflows/nightly.yml +++ b/.github/workflows/nightly.yml @@ -38,7 +38,7 @@ jobs: java-version: ${{ matrix.JDK }} - name: Install sbt - uses: sbt/setup-sbt@66fb4376e81982c7d92a4074170846fff88e2e30 # v1.5.0 + uses: sbt/setup-sbt@8feba82adc7f01ddcf8165b86f778bdb5b82cebc # v1.5.7 - name: Cache Coursier cache uses: coursier/cache-action@95e5b1029b6b86e7bac033ee44a0697d8a527d2d # v8.1.1 diff --git a/.github/workflows/publish-1.0-docs.yml b/.github/workflows/publish-1.0-docs.yml index 69e7547afd..1b70f94065 100644 --- a/.github/workflows/publish-1.0-docs.yml +++ b/.github/workflows/publish-1.0-docs.yml @@ -40,7 +40,7 @@ jobs: java-version: 8 - name: Install sbt - uses: sbt/setup-sbt@66fb4376e81982c7d92a4074170846fff88e2e30 # v1.5.0 + uses: sbt/setup-sbt@8feba82adc7f01ddcf8165b86f778bdb5b82cebc # v1.5.7 - name: Cache Coursier cache uses: coursier/cache-action@95e5b1029b6b86e7bac033ee44a0697d8a527d2d # v8.1.1 diff --git a/.github/workflows/publish-1.0-snapshots.yml b/.github/workflows/publish-1.0-snapshots.yml index 6c7745bc49..ecc2b95e02 100644 --- a/.github/workflows/publish-1.0-snapshots.yml +++ b/.github/workflows/publish-1.0-snapshots.yml @@ -33,7 +33,7 @@ jobs: java-version: 8 - name: Install sbt - uses: sbt/setup-sbt@66fb4376e81982c7d92a4074170846fff88e2e30 # v1.5.0 + uses: sbt/setup-sbt@8feba82adc7f01ddcf8165b86f778bdb5b82cebc # v1.5.7 - name: Cache Coursier cache uses: coursier/cache-action@95e5b1029b6b86e7bac033ee44a0697d8a527d2d # v8.1.1 diff --git a/.github/workflows/publish-1.1-docs.yml b/.github/workflows/publish-1.1-docs.yml index aec148b0a0..751585fffd 100644 --- a/.github/workflows/publish-1.1-docs.yml +++ b/.github/workflows/publish-1.1-docs.yml @@ -40,7 +40,7 @@ jobs: java-version: 8 - name: Install sbt - uses: sbt/setup-sbt@66fb4376e81982c7d92a4074170846fff88e2e30 # v1.5.0 + uses: sbt/setup-sbt@8feba82adc7f01ddcf8165b86f778bdb5b82cebc # v1.5.7 - name: Cache Coursier cache uses: coursier/cache-action@95e5b1029b6b86e7bac033ee44a0697d8a527d2d # v8.1.1 diff --git a/.github/workflows/publish-1.1-snapshots.yml b/.github/workflows/publish-1.1-snapshots.yml index 74cdbccbc9..f7688938ca 100644 --- a/.github/workflows/publish-1.1-snapshots.yml +++ b/.github/workflows/publish-1.1-snapshots.yml @@ -33,7 +33,7 @@ jobs: java-version: 8 - name: Install sbt - uses: sbt/setup-sbt@66fb4376e81982c7d92a4074170846fff88e2e30 # v1.5.0 + uses: sbt/setup-sbt@8feba82adc7f01ddcf8165b86f778bdb5b82cebc # v1.5.7 - name: Cache Coursier cache uses: coursier/cache-action@95e5b1029b6b86e7bac033ee44a0697d8a527d2d # v8.1.1 diff --git a/.github/workflows/publish-1.2-docs.yml b/.github/workflows/publish-1.2-docs.yml index 5ee98e7626..7c73ec300d 100644 --- a/.github/workflows/publish-1.2-docs.yml +++ b/.github/workflows/publish-1.2-docs.yml @@ -39,7 +39,7 @@ jobs: java-version: 8 - name: Install sbt - uses: sbt/setup-sbt@v1 + uses: sbt/setup-sbt@8feba82adc7f01ddcf8165b86f778bdb5b82cebc # v1.5.7 - name: Cache Coursier cache uses: coursier/cache-action@95e5b1029b6b86e7bac033ee44a0697d8a527d2d # v8.1.1 diff --git a/.github/workflows/publish-1.3-docs.yml b/.github/workflows/publish-1.3-docs.yml index ca4caf397b..85de496879 100644 --- a/.github/workflows/publish-1.3-docs.yml +++ b/.github/workflows/publish-1.3-docs.yml @@ -40,7 +40,7 @@ jobs: java-version: 8 - name: Install sbt - uses: sbt/setup-sbt@6c68d2fe8dfbc0a0534d70101baa2e0420e1a506 # v1.1.9 + uses: sbt/setup-sbt@8feba82adc7f01ddcf8165b86f778bdb5b82cebc # v1.5.7 - name: Cache Coursier cache uses: coursier/cache-action@95e5b1029b6b86e7bac033ee44a0697d8a527d2d # v8.1.1 diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index cb2cf693e7..e303ecaa10 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -41,7 +41,7 @@ jobs: java-version: 8 - name: Install sbt - uses: sbt/setup-sbt@66fb4376e81982c7d92a4074170846fff88e2e30 # v1.5.0 + uses: sbt/setup-sbt@8feba82adc7f01ddcf8165b86f778bdb5b82cebc # v1.5.7 - name: Cache Coursier cache uses: coursier/cache-action@95e5b1029b6b86e7bac033ee44a0697d8a527d2d # v8.1.1 @@ -74,7 +74,7 @@ jobs: java-version: 8 - name: Install sbt - uses: sbt/setup-sbt@66fb4376e81982c7d92a4074170846fff88e2e30 # v1.5.0 + uses: sbt/setup-sbt@8feba82adc7f01ddcf8165b86f778bdb5b82cebc # v1.5.7 - name: Cache Coursier cache uses: coursier/cache-action@95e5b1029b6b86e7bac033ee44a0697d8a527d2d # v8.1.1 diff --git a/.github/workflows/stage-release-candidate.yml b/.github/workflows/stage-release-candidate.yml index d0abc8fdf6..613cf1bf48 100644 --- a/.github/workflows/stage-release-candidate.yml +++ b/.github/workflows/stage-release-candidate.yml @@ -212,7 +212,7 @@ jobs: java-version: 8 - name: Install sbt - uses: sbt/setup-sbt@66fb4376e81982c7d92a4074170846fff88e2e30 # v1.5.0 + uses: sbt/setup-sbt@8feba82adc7f01ddcf8165b86f778bdb5b82cebc # v1.5.7 - name: Install Graphviz run: |- diff --git a/.github/workflows/validate-and-test.yml b/.github/workflows/validate-and-test.yml index e63283fa52..e440184c6a 100644 --- a/.github/workflows/validate-and-test.yml +++ b/.github/workflows/validate-and-test.yml @@ -38,7 +38,7 @@ jobs: java-version: 8 - name: Install sbt - uses: sbt/setup-sbt@66fb4376e81982c7d92a4074170846fff88e2e30 # v1.5.0 + uses: sbt/setup-sbt@8feba82adc7f01ddcf8165b86f778bdb5b82cebc # v1.5.7 - name: Cache Coursier cache uses: coursier/cache-action@95e5b1029b6b86e7bac033ee44a0697d8a527d2d # v8.1.1 @@ -86,7 +86,7 @@ jobs: java-version: ${{ matrix.JDK }} - name: Install sbt - uses: sbt/setup-sbt@66fb4376e81982c7d92a4074170846fff88e2e30 # v1.5.0 + uses: sbt/setup-sbt@8feba82adc7f01ddcf8165b86f778bdb5b82cebc # v1.5.7 - name: Cache Coursier cache uses: coursier/cache-action@95e5b1029b6b86e7bac033ee44a0697d8a527d2d # v8.1.1