Skip to content

Commit

Permalink
Merge pull request #703 from armanbilge/feature/native
Browse files Browse the repository at this point in the history
Scala Native
  • Loading branch information
mpilquist authored Nov 24, 2022
2 parents f41b328 + af779fe commit 456ade1
Show file tree
Hide file tree
Showing 19 changed files with 1,768 additions and 107 deletions.
66 changes: 52 additions & 14 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,10 @@ jobs:
name: Build and Test
strategy:
matrix:
os: [ubuntu-latest]
os: [ubuntu-22.04]
scala: [2.12.17, 2.13.10, 3.2.1]
java: [temurin@11]
project: [rootJS, rootJVM]
project: [rootJS, rootJVM, rootNative]
runs-on: ${{ matrix.os }}
steps:
- name: Checkout current branch (full)
Expand Down Expand Up @@ -66,6 +66,10 @@ jobs:
~/Library/Caches/Coursier/v1
key: ${{ runner.os }}-sbt-cache-v2-${{ hashFiles('**/*.sbt') }}-${{ hashFiles('project/build.properties') }}

- name: Install brew formulae (ubuntu)
if: (matrix.project == 'rootNative') && startsWith(matrix.os, 'ubuntu')
run: /home/linuxbrew/.linuxbrew/bin/brew install s2n utf8proc

- name: Set up cert permissions
run: |
chmod 600 world/server.key
Expand All @@ -78,38 +82,42 @@ jobs:
run: sbt githubWorkflowCheck

- name: Check Headers
run: sbt 'project ${{ matrix.project }}' '++ ${{ matrix.scala }}' headerCheckAll
run: sbt 'project ${{ matrix.project }}' '++${{ matrix.scala }}' headerCheckAll

- name: Check headers and formatting
if: matrix.java == 'temurin@11'
run: sbt 'project ${{ matrix.project }}' '++ ${{ matrix.scala }}' headerCheckAll scalafmtCheckAll 'project /' scalafmtSbtCheck
run: sbt 'project ${{ matrix.project }}' '++${{ matrix.scala }}' headerCheckAll scalafmtCheckAll 'project /' scalafmtSbtCheck

- name: scalaJSLink
if: matrix.project == 'rootJS'
run: sbt 'project ${{ matrix.project }}' '++ ${{ matrix.scala }}' Test/scalaJSLinkerResult
run: sbt 'project ${{ matrix.project }}' '++${{ matrix.scala }}' Test/scalaJSLinkerResult

- name: nativeLink
if: matrix.project == 'rootNative'
run: sbt 'project ${{ matrix.project }}' '++${{ matrix.scala }}' Test/nativeLink

- name: Test
run: sbt 'project ${{ matrix.project }}' '++ ${{ matrix.scala }}' test
run: sbt 'project ${{ matrix.project }}' '++${{ matrix.scala }}' test

- name: Check binary compatibility
if: matrix.java == 'temurin@11'
run: sbt 'project ${{ matrix.project }}' '++ ${{ matrix.scala }}' mimaReportBinaryIssues
run: sbt 'project ${{ matrix.project }}' '++${{ matrix.scala }}' mimaReportBinaryIssues

- name: Generate API documentation
if: matrix.java == 'temurin@11'
run: sbt 'project ${{ matrix.project }}' '++ ${{ matrix.scala }}' doc
run: sbt 'project ${{ matrix.project }}' '++${{ matrix.scala }}' doc

- name: Check Doc Site (2.13.10 JVM only)
if: matrix.scala == '2.13.10' && matrix.project == 'rootJVM'
run: sbt 'project ${{ matrix.project }}' '++ ${{ matrix.scala }}' docs/makeSite
run: sbt 'project ${{ matrix.project }}' '++${{ matrix.scala }}' docs/makeSite

- name: Make target directories
if: github.event_name != 'pull_request' && (startsWith(github.ref, 'refs/tags/v') || github.ref == 'refs/heads/main')
run: mkdir -p modules/circe/.jvm/target target .js/target modules/docs/target modules/core/js/target modules/circe/.js/target modules/core/jvm/target modules/tests/js/target .jvm/target .native/target modules/refined/.js/target modules/refined/.jvm/target modules/tests/jvm/target modules/example/target project/target
run: mkdir -p modules/circe/.jvm/target target .js/target modules/core/native/target modules/docs/target modules/core/js/target modules/circe/.js/target modules/core/jvm/target modules/tests/js/target modules/refined/.native/target .jvm/target .native/target modules/refined/.js/target modules/refined/.jvm/target modules/circe/.native/target modules/tests/jvm/target modules/example/target modules/tests/native/target project/target

