From d773a3010410777680949725910c11fc7586f282 Mon Sep 17 00:00:00 2001 From: Chunsen Wang Date: Fri, 2 Feb 2024 14:25:51 +0800 Subject: [PATCH 01/11] enhance plugin --- .github/workflows/release.yml | 78 ++++++ .gitignore | 2 + .scalafmt.conf | 11 + README.md | 2 +- build.sbt | 71 ++--- project/Repo.scala | 22 ++ project/Versions.scala | 6 + project/artifactory.sbt | 23 ++ project/plugins.sbt | 3 +- scripts/github-actions/release.sh | 5 + .../tototoshi/sbt/slick/CodegenPlugin.scala | 252 ++++++++++-------- .../tototoshi/sbt/slick/PluginDBSupport.scala | 53 ++++ src/main/scala/com/tubitv/CodeGenConfig.scala | 10 + .../com/tubitv/CodeGenPostgresProfile.scala | 54 ++++ .../tubitv/CustomizeSourceCodeGenerator.scala | 28 ++ .../scala/com/tubitv/PostgresContainer.scala | 68 +++++ 16 files changed, 518 insertions(+), 170 deletions(-) create mode 100644 .github/workflows/release.yml create mode 100644 .scalafmt.conf create mode 100644 project/Repo.scala create mode 100644 project/Versions.scala create mode 100644 project/artifactory.sbt create mode 100755 scripts/github-actions/release.sh create mode 100644 src/main/scala/com/github/tototoshi/sbt/slick/PluginDBSupport.scala create mode 100644 src/main/scala/com/tubitv/CodeGenConfig.scala create mode 100644 src/main/scala/com/tubitv/CodeGenPostgresProfile.scala create mode 100644 src/main/scala/com/tubitv/CustomizeSourceCodeGenerator.scala create mode 100644 src/main/scala/com/tubitv/PostgresContainer.scala diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..269d784 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,78 @@ +name: Release + +on: + workflow_dispatch: + inputs: + TUBI_PROJECT_VERSION: + description: 'semantic version part to bump, either patch, minor or major' + required: true + default: 'patch' + release: + types: + - published + +# can maybe test via https://github.com/nektos/act +# act -s DOCKER_USERNAME=abc -s DOCKER_PASSWORD=xyz --container-architecture linux/arm64 -W .github/workflows/release.yml release +jobs: + release: + runs-on: ubuntu-20.04 + steps: + - name: Dump GitHub context + env: + GITHUB_CONTEXT: ${{ toJson(github) }} + run: echo "$GITHUB_CONTEXT" +# - name: Set build info +# id: build_info +# run: | +# if [ ${{ github.event_name }} == 'workflow_dispatch' ]; then +# echo "tubi-project-name=${{ github.event.inputs.TUBI_PROJECT_NAME }}" >> "$GITHUB_OUTPUT" +# echo "tubi-project-version=${{ github.event.inputs.TUBI_PROJECT_VERSION }}" >> "$GITHUB_OUTPUT" +# else +# tag="${{ github.event.release.tag_name }}" +# name="${tag%-v[0-9]*\.[0-9]*\.[0-9]*}" +# echo "tubi-project-name=${name}" >> "$GITHUB_OUTPUT" +# echo "tubi-project-version=${tag##[a-z]*-v}" >> "$GITHUB_OUTPUT" +# fi + + - name: Configure git user to push + run: | + git config --global user.email "sbt@tubi.tv" + git config --global user.name "sbt" + + - name: Clone and checkout to current branch + uses: actions/checkout@v3.5.2 + with: + fetch-depth: 0 + + - name: Set up JDK 11 # it auto caches https://github.com/actions/setup-java#caching-packages-dependencies + uses: actions/setup-java@v3.11.0 + with: + java-version: '11' + distribution: 'temurin' + cache: 'sbt' + + - name: Generate artifact + run: ./scripts/github-action/release.sh + env: + TUBI_PROJECT_NAME: ${{ steps.build_info.outputs.tubi-project-name }} + TUBI_PROJECT_VERSION: ${{ steps.build_info.outputs.tubi-project-version }} + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + ARTIFACTORY_USERNAME: ${{ secrets.ARTIFACTORY_USERNAME }} + ARTIFACTORY_PASSWORD: ${{ secrets.ARTIFACTORY_PASSWORD }} + AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} + AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + AWS_ACCOUNT_ID: 370025973162 + AWS_REGION: us-east-2 + +# - name: notify build status +# if: always() +# uses: slackapi/slack-github-action@v1.23.0 +# with: +# # Slack channel id, channel name, or user id to post message. +# # See also: https://api.slack.com/methods/chat.postMessage#channels +# # You can pass in multiple channels to post to by providing a comma-delimited list of channel IDs. +# channel-id: ${{ github.event.repository.name }}-cicd +# # For posting a simple plain text message, no md just for link shortening +# slack-message: "${{ steps.build_info.outputs.tubi-project-name }}-v${{ steps.build_info.outputs.tubi-project-version }} release ${{ job.status }}: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}" +# env: +# SLACK_BOT_TOKEN: ${{ secrets.BUILD_NOTIFY_SLACK_APP_TOKEN }} diff --git a/.gitignore b/.gitignore index 93f7430..34a77d8 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,5 @@ /project/project/ /project/target/ /target/ +/.idea/ +/.bsp/ diff --git a/.scalafmt.conf b/.scalafmt.conf new file mode 100644 index 0000000..3f81bd6 --- /dev/null +++ b/.scalafmt.conf @@ -0,0 +1,11 @@ +version = "3.4.3" +runner.dialect = scala213 +preset = IntelliJ +maxColumn = 120 +docstrings.style = SpaceAsterisk +docstrings.wrap = "no" +newlines.beforeCurlyLambdaParams = multiline +rewrite.rules = [Imports] +rewrite.imports.sort = scalastyle +rewrite.imports.groups = [["sbt\\..*"], ["java\\..*", "javax\\..*"], ["scala\\..*"]] +rewrite.trailingCommas.style = keep diff --git a/README.md b/README.md index a11c205..28c1a1f 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ [![CI](https://github.com/tototoshi/sbt-slick-codegen/actions/workflows/ci.yml/badge.svg)](https://github.com/tototoshi/sbt-slick-codegen/actions/workflows/ci.yml) -slick-codegen compile hook for sbt +slick-codegen plugin, forked from [sbt-slick-codegen](https://github.com/tototoshi/sbt-slick-codegen), which aims to generate code specially for Postgres ## Install diff --git a/build.sbt b/build.sbt index 5cf5277..86213c2 100644 --- a/build.sbt +++ b/build.sbt @@ -1,69 +1,34 @@ -import scalariform.formatter.preferences._ -import scala.collection.JavaConverters._ import java.lang.management.ManagementFactory +import scala.collection.JavaConverters.* +//import scalariform.formatter.preferences.* + enablePlugins(SbtPlugin) -scalariformPreferences := scalariformPreferences.value - .setPreference(AlignSingleLineCaseStatements, true) - .setPreference(DoubleIndentConstructorArguments, true) - .setPreference(DanglingCloseParenthesis, Preserve) +//scalariformPreferences := scalariformPreferences.value +// .setPreference(AlignSingleLineCaseStatements, true) +// .setPreference(DoubleIndentConstructorArguments, true) +// .setPreference(DanglingCloseParenthesis, Preserve) sbtPlugin := true - name := """sbt-slick-codegen""" - -organization := "com.github.tototoshi" - -version := "2.0.0" - -crossSbtVersions := Seq("1.8.0") - -val slickVersion = SettingKey[String]("slickVersion") - -slickVersion := "3.3.3" +organization := "com.tubitv" +version := "0.0.1-SNAPSHOT" libraryDependencies ++= Seq( - "com.typesafe.slick" %% "slick" % slickVersion.value, - "com.typesafe.slick" %% "slick-codegen" % slickVersion.value + "com.typesafe.slick" %% "slick" % Versions.slick, + "com.typesafe.slick" %% "slick-codegen" % Versions.slick, + "org.postgresql" % "postgresql" % Versions.postgresql, + "com.github.docker-java" % "docker-java" % Versions.dockerJava, ) +addSbtPlugin("io.github.davidmweber" % "flyway-sbt" % Versions.flywaySbt) -publishMavenStyle := true - -publishTo := { - val nexus = "https://oss.sonatype.org/" - if (version.value.trim.endsWith("SNAPSHOT")) Some("snapshots" at nexus + "content/repositories/snapshots") - else Some("releases" at nexus + "service/local/staging/deploy/maven2") -} +publishTo := Some(if (isSnapshot.value) Repo.Jfrog.Tubins.sbtDev else Repo.Jfrog.Tubins.sbtRelease) Test / publishArtifact := false -pomExtra := - https://github.com/tototoshi/sbt-slick-codegen - - - Apache License, Version 2.0 - https://www.apache.org/licenses/LICENSE-2.0.html - repo - - - - git@github.com:tototoshi/sbt-slick-codegen - scm:git:git@github.com:tototoshi/sbt-slick-codegen.git - - - - tototoshi - Toshiyuki Takahashi - https://tototoshi.github.io - - - scriptedBufferLog := false -scriptedLaunchOpts ++= ManagementFactory.getRuntimeMXBean.getInputArguments.asScala.toList.filter(a => - Seq("-Xmx", "-Xms", "-XX", "-Dsbt.log.noformat").exists(a.startsWith) -) -scriptedLaunchOpts ++= Seq( - "-Dplugin.version=" + version.value, - "-Dslick.version=" + slickVersion.value +scriptedLaunchOpts ++= ManagementFactory.getRuntimeMXBean.getInputArguments.asScala.toList.filter( + a => Seq("-Xmx", "-Xms", "-XX", "-Dsbt.log.noformat").exists(a.startsWith) ) +scriptedLaunchOpts ++= Seq("-Dplugin.version=" + version.value, "-Dslick.version=" + Versions.slick) diff --git a/project/Repo.scala b/project/Repo.scala new file mode 100644 index 0000000..5938304 --- /dev/null +++ b/project/Repo.scala @@ -0,0 +1,22 @@ +import sbt.* + +object Repo { + + object Jfrog { + private val domain = "tubins.jfrog.io" + private val jFrogRoot = s"https://$domain" + + object Tubins { + private val pathPrefix = "tubins" + + lazy val sbtDev: MavenRepository = "sbt-dev" at s"$jFrogRoot/$pathPrefix/sbt-dev" + + lazy val sbtRelease: MavenRepository = "sbt-release" at s"$jFrogRoot/$pathPrefix/sbt-release" + + lazy val jvmSnapshot: MavenRepository = "jvm-snapshot" at s"$jFrogRoot/$pathPrefix/jvm-snapshots" + + lazy val jvm: MavenRepository = "jvm-release" at s"$jFrogRoot/$pathPrefix/jvm" + } + } + +} diff --git a/project/Versions.scala b/project/Versions.scala new file mode 100644 index 0000000..ac69567 --- /dev/null +++ b/project/Versions.scala @@ -0,0 +1,6 @@ +object Versions { + val slick = "3.4.0" + val postgresql = "42.6.0" + val dockerJava = "3.3.4" + val flywaySbt = "7.4.0" +} \ No newline at end of file diff --git a/project/artifactory.sbt b/project/artifactory.sbt new file mode 100644 index 0000000..c3af7a4 --- /dev/null +++ b/project/artifactory.sbt @@ -0,0 +1,23 @@ +ThisBuild / credentials ++= { + val logger = streams.value.log + if (sys.env.contains("ARTIFACTORY_USERNAME")) { + logger.info("spotted credential in env, will add to credentials") + Some( + Credentials( + "Artifactory Realm", + "tubins.jfrog.io", + sys.env.getOrElse("ARTIFACTORY_USERNAME", ""), + sys.env.getOrElse("ARTIFACTORY_PASSWORD", "") + ) + ) + } else { + Credentials.loadCredentials(Path.userHome / ".artifactory" / "credentials") match { + case Right(credentials: DirectCredentials) => + logger.info(s"Using credentials found in the home directory for host ${credentials.host}") + Some(credentials) + case Left(err: String) => + logger.warn(s"Could not find artifactory credentials in home directory: $err") + None + } + } +} \ No newline at end of file diff --git a/project/plugins.sbt b/project/plugins.sbt index b6cb072..c186428 100644 --- a/project/plugins.sbt +++ b/project/plugins.sbt @@ -1,3 +1,4 @@ -addSbtPlugin("org.scalariform" % "sbt-scalariform" % "1.8.3") +//addSbtPlugin("org.scalariform" % "sbt-scalariform" % "1.8.3") addSbtPlugin("org.xerial.sbt" % "sbt-sonatype" % "3.9.18") addSbtPlugin("com.github.sbt" % "sbt-pgp" % "2.2.1") +addSbtPlugin("com.github.gseitz" % "sbt-release" % "1.0.11") diff --git a/scripts/github-actions/release.sh b/scripts/github-actions/release.sh new file mode 100755 index 0000000..31fd37c --- /dev/null +++ b/scripts/github-actions/release.sh @@ -0,0 +1,5 @@ +#!/usr/bin/env bash + +SBT_JVM_OPTS="-J-Xms4g -J-Xmx4g -J-Xss8m -J-XX:MaxMetaspaceSize=1024m -J-XX:+UseG1GC" + +sbt $SBT_JVM_OPTS "release release-version $TUBI_PROJECT_VERSION with-defaults"; \ No newline at end of file diff --git a/src/main/scala/com/github/tototoshi/sbt/slick/CodegenPlugin.scala b/src/main/scala/com/github/tototoshi/sbt/slick/CodegenPlugin.scala index 1796156..900eed4 100644 --- a/src/main/scala/com/github/tototoshi/sbt/slick/CodegenPlugin.scala +++ b/src/main/scala/com/github/tototoshi/sbt/slick/CodegenPlugin.scala @@ -1,29 +1,33 @@ package com.github.tototoshi.sbt.slick -import sbt._ -import Keys._ -import slick.codegen.SourceCodeGenerator -import slick.jdbc.JdbcProfile -import slick.{ model => m } +import sbt.* +import scala.collection.mutable.ListBuffer import scala.concurrent.Await import scala.concurrent.ExecutionContext.Implicits.global import scala.concurrent.duration.Duration -import scala.collection.mutable.ListBuffer +import scala.io.Source +import scala.util.Using -object CodegenPlugin extends sbt.AutoPlugin { +import Keys.* +import com.tubitv.{CodeGenConfig, CodeGenPostgresProfile, CustomizeSourceCodeGenerator, PostgresContainer} +import slick.{model => m} +import slick.codegen.SourceCodeGenerator +import slick.dbio.{DBIOAction, Effect, NoStream} +import slick.jdbc.JdbcProfile + +object CodegenPlugin extends sbt.AutoPlugin with PluginDBSupport { + private val createdConfigs = + settingKey[Set[Configuration]]("The configurations for the config, other than default") object autoImport { lazy val slickCodegen: TaskKey[Seq[File]] = taskKey[Seq[File]]("Command to run codegen") + lazy val slickCodegenAll: TaskKey[Unit] = taskKey[Unit]("Command to run all the codegen") + lazy val slickCodegenVerify: TaskKey[Unit] = taskKey("Verify if the generated code is out of date") + lazy val slickCodegenVerifyAll: TaskKey[Unit] = taskKey("Verify any of the generated code is out of date") - lazy val slickCodegenDatabaseUrl: SettingKey[String] = - settingKey[String]("URL of database used by codegen") - - lazy val slickCodegenDatabaseUser: SettingKey[String] = - settingKey[String]("User of database used by codegen") - - lazy val slickCodegenDatabasePassword: SettingKey[String] = - settingKey[String]("Password of database used by codegen") + lazy val slickCodegenConfig: SettingKey[CodeGenConfig] = + settingKey("The configuration for the code generator") lazy val slickCodegenDriver: SettingKey[JdbcProfile] = settingKey[JdbcProfile]("Slick driver used by codegen") @@ -57,33 +61,30 @@ object CodegenPlugin extends sbt.AutoPlugin { "Tables that should be included. If this list is not nil, only the included tables minus excluded will be taken." ) - lazy val defaultSourceCodeGenerator: m.Model => SourceCodeGenerator = (model: m.Model) => - new SourceCodeGenerator(model) - - @deprecated("use enablePlugins(CodegenPlugin)", "") - lazy val slickCodegenSettings: Seq[Setting[_]] = projectSettings + /** Define a new configuration scope for slick code gen, + * for the cases where there are multiple codegen in a project + * @param configName + * @param setting + * @return + */ + def codeGen(configName: String)(setting: Setting[_]*): Seq[Setting[_]] = { + val theConfig = Configuration.of(configName.take(1).toUpperCase() + configName.drop(1), configName) + Seq(createdConfigs += theConfig) ++ inConfig(theConfig)(defaultConfigs ++ setting) + } } import autoImport._ - private def gen( - generator: m.Model => SourceCodeGenerator, - driver: JdbcProfile, - jdbcDriver: String, - url: String, - user: String, - password: String, - outputDir: String, - pkg: String, - fileName: String, - outputToMultipleFiles: Boolean, - container: String, - excluded: Seq[String], - included: Seq[String], - s: TaskStreams - ): Seq[File] = { - - val database = driver.api.Database.forURL(url = url, driver = jdbcDriver, user = user, password = password) + private def withGenerator[T](fun: SourceCodeGenerator => T) = Def.task { + val generator = slickCodegenCodeGenerator.value + val driver = slickCodegenDriver.value + + val database = driver.api.Database.forURL( + url = postgresDbUrl.value, + driver = slickCodegenJdbcDriver.value, + user = dbUser, + password = dbPass + ) try { database.source.createConnection().close() @@ -92,107 +93,128 @@ object CodegenPlugin extends sbt.AutoPlugin { throw new RuntimeException("Failed to run slick-codegen: " + e.getMessage, e) } - s.log.info(s"Generate source code with slick-codegen: url=${url}, user=${user}") + val excluded = slickCodegenExcludedTables.value + val included = slickCodegenIncludedTables.value val tables = driver.defaultTables .map(ts => ts.filter(t => included.isEmpty || (included contains t.name.name))) .map(ts => ts.filterNot(t => excluded contains t.name.name)) - val driverClassName = driver.getClass.getName - val profile = { - // if it's a singleton object, then just reference it directly - if (driverClassName.endsWith("$")) driverClassName.stripSuffix("$") - // if it's an instance of a regular class, we don't know constructor args; try the no-arguments constructor and hope for the best - else s"new $driverClassName()" - } - - val dbio = for { + val dbio: DBIOAction[T, NoStream, Effect.All] = for { m <- driver.createModel(Some(tables)) - } yield { - val sourceGen = generator(m) - if (outputToMultipleFiles) { - sourceGen.writeToMultipleFiles( - profile = profile, - folder = outputDir, - pkg = pkg, - container = container - ) + } yield fun(generator(m)) + + Await.result(database.run(dbio), Duration.Inf) + } + + private def profile = Def.task { + val driverClassName = slickCodegenDriver.value.getClass.getName + // if it's a singleton object, then just reference it directly + if (driverClassName.endsWith("$")) driverClassName.stripSuffix("$") + // if it's an instance of a regular class, we don't know constructor args; try the no-arguments constructor and hope for the best + else s"new $driverClassName()" + } + + private def gen = Def.taskDyn { + val p = profile.value + val outDir = { + val folder = slickCodegenOutputDir.value + + if (folder.exists()) { + require(folder.isDirectory, s"file :[$folder] is not a directory") } else { - sourceGen.writeToFile( - profile = profile, - folder = outputDir, - pkg = pkg, - container = container, - fileName = fileName - ) + folder.mkdir() } + folder.getPath } + val pkg = slickCodegenOutputPackage.value + val fileName = slickCodegenOutputFile.value + val container = slickCodegenOutputContainer.value + + val s = streams.value + val outputToMultipleFiles = slickCodegenOutputToMultipleFiles.value + + Def.taskDyn { + val _ = withGenerator { + sourceGen => + if (outputToMultipleFiles) { + sourceGen.writeToMultipleFiles(profile = p, folder = outDir, pkg = pkg, container = container) + } else { + sourceGen.writeToFile(profile = p, folder = outDir, pkg = pkg, container = container, fileName = fileName) + } + }.value + + Def.task { + if (outputToMultipleFiles) { + val outDirFile = file(outDir) + s.log.info(s"Source code files have been generated in ${outDirFile.getAbsolutePath}") + listScalaFileRecursively(outDirFile) + } else { + val generatedFile = outDir + "/" + pkg.replaceAllLiterally(".", "/") + "/" + fileName + s.log.info(s"Source code has generated in ${generatedFile}") + Seq(file(generatedFile)) + } + } + } + } - Await.result(database.run(dbio), Duration.Inf) - - if (outputToMultipleFiles) { - val outDir = file(outputDir) - s.log.info(s"Source code files have been generated in ${outDir.getAbsolutePath}") - listScalaFileRecursively(outDir) - } else { - val generatedFile = outputDir + "/" + pkg.replaceAllLiterally(".", "/") + "/" + fileName - s.log.info(s"Source code has generated in ${generatedFile}") - Seq(file(generatedFile)) + private def verify = Def.taskDyn { + val p = profile.value + val pkg = slickCodegenOutputPackage.value + val container = slickCodegenOutputContainer.value + + val theConf = configuration.?.value + val s = streams.value + + withGenerator { + sourceGen => + val theCode = sourceGen.packageCode(profile = p, pkg = pkg, container = container, sourceGen.parentType) + val file = slickCodegenOutputDir.value + "/" + pkg.replace(".", "/") + "/" + slickCodegenOutputFile.value + val generated = Using(Source.fromFile(file))(_.mkString).get + if (theCode.trim != generated.trim) { + throw new Exception( + s"Schema file: $file is out of date, please regenerate it by [ ${theConf.map(c => c.name + " / ").getOrElse("")}slickCodegen ]" + ) + } + s.log.info(s"Verify schema $file success") } } - override lazy val projectSettings: Seq[Setting[_]] = Seq( - slickCodegenDriver := slick.jdbc.PostgresProfile, + private def defaultConfigs = Seq( + slickCodegenConfig := CodeGenConfig(), + slickCodegenDriver := new CodeGenPostgresProfile(slickCodegenConfig.value), slickCodegenJdbcDriver := "org.postgresql.Driver", - slickCodegenDatabaseUrl := "Database url is not set", - slickCodegenDatabaseUser := "Database user is not set", - slickCodegenDatabasePassword := "Database password is not set", slickCodegenOutputPackage := "com.example", - slickCodegenOutputFile := "Tables.scala", + slickCodegenOutputFile := s"${slickCodegenOutputContainer.value}.scala", slickCodegenOutputToMultipleFiles := false, - slickCodegenOutputDir := (Compile / sourceManaged).value, + slickCodegenOutputDir := (Compile / Keys.scalaSource).value, slickCodegenOutputContainer := "Tables", slickCodegenExcludedTables := Seq(), slickCodegenIncludedTables := Seq(), - slickCodegenCodeGenerator := defaultSourceCodeGenerator, - slickCodegen := { - val outDir = { - val folder = slickCodegenOutputDir.value - if (folder.exists()) { - require(folder.isDirectory, s"file :[$folder] is not a directory") - } else { - folder.mkdir() - } - folder.getPath - } - val outPkg = (slickCodegenOutputPackage).value - val outFile = (slickCodegenOutputFile).value - val outputToMultipleFiles = slickCodegenOutputToMultipleFiles.value - gen( - (slickCodegenCodeGenerator).value, - (slickCodegenDriver).value, - (slickCodegenJdbcDriver).value, - (slickCodegenDatabaseUrl).value, - (slickCodegenDatabaseUser).value, - (slickCodegenDatabasePassword).value, - outDir, - outPkg, - outFile, - outputToMultipleFiles, - slickCodegenOutputContainer.value, - slickCodegenExcludedTables.value, - slickCodegenIncludedTables.value, - streams.value - ) - } + slickCodegenCodeGenerator := { (m) => new CustomizeSourceCodeGenerator(m, slickCodegenConfig.value) }, + slickCodegen := gen.value, + slickCodegenVerify := verify.value ) + override lazy val projectSettings: Seq[Setting[_]] = + dbSettings ++ defaultConfigs ++ + Seq( + createdConfigs := Set.empty, + slickCodegenAll := withDb(Def.taskDyn { + Def.sequential(slickCodegen +: createdConfigs.value.toList.map(c => c / slickCodegen)) + }).value, + slickCodegenVerifyAll := withDb(Def.taskDyn { + Def.sequential(slickCodegenVerify +: createdConfigs.value.toList.map(c => c / slickCodegenVerify)) + }).value, + ) + private def listScalaFileRecursively(dir: File): Seq[File] = { val buf = new ListBuffer[File]() def addFiles(d: File): Unit = { - d.listFiles().foreach { f => - if (f.isDirectory) { addFiles(f) } - else if (f.getName.endsWith(".scala")) { buf += f } + d.listFiles().foreach { + f => + if (f.isDirectory) { addFiles(f) } + else if (f.getName.endsWith(".scala")) { buf += f } } } addFiles(dir) diff --git a/src/main/scala/com/github/tototoshi/sbt/slick/PluginDBSupport.scala b/src/main/scala/com/github/tototoshi/sbt/slick/PluginDBSupport.scala new file mode 100644 index 0000000..d13a6ba --- /dev/null +++ b/src/main/scala/com/github/tototoshi/sbt/slick/PluginDBSupport.scala @@ -0,0 +1,53 @@ +package com.github.tototoshi.sbt.slick + +import sbt.* + +import _root_.io.github.davidmweber.FlywayPlugin +import com.tubitv.PostgresContainer + +trait PluginDBSupport { + + protected final val dbUser = "postgres" + protected final val dbPass = "password" + + protected val postgresDbUrl = settingKey[String]("The database urlt") + + lazy val postgresContainerPort = settingKey[Int]("The port of postgres port") + lazy val postgresVersion = settingKey[String]("The postgres version") + + protected def dbSettings = { + import FlywayPlugin.autoImport._ + FlywayPlugin.projectSettings ++ + Seq( + postgresDbUrl := s"jdbc:postgresql://127.0.0.1:${postgresContainerPort.value}/postgres", + postgresContainerPort := 15432, + postgresVersion := "13.7", + flywayUrl := postgresDbUrl.value, + flywayUser := dbUser, + flywayPassword := dbPass, + flywayLocations := Seq(s"filesystem:${(Compile / Keys.resourceDirectory).value.getAbsoluteFile}/db/migration") + ) + } + + /** Run the task with db ready , will start the postgres docker, and run flyway migrate before the task + * and also, it will make sure stop and remove the container after the task + * @param task the task to be executed + * @tparam A + * @return + */ + protected def withDb[A](task: Def.Initialize[Task[A]]): Def.Initialize[Task[A]] = Def.taskDyn { + val containerClosable = Def.task { + PostgresContainer.run( + exportPort = postgresContainerPort.value, + password = dbPass, + postgresVersion = postgresVersion.value, + logger = Keys.streams.value.log + ) + }.value + + Def + .sequential(FlywayPlugin.autoImport.flywayMigrate, task) + .andFinally(containerClosable.close()) + } + +} diff --git a/src/main/scala/com/tubitv/CodeGenConfig.scala b/src/main/scala/com/tubitv/CodeGenConfig.scala new file mode 100644 index 0000000..2b739f7 --- /dev/null +++ b/src/main/scala/com/tubitv/CodeGenConfig.scala @@ -0,0 +1,10 @@ +package com.tubitv + +case class CodeGenConfig( + byNameMapper: PartialFunction[(String, String), String] = PartialFunction.empty, + byTypeMapper: PartialFunction[String, String] = PartialFunction.empty, + ignoredColumns: (String, String) => Boolean = (_, _) => false, + profile: String = "slick.jdbc.PostgresProfile", + // some extra imports need for the generated code + extraImports: Seq[String] = Seq.empty, +) diff --git a/src/main/scala/com/tubitv/CodeGenPostgresProfile.scala b/src/main/scala/com/tubitv/CodeGenPostgresProfile.scala new file mode 100644 index 0000000..c79087e --- /dev/null +++ b/src/main/scala/com/tubitv/CodeGenPostgresProfile.scala @@ -0,0 +1,54 @@ +package com.tubitv + +import java.sql.Types.{ CHAR, LONGNVARCHAR, LONGVARCHAR, NCHAR, NVARCHAR, VARCHAR } + +import scala.concurrent.ExecutionContext + +import slick.jdbc.PostgresProfile +import slick.jdbc.meta._ + +class CodeGenPostgresProfile(config: CodeGenConfig) extends PostgresProfile { + + override def createModelBuilder(tables: Seq[MTable], ignoreInvalidDefaults: Boolean)(implicit ec: ExecutionContext): slick.jdbc.JdbcModelBuilder = new ModelBuilder(tables, ignoreInvalidDefaults)(ec) { + + val typeMapper: PartialFunction[String, String] = config.byTypeMapper orElse { + case "name" | "text" | "varchar" => "String" + case "int4" | "serial" => "Int" + case "int2" | "smallserial" => "Short" + case "int8" | "bigserial" | "oid" => "Long" + case "bool" | "bit" => "Boolean" + } + + val arraryDetector: PartialFunction[String, String] = { + case a if a.startsWith("_") => a.substring(1) + } + + override def createColumnBuilder(tableBuilder: TableBuilder, meta: MColumn): ColumnBuilder = + new ColumnBuilder(tableBuilder, meta) { + override def tpe = + config.byNameMapper + .lift(meta.table.name -> meta.name) + .orElse(typeMapper.lift(meta.typeName)) + .orElse(arraryDetector.andThen(typeMapper).andThen(a => s"List[$a]").lift(meta.typeName)) + .getOrElse({ + val rt = super.tpe + if (rt == "String" && !isJdbcStringType(meta.sqlType)) { + logger.warn( + s"Column [${meta.name}] in table [${meta.table.name}] with type [${meta.typeName}] " + + s"does not have a custom mapping, so defaults map as [String], consider defining a custom type mapper") + } + rt + }) + } + + override def readColumns(t: MTable): api.DBIO[Vector[MColumn]] = super.readColumns(t).map { + v => + v.filterNot(m => config.ignoredColumns(m.table.name, m.name)) + } + + private def isJdbcStringType(sqlType: Int) = sqlType match { + case CHAR | VARCHAR | LONGVARCHAR | NCHAR | NVARCHAR | LONGNVARCHAR => true + case _ => false + } + } +} diff --git a/src/main/scala/com/tubitv/CustomizeSourceCodeGenerator.scala b/src/main/scala/com/tubitv/CustomizeSourceCodeGenerator.scala new file mode 100644 index 0000000..92a35d4 --- /dev/null +++ b/src/main/scala/com/tubitv/CustomizeSourceCodeGenerator.scala @@ -0,0 +1,28 @@ +package com.tubitv + +import slick.{ model => m } +import slick.codegen.SourceCodeGenerator + +class CustomizeSourceCodeGenerator(model: m.Model, config: CodeGenConfig) extends SourceCodeGenerator(model) { + override def packageCode(profile: String, pkg: String, container: String, parentType: Option[String]): String = { + s""" + |package $pkg + | + |// format: off + |// AUTO-GENERATED Slick data model + |// scalastyle:off + |/** + | * Stand-alone Slick data model for immediate use + | * Please do not touch this file manually, use slick-codegen + | */ + |object $container extends { + | ${indent(config.extraImports.map(i => s"import ${i}").mkString("\n"))} + | + | val profile = ${config.profile} + | import profile.api._ + | + | ${indent(code)} + |}""".stripMargin.trim() + + } +} diff --git a/src/main/scala/com/tubitv/PostgresContainer.scala b/src/main/scala/com/tubitv/PostgresContainer.scala new file mode 100644 index 0000000..08c64d9 --- /dev/null +++ b/src/main/scala/com/tubitv/PostgresContainer.scala @@ -0,0 +1,68 @@ +package com.tubitv + +import sbt.Logger + +import java.util.concurrent.atomic.AtomicBoolean + +import scala.concurrent.{Await, Promise} +import scala.concurrent.duration.Duration +import scala.util.Success + +import com.github.dockerjava.api.async.ResultCallback +import com.github.dockerjava.api.model.{Frame, PortBinding} +import com.github.dockerjava.core.DockerClientBuilder + +object PostgresContainer { + + private val isRunning = new AtomicBoolean(false) + def run(exportPort: Int, password: String, postgresVersion: String, logger: Logger): AutoCloseable = { + if (isRunning.get()) { + () => {} + } else { + val dockerClient = DockerClientBuilder.getInstance.build + val container = dockerClient + .createContainerCmd(s"postgres:$postgresVersion") + .withEnv(s"POSTGRES_PASSWORD=$password") + + container.getHostConfig + .withPortBindings(PortBinding.parse(s"$exportPort:5432")) + + val containerId = container.exec().getId + logger.info(s"Starting docker container:${containerId.substring(0, 12)} [postgres:$postgresVersion]") + + dockerClient.startContainerCmd(containerId).exec() + + val ready = Promise[Unit]() + dockerClient + .logContainerCmd(containerId) + .withFollowStream(true) + .withSince(0) + .withStdOut(true) + .withStdErr(true) + .exec(new ResultCallback.Adapter[Frame] { + override def onNext(`object`: Frame): Unit = { + val lines = new String(`object`.getPayload).split("\n") + if (lines.exists(_.endsWith("database system is ready to accept connections"))) { + ready.complete(Success(())) + this.close() + } + } + }) + + Await.result(ready.future, Duration.Inf) + isRunning.set(true) + logger.info("Postgres start success") + + () => { + try { + dockerClient.stopContainerCmd(containerId).exec() + } catch { + case _: Throwable => + } + dockerClient.removeContainerCmd(containerId).exec() + isRunning.set(false) + logger.info(s"Docker container [postgres:$postgresVersion] is stopped") + } + } + } +} From d9e5e873a8c67607985a8d6d467cb0d64279cfeb Mon Sep 17 00:00:00 2001 From: Chunsen Wang Date: Fri, 2 Feb 2024 15:41:35 +0800 Subject: [PATCH 02/11] fix publish ci --- .../workflows/{release.yml => publish.yml} | 28 ++----------------- project/plugins.sbt | 1 - .../github-actions/{release.sh => publish.sh} | 3 +- 3 files changed, 3 insertions(+), 29 deletions(-) rename .github/workflows/{release.yml => publish.yml} (61%) rename scripts/github-actions/{release.sh => publish.sh} (56%) diff --git a/.github/workflows/release.yml b/.github/workflows/publish.yml similarity index 61% rename from .github/workflows/release.yml rename to .github/workflows/publish.yml index 269d784..cd62001 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/publish.yml @@ -1,12 +1,6 @@ name: Release on: - workflow_dispatch: - inputs: - TUBI_PROJECT_VERSION: - description: 'semantic version part to bump, either patch, minor or major' - required: true - default: 'patch' release: types: - published @@ -21,18 +15,6 @@ jobs: env: GITHUB_CONTEXT: ${{ toJson(github) }} run: echo "$GITHUB_CONTEXT" -# - name: Set build info -# id: build_info -# run: | -# if [ ${{ github.event_name }} == 'workflow_dispatch' ]; then -# echo "tubi-project-name=${{ github.event.inputs.TUBI_PROJECT_NAME }}" >> "$GITHUB_OUTPUT" -# echo "tubi-project-version=${{ github.event.inputs.TUBI_PROJECT_VERSION }}" >> "$GITHUB_OUTPUT" -# else -# tag="${{ github.event.release.tag_name }}" -# name="${tag%-v[0-9]*\.[0-9]*\.[0-9]*}" -# echo "tubi-project-name=${name}" >> "$GITHUB_OUTPUT" -# echo "tubi-project-version=${tag##[a-z]*-v}" >> "$GITHUB_OUTPUT" -# fi - name: Configure git user to push run: | @@ -51,18 +33,12 @@ jobs: distribution: 'temurin' cache: 'sbt' - - name: Generate artifact - run: ./scripts/github-action/release.sh + - name: Publish + run: ./scripts/github-action/publish.sh env: - TUBI_PROJECT_NAME: ${{ steps.build_info.outputs.tubi-project-name }} - TUBI_PROJECT_VERSION: ${{ steps.build_info.outputs.tubi-project-version }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} ARTIFACTORY_USERNAME: ${{ secrets.ARTIFACTORY_USERNAME }} ARTIFACTORY_PASSWORD: ${{ secrets.ARTIFACTORY_PASSWORD }} - AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} - AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} - AWS_ACCOUNT_ID: 370025973162 - AWS_REGION: us-east-2 # - name: notify build status # if: always() diff --git a/project/plugins.sbt b/project/plugins.sbt index c186428..5b9e540 100644 --- a/project/plugins.sbt +++ b/project/plugins.sbt @@ -1,4 +1,3 @@ //addSbtPlugin("org.scalariform" % "sbt-scalariform" % "1.8.3") addSbtPlugin("org.xerial.sbt" % "sbt-sonatype" % "3.9.18") addSbtPlugin("com.github.sbt" % "sbt-pgp" % "2.2.1") -addSbtPlugin("com.github.gseitz" % "sbt-release" % "1.0.11") diff --git a/scripts/github-actions/release.sh b/scripts/github-actions/publish.sh similarity index 56% rename from scripts/github-actions/release.sh rename to scripts/github-actions/publish.sh index 31fd37c..9dc66ba 100755 --- a/scripts/github-actions/release.sh +++ b/scripts/github-actions/publish.sh @@ -1,5 +1,4 @@ #!/usr/bin/env bash SBT_JVM_OPTS="-J-Xms4g -J-Xmx4g -J-Xss8m -J-XX:MaxMetaspaceSize=1024m -J-XX:+UseG1GC" - -sbt $SBT_JVM_OPTS "release release-version $TUBI_PROJECT_VERSION with-defaults"; \ No newline at end of file +sbt $SBT_JVM_OPTS publish \ No newline at end of file From 3358c11f1ae40d3195719df7bf1610fdc6bdfec9 Mon Sep 17 00:00:00 2001 From: Chunsen Wang Date: Fri, 2 Feb 2024 15:49:11 +0800 Subject: [PATCH 03/11] fix ci --- .github/workflows/publish.yml | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index cd62001..a9d11a0 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -1,9 +1,7 @@ -name: Release +name: Publish on: - release: - types: - - published + workflow_dispatch: # can maybe test via https://github.com/nektos/act # act -s DOCKER_USERNAME=abc -s DOCKER_PASSWORD=xyz --container-architecture linux/arm64 -W .github/workflows/release.yml release From f8c7f57b73ce385b7163d7403bbb8a6e5531d9b8 Mon Sep 17 00:00:00 2001 From: Chunsen Wang Date: Fri, 2 Feb 2024 15:51:34 +0800 Subject: [PATCH 04/11] fix ci --- .github/workflows/publish.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index a9d11a0..e709c3b 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -32,7 +32,7 @@ jobs: cache: 'sbt' - name: Publish - run: ./scripts/github-action/publish.sh + run: ./scripts/github-actions/publish.sh env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} ARTIFACTORY_USERNAME: ${{ secrets.ARTIFACTORY_USERNAME }} From 8bf0aca4659f06e3f583360954a6665e2bf9e6ce Mon Sep 17 00:00:00 2001 From: Chunsen Wang Date: Fri, 2 Feb 2024 15:54:20 +0800 Subject: [PATCH 05/11] set versionScheme --- build.sbt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.sbt b/build.sbt index 86213c2..6c0a739 100644 --- a/build.sbt +++ b/build.sbt @@ -24,7 +24,7 @@ libraryDependencies ++= Seq( addSbtPlugin("io.github.davidmweber" % "flyway-sbt" % Versions.flywaySbt) publishTo := Some(if (isSnapshot.value) Repo.Jfrog.Tubins.sbtDev else Repo.Jfrog.Tubins.sbtRelease) - +ThisBuild / versionScheme := Some("early-semver") Test / publishArtifact := false scriptedBufferLog := false From 7f9abd07d217a9c9d35c3b2a6d41975f22363e6e Mon Sep 17 00:00:00 2001 From: Chunsen Wang Date: Fri, 2 Feb 2024 15:59:59 +0800 Subject: [PATCH 06/11] fix publish --- project/plugins.sbt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/project/plugins.sbt b/project/plugins.sbt index 5b9e540..8b02da5 100644 --- a/project/plugins.sbt +++ b/project/plugins.sbt @@ -1,3 +1,3 @@ //addSbtPlugin("org.scalariform" % "sbt-scalariform" % "1.8.3") -addSbtPlugin("org.xerial.sbt" % "sbt-sonatype" % "3.9.18") -addSbtPlugin("com.github.sbt" % "sbt-pgp" % "2.2.1") +//addSbtPlugin("org.xerial.sbt" % "sbt-sonatype" % "3.9.18") +//addSbtPlugin("com.github.sbt" % "sbt-pgp" % "2.2.1") From 4bc782699cefb16e52cdb3025bf4a56fe7893ad4 Mon Sep 17 00:00:00 2001 From: Chunsen Wang Date: Mon, 5 Feb 2024 12:15:05 +0800 Subject: [PATCH 07/11] fix lint warn --- jd-gui.cfg | 22 +++ .../tototoshi/sbt/slick/CodegenPlugin.scala | 180 +++++++++--------- .../tototoshi/sbt/slick/PluginDBSupport.scala | 38 ++-- src/main/scala/com/tubitv/CodeGenConfig.scala | 2 +- .../scala/com/tubitv/PostgresContainer.scala | 86 +++++---- 5 files changed, 183 insertions(+), 145 deletions(-) create mode 100644 jd-gui.cfg diff --git a/jd-gui.cfg b/jd-gui.cfg new file mode 100644 index 0000000..60aaf01 --- /dev/null +++ b/jd-gui.cfg @@ -0,0 +1,22 @@ + + + + + + + true + + com.apple.laf.AquaLookAndFeel + + + /Users/chunsenwang/tubi/sbt-slick-codegen/target/scala-2.12/sbt-1.0/classes/com/github/tototoshi/sbt/slick/CodegenPlugin$.class + + + /Users/chunsenwang/tubi/sbt-slick-codegen/target/scala-2.12/sbt-1.0/classes/com/github/tototoshi/sbt/slick + /Users/chunsenwang/tubi/sbt-slick-codegen + + + 0xFF6666 + 1.1.3 + + \ No newline at end of file diff --git a/src/main/scala/com/github/tototoshi/sbt/slick/CodegenPlugin.scala b/src/main/scala/com/github/tototoshi/sbt/slick/CodegenPlugin.scala index 900eed4..409b7de 100644 --- a/src/main/scala/com/github/tototoshi/sbt/slick/CodegenPlugin.scala +++ b/src/main/scala/com/github/tototoshi/sbt/slick/CodegenPlugin.scala @@ -13,20 +13,25 @@ import Keys.* import com.tubitv.{CodeGenConfig, CodeGenPostgresProfile, CustomizeSourceCodeGenerator, PostgresContainer} import slick.{model => m} import slick.codegen.SourceCodeGenerator -import slick.dbio.{DBIOAction, Effect, NoStream} import slick.jdbc.JdbcProfile +import slick.jdbc.meta.MTable object CodegenPlugin extends sbt.AutoPlugin with PluginDBSupport { - private val createdConfigs = - settingKey[Set[Configuration]]("The configurations for the config, other than default") + private val createdConfigs_ = + settingKey[Set[Configuration]]("The configurations for the config, other than default").withRank(KeyRanks.Invisible) + private val generators_ = settingKey[() => SourceCodeGenerator]( + "The setting to create the generator, to avoid the boilerplate code" + ).withRank(KeyRanks.Invisible) + + private val generateCode_ = taskKey[Seq[File]]("Generate the code, without starting db").withRank(KeyRanks.Invisible) + private val verifyCode_ = taskKey[Unit]("Verify the generated code, without starting db").withRank(KeyRanks.Invisible) object autoImport { lazy val slickCodegen: TaskKey[Seq[File]] = taskKey[Seq[File]]("Command to run codegen") lazy val slickCodegenAll: TaskKey[Unit] = taskKey[Unit]("Command to run all the codegen") - lazy val slickCodegenVerify: TaskKey[Unit] = taskKey("Verify if the generated code is out of date") lazy val slickCodegenVerifyAll: TaskKey[Unit] = taskKey("Verify any of the generated code is out of date") - lazy val slickCodegenConfig: SettingKey[CodeGenConfig] = + lazy val slickCodegenGeneratorConfig: SettingKey[CodeGenConfig] = settingKey("The configuration for the code generator") lazy val slickCodegenDriver: SettingKey[JdbcProfile] = @@ -63,60 +68,28 @@ object CodegenPlugin extends sbt.AutoPlugin with PluginDBSupport { /** Define a new configuration scope for slick code gen, * for the cases where there are multiple codegen in a project - * @param configName - * @param setting + * @param configName the name of this config + * @param setting customized settings for the code gen * @return */ def codeGen(configName: String)(setting: Setting[_]*): Seq[Setting[_]] = { val theConfig = Configuration.of(configName.take(1).toUpperCase() + configName.drop(1), configName) - Seq(createdConfigs += theConfig) ++ inConfig(theConfig)(defaultConfigs ++ setting) + Seq(createdConfigs_ += theConfig) ++ inConfig(theConfig)(defaultConfigs ++ setting) } } import autoImport._ - private def withGenerator[T](fun: SourceCodeGenerator => T) = Def.task { - val generator = slickCodegenCodeGenerator.value - val driver = slickCodegenDriver.value - - val database = driver.api.Database.forURL( - url = postgresDbUrl.value, - driver = slickCodegenJdbcDriver.value, - user = dbUser, - password = dbPass - ) - - try { - database.source.createConnection().close() - } catch { - case e: Throwable => - throw new RuntimeException("Failed to run slick-codegen: " + e.getMessage, e) - } - - val excluded = slickCodegenExcludedTables.value - val included = slickCodegenIncludedTables.value - - val tables = driver.defaultTables - .map(ts => ts.filter(t => included.isEmpty || (included contains t.name.name))) - .map(ts => ts.filterNot(t => excluded contains t.name.name)) - - val dbio: DBIOAction[T, NoStream, Effect.All] = for { - m <- driver.createModel(Some(tables)) - } yield fun(generator(m)) - - Await.result(database.run(dbio), Duration.Inf) - } - - private def profile = Def.task { - val driverClassName = slickCodegenDriver.value.getClass.getName + private def profile(p: JdbcProfile): String = { + val driverClassName = p.getClass.getName // if it's a singleton object, then just reference it directly if (driverClassName.endsWith("$")) driverClassName.stripSuffix("$") // if it's an instance of a regular class, we don't know constructor args; try the no-arguments constructor and hope for the best else s"new $driverClassName()" } - private def gen = Def.taskDyn { - val p = profile.value + private def generate: Def.Initialize[Task[Seq[File]]] = Def.task { + val p = profile(slickCodegenDriver.value) val outDir = { val folder = slickCodegenOutputDir.value @@ -127,6 +100,7 @@ object CodegenPlugin extends sbt.AutoPlugin with PluginDBSupport { } folder.getPath } + val pkg = slickCodegenOutputPackage.value val fileName = slickCodegenOutputFile.value val container = slickCodegenOutputContainer.value @@ -134,55 +108,74 @@ object CodegenPlugin extends sbt.AutoPlugin with PluginDBSupport { val s = streams.value val outputToMultipleFiles = slickCodegenOutputToMultipleFiles.value - Def.taskDyn { - val _ = withGenerator { - sourceGen => - if (outputToMultipleFiles) { - sourceGen.writeToMultipleFiles(profile = p, folder = outDir, pkg = pkg, container = container) - } else { - sourceGen.writeToFile(profile = p, folder = outDir, pkg = pkg, container = container, fileName = fileName) - } - }.value - - Def.task { - if (outputToMultipleFiles) { - val outDirFile = file(outDir) - s.log.info(s"Source code files have been generated in ${outDirFile.getAbsolutePath}") - listScalaFileRecursively(outDirFile) - } else { - val generatedFile = outDir + "/" + pkg.replaceAllLiterally(".", "/") + "/" + fileName - s.log.info(s"Source code has generated in ${generatedFile}") - Seq(file(generatedFile)) - } - } + val sourceGen = generators_.value() + if (outputToMultipleFiles) { + sourceGen.writeToMultipleFiles(profile = p, folder = outDir, pkg = pkg, container = container) + val outDirFile = file(outDir) + s.log.info(s"Source code files have been generated in ${outDirFile.getAbsolutePath}") + listScalaFileRecursively(outDirFile) + } else { + sourceGen.writeToFile(profile = p, folder = outDir, pkg = pkg, container = container, fileName = fileName) + val generatedFile = file(outDir + "/" + pkg.replaceAllLiterally(".", "/") + "/" + fileName) + s.log.info(s"Source code has generated in ${generatedFile.getAbsolutePath}") + Seq(generatedFile) } + } - private def verify = Def.taskDyn { - val p = profile.value + private def verify: Def.Initialize[Task[Unit]] = Def.task { + val p = profile(slickCodegenDriver.value) val pkg = slickCodegenOutputPackage.value val container = slickCodegenOutputContainer.value val theConf = configuration.?.value val s = streams.value - withGenerator { - sourceGen => - val theCode = sourceGen.packageCode(profile = p, pkg = pkg, container = container, sourceGen.parentType) - val file = slickCodegenOutputDir.value + "/" + pkg.replace(".", "/") + "/" + slickCodegenOutputFile.value - val generated = Using(Source.fromFile(file))(_.mkString).get - if (theCode.trim != generated.trim) { - throw new Exception( - s"Schema file: $file is out of date, please regenerate it by [ ${theConf.map(c => c.name + " / ").getOrElse("")}slickCodegen ]" - ) - } - s.log.info(s"Verify schema $file success") + val sourceGen = generators_.value() + val theCode = sourceGen.packageCode(profile = p, pkg = pkg, container = container, sourceGen.parentType) + val file = slickCodegenOutputDir.value + "/" + pkg.replace(".", "/") + "/" + slickCodegenOutputFile.value + val generated = Using(Source.fromFile(file))(_.mkString).get + if (theCode.trim != generated.trim) { + throw new Exception( + s"Schema file: $file is out of date, please re-generate it by [ ${theConf.map(c => c.id + " / ").getOrElse("")}slickCodegen ]" + ) } + s.log.info(s"Verify schema $file success") + } private def defaultConfigs = Seq( - slickCodegenConfig := CodeGenConfig(), - slickCodegenDriver := new CodeGenPostgresProfile(slickCodegenConfig.value), + generators_ := { + val generator = slickCodegenCodeGenerator.value + val driver = slickCodegenDriver.value + val url = postgresDbUrl.value + val jdbcDriver = slickCodegenJdbcDriver.value + val excluded = slickCodegenExcludedTables.value + val included = slickCodegenIncludedTables.value + val database = driver.api.Database.forURL(url = url, driver = jdbcDriver, user = dbUser, password = dbPass) + + () => { + try { + database.source.createConnection().close() + } catch { + case e: Throwable => + throw new RuntimeException("Failed to run slick-codegen: " + e.getMessage, e) + } + + val tables = MTable + .getTables(None, None, None, Some(Seq("TABLE", "VIEW", "MATERIALIZED VIEW"))) + .map(ts => ts.filter(t => included.isEmpty || (included contains t.name.name))) + .map(ts => ts.filterNot(t => excluded contains t.name.name)) + + val dbio = for { + m <- driver.createModel(Some(tables)) + } yield generator(m) + + Await.result(database.run(dbio), Duration.Inf) + } + }, + slickCodegenGeneratorConfig := CodeGenConfig(), + slickCodegenDriver := new CodeGenPostgresProfile(slickCodegenGeneratorConfig.value), slickCodegenJdbcDriver := "org.postgresql.Driver", slickCodegenOutputPackage := "com.example", slickCodegenOutputFile := s"${slickCodegenOutputContainer.value}.scala", @@ -191,21 +184,28 @@ object CodegenPlugin extends sbt.AutoPlugin with PluginDBSupport { slickCodegenOutputContainer := "Tables", slickCodegenExcludedTables := Seq(), slickCodegenIncludedTables := Seq(), - slickCodegenCodeGenerator := { (m) => new CustomizeSourceCodeGenerator(m, slickCodegenConfig.value) }, - slickCodegen := gen.value, - slickCodegenVerify := verify.value + slickCodegenCodeGenerator := { (m) => new CustomizeSourceCodeGenerator(m, slickCodegenGeneratorConfig.value) }, + generateCode_ := generate.value, + verifyCode_ := verify.value, + slickCodegen := withDb(generate).value ) override lazy val projectSettings: Seq[Setting[_]] = dbSettings ++ defaultConfigs ++ Seq( - createdConfigs := Set.empty, - slickCodegenAll := withDb(Def.taskDyn { - Def.sequential(slickCodegen +: createdConfigs.value.toList.map(c => c / slickCodegen)) - }).value, - slickCodegenVerifyAll := withDb(Def.taskDyn { - Def.sequential(slickCodegenVerify +: createdConfigs.value.toList.map(c => c / slickCodegenVerify)) - }).value, + createdConfigs_ := Set.empty, + slickCodegenAll := withDb( + Def.taskDyn( + Def + .sequential(generateCode_ +: createdConfigs_.value.toList.map(c => c / generateCode_)) + ) + ).value, + slickCodegenVerifyAll := withDb( + Def.taskDyn( + Def + .sequential(verifyCode_ +: createdConfigs_.value.toList.map(c => c / verifyCode_)) + ) + ).value ) private def listScalaFileRecursively(dir: File): Seq[File] = { diff --git a/src/main/scala/com/github/tototoshi/sbt/slick/PluginDBSupport.scala b/src/main/scala/com/github/tototoshi/sbt/slick/PluginDBSupport.scala index d13a6ba..24db30f 100644 --- a/src/main/scala/com/github/tototoshi/sbt/slick/PluginDBSupport.scala +++ b/src/main/scala/com/github/tototoshi/sbt/slick/PluginDBSupport.scala @@ -11,21 +11,40 @@ trait PluginDBSupport { protected final val dbPass = "password" protected val postgresDbUrl = settingKey[String]("The database urlt") + protected val stopDb = taskKey[Unit]("Start the postgres docker container and run flyway migrate") + protected val startDb = taskKey[Unit]("Stop and remove the postgres container") lazy val postgresContainerPort = settingKey[Int]("The port of postgres port") lazy val postgresVersion = settingKey[String]("The postgres version") - protected def dbSettings = { + protected def dbSettings: Seq[sbt.Setting[_]] = { import FlywayPlugin.autoImport._ FlywayPlugin.projectSettings ++ Seq( + flywayDefaults / Keys.logLevel := Level.Warn, postgresDbUrl := s"jdbc:postgresql://127.0.0.1:${postgresContainerPort.value}/postgres", postgresContainerPort := 15432, postgresVersion := "13.7", flywayUrl := postgresDbUrl.value, flywayUser := dbUser, flywayPassword := dbPass, - flywayLocations := Seq(s"filesystem:${(Compile / Keys.resourceDirectory).value.getAbsoluteFile}/db/migration") + flywayLocations := Seq(s"filesystem:${(Compile / Keys.resourceDirectory).value.getAbsoluteFile}/db/migration"), + startDb := Def + .sequential( + Def.task { + PostgresContainer.start( + exportPort = postgresContainerPort.value, + password = dbPass, + postgresVersion = postgresVersion.value, + logger = Keys.streams.value.log + ) + }, + FlywayPlugin.autoImport.flywayMigrate + ) + .value, + stopDb := { + PostgresContainer.stop(Keys.streams.value.log) + } ) } @@ -36,18 +55,9 @@ trait PluginDBSupport { * @return */ protected def withDb[A](task: Def.Initialize[Task[A]]): Def.Initialize[Task[A]] = Def.taskDyn { - val containerClosable = Def.task { - PostgresContainer.run( - exportPort = postgresContainerPort.value, - password = dbPass, - postgresVersion = postgresVersion.value, - logger = Keys.streams.value.log - ) - }.value - - Def - .sequential(FlywayPlugin.autoImport.flywayMigrate, task) - .andFinally(containerClosable.close()) + task + .dependsOn(startDb) + .doFinally(stopDb.taskValue) } } diff --git a/src/main/scala/com/tubitv/CodeGenConfig.scala b/src/main/scala/com/tubitv/CodeGenConfig.scala index 2b739f7..c35319e 100644 --- a/src/main/scala/com/tubitv/CodeGenConfig.scala +++ b/src/main/scala/com/tubitv/CodeGenConfig.scala @@ -3,7 +3,7 @@ package com.tubitv case class CodeGenConfig( byNameMapper: PartialFunction[(String, String), String] = PartialFunction.empty, byTypeMapper: PartialFunction[String, String] = PartialFunction.empty, - ignoredColumns: (String, String) => Boolean = (_, _) => false, + ignoredColumns: ((String, String)) => Boolean = _ => false, profile: String = "slick.jdbc.PostgresProfile", // some extra imports need for the generated code extraImports: Seq[String] = Seq.empty, diff --git a/src/main/scala/com/tubitv/PostgresContainer.scala b/src/main/scala/com/tubitv/PostgresContainer.scala index 08c64d9..2ac62af 100644 --- a/src/main/scala/com/tubitv/PostgresContainer.scala +++ b/src/main/scala/com/tubitv/PostgresContainer.scala @@ -2,7 +2,7 @@ package com.tubitv import sbt.Logger -import java.util.concurrent.atomic.AtomicBoolean +import java.util.concurrent.atomic.AtomicReference import scala.concurrent.{Await, Promise} import scala.concurrent.duration.Duration @@ -14,55 +14,61 @@ import com.github.dockerjava.core.DockerClientBuilder object PostgresContainer { - private val isRunning = new AtomicBoolean(false) - def run(exportPort: Int, password: String, postgresVersion: String, logger: Logger): AutoCloseable = { - if (isRunning.get()) { - () => {} - } else { - val dockerClient = DockerClientBuilder.getInstance.build - val container = dockerClient - .createContainerCmd(s"postgres:$postgresVersion") - .withEnv(s"POSTGRES_PASSWORD=$password") + private val runningDb = new AtomicReference[Option[String]](None) - container.getHostConfig - .withPortBindings(PortBinding.parse(s"$exportPort:5432")) + def start(exportPort: Int, password: String, postgresVersion: String, logger: Logger): Unit = { + runningDb.get() match { + case None => + val dockerClient = DockerClientBuilder.getInstance.build + val container = dockerClient + .createContainerCmd(s"postgres:$postgresVersion") + .withEnv(s"POSTGRES_PASSWORD=$password") - val containerId = container.exec().getId - logger.info(s"Starting docker container:${containerId.substring(0, 12)} [postgres:$postgresVersion]") + container.getHostConfig + .withPortBindings(PortBinding.parse(s"$exportPort:5432")) - dockerClient.startContainerCmd(containerId).exec() + val containerId = container.exec().getId + if (runningDb.compareAndSet(None, Some(containerId))) { + logger.info(s"Starting docker container:${containerId.substring(0, 12)} [postgres:$postgresVersion]") - val ready = Promise[Unit]() - dockerClient - .logContainerCmd(containerId) - .withFollowStream(true) - .withSince(0) - .withStdOut(true) - .withStdErr(true) - .exec(new ResultCallback.Adapter[Frame] { - override def onNext(`object`: Frame): Unit = { - val lines = new String(`object`.getPayload).split("\n") - if (lines.exists(_.endsWith("database system is ready to accept connections"))) { - ready.complete(Success(())) - this.close() - } - } - }) + dockerClient.startContainerCmd(containerId).exec() - Await.result(ready.future, Duration.Inf) - isRunning.set(true) - logger.info("Postgres start success") + val ready = Promise[Unit]() + dockerClient + .logContainerCmd(containerId) + .withFollowStream(true) + .withSince(0) + .withStdOut(true) + .withStdErr(true) + .exec(new ResultCallback.Adapter[Frame] { + override def onNext(`object`: Frame): Unit = { + val lines = new String(`object`.getPayload).split("\n") + if (lines.exists(_.endsWith("database system is ready to accept connections"))) { + ready.complete(Success(())) + this.close() + } + } + }) - () => { + Await.result(ready.future, Duration.Inf) + } + case _ => + } + } + + def stop(logger: Logger): Unit = { + runningDb.get() match { + case Some(id) => + val dockerClient = DockerClientBuilder.getInstance.build try { - dockerClient.stopContainerCmd(containerId).exec() + dockerClient.stopContainerCmd(id).exec() } catch { case _: Throwable => } - dockerClient.removeContainerCmd(containerId).exec() - isRunning.set(false) - logger.info(s"Docker container [postgres:$postgresVersion] is stopped") - } + dockerClient.removeContainerCmd(id).exec() + runningDb.set(None) + logger.info(s"Docker container [postgres] is stopped") + case _ => } } } From 2e9c324181454d0dbc40a8101c1a088c0c02a7cd Mon Sep 17 00:00:00 2001 From: Chunsen Wang Date: Thu, 8 Feb 2024 16:40:16 +0800 Subject: [PATCH 08/11] fix posgres start logic, pull image before start --- .../scala/com/tubitv/PostgresContainer.scala | 24 +++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/src/main/scala/com/tubitv/PostgresContainer.scala b/src/main/scala/com/tubitv/PostgresContainer.scala index 2ac62af..56b2d92 100644 --- a/src/main/scala/com/tubitv/PostgresContainer.scala +++ b/src/main/scala/com/tubitv/PostgresContainer.scala @@ -5,11 +5,12 @@ import sbt.Logger import java.util.concurrent.atomic.AtomicReference import scala.concurrent.{Await, Promise} -import scala.concurrent.duration.Duration -import scala.util.Success +import scala.concurrent.duration.{Duration, DurationInt} +import scala.util.{Success, Try} import com.github.dockerjava.api.async.ResultCallback -import com.github.dockerjava.api.model.{Frame, PortBinding} +import com.github.dockerjava.api.exception.NotFoundException +import com.github.dockerjava.api.model.{Frame, PortBinding, PullResponseItem} import com.github.dockerjava.core.DockerClientBuilder object PostgresContainer { @@ -19,9 +20,24 @@ object PostgresContainer { def start(exportPort: Int, password: String, postgresVersion: String, logger: Logger): Unit = { runningDb.get() match { case None => + val image = s"postgres:$postgresVersion" val dockerClient = DockerClientBuilder.getInstance.build + Try(dockerClient.inspectImageCmd(image).exec()).recover { + case _: NotFoundException => + logger.info(s"Pulling image ${image} ...") + val done = Promise[Unit] + dockerClient + .pullImageCmd(image) + .exec(new ResultCallback.Adapter[PullResponseItem] { + override def onComplete(): Unit = done.success(()) + }) + + Await.result(done.future, 5.minutes) + logger.info(s"Image $image pull done") + } + val container = dockerClient - .createContainerCmd(s"postgres:$postgresVersion") + .createContainerCmd(image) .withEnv(s"POSTGRES_PASSWORD=$password") container.getHostConfig From 23c5db0500630c770d0c68185778377de4ab6e1a Mon Sep 17 00:00:00 2001 From: Chunsen Wang Date: Thu, 8 Feb 2024 18:16:25 +0800 Subject: [PATCH 09/11] fix container ready check logic --- .../scala/com/tubitv/PostgresContainer.scala | 39 +++++++++---------- 1 file changed, 19 insertions(+), 20 deletions(-) diff --git a/src/main/scala/com/tubitv/PostgresContainer.scala b/src/main/scala/com/tubitv/PostgresContainer.scala index 56b2d92..397336a 100644 --- a/src/main/scala/com/tubitv/PostgresContainer.scala +++ b/src/main/scala/com/tubitv/PostgresContainer.scala @@ -2,15 +2,16 @@ package com.tubitv import sbt.Logger +import java.sql.DriverManager import java.util.concurrent.atomic.AtomicReference import scala.concurrent.{Await, Promise} -import scala.concurrent.duration.{Duration, DurationInt} -import scala.util.{Success, Try} +import scala.concurrent.duration.DurationInt +import scala.util.{Try, Using} import com.github.dockerjava.api.async.ResultCallback import com.github.dockerjava.api.exception.NotFoundException -import com.github.dockerjava.api.model.{Frame, PortBinding, PullResponseItem} +import com.github.dockerjava.api.model.{PortBinding, PullResponseItem} import com.github.dockerjava.core.DockerClientBuilder object PostgresContainer { @@ -49,24 +50,22 @@ object PostgresContainer { dockerClient.startContainerCmd(containerId).exec() - val ready = Promise[Unit]() - dockerClient - .logContainerCmd(containerId) - .withFollowStream(true) - .withSince(0) - .withStdOut(true) - .withStdErr(true) - .exec(new ResultCallback.Adapter[Frame] { - override def onNext(`object`: Frame): Unit = { - val lines = new String(`object`.getPayload).split("\n") - if (lines.exists(_.endsWith("database system is ready to accept connections"))) { - ready.complete(Success(())) - this.close() + var isReady = false + val deadLine = 3.minutes.fromNow + while (!isReady) { + Class.forName("org.postgresql.Driver") + val url = s"jdbc:postgresql://127.0.0.1:${exportPort}/postgres" + Using(DriverManager.getConnection(url, "postgres", password))(_ => ()).toOption match { + case Some(_) => + isReady = true + case _ => + if (deadLine.isOverdue()) { + throw new Exception("Postgres container is not ready after 3 minutes") } - } - }) - - Await.result(ready.future, Duration.Inf) + Thread.sleep(1000) + } + } + logger.info("Docker container postgres started") } case _ => } From 64b9f018b8f128f1dd354b0942e70211ce6b83c5 Mon Sep 17 00:00:00 2001 From: Chunsen Wang Date: Wed, 21 Feb 2024 15:11:32 +0800 Subject: [PATCH 10/11] fix tests --- .github/workflows/ci.yml | 9 +++++++- build.sbt | 1 - jd-gui.cfg | 22 ------------------- project/plugins.sbt | 1 + src/sbt-test/test/basic/build.sbt | 17 ++++++-------- src/sbt-test/test/basic/project/plugins.sbt | 2 +- .../db/migration/V001__create-demo-table.sql | 0 src/sbt-test/test/basic/test | 7 +++--- 8 files changed, 20 insertions(+), 39 deletions(-) delete mode 100644 jd-gui.cfg create mode 100644 src/sbt-test/test/basic/src/main/resources/db/migration/V001__create-demo-table.sql diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b7dda07..99143e2 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -19,5 +19,12 @@ jobs: steps: - uses: actions/checkout@v2 + - name: Set up JDK 11 # it auto caches https://github.com/actions/setup-java#caching-packages-dependencies + uses: actions/setup-java@v3.11.0 + with: + java-version: '11' + distribution: 'temurin' + cache: 'sbt' + - name: Run tests - run: SBT_VERSION="${{ matrix.sbt_version }}" make test + run: sbt scripted diff --git a/build.sbt b/build.sbt index 6c0a739..10e42bf 100644 --- a/build.sbt +++ b/build.sbt @@ -13,7 +13,6 @@ enablePlugins(SbtPlugin) sbtPlugin := true name := """sbt-slick-codegen""" organization := "com.tubitv" -version := "0.0.1-SNAPSHOT" libraryDependencies ++= Seq( "com.typesafe.slick" %% "slick" % Versions.slick, diff --git a/jd-gui.cfg b/jd-gui.cfg deleted file mode 100644 index 60aaf01..0000000 --- a/jd-gui.cfg +++ /dev/null @@ -1,22 +0,0 @@ - - - - - - - true - - com.apple.laf.AquaLookAndFeel - - - /Users/chunsenwang/tubi/sbt-slick-codegen/target/scala-2.12/sbt-1.0/classes/com/github/tototoshi/sbt/slick/CodegenPlugin$.class - - - /Users/chunsenwang/tubi/sbt-slick-codegen/target/scala-2.12/sbt-1.0/classes/com/github/tototoshi/sbt/slick - /Users/chunsenwang/tubi/sbt-slick-codegen - - - 0xFF6666 - 1.1.3 - - \ No newline at end of file diff --git a/project/plugins.sbt b/project/plugins.sbt index 8b02da5..8eb9873 100644 --- a/project/plugins.sbt +++ b/project/plugins.sbt @@ -1,3 +1,4 @@ //addSbtPlugin("org.scalariform" % "sbt-scalariform" % "1.8.3") //addSbtPlugin("org.xerial.sbt" % "sbt-sonatype" % "3.9.18") //addSbtPlugin("com.github.sbt" % "sbt-pgp" % "2.2.1") +addSbtPlugin("com.github.sbt" % "sbt-dynver" % "5.0.1") diff --git a/src/sbt-test/test/basic/build.sbt b/src/sbt-test/test/basic/build.sbt index 2057d74..ab3552e 100644 --- a/src/sbt-test/test/basic/build.sbt +++ b/src/sbt-test/test/basic/build.sbt @@ -1,17 +1,14 @@ + crossScalaVersions := Seq("2.12.15", "2.13.8") Global / onChangedBuildSource := ReloadOnSourceChanges -libraryDependencies += "com.typesafe.slick" %% "slick" % System.getProperty("slick.version") - enablePlugins(CodegenPlugin) -Compile / sourceGenerators += slickCodegen - -slickCodegenDatabaseUrl := "jdbc:postgresql://postgres/example" - -slickCodegenDatabaseUser := "test" - -slickCodegenDatabasePassword := "test" +slickCodegenOutputContainer := "Table" +slickCodegenOutputPackage := "com.demo" +//) -slickCodegenOutputToMultipleFiles := true +codeGen("etl")( + slickCodegenOutputContainer := "Etl", +) diff --git a/src/sbt-test/test/basic/project/plugins.sbt b/src/sbt-test/test/basic/project/plugins.sbt index 1a21b61..1e5f998 100644 --- a/src/sbt-test/test/basic/project/plugins.sbt +++ b/src/sbt-test/test/basic/project/plugins.sbt @@ -1,5 +1,5 @@ sys.props.get("plugin.version") match { - case Some(x) => addSbtPlugin("com.github.tototoshi" % "sbt-slick-codegen" % x) + case Some(x) => addSbtPlugin("com.tubitv" % "sbt-slick-codegen" % x) case _ => sys.error("""|The system property 'plugin.version' is not defined. |Specify this property using the scriptedLaunchOpts -D.""".stripMargin) } diff --git a/src/sbt-test/test/basic/src/main/resources/db/migration/V001__create-demo-table.sql b/src/sbt-test/test/basic/src/main/resources/db/migration/V001__create-demo-table.sql new file mode 100644 index 0000000..e69de29 diff --git a/src/sbt-test/test/basic/test b/src/sbt-test/test/basic/test index 0287d84..730ff77 100644 --- a/src/sbt-test/test/basic/test +++ b/src/sbt-test/test/basic/test @@ -1,5 +1,4 @@ -$ exec psql -c 'create table if not exists users (id bigint primary key, name varchar(256));' -U test -h postgres example -> + compile +> + slickCodegenAll -$ exists target/scala-2.13/src_managed/main/com/example/Tables.scala -$ exists target/scala-2.13/src_managed/main/com/example/UsersTable.scala +$ exists src/main/scala/com/demo/Table.scala +$ exists src/main/scala/com/example/Etl.scala From 83395163143a6778177e7c9b5ee96910385dd639 Mon Sep 17 00:00:00 2001 From: Chunsen Wang Date: Wed, 21 Feb 2024 15:33:53 +0800 Subject: [PATCH 11/11] clean unsed files --- .github/workflows/ci.yml | 2 +- Dockerfile | 16 --------------- Makefile | 20 ------------------- build.sbt | 5 ----- docker-compose.yml | 17 ---------------- project/plugins.sbt | 5 ++--- scripts/github-actions/publish.sh | 4 ---- .../com/tubitv/CodeGenPostgresProfile.scala | 19 ++++++++++-------- .../tubitv/CustomizeSourceCodeGenerator.scala | 2 +- 9 files changed, 15 insertions(+), 75 deletions(-) delete mode 100644 Dockerfile delete mode 100644 Makefile delete mode 100644 docker-compose.yml delete mode 100755 scripts/github-actions/publish.sh diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 99143e2..7873aed 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -27,4 +27,4 @@ jobs: cache: 'sbt' - name: Run tests - run: sbt scripted + run: sbt scalafmtAll scripted diff --git a/Dockerfile b/Dockerfile deleted file mode 100644 index 3d36705..0000000 --- a/Dockerfile +++ /dev/null @@ -1,16 +0,0 @@ -FROM debian:buster-slim - -RUN apt-get update -RUN apt-get -y install locales-all - -ENV LANG ja_JP.UTF-8 -ENV LANGUAGE ja_JP:ja -ENV LC_ALL ja_JP.UTF-8 - -RUN apt-get update && \ - apt-get install -y build-essential \ - openjdk-11-jdk \ - curl -RUN apt-get install -y postgresql - -CMD "/bin/bash" diff --git a/Makefile b/Makefile deleted file mode 100644 index d523f7c..0000000 --- a/Makefile +++ /dev/null @@ -1,20 +0,0 @@ -.PHONY: test up down - -test: down up - if [ "$(SBT_VERSION)" == "" ]; then \ - echo "SBT_VERSION is not set"; \ - exit 1 ; \ - fi - docker-compose exec -T scala ./sbt ^^$(SBT_VERSION) test:compile clean - docker-compose exec -T scala ./sbt ^^$(SBT_VERSION) scripted - -up: - docker-compose build - docker-compose up -d - -down: - docker-compose down - -sbt: - curl -Ls https://git.io/sbt > sbt - chmod +x ./sbt diff --git a/build.sbt b/build.sbt index 10e42bf..d15bdfe 100644 --- a/build.sbt +++ b/build.sbt @@ -5,11 +5,6 @@ import scala.collection.JavaConverters.* enablePlugins(SbtPlugin) -//scalariformPreferences := scalariformPreferences.value -// .setPreference(AlignSingleLineCaseStatements, true) -// .setPreference(DoubleIndentConstructorArguments, true) -// .setPreference(DanglingCloseParenthesis, Preserve) - sbtPlugin := true name := """sbt-slick-codegen""" organization := "com.tubitv" diff --git a/docker-compose.yml b/docker-compose.yml deleted file mode 100644 index 07a5650..0000000 --- a/docker-compose.yml +++ /dev/null @@ -1,17 +0,0 @@ -version: '3' -services: - scala: - build: . - stdin_open: true - working_dir: $PWD - volumes: - - $PWD:$PWD - postgres: - environment: - - POSTGRES_DB=example - - POSTGRES_USER=test - - POSTGRES_PASSWORD=test - - POSTGRES_HOST_AUTH_METHOD=trust - image: "postgres:latest" - ports: - - "5432:5432" diff --git a/project/plugins.sbt b/project/plugins.sbt index 8eb9873..820d270 100644 --- a/project/plugins.sbt +++ b/project/plugins.sbt @@ -1,4 +1,3 @@ -//addSbtPlugin("org.scalariform" % "sbt-scalariform" % "1.8.3") -//addSbtPlugin("org.xerial.sbt" % "sbt-sonatype" % "3.9.18") -//addSbtPlugin("com.github.sbt" % "sbt-pgp" % "2.2.1") addSbtPlugin("com.github.sbt" % "sbt-dynver" % "5.0.1") +addSbtPlugin("org.scalameta" % "sbt-scalafmt" % "2.5.2") + diff --git a/scripts/github-actions/publish.sh b/scripts/github-actions/publish.sh deleted file mode 100755 index 9dc66ba..0000000 --- a/scripts/github-actions/publish.sh +++ /dev/null @@ -1,4 +0,0 @@ -#!/usr/bin/env bash - -SBT_JVM_OPTS="-J-Xms4g -J-Xmx4g -J-Xss8m -J-XX:MaxMetaspaceSize=1024m -J-XX:+UseG1GC" -sbt $SBT_JVM_OPTS publish \ No newline at end of file diff --git a/src/main/scala/com/tubitv/CodeGenPostgresProfile.scala b/src/main/scala/com/tubitv/CodeGenPostgresProfile.scala index c79087e..bd43e2b 100644 --- a/src/main/scala/com/tubitv/CodeGenPostgresProfile.scala +++ b/src/main/scala/com/tubitv/CodeGenPostgresProfile.scala @@ -1,6 +1,6 @@ package com.tubitv -import java.sql.Types.{ CHAR, LONGNVARCHAR, LONGVARCHAR, NCHAR, NVARCHAR, VARCHAR } +import java.sql.Types.{CHAR, LONGNVARCHAR, LONGVARCHAR, NCHAR, NVARCHAR, VARCHAR} import scala.concurrent.ExecutionContext @@ -9,14 +9,16 @@ import slick.jdbc.meta._ class CodeGenPostgresProfile(config: CodeGenConfig) extends PostgresProfile { - override def createModelBuilder(tables: Seq[MTable], ignoreInvalidDefaults: Boolean)(implicit ec: ExecutionContext): slick.jdbc.JdbcModelBuilder = new ModelBuilder(tables, ignoreInvalidDefaults)(ec) { + override def createModelBuilder(tables: Seq[MTable], ignoreInvalidDefaults: Boolean)(implicit + ec: ExecutionContext + ): slick.jdbc.JdbcModelBuilder = new ModelBuilder(tables, ignoreInvalidDefaults)(ec) { val typeMapper: PartialFunction[String, String] = config.byTypeMapper orElse { - case "name" | "text" | "varchar" => "String" - case "int4" | "serial" => "Int" - case "int2" | "smallserial" => "Short" + case "name" | "text" | "varchar" => "String" + case "int4" | "serial" => "Int" + case "int2" | "smallserial" => "Short" case "int8" | "bigserial" | "oid" => "Long" - case "bool" | "bit" => "Boolean" + case "bool" | "bit" => "Boolean" } val arraryDetector: PartialFunction[String, String] = { @@ -35,7 +37,8 @@ class CodeGenPostgresProfile(config: CodeGenConfig) extends PostgresProfile { if (rt == "String" && !isJdbcStringType(meta.sqlType)) { logger.warn( s"Column [${meta.name}] in table [${meta.table.name}] with type [${meta.typeName}] " + - s"does not have a custom mapping, so defaults map as [String], consider defining a custom type mapper") + s"does not have a custom mapping, so defaults map as [String], consider defining a custom type mapper" + ) } rt }) @@ -48,7 +51,7 @@ class CodeGenPostgresProfile(config: CodeGenConfig) extends PostgresProfile { private def isJdbcStringType(sqlType: Int) = sqlType match { case CHAR | VARCHAR | LONGVARCHAR | NCHAR | NVARCHAR | LONGNVARCHAR => true - case _ => false + case _ => false } } } diff --git a/src/main/scala/com/tubitv/CustomizeSourceCodeGenerator.scala b/src/main/scala/com/tubitv/CustomizeSourceCodeGenerator.scala index 92a35d4..d362b53 100644 --- a/src/main/scala/com/tubitv/CustomizeSourceCodeGenerator.scala +++ b/src/main/scala/com/tubitv/CustomizeSourceCodeGenerator.scala @@ -1,6 +1,6 @@ package com.tubitv -import slick.{ model => m } +import slick.{model => m} import slick.codegen.SourceCodeGenerator class CustomizeSourceCodeGenerator(model: m.Model, config: CodeGenConfig) extends SourceCodeGenerator(model) {