diff --git a/.github/workflows/docker-ci.yaml b/.github/workflows/docker-ci.yaml index ef9b233..db7be94 100644 --- a/.github/workflows/docker-ci.yaml +++ b/.github/workflows/docker-ci.yaml @@ -5,13 +5,66 @@ on: - .github/workflows/docker-ci.yaml - .github/scripts/** - build.sbt + - src/** env: GITHUB_TOKEN: ${{ secrets.READ_PACKAGES }} jobs: - build: + build-and-test: + name: Build & Test runs-on: self-hosted steps: - uses: actions/checkout@v4 - - run: .github/scripts/dnd-sbt docker/Docker/publishLocal + + - name: Build Docker image + run: | + .github/scripts/dnd-sbt Docker/publishLocal + IMAGE_NAME=$(.github/scripts/dnd-sbt printDockerImageName | grep DOCKER_IMAGE | cut -d= -f2) + echo "IMAGE=${IMAGE_NAME}" >> $GITHUB_ENV + + - name: Test image - run container + run: | + CONTAINER_ID=$(docker run -d -p 50051 ${IMAGE}) + echo "CONTAINER_ID=${CONTAINER_ID}" >> $GITHUB_ENV + HOST_PORT=$(docker port ${CONTAINER_ID} 50051 | cut -d':' -f2) + echo "HOST_PORT=${HOST_PORT}" >> $GITHUB_ENV + sleep 15 + + - name: Test image - verify service is running + run: | + nc -z localhost ${HOST_PORT} + if [ $? -ne 0 ]; then + echo "Service check failed!" + exit 1 + fi + + - name: Cleanup container + if: always() + run: | + if [ ! -z "${CONTAINER_ID}" ]; then + docker stop ${CONTAINER_ID} + docker rm ${CONTAINER_ID} + fi + + security-scan: + name: Security Scan + runs-on: self-hosted + steps: + - uses: actions/checkout@v4 + + - name: Build Docker image + run: | + .github/scripts/dnd-sbt Docker/publishLocal + IMAGE_NAME=$(.github/scripts/dnd-sbt printDockerImageName | grep DOCKER_IMAGE | cut -d= -f2) + echo "IMAGE=${IMAGE_NAME}" >> $GITHUB_ENV + + - name: Run Trivy vulnerability scanner + uses: aquasecurity/trivy-action@master + with: + image-ref: ${{ env.IMAGE }} + format: 'table' + exit-code: '1' + ignore-unfixed: true + vuln-type: 'os,library' + severity: 'CRITICAL,HIGH' diff --git a/.github/workflows/publish.yaml b/.github/workflows/publish.yaml index 124f1e9..0e76e7e 100644 --- a/.github/workflows/publish.yaml +++ b/.github/workflows/publish.yaml @@ -36,7 +36,7 @@ jobs: password: ${{ secrets.WRITE_PACKAGES }} logout: false - name: publish docker images - run: .github/scripts/dnd-sbt docker/Docker/publish + run: .github/scripts/dnd-sbt Docker/publish - name: set should_trigger_deploy id: should_trigger_deploy shell: bash diff --git a/build.sbt b/build.sbt index fd933b5..f5f0dc9 100644 --- a/build.sbt +++ b/build.sbt @@ -43,7 +43,9 @@ lazy val compileSettings = Seq( Compile / packageBin / packageOptions += Package.ManifestAttributes( "Automatic-Module-Name" -> name.value.replace('-', '.')), // Ensure Java annotations get compiled first, so that they are accessible from Scala. - compileOrder := CompileOrder.JavaThenScala) + compileOrder := CompileOrder.JavaThenScala, + Compile / mainClass := Some("com.rawlabs.das.server.DASServer") + ) lazy val testSettings = Seq( // Ensuring tests are run in a forked JVM for isolation. @@ -80,6 +82,7 @@ lazy val strictBuildSettings = commonSettings ++ compileSettings ++ buildSettings ++ testSettings ++ Seq(scalacOptions ++= Seq("-Xfatal-warnings")) lazy val root = (project in file(".")) + .enablePlugins(JavaAppPackaging, DockerPlugin) .settings( name := "das-databricks", strictBuildSettings, @@ -89,48 +92,26 @@ lazy val root = (project in file(".")) "com.raw-labs" %% "protocol-das" % "1.0.0" % "compile->compile;test->test", "com.raw-labs" %% "das-server-scala" % "0.4.1" % "compile->compile;test->test", // Databricks - "com.databricks" % "databricks-sdk-java" % "0.41.0" % "compile->compile")) - -val amzn_jdk_version = "21.0.4.7-1" -val amzn_corretto_bin = s"java-21-amazon-corretto-jdk_${amzn_jdk_version}_amd64.deb" -val amzn_corretto_bin_dl_url = s"https://corretto.aws/downloads/resources/${amzn_jdk_version.replace('-', '.')}" + "com.databricks" % "databricks-sdk-java" % "0.41.0" % "compile->compile"), + dockerSettings + ) -lazy val dockerSettings = strictBuildSettings ++ Seq( - name := "das-databricks-server", - dockerBaseImage := s"--platform=amd64 debian:bookworm-slim", +lazy val dockerSettings = Seq( + Docker/ packageName := "das-databricks-server", + dockerBaseImage := "eclipse-temurin:21-jre", dockerLabels ++= Map( "vendor" -> "RAW Labs SA", "product" -> "das-databricks-server", "image-type" -> "final", "org.opencontainers.image.source" -> "https://github.com/raw-labs/das-databricks"), Docker / daemonUser := "raw", + Docker / daemonUserUid := Some("1001"), + Docker / daemonGroup := "raw", + Docker / daemonGroupGid := Some("1001"), dockerExposedVolumes := Seq("/var/log/raw"), dockerExposedPorts := Seq(50051), dockerEnvVars := Map("PATH" -> s"${(Docker / defaultLinuxInstallLocation).value}/bin:$$PATH"), - // We remove the automatic switch to USER 1001:0. - // We we want to run as root to install the JDK, also later we will switch to a non-root user. - dockerCommands := dockerCommands.value.filterNot { - case Cmd("USER", args @ _*) => args.contains("1001:0") - case cmd => false - }, - dockerCommands ++= Seq( - Cmd( - "RUN", - s"""set -eux \\ - && apt-get update \\ - && apt-get install -y --no-install-recommends \\ - curl wget ca-certificates gnupg software-properties-common fontconfig java-common \\ - && wget $amzn_corretto_bin_dl_url/$amzn_corretto_bin \\ - && dpkg --install $amzn_corretto_bin \\ - && rm -f $amzn_corretto_bin \\ - && apt-get purge -y --auto-remove -o APT::AutoRemove::RecommendsImportant=false \\ - wget gnupg software-properties-common"""), - Cmd("USER", "raw")), dockerEnvVars += "LANG" -> "C.UTF-8", - dockerEnvVars += "JAVA_HOME" -> "/usr/lib/jvm/java-21-amazon-corretto", - Compile / doc / sources := Seq.empty, // Do not generate scaladocs - // Skip docs to speed up build - Compile / packageDoc / mappings := Seq(), updateOptions := updateOptions.value.withLatestSnapshots(true), Linux / linuxPackageMappings += packageTemplateMapping(s"/var/lib/${packageName.value}")(), bashScriptDefines := { @@ -158,8 +139,6 @@ lazy val dockerSettings = strictBuildSettings ++ Seq( } case lm @ _ => lm }, - Compile / mainClass := Some("com.rawlabs.das.server.DASServer"), - Docker / dockerAutoremoveMultiStageIntermediateImages := false, dockerAlias := dockerAlias.value.withTag(Option(version.value.replace("+", "-"))), dockerAliases := { val devRegistry = sys.env.getOrElse("DEV_REGISTRY", "ghcr.io/raw-labs/das-databricks") @@ -172,11 +151,11 @@ lazy val dockerSettings = strictBuildSettings ++ Seq( } }) -lazy val docker = (project in file("docker")) - .dependsOn(root % "compile->compile;test->test") - .enablePlugins(JavaAppPackaging, DockerPlugin) - .settings( - strictBuildSettings, - dockerSettings, - libraryDependencies ++= Seq("com.raw-labs" %% "das-server-scala" % "0.4.1" % "compile->compile;test->test") - ) +lazy val printDockerImageName = taskKey[Unit]("Prints the full Docker image name that will be produced") + +printDockerImageName := { + // Get the main Docker alias (the first one in the sequence) + val alias = (Docker / dockerAliases).value.head + // The toString method already returns the full image name with registry and tag + println(s"DOCKER_IMAGE=${alias}") +} diff --git a/project/plugins.sbt b/project/plugins.sbt index ab3f73c..b852ee6 100755 --- a/project/plugins.sbt +++ b/project/plugins.sbt @@ -8,7 +8,7 @@ addSbtPlugin("org.scalameta" % "sbt-scalafmt" % "2.4.0") addSbtPlugin("nl.gn0s1s" % "sbt-dotenv" % "3.1.1") -addSbtPlugin("com.github.sbt" % "sbt-native-packager" % "1.10.4") +addSbtPlugin("com.github.sbt" % "sbt-native-packager" % "1.11.1") addSbtPlugin("de.heikoseeberger" % "sbt-header" % "5.10.0")