From b9affed3940845943ed199ee6fbe8f9d81c57d11 Mon Sep 17 00:00:00 2001 From: "Taro L. Saito" Date: Sun, 21 Apr 2024 16:49:15 -0700 Subject: [PATCH 1/7] log (feature): Scala Native support --- .../scala-3/java/util/logging/Level.scala | 20 ---- .../main/scala/java/util/logging/Level.scala | 32 ++++++ .../java/util/logging/Logger.scala | 98 ++++++++++--------- .../{scala-3 => scala}/wvlet/log/LogEnv.scala | 15 +-- .../wvlet/log/LogTimestampFormatter.scala | 21 +++- build.sbt | 17 +++- project/plugin.sbt | 2 +- 7 files changed, 127 insertions(+), 78 deletions(-) delete mode 100644 airframe-log/.native/src/main/scala-3/java/util/logging/Level.scala create mode 100644 airframe-log/.native/src/main/scala/java/util/logging/Level.scala rename airframe-log/.native/src/main/{scala-3 => scala}/java/util/logging/Logger.scala (51%) rename airframe-log/.native/src/main/{scala-3 => scala}/wvlet/log/LogEnv.scala (81%) rename airframe-log/.native/src/main/{scala-3 => scala}/wvlet/log/LogTimestampFormatter.scala (57%) diff --git a/airframe-log/.native/src/main/scala-3/java/util/logging/Level.scala b/airframe-log/.native/src/main/scala-3/java/util/logging/Level.scala deleted file mode 100644 index 61ea700db..000000000 --- a/airframe-log/.native/src/main/scala-3/java/util/logging/Level.scala +++ /dev/null @@ -1,20 +0,0 @@ -package java.util.logging - - -case class Level(name: String, value: Int) extends Ordered[Level] { - override def compare(other: Level): Int = value.compare(other.value) - def intValue(): Int = value - override def toString: String = name -} - -object Level: - val OFF = Level("OFF", 0) - val SEVERE = Level("SEVERE", 1000) - val WARNING = Level("WARNING", 900) - val INFO = Level("INFO", 800) - val CONFIG = Level("CONFIG", 700) - val FINE = Level("FINE", 500) - val FINER = Level("FINER", 400) - val FINEST = Level("FINEST", 300) - val ALL = Level("ALL", Integer.MIN_VALUE) - diff --git a/airframe-log/.native/src/main/scala/java/util/logging/Level.scala b/airframe-log/.native/src/main/scala/java/util/logging/Level.scala new file mode 100644 index 000000000..4cbd80e2e --- /dev/null +++ b/airframe-log/.native/src/main/scala/java/util/logging/Level.scala @@ -0,0 +1,32 @@ +/* + * Licensed 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. + */ +package java.util.logging + +case class Level(name: String, value: Int) extends Ordered[Level] { + override def compare(other: Level): Int = value.compare(other.value) + def intValue(): Int = value + override def toString: String = name +} + +object Level { + val OFF = Level("OFF", 0) + val SEVERE = Level("SEVERE", 1000) + val WARNING = Level("WARNING", 900) + val INFO = Level("INFO", 800) + val CONFIG = Level("CONFIG", 700) + val FINE = Level("FINE", 500) + val FINER = Level("FINER", 400) + val FINEST = Level("FINEST", 300) + val ALL = Level("ALL", Integer.MIN_VALUE) +} diff --git a/airframe-log/.native/src/main/scala-3/java/util/logging/Logger.scala b/airframe-log/.native/src/main/scala/java/util/logging/Logger.scala similarity index 51% rename from airframe-log/.native/src/main/scala-3/java/util/logging/Logger.scala rename to airframe-log/.native/src/main/scala/java/util/logging/Logger.scala index 5aac70e36..4fbd4a2d3 100644 --- a/airframe-log/.native/src/main/scala-3/java/util/logging/Logger.scala +++ b/airframe-log/.native/src/main/scala/java/util/logging/Logger.scala @@ -1,78 +1,84 @@ +/* + * Licensed 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. + */ package java.util.logging - -abstract class Handler extends AutoCloseable: +abstract class Handler extends AutoCloseable { def publish(record: LogRecord): Unit + def flush(): Unit +} /** - * Implements java.util.logging.Logger interface, which is not avaialble - * in Scala Native - * @param name - */ + * Implements java.util.logging.Logger interface, which is not avaialble in Scala Native + * @param name + */ class Logger(parent: Option[Logger], name: String) { - private var handlers = List.empty[Handler] - private var useParentHandlers = true + private var handlers = List.empty[Handler] + private var useParentHandlers = true private var level: Option[Level] = None def getName(): String = name - def log(level: Level, msg: String): Unit = { + def log(level: Level, msg: String): Unit = log(LogRecord(level, msg)) - } def log(record: LogRecord): Unit = { - if(isLoggable(record.getLevel())) { - if(record.getLoggerName() == null) { - record.setLoggerName(name) - } - if(parent.nonEmpty && useParentHandlers) then + if (isLoggable(record.getLevel())) { + if (record.getLoggerName() == null) record.setLoggerName(name) + if (parent.nonEmpty && useParentHandlers) { getParent().log(record) - else + } else { handlers.foreach { h => h.publish(record) } + } } } def isLoggable(level: Level): Boolean = { val l = getLevel() - if(level.intValue() < l.intValue()) then false else true + if (level.intValue() < l.intValue()) false else true } - def getParent(): Logger = { + def getParent(): Logger = parent.getOrElse(null) - } - def getLevel(): Level = { + def getLevel(): Level = level.orElse(parent.map(_.getLevel())).getOrElse(Level.INFO) - } - def setLevel(newLevel: Level): Unit = { + def setLevel(newLevel: Level): Unit = level = Some(newLevel) - } - def resetLogLevel(): Unit = { + def resetLogLevel(): Unit = level = None - } - def setUseParentHandlers(useParentHandlers: Boolean): Unit = { + def setUseParentHandlers(useParentHandlers: Boolean): Unit = this.useParentHandlers = useParentHandlers - } - def addHandler(h: Handler): Unit = { + def addHandler(h: Handler): Unit = handlers = h :: handlers - } - def removeHandler(h: Handler): Unit = { + def removeHandler(h: Handler): Unit = handlers = handlers.filter(_ != h) - } def getHandlers: Array[Handler] = handlers.toArray } -object Logger: +object Logger { + import scala.jdk.CollectionConverters.* + private val loggerTable = new java.util.concurrent.ConcurrentHashMap[String, Logger]().asScala - private val rootLogger = Logger(None, "") + private val rootLogger = Logger(None, "") def getLogger(name: String): Logger = { loggerTable.get(name) match { @@ -90,31 +96,35 @@ object Logger: name match { case null | "" => rootLogger case other => - val parentName = name.substring(0, name.lastIndexOf('.').max(0)) + val parentName = name.substring(0, name.lastIndexOf('.').max(0)) val parentLogger = getLogger(parentName) Logger(Some(parentLogger), name) } } +} - -abstract class Formatter: +abstract class Formatter { def format(record: LogRecord): String +} - -class LogRecord(_level: Level, msg: String) extends Serializable: - private val millis = System.currentTimeMillis() - private var loggerName = "" +class LogRecord(_level: Level, msg: String) extends Serializable { + private val millis = System.currentTimeMillis() + private var loggerName = "" private var thrown: Throwable = null def getMessage(): String = msg + def getMillis(): Long = millis + def getLoggerName(): String = loggerName + def getLevel(): Level = _level + def getThrown(): Throwable = thrown - def setLoggerName(name: String): Unit = { + def setLoggerName(name: String): Unit = this.loggerName = name - } - def setThrown(e: Throwable): Unit = { + + def setThrown(e: Throwable): Unit = thrown = e - } +} diff --git a/airframe-log/.native/src/main/scala-3/wvlet/log/LogEnv.scala b/airframe-log/.native/src/main/scala/wvlet/log/LogEnv.scala similarity index 81% rename from airframe-log/.native/src/main/scala-3/wvlet/log/LogEnv.scala rename to airframe-log/.native/src/main/scala/wvlet/log/LogEnv.scala index 2b9548982..aa9c1f3d7 100644 --- a/airframe-log/.native/src/main/scala-3/wvlet/log/LogEnv.scala +++ b/airframe-log/.native/src/main/scala/wvlet/log/LogEnv.scala @@ -22,18 +22,22 @@ private[log] object LogEnv extends LogEnvBase { // do nothing by default } - override def isScalaJS: Boolean = false - override def defaultLogLevel: LogLevel = LogLevel.INFO + override def isScalaJS: Boolean = false + + override def defaultLogLevel: LogLevel = LogLevel.INFO + override def defaultHandler: java.util.logging.Handler = new ConsoleLogHandler(SourceCodeLogFormatter) - override def defaultConsoleOutput: PrintStream = System.err + + override def defaultConsoleOutput: PrintStream = System.err /** * @param cl * @return */ - override def getLoggerName(cl: Class[_]): String = cl.getName + override def getLoggerName(cl: Class[?]): String = cl.getName + + override def scheduleLogLevelScan: Unit = {} - override def scheduleLogLevelScan: Unit = {} override def stopScheduledLogLevelScan: Unit = {} /** @@ -53,5 +57,4 @@ private[log] object LogEnv extends LogEnvBase { /** */ override def unregisterJMX: Unit = {} - } diff --git a/airframe-log/.native/src/main/scala-3/wvlet/log/LogTimestampFormatter.scala b/airframe-log/.native/src/main/scala/wvlet/log/LogTimestampFormatter.scala similarity index 57% rename from airframe-log/.native/src/main/scala-3/wvlet/log/LogTimestampFormatter.scala rename to airframe-log/.native/src/main/scala/wvlet/log/LogTimestampFormatter.scala index d3f060dee..7b7904a92 100644 --- a/airframe-log/.native/src/main/scala-3/wvlet/log/LogTimestampFormatter.scala +++ b/airframe-log/.native/src/main/scala/wvlet/log/LogTimestampFormatter.scala @@ -1,3 +1,16 @@ +/* + * Licensed 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. + */ package wvlet.log import scalanative.posix.time.* @@ -17,7 +30,7 @@ object LogTimestampFormatter { !ttPtr = (timeMillis / 1000).toSize val tmPtr = alloc[tm]() localtime_r(ttPtr, tmPtr) - val bufSize = 26.toUSize + val bufSize = 26.toUSize val buf: Ptr[Byte] = alloc[Byte](bufSize) strftime(buf, bufSize, pattern, tmPtr) val ms = timeMillis % 1000 @@ -33,11 +46,9 @@ object LogTimestampFormatter { } } - def formatTimestamp(timeMillis: Long): String = { + def formatTimestamp(timeMillis: Long): String = format(c"%Y-%m-%d %H:%M:%S.", timeMillis) - } - def formatTimestampWithNoSpaace(timeMillis: Long): String = { + def formatTimestampWithNoSpaace(timeMillis: Long): String = format(c"%Y-%m-%dT%H:%M:%S.", timeMillis) - } } diff --git a/build.sbt b/build.sbt index 1e269f027..f86f96a98 100644 --- a/build.sbt +++ b/build.sbt @@ -11,7 +11,7 @@ val targetScalaVersions = SCALA_3 :: uptoScala2 ThisBuild / resolvers ++= Resolver.sonatypeOssRepos("snapshots") val AIRSPEC_VERSION = sys.env.getOrElse("AIRSPEC_VERSION", "24.4.1") -val SCALACHECK_VERSION = "1.17.1" +val SCALACHECK_VERSION = "1.18.0" val MSGPACK_VERSION = "0.9.8" val SCALA_PARSER_COMBINATOR_VERSION = "2.4.0" val SQLITE_JDBC_VERSION = "3.45.3.0" @@ -162,6 +162,12 @@ val jsBuildSettings = Seq[Setting[?]]( coverageEnabled := false ) +val nativeBuildSettings = Seq[Setting[?]]( + scalaVersion := SCALA_3, + crossScalaVersions := List(SCALA_3), + coverageEnabled := false +) + val noPublish = Seq( publishArtifact := false, publish := {}, @@ -252,6 +258,10 @@ lazy val jsProjects: Seq[ProjectReference] = Seq( widgetJS ) +lazy val nativeProjects: Seq[ProjectReference] = Seq( + log.native +) + // Integration test projects lazy val itProjects: Seq[ProjectReference] = Seq( integrationTestApi.jvm, @@ -559,7 +569,7 @@ val logJVMDependencies = Seq( // airframe-log should have minimum dependencies lazy val log: sbtcrossproject.CrossProject = - crossProject(JVMPlatform, JSPlatform) + crossProject(JVMPlatform, JSPlatform, NativePlatform) .crossType(CrossType.Pure) .in(file("airframe-log")) .settings(buildSettings) @@ -582,6 +592,9 @@ lazy val log: sbtcrossproject.CrossProject = ("org.scala-js" %%% "scalajs-java-logging" % JS_JAVA_LOGGING_VERSION).cross(CrossVersion.for3Use2_13) ) ) + .nativeSettings( + nativeBuildSettings + ) lazy val metrics = crossProject(JVMPlatform, JSPlatform) diff --git a/project/plugin.sbt b/project/plugin.sbt index 58e6dc146..75654a91f 100755 --- a/project/plugin.sbt +++ b/project/plugin.sbt @@ -32,7 +32,7 @@ libraryDependencies += "io.github.gmkumar2005" %% "scala-js-env-playwright" % "0 // For Scala native addSbtPlugin("org.portable-scala" % "sbt-scala-native-crossproject" % "1.3.2") -addSbtPlugin("org.scala-native" % "sbt-scala-native" % "0.4.17") +addSbtPlugin("org.scala-native" % "sbt-scala-native" % "0.5.1") // For setting explicit versions for each commit addSbtPlugin("com.github.sbt" % "sbt-dynver" % "5.0.1") From e32413175c2348d607fe145624c3cc2e09ea3ede Mon Sep 17 00:00:00 2001 From: "Taro L. Saito" Date: Mon, 22 Apr 2024 07:46:04 -0700 Subject: [PATCH 2/7] format --- .../.native/src/main/scala/wvlet/log/LogEnv.scala | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/airframe-log/.native/src/main/scala/wvlet/log/LogEnv.scala b/airframe-log/.native/src/main/scala/wvlet/log/LogEnv.scala index aa9c1f3d7..0b542c4c8 100644 --- a/airframe-log/.native/src/main/scala/wvlet/log/LogEnv.scala +++ b/airframe-log/.native/src/main/scala/wvlet/log/LogEnv.scala @@ -22,23 +22,18 @@ private[log] object LogEnv extends LogEnvBase { // do nothing by default } - override def isScalaJS: Boolean = false - - override def defaultLogLevel: LogLevel = LogLevel.INFO - + override def isScalaJS: Boolean = false + override def defaultLogLevel: LogLevel = LogLevel.INFO override def defaultHandler: java.util.logging.Handler = new ConsoleLogHandler(SourceCodeLogFormatter) - - override def defaultConsoleOutput: PrintStream = System.err + override def defaultConsoleOutput: PrintStream = System.err /** * @param cl * @return */ override def getLoggerName(cl: Class[?]): String = cl.getName - - override def scheduleLogLevelScan: Unit = {} - - override def stopScheduledLogLevelScan: Unit = {} + override def scheduleLogLevelScan: Unit = {} + override def stopScheduledLogLevelScan: Unit = {} /** * Scan the default log level file only once. To periodically scan, use scheduleLogLevelScan From 0cc58c7bb8bf010d40f963bb0803f82724625f98 Mon Sep 17 00:00:00 2001 From: "Taro L. Saito" Date: Mon, 22 Apr 2024 15:03:22 -0700 Subject: [PATCH 3/7] Fix for Scala Native --- .../.native/src/main/scala/wvlet/log/LogEnv.scala | 15 ++++++++++++--- .../src/test/scala/wvlet/log/LoggerTest.scala | 8 ++++---- 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/airframe-log/.native/src/main/scala/wvlet/log/LogEnv.scala b/airframe-log/.native/src/main/scala/wvlet/log/LogEnv.scala index 0b542c4c8..75af34c6a 100644 --- a/airframe-log/.native/src/main/scala/wvlet/log/LogEnv.scala +++ b/airframe-log/.native/src/main/scala/wvlet/log/LogEnv.scala @@ -31,9 +31,18 @@ private[log] object LogEnv extends LogEnvBase { * @param cl * @return */ - override def getLoggerName(cl: Class[?]): String = cl.getName - override def scheduleLogLevelScan: Unit = {} - override def stopScheduledLogLevelScan: Unit = {} + override def getLoggerName(cl: Class[?]): String = { + var name = cl.getName + + val pos = name.indexOf("$") + if (pos > 0) { + // Remove trailing $xxx + name = name.substring(0, pos) + } + name + } + override def scheduleLogLevelScan: Unit = {} + override def stopScheduledLogLevelScan: Unit = {} /** * Scan the default log level file only once. To periodically scan, use scheduleLogLevelScan diff --git a/airframe-log/src/test/scala/wvlet/log/LoggerTest.scala b/airframe-log/src/test/scala/wvlet/log/LoggerTest.scala index 2e92e63bb..7ee1f8d01 100644 --- a/airframe-log/src/test/scala/wvlet/log/LoggerTest.scala +++ b/airframe-log/src/test/scala/wvlet/log/LoggerTest.scala @@ -186,19 +186,19 @@ class LoggerTest extends Spec { } test("use succinct name when used with anonymous trait") { - if (LogEnv.isScalaJS) { - pending("Scala.js cannot get a logger name") + if (isScalaJS || isScalaNative) { + pending("Scala.js/Native cannot get a logger name from anonymous trait") } else { val l = new Sample with LogSupport { self => - assert(self.logger.getName == ("wvlet.log.Sample")) + self.logger.getName shouldBe "wvlet.log.Sample" } } } test("Remove $ from object name") { val o = Sample - assert(o.loggerName == "wvlet.log.Sample") + o.loggerName shouldBe "wvlet.log.Sample" } test("clear parent handlers") { From 7c4064c755fdbd85de835dbb7657c76908c1d237 Mon Sep 17 00:00:00 2001 From: "Taro L. Saito" Date: Mon, 22 Apr 2024 15:23:00 -0700 Subject: [PATCH 4/7] Fix in case log level is null --- .../.native/src/main/scala/java/util/logging/Logger.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/airframe-log/.native/src/main/scala/java/util/logging/Logger.scala b/airframe-log/.native/src/main/scala/java/util/logging/Logger.scala index 4fbd4a2d3..be91a23f0 100644 --- a/airframe-log/.native/src/main/scala/java/util/logging/Logger.scala +++ b/airframe-log/.native/src/main/scala/java/util/logging/Logger.scala @@ -46,7 +46,7 @@ class Logger(parent: Option[Logger], name: String) { def isLoggable(level: Level): Boolean = { val l = getLevel() - if (level.intValue() < l.intValue()) false else true + level.intValue() >= l.intValue() } def getParent(): Logger = @@ -56,7 +56,7 @@ class Logger(parent: Option[Logger], name: String) { level.orElse(parent.map(_.getLevel())).getOrElse(Level.INFO) def setLevel(newLevel: Level): Unit = - level = Some(newLevel) + level = Option(newLevel) def resetLogLevel(): Unit = level = None From 75fac39c8e0d33ccd5bffb1be7fcf2aed8362ea1 Mon Sep 17 00:00:00 2001 From: "Taro L. Saito" Date: Mon, 22 Apr 2024 15:24:58 -0700 Subject: [PATCH 5/7] Add CI for Scala Native --- .github/workflows/test.yml | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index ce37f80f6..0451d54a4 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -212,6 +212,25 @@ jobs: check_name: Test Report Scala.js / Scala 3 annotate_only: true detailed_summary: true + test_native_3: + name: Scala Native / Scala 3 + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-java@v4 + with: + distribution: 'zulu' + java-version: '21' + - name: Scala Native test + run: JVM_OPTS=-Xmx4g ./sbt "++ 3; projectNative/test" + - name: Publish Test Report + uses: mikepenz/action-junit-report@v4 + if: always() # always run even if the previous step fails + with: + report_paths: '**/target/test-reports/TEST-*.xml' + check_name: Test Report Scala Native / Scala 3 + annotate_only: true + detailed_summary: true test_airspec: name: AirSpec runs-on: ubuntu-latest From 718239e2474e989425ace80bc8118f784b7db0c4 Mon Sep 17 00:00:00 2001 From: "Taro L. Saito" Date: Mon, 22 Apr 2024 15:34:24 -0700 Subject: [PATCH 6/7] publish native artifacts --- build.sbt | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/build.sbt b/build.sbt index f86f96a98..0eed95958 100644 --- a/build.sbt +++ b/build.sbt @@ -32,13 +32,13 @@ val AIRFRAME_BINARY_COMPAT_VERSION = "23.6.0" // A short cut for publishing snapshots to Sonatype addCommandAlias( "publishSnapshots", - s"+ projectJVM/publish; + projectJS/publish" + s"+ projectJVM/publish; + projectJS/publish; + projectNative/publish" ) // [Development purpose] publish all artifacts to the local repo addCommandAlias( "publishAllLocal", - s"+ projectJVM/publishLocal; + projectJS/publishLocal;" + s"+ projectJVM/publishLocal; + projectJS/publishLocal; + projectNative/publishLocal" ) // [Development purpose] publish all sbt-airframe related artifacts to local repo @@ -301,6 +301,15 @@ lazy val projectJS = ) .aggregate(jsProjects: _*) +lazy val projectNative = + project + .settings(noPublish) + .settings( + // Skip importing aggregated projects in IntelliJ IDEA + ideSkipProject := true + ) + .aggregate(nativeProjects: _*) + lazy val projectIt = project .settings(noPublish) From d8c3c8aea348a80ec601c71971ac20b77b4b16a7 Mon Sep 17 00:00:00 2001 From: "Taro L. Saito" Date: Mon, 22 Apr 2024 15:39:29 -0700 Subject: [PATCH 7/7] Add publish settings --- .github/workflows/release-js.yml | 2 +- .github/workflows/release-native.yml | 35 ++++++++++++++++++++++++++++ build.sbt | 10 ++++++-- 3 files changed, 44 insertions(+), 3 deletions(-) create mode 100644 .github/workflows/release-native.yml diff --git a/.github/workflows/release-js.yml b/.github/workflows/release-js.yml index 8e3ad38e0..844c73829 100644 --- a/.github/workflows/release-js.yml +++ b/.github/workflows/release-js.yml @@ -32,4 +32,4 @@ jobs: env: SONATYPE_USERNAME: '${{ secrets.SONATYPE_USER }}' SONATYPE_PASSWORD: '${{ secrets.SONATYPE_PASS }}' - run: SCALAJS=true ./sbt sonatypeBundleRelease + run: SCALA_JS=true ./sbt sonatypeBundleRelease diff --git a/.github/workflows/release-native.yml b/.github/workflows/release-native.yml new file mode 100644 index 000000000..a19ec493b --- /dev/null +++ b/.github/workflows/release-native.yml @@ -0,0 +1,35 @@ +name: Release Scala Native + +on: + push: + tags: + - v* + workflow_dispatch: + +jobs: + publish_js: + name: Publish Scala Native + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + # Fetch all tags so that sbt-dynver can find the previous release version + fetch-depth: 0 + - run: git fetch --tags -f + - uses: actions/setup-java@v4 + with: + distribution: 'zulu' + java-version: '17' + - name: Setup GPG + env: + PGP_SECRET: ${{ secrets.PGP_SECRET }} + run: echo $PGP_SECRET | base64 --decode | gpg --import --batch --yes + - name: Build for Scala Native + env: + PGP_PASSPHRASE: ${{ secrets.PGP_PASSPHRASE }} + run: ./sbt publishNativeSigned + - name: Release to Sonatype + env: + SONATYPE_USERNAME: '${{ secrets.SONATYPE_USER }}' + SONATYPE_PASSWORD: '${{ secrets.SONATYPE_PASS }}' + run: SCALA_NATIVE=true ./sbt sonatypeBundleRelease diff --git a/build.sbt b/build.sbt index 0eed95958..f06e7b333 100644 --- a/build.sbt +++ b/build.sbt @@ -55,6 +55,10 @@ addCommandAlias( "publishJSLocal", s"+ projectJS/publishLocal" ) +addCommandAlias( + "publishNativeSigned", + s"+ projectNative/publishSigned" +) // Allow using Ctrl+C in sbt without exiting the prompt // Global / cancelable := true @@ -193,9 +197,11 @@ lazy val root = .settings( sonatypeProfileName := "org.wvlet", sonatypeSessionName := { - if (sys.env.isDefinedAt("SCALAJS")) { - // Use a different session for Scala.js projects + // Use different session names for parallel publishing to Sonatype + if (sys.env.isDefinedAt("SCALA_JS")) { s"${sonatypeSessionName.value} for Scala.js" + } else if (sys.env.isDefinedAt("SCALA_NATIVE")) { + s"${sonatypeSessionName.value} for Scala Native" } else { sonatypeSessionName.value }