- name: Compress target directories
if: github.event_name != 'pull_request' && (startsWith(github.ref, 'refs/tags/v') || github.ref == 'refs/heads/main')
run: tar cf targets.tar modules/circe/.jvm/target target .js/target modules/docs/target modules/core/js/target modules/circe/.js/target modules/core/jvm/target modules/tests/js/target .jvm/target .native/target modules/refined/.js/target modules/refined/.jvm/target modules/tests/jvm/target modules/example/target project/target
run: tar cf targets.tar modules/circe/.jvm/target target .js/target modules/core/native/target modules/docs/target modules/core/js/target modules/circe/.js/target modules/core/jvm/target modules/tests/js/target modules/refined/.native/target .jvm/target .native/target modules/refined/.js/target modules/refined/.jvm/target modules/circe/.native/target modules/tests/jvm/target modules/example/target modules/tests/native/target project/target

- name: Upload target directories
if: github.event_name != 'pull_request' && (startsWith(github.ref, 'refs/tags/v') || github.ref == 'refs/heads/main')
Expand All @@ -124,7 +132,7 @@ jobs:
if: github.event_name != 'pull_request' && (startsWith(github.ref, 'refs/tags/v') || github.ref == 'refs/heads/main')
strategy:
matrix:
os: [ubuntu-latest]
os: [ubuntu-22.04]
scala: [2.13.10]
java: [temurin@11]
runs-on: ${{ matrix.os }}
Expand Down Expand Up @@ -182,6 +190,16 @@ jobs:
tar xf targets.tar
rm targets.tar
- name: Download target directories (2.12.17, rootNative)
uses: actions/download-artifact@v2
with:
name: target-${{ matrix.os }}-${{ matrix.java }}-2.12.17-rootNative

- name: Inflate target directories (2.12.17, rootNative)
run: |
tar xf targets.tar
rm targets.tar
- name: Download target directories (2.13.10, rootJS)
uses: actions/download-artifact@v2
with:
Expand All @@ -202,6 +220,16 @@ jobs:
tar xf targets.tar
rm targets.tar
- name: Download target directories (2.13.10, rootNative)
uses: actions/download-artifact@v2
with:
name: target-${{ matrix.os }}-${{ matrix.java }}-2.13.10-rootNative

- name: Inflate target directories (2.13.10, rootNative)
run: |
tar xf targets.tar
rm targets.tar
- name: Download target directories (3.2.1, rootJS)
uses: actions/download-artifact@v2
with:
Expand All @@ -222,6 +250,16 @@ jobs:
tar xf targets.tar
rm targets.tar
- name: Download target directories (3.2.1, rootNative)
uses: actions/download-artifact@v2
with:
name: target-${{ matrix.os }}-${{ matrix.java }}-3.2.1-rootNative

- name: Inflate target directories (3.2.1, rootNative)
run: |
tar xf targets.tar
rm targets.tar
- name: Import signing key
if: env.PGP_SECRET != '' && env.PGP_PASSPHRASE == ''
run: echo $PGP_SECRET | base64 -di | gpg --import
Expand All @@ -234,7 +272,7 @@ jobs:
(echo "$PGP_PASSPHRASE"; echo; echo) | gpg --command-fd 0 --pinentry-mode loopback --change-passphrase $(gpg --list-secret-keys --with-colons 2> /dev/null | grep '^sec:' | cut --delimiter ':' --fields 5 | tail -n 1)
- name: Publish
run: sbt '++ ${{ matrix.scala }}' tlRelease
run: sbt '++${{ matrix.scala }}' tlRelease

coverage:
name: Generate coverage report (2.13.10 JVM only)
Expand Down Expand Up @@ -286,7 +324,7 @@ jobs:
- name: Start up Postgres
run: docker-compose up -d

- run: sbt '++ ${{ matrix.scala }}' coverage rootJVM/test coverageReport
- run: sbt '++${{ matrix.scala }}' coverage rootJVM/test coverageReport

- name: Upload code coverage data
run: 'bash <(curl -s https://codecov.io/bash)'
75 changes: 32 additions & 43 deletions build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,13 @@ ThisBuild / developers := List(

ThisBuild / tlCiReleaseBranches := Seq("main") // publish snapshits on `main`
ThisBuild / tlSonatypeUseLegacyHost := false
ThisBuild / githubWorkflowOSes := Seq("ubuntu-22.04")
ThisBuild / githubWorkflowJavaVersions := Seq(JavaSpec.temurin("11"))
ThisBuild / tlJdkRelease := Some(8)

ThisBuild / githubWorkflowBuildPreamble ++= nativeBrewInstallWorkflowSteps.value
ThisBuild / nativeBrewInstallCond := Some("matrix.project == 'rootNative'")

lazy val setupCertAndDocker = Seq(
WorkflowStep.Run(
commands = List("chmod 600 world/server.key", "sudo chown 999 world/server.key"),
Expand Down Expand Up @@ -69,8 +73,8 @@ ThisBuild / libraryDependencySchemes ++= Seq(
)

// This is used in a couple places
lazy val fs2Version = "3.3.0"
lazy val natchezVersion = "0.1.6"
lazy val fs2Version = "3.4.0"
lazy val natchezVersion = "0.2.2"

// Global Settings
lazy val commonSettings = Seq(
Expand All @@ -97,45 +101,21 @@ lazy val commonSettings = Seq(
"-sourcepath", (LocalRootProject / baseDirectory).value.getAbsolutePath,
"-doc-source-url", "https://github.com/tpolecat/skunk/blob/v" + version.value + "€{FILE_PATH}.scala",
),
libraryDependencies ++= Seq(
compilerPlugin("org.typelevel" %% "kind-projector" % "0.13.2" cross CrossVersion.full),
).filterNot(_ => tlIsScala3.value),

// Coverage Exclusions
coverageExcludedPackages := "ffstest.*;tests.*;example.*;natchez.http4s.*",

// uncomment in case of emergency
// scalacOptions ++= { if (scalaVersion.value.startsWith("3.")) Seq("-source:3.0-migration") else Nil },

// Add some more source directories
Compile / unmanagedSourceDirectories ++= {
val sourceDir = (Compile / sourceDirectory).value
CrossVersion.partialVersion(scalaVersion.value) match {
case Some((3, _)) => Seq(sourceDir / "scala-2.13+", file(sourceDir.getPath.replaceFirst("jvm", "shared").replaceFirst("js", "shared")) / "scala-2.13+")
case Some((2, 12)) => Seq()
case Some((2, _)) => Seq(sourceDir / "scala-2.13+", file(sourceDir.getPath.replaceFirst("jvm", "shared").replaceFirst("js", "shared")) / "scala-2.13+")
case _ => Seq()
}
},


// dottydoc really doesn't work at all right now
Compile / doc / sources := {
val old = (Compile / doc / sources).value
if (scalaVersion.value.startsWith("3."))
Seq()
else
old
},

)

lazy val skunk = tlCrossRootProject
.settings(name := "skunk")
.aggregate(core, tests, circe, refined, example)
.settings(commonSettings)

lazy val core = crossProject(JVMPlatform, JSPlatform)
lazy val core = crossProject(JVMPlatform, JSPlatform, NativePlatform)
.crossType(CrossType.Full)
.in(file("modules/core"))
.enablePlugins(AutomateHeaderPlugin)
Expand All @@ -153,22 +133,26 @@ lazy val core = crossProject(JVMPlatform, JSPlatform)
"org.scodec" %%% "scodec-core" % (if (tlIsScala3.value) "2.2.0" else "1.11.10"),
"org.scodec" %%% "scodec-cats" % "1.2.0",
"org.tpolecat" %%% "natchez-core" % natchezVersion,
"org.tpolecat" %%% "sourcepos" % "1.0.1",
"org.tpolecat" %%% "sourcepos" % "1.1.0",
"org.scala-lang.modules" %%% "scala-collection-compat" % "2.8.1",
) ++ Seq(
"com.beachape" %%% "enumeratum" % "1.6.1",
).filterNot(_ => tlIsScala3.value)
)
).jvmSettings(
libraryDependencies += "com.ongres.scram" % "client" % "2.1",
).jsSettings(
)
.platformsSettings(JVMPlatform, JSPlatform)(
libraryDependencies ++= Seq(
"com.beachape" %%% "enumeratum" % "1.6.1",
).filterNot(_ => tlIsScala3.value)
)
.platformsSettings(JSPlatform, NativePlatform)(
libraryDependencies ++= Seq(
"com.armanbilge" %%% "saslprep" % "0.1.1",
"io.github.cquiroz" %%% "scala-java-time" % "2.4.0",
"io.github.cquiroz" %%% "locales-minimal-en_us-db" % "1.4.1"
),
)

lazy val refined = crossProject(JVMPlatform, JSPlatform)
lazy val refined = crossProject(JVMPlatform, JSPlatform, NativePlatform)
.crossType(CrossType.Pure)
.in(file("modules/refined"))
.dependsOn(core)
Expand All @@ -180,7 +164,7 @@ lazy val refined = crossProject(JVMPlatform, JSPlatform)
)
)

lazy val circe = crossProject(JVMPlatform, JSPlatform)
lazy val circe = crossProject(JVMPlatform, JSPlatform, NativePlatform)
.crossType(CrossType.Pure)
.in(file("modules/circe"))
.dependsOn(core)
Expand All @@ -194,7 +178,7 @@ lazy val circe = crossProject(JVMPlatform, JSPlatform)
)
)

