Skip to content

Commit

Permalink
feature: update to support both scala 3 and scala 2.13
Browse files Browse the repository at this point in the history
  • Loading branch information
cloudmark committed Jul 16, 2024
1 parent dfe45cb commit fbc1ead
Show file tree
Hide file tree
Showing 22 changed files with 200 additions and 127 deletions.
24 changes: 22 additions & 2 deletions .github/workflows/release-publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ name: Publish Package on Release
on:
release:
types: [published]
workflow_dispatch:

jobs:
publish:
Expand Down Expand Up @@ -39,7 +40,26 @@ jobs:
mkdir -p ~/.sbt/1.0
echo "credentials += Credentials(\"GitHub Package Registry\", \"maven.pkg.github.com\", \"${{ github.actor }}\", \"${{ secrets.GITHUB_TOKEN }}\")" >> ~/.sbt/1.0/global.sbt
- name: Build and publish
run: sbt clean compile publish
- name: Build and publish for Scala 2.13
run: |
sbt ++2.13.12 clean compile publishM2
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

- name: Publish for Scala 2.13
run: |
sbt ++2.13.12 publish
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

- name: Build and publish for Scala 3
run: |
sbt ++3.3.0 clean compile publishM2
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

- name: Publish for Scala 3
run: |
sbt ++3.3.0 publish
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
51 changes: 34 additions & 17 deletions .github/workflows/scala.yml
Original file line number Diff line number Diff line change
@@ -1,15 +1,11 @@
# This workflow uses actions that are not certified by GitHub.
# They are provided by a third-party and are governed by
# separate terms of service, privacy policy, and support
# documentation.

name: Scala CI

on:
push:
branches: [ "main" ]
pull_request:
branches: [ "main" ]
workflow_dispatch:

permissions:
contents: read
Expand All @@ -23,17 +19,38 @@ jobs:
pull-requests: write
repository-projects: write
steps:
- uses: actions/checkout@v4
- name: Set up JDK 11
uses: actions/setup-java@v3
with:
java-version: '11'
distribution: 'temurin'
cache: 'sbt'
- name: Run tests
run: sbt test
# Optional: This step uploads information to the GitHub dependency graph and unblocking Dependabot alerts for the repository
- name: Sbt Dependency Submission
uses: scalacenter/sbt-dependency-submission@v3.0.1
- uses: actions/checkout@v4
- name: Set up JDK 11
uses: actions/setup-java@v3
with:
java-version: '11'
distribution: 'temurin'
cache: 'sbt'

- name: Cache sbt
uses: actions/cache@v2
with:
path: ~/.sbt
key: ${{ runner.os }}-sbt-${{ hashFiles('**/build.sbt') }}
restore-keys: |
${{ runner.os }}-sbt-
- name: Cache Coursier
uses: actions/cache@v2
with:
path: ~/.coursier
key: ${{ runner.os }}-coursier-${{ hashFiles('**/build.sbt') }}
restore-keys: |
${{ runner.os }}-coursier-
- name: Run tests for Scala 2.13
run: sbt ++2.13.12 clean compile test

- name: Run tests for Scala 3
run: sbt ++3.3.0 clean compile test

- name: Sbt Dependency Submission for Scala 2.13
uses: scalacenter/sbt-dependency-submission@v3.0.1

- name: Sbt Dependency Submission for Scala 3
uses: scalacenter/sbt-dependency-submission@v3.0.1
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -68,3 +68,5 @@ project/**/metals.sbt

.bsp
.history
scala2/target
scala3/target
9 changes: 6 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ Add the following to your `build.sbt`:
```scala
resolvers += "jitpack" at "https://jitpack.io"

libraryDependencies += "com.github.suprnation" % "cats-actors" % "2.0.0-RC1"
libraryDependencies += "com.github.suprnation" &% "cats-actors" % "2.0.0-RC2"
```

#### Using Maven
Expand All @@ -110,7 +110,9 @@ Add the following to your `pom.xml`:
<dependency>
<groupId>com.github.suprnation</groupId>
<artifactId>cats-actors_2.13</artifactId>
<version>2.0.0-RC1</version>
or
<artifactId>cats-actors_3</artifactId>
<version>2.0.0-RC2</version>
</dependency>
```

Expand All @@ -124,7 +126,8 @@ Add the following to your `repositories.bzl` and `WORKSPACE` files:
def load_dependencies():
maven_install(
artifacts = [
"com.github.suprnation:cats-actors:2.0.0-RC1",
"com.github.suprnation:cats-actors_2_13:2.0.0-RC2", // or
"com.github.suprnation:cats-actors_3:2.0.0-RC2",
],
repositories = [
"https://jitpack.io",
Expand Down
123 changes: 92 additions & 31 deletions build.sbt
Original file line number Diff line number Diff line change
@@ -1,41 +1,102 @@
ThisBuild / version := "2.0.0-RC1"

ThisBuild / scalaVersion := "2.13.12"

addCompilerPlugin("org.typelevel" % "kind-projector" % "0.13.2" cross CrossVersion.full)
import sbt._
import Keys._

ThisBuild / organization := "com.suprnation"
ThisBuild / name := "cats-actors"
ThisBuild / version := "2.0.0-RC2"
ThisBuild / organizationName := "SuprNation"
ThisBuild / startYear := Some(2024)
ThisBuild / licenses += ("Apache-2.0", url("https://www.apache.org/licenses/LICENSE-2.0.txt"))

organizationName := "SuprNation"
startYear := Some(2024)
licenses += ("Apache-2.0", url("https://www.apache.org/licenses/LICENSE-2.0.txt"))
ThisBuild / crossScalaVersions := Seq("2.13.12", "3.3.0")
ThisBuild / scalaVersion := crossScalaVersions.value.head

Test / parallelExecution := false
lazy val commonSettings = Seq(
Test / parallelExecution := false,
libraryDependencies ++= Seq(
"org.typelevel" %% "cats-effect" % "3.5.2",
"org.scalatest" %% "scalatest" % "3.2.19" % Test
),
publishMavenStyle := true,
publishTo := {
val owner = "suprnation"
val repo = "cats-actors"
if (isSnapshot.value)
Some("GitHub Package Registry".at(s"https://maven.pkg.github.com/$owner/$repo"))
else
Some("GitHub Package Registry".at(s"https://maven.pkg.github.com/$owner/$repo"))
},
publishConfiguration := publishConfiguration.value.withOverwrite(true),
publishLocalConfiguration := publishLocalConfiguration.value.withOverwrite(true)
)

scalacOptions ++= Seq( // use ++= to add to existing options
"-deprecation",
"-encoding",
"utf8", // if an option takes an arg, supply it on the same line
"-feature", // then put the next option on a new line for easy editing
"-language:implicitConversions",
"-language:existentials",
"-unchecked",
"-Werror",
"-Xlint" // exploit "trailing comma" syntax so you can add an option without editing this line
lazy val scala2Settings = Seq(
libraryDependencies += "org.typelevel" %% "kind-projector" % "0.13.2" cross CrossVersion.full,
scalacOptions ++= Seq(
"-language:implicitConversions",
"-language:existentials"
)
)

libraryDependencies ++= Seq(
"org.typelevel" %% "cats-effect" % "3.5.2", // if needed for other dependencies
"org.typelevel" %% "cats-effect" % "3.5.0",
"org.scalatest" %% "scalatest" % "3.2.18" % Test
lazy val scala3Settings = Seq(
// Scala 3 specific settings can be added here
)

publishTo := {
val owner = "suprnation"
val repo = "cats-actors"
if (isSnapshot.value)
Some("GitHub Package Registry".at(s"https://maven.pkg.github.com/$owner/$repo"))
else
Some("GitHub Package Registry".at(s"https://maven.pkg.github.com/$owner/$repo"))
}
lazy val root = (project in file("."))
.settings(commonSettings)
.settings(
name := "cats-actors",
// Conditionally apply settings based on Scala version
libraryDependencies ++= (scalaVersion.value match {
case v if v.startsWith("2.") => Seq("org.typelevel" %% "kind-projector" % "0.13.2" cross CrossVersion.full)
case _ => Seq()
}),
scalacOptions ++= (scalaVersion.value match {
case v if v.startsWith("2.") => Seq(
"-deprecation",
"-encoding", "utf8",
"-feature",
"-unchecked",
"-Werror",
"-language:implicitConversions",
"-language:existentials",
"-Xlint"
)
case _ => Seq()
}),
Compile / unmanagedSourceDirectories ++= Seq(
baseDirectory.value / "src" / "main" / "scala"
),
Test / unmanagedSourceDirectories ++= Seq(
baseDirectory.value / "src" / "test" / "scala"
),
// Ensure the artifacts are published with the Scala version in the name
Compile / packageBin / artifactPath := {
val artPath = (Compile / packageBin / artifactPath).value
val scalaVer = scalaBinaryVersion.value match {
case "2.13" => "2_13"
case "3" => "3"
case other => other.replace('.', '_')
}
file(s"${artPath.getParent}/cats-actors_${scalaVer}-${version.value}.jar")
},
// Add tasks to create source and javadoc JARs
Compile / packageSrc / artifactPath := {
val artPath = (Compile / packageSrc / artifactPath).value
val scalaVer = scalaBinaryVersion.value match {
case "2.13" => "2_13"
case "3" => "3"
case other => other.replace('.', '_')
}
file(s"${artPath.getParent}/cats-actors_${scalaVer}-${version.value}-sources.jar")
},
Compile / packageDoc / artifactPath := {
val artPath = (Compile / packageDoc / artifactPath).value
val scalaVer = scalaBinaryVersion.value match {
case "2.13" => "2_13"
case "3" => "3"
case other => other.replace('.', '_')
}
file(s"${artPath.getParent}/cats-actors_${scalaVer}-${version.value}-javadoc.jar")
}
)
4 changes: 2 additions & 2 deletions src/main/scala/com/suprnation/EscalatingReplyingActor.scala
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@
package com.suprnation

import cats.Parallel
import cats.effect.std.Console
import cats.effect.{Async, Concurrent, Sync, Temporal}
import cats.effect._
import cats.effect.std._
import cats.implicits._
import com.suprnation.actor.Actor.ReplyingReceive
import com.suprnation.actor.SupervisorStrategy.Escalate
Expand Down
7 changes: 4 additions & 3 deletions src/main/scala/com/suprnation/actor/Actor.scala
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ object ReplyingActor {
): Actor[F, Request] =
withReceive(Behaviour.ignoringBehaviour(name))

def withReceive[F[+_]: Parallel: Concurrent: Temporal, Request, Response](
private def withReceive[F[+_]: Parallel: Concurrent: Temporal, Request, Response](
_receive: PartialFunction[Request, F[Response]]
): ReplyingActor[F, Request, Response] = new ReplyingActor[F, Request, Response] {
override def receive: ReplyingReceive[F, Request, Response] = _receive
Expand Down Expand Up @@ -112,7 +112,7 @@ abstract class ReplyingActor[F[+_]: Concurrent: Parallel: Temporal, Request, Res
// We do this because we do not want a Ref for the context to make the DX easier.
// We also do not want a Ref on self to make the DX easier.
implicit val context: ActorContext[F, Request, Response] = null
final val self: ReplyingActorRef[F, Request, Response] = null
val self: ReplyingActorRef[F, Request, Response] = null
implicit def implicitSelf: Option[ReplyingActorRef[F, Request, Response]] = Option(self)

def init: F[Unit] = Concurrent[F].unit
Expand Down Expand Up @@ -149,7 +149,8 @@ abstract class ReplyingActor[F[+_]: Concurrent: Parallel: Temporal, Request, Res
/** User overridable callback. <p/> Is called when a message isn't handled by the current behaviour of the actor by default it fails with either [[com.suprnation.actor.DeathPactException]] (in case of an unhandled [[com.suprnation.actor.Terminated]] message) or publishes an [[com.suprnation.actor.UnhandledMessage]] to the actor system's event stream.
*/
def unhandled(message: Any): F[Any] = message match {
case Terminated(dead, _) => MonadThrow[F].raiseError(DeathPactException(dead))
case Terminated(dead, _) =>
MonadThrow[F].raiseError(DeathPactException[F](dead.asInstanceOf[NoSendActorRef[F]]))
case _ =>
context.system.eventStream
.offer(UnhandledMessage(message, context.sender, self).toString)
Expand Down
27 changes: 4 additions & 23 deletions src/main/scala/com/suprnation/actor/ActorContext.scala
Original file line number Diff line number Diff line change
Expand Up @@ -79,10 +79,6 @@ object ActorContext {
// Here we will call the parent to handle this..
_self match {
case local: InternalActorRef[F, ?, ?] => local.stop
case unexpected =>
MonadThrow[F].raiseError(
new IllegalArgumentException(s"ActorRef is not internal: $unexpected")
)
}
} else {
Concurrent[F].raiseError(
Expand All @@ -97,41 +93,26 @@ object ActorContext {
self match {
case local: InternalActorRef[F, ?, ?] =>
local.assertCellActiveAndDo(_.watch(actorRef, onTerminated))
case unexpected =>
MonadThrow[F].raiseError(
new IllegalArgumentException(s"ActorRef is not internal: $unexpected")
) // Done for completeness
}

def unwatch(actorRef: NoSendActorRef[F]): F[NoSendActorRef[F]] =
self match {
case local: InternalActorRef[F, ?, ?] =>
local.assertCellActiveAndDo(_.unwatch(actorRef))
case unexpected =>
MonadThrow[F].raiseError(
new IllegalArgumentException(s"ActorRef is not internal: $unexpected")
) // Done for completeness
}

override def setReceiveTimeout(timeout: FiniteDuration, onTimeout: => Request): F[Unit] =
self match {
case local: InternalActorRef[F, ?, ?] =>
local.assertCellActiveAndDo(_.setReceiveTimeout(timeout, onTimeout))
case unexpected =>
MonadThrow[F].raiseError(
new IllegalArgumentException(s"ActorRef is not internal: $unexpected")
) // Done for completeness
}

override val cancelReceiveTimeout: F[Unit] =
Temporal[F].delay(self).flatMap {
case local: InternalActorRef[F, ?, ?] =>
Temporal[F]
.delay(self)
.flatMap((local: InternalActorRef[F, ?, ?]) =>
local.assertCellActiveAndDo(_.cancelReceiveTimeout)
case unexpected =>
MonadThrow[F].raiseError(
new IllegalArgumentException(s"ActorRef is not internal: $unexpected")
) // Done for completeness
}
)

override def replyingActorOf[ChildRequest, ChildResponse](
props: F[ReplyingActor[F, ChildRequest, ChildResponse]],
Expand Down
4 changes: 2 additions & 2 deletions src/main/scala/com/suprnation/actor/ChildStats.scala
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,8 @@ final case class ChildRestartStats[F[+_]](
*/
val retriesDone = maxNrOfRetriesCount + 1
val now = System.nanoTime
val windowStart =
if (restartTimeWindowStartNanos == 0) {
val windowStart: Long =
if (restartTimeWindowStartNanos == 0L) {
restartTimeWindowStartNanos = now
now
} else restartTimeWindowStartNanos
Expand Down
4 changes: 2 additions & 2 deletions src/main/scala/com/suprnation/actor/Exceptions.scala
Original file line number Diff line number Diff line change
Expand Up @@ -120,11 +120,11 @@ final case class PostRestartException[F[+_]](

/** InvalidMessageException is thrown when an invalid message is sent to an Actor; Currently only `null` is an invalid message.
*/
final case class InvalidMessageException private (message: String) extends AkkaException(message)
final case class InvalidMessageException(message: String) extends AkkaException(message)

/** A DeathPactException is thrown by an Actor that receives a Terminated(someActor) message that it doesn't handle itself, effectively crashing the Actor and escalating to the supervisor.
*/
final case class DeathPactException[F[+_]] private (dead: NoSendActorRef[F])
final case class DeathPactException[F[+_]](dead: NoSendActorRef[F])
extends AkkaException("Monitored actor [" + dead + "] terminated")
with NoStackTrace

Expand Down
Loading

0 comments on commit fbc1ead

Please sign in to comment.