lazy val tests = crossProject(JVMPlatform, JSPlatform)
lazy val tests = crossProject(JVMPlatform, JSPlatform, NativePlatform)
.crossType(CrossType.Full)
.in(file("modules/tests"))
.dependsOn(core, circe)
Expand All @@ -203,21 +187,26 @@ lazy val tests = crossProject(JVMPlatform, JSPlatform)
.settings(
scalacOptions -= "-Xfatal-warnings",
libraryDependencies ++= Seq(
"org.scalameta" %%% "munit" % "0.7.29",
"org.scalameta" % "junit-interface" % "0.7.29",
"org.typelevel" %%% "scalacheck-effect-munit" % "1.0.4",
"org.typelevel" %%% "munit-cats-effect-3" % "1.0.7",
"org.scalameta" %%% "munit" % "1.0.0-M6",
"org.scalameta" % "junit-interface" % "1.0.0-M6",
"org.typelevel" %%% "scalacheck-effect-munit" % "2.0.0-M2",
"org.typelevel" %%% "munit-cats-effect" % "2.0.0-M3",
"org.typelevel" %%% "cats-free" % "2.9.0",
"org.typelevel" %%% "cats-laws" % "2.9.0",
"org.typelevel" %%% "discipline-munit" % "1.0.9",
) ++ Seq(
"io.chrisdavenport" %%% "cats-time" % "0.3.4",
).filterNot(_ => scalaVersion.value.startsWith("3.")),
"org.typelevel" %%% "discipline-munit" % "2.0.0-M3",
"org.typelevel" %%% "cats-time" % "0.5.1",
),
testFrameworks += new TestFramework("munit.Framework")
)
.jsSettings(
Test / scalaJSLinkerConfig ~= (_.withModuleKind(ModuleKind.CommonJSModule)),
)
.nativeEnablePlugins(ScalaNativeBrewedConfigPlugin)
.nativeSettings(
libraryDependencies += "com.armanbilge" %%% "epollcat" % "0.0-ab1026e",
Test / nativeBrewFormulas ++= Set("s2n", "utf8proc"),
Test / envVars ++= Map("S2N_DONT_MLOCK" -> "1")
)

lazy val example = project
.in(file("modules/example"))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,13 @@ import com.armanbilge.SaslPrep
import scodec.bits.ByteVector
import scodec.codecs.utf8

import scala.scalajs.js
import scala.scalajs.js.typedarray.Uint8Array

/**
* Partial implementation of [RFC5802](https://tools.ietf.org/html/rfc5802), as needed by PostgreSQL.
*
* That is, only features used by PostgreSQL are implemented -- e.g., channel binding is not supported and
* optional message fields omitted by PostgreSQL are not supported.
*/
private[skunk] object Scram {
private[skunk] object Scram extends ScramPlatform {
val SaslMechanism = "SCRAM-SHA-256"

val NoChannelBinding = ByteVector.view("n,,".getBytes)
Expand All @@ -26,11 +23,6 @@ private[skunk] object Scram {
def bytesUtf8: ByteVector = ByteVector.view(value.getBytes(java.nio.charset.StandardCharsets.UTF_8))
}

def clientFirstBareWithRandomNonce: ByteVector = {
val nonce = ByteVector.view(crypto.randomBytes(32).asInstanceOf[Uint8Array]).toBase64
clientFirstBareWithNonce(nonce)
}

def clientFirstBareWithNonce(nonce: String): ByteVector =
s"n=,r=${nonce}".bytesUtf8

Expand Down Expand Up @@ -69,27 +61,6 @@ private[skunk] object Scram {
}
}

private val crypto = js.Dynamic.global.require("crypto")

private def HMAC(key: ByteVector, str: ByteVector): ByteVector = {
val mac = crypto.createHmac("sha256", key.toUint8Array)
mac.update(str.toUint8Array)
ByteVector.view(mac.digest().asInstanceOf[Uint8Array])
}

private def H(input: ByteVector): ByteVector = {
val hash = crypto.createHash("sha256")
hash.update(input.toUint8Array)
ByteVector.view(hash.digest().asInstanceOf[Uint8Array])
}

private def Hi(str: String, salt: ByteVector, iterations: Int): ByteVector = {
// TODO It is unfortunate that we have to use a sync API here when an async is available
// To make the change here will require running an F[_]: Async up the hiearchy
val salted = crypto.pbkdf2Sync(str, salt.toUint8Array, iterations, 8 * 32, "sha256")
ByteVector.view(salted.asInstanceOf[Uint8Array]).take(32)
}

private def makeClientProofAndServerSignature(password: String, salt: ByteVector, iterations: Int, clientFirstMessageBare: ByteVector, serverFirstMessage: ByteVector, clientFinalMessageWithoutProof: ByteVector): (ClientProof, Verifier) = {
val saltedPassword = Hi(SaslPrep.saslPrepStored(password), salt, iterations)
val clientKey = HMAC(saltedPassword, "Client Key".bytesUtf8)
Expand Down
Loading

0 comments on commit 456ade1

Please sign in to comment.