Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

Sbt職人のススメ

7,238 views

Published on

Sbt職人のススメ

Published in: Software
  • Hello! Get Your Professional Job-Winning Resume Here - Check our website! https://vk.cc/818RFv
       Reply 
    Are you sure you want to  Yes  No
    Your message goes here

Sbt職人のススメ

  1. 1. sbt(すぶた)職人のススメ かとじゅん@j5ik2o
  2. 2. 誰? • 加藤潤一(かとうじゅんいち) • j5ik2o = junichikato = j unich i k at o = j5ik2o • 年齢 0x2B • ChatWork社 テックリード • スキル • DDD x Scala 伝道師 • ケトジェニック・ダイエット・アドバイザー • 他にもスキル習得中。8月公開予定
  3. 3. 今日のテーマは…
  4. 4. 酢豚じゃなくsbtの話です
  5. 5. アジェンダ • 基礎 • 実践 • セッティング • タスク • プラグイン
  6. 6. 基礎
  7. 7. インストール $ brew install sbt
 ==> Downloading https://homebrew.bintray.com/bottles/ sbt-0.13.8.yosemite.bottle.tar.gz Already downloaded: /Library/Caches/Homebrew/ sbt-0.13.8.yosemite.bottle.tar.gz ==> Pouring sbt-0.13.8.yosemite.bottle.tar.gz ==> Caveats You can use $SBT_OPTS to pass additional JVM options to SBT: SBT_OPTS="-XX:+CMSClassUnloadingEnabled -XX:MaxPermSize=256M" This formula is now using the standard typesafe sbt launcher script. Project specific options should be placed in .sbtopts in the root of your project. Global settings should be placed in /usr/local/etc/sbtopts ==> Summary 🍺 /usr/local/Cellar/sbt/0.13.8: 5 files, 1.2M
  8. 8. プロジェクトの最小構成 $ brew install typesafe-activator ==> Downloading https://downloads.typesafe.com/typesafe-activator/1.3.2/typesafe- activator-1.3.2.zip Already downloaded: /Library/Caches/Homebrew/typesafe-activator-1.3.2.zip 🍺 /usr/local/Cellar/typesafe-activator/1.3.2: 4413 files, 470M, built in 9 seconds $ activator new sbt-simple Fetching the latest list of templates... Browse the list of templates: http://typesafe.com/activator/templates Choose from these featured templates or enter a template name: 1) minimal-akka-java-seed 2) minimal-akka-scala-seed 3) minimal-java 4) minimal-scala 5) play-java 6) play-scala (hit tab to see a list of all templates) > 4
 OK, application "sbt-simple" is being created using the "minimal-scala" template. To run "sbt-simple" from the command line, "cd sbt-simple" then: /Users/cw-junichi/temp/sbt-simple/activator run To run the test for "sbt-simple" from the command line, "cd sbt-simple" then: /Users/cw-junichi/temp/sbt-simple/activator test To run the Activator UI for "sbt-simple" from the command line, "cd sbt-simple" then: /Users/cw-junichi/temp/sbt-simple/activator ui
  9. 9. 生成されたファイル群 . "## LICENSE "## activator "## activator-launch-1.3.2.jar "## build.sbt "## project $   &## build.properties &## src "## main $   &## scala $   &## com $   &## example $   &## Hello.scala &## test &## scala &## HelloSpec.scala
  10. 10. build.sbt name := """sbt-simple""" version := "1.0" scalaVersion := "2.11.7" // Change this to another test framework if you prefer libraryDependencies += "org.scalatest" %% "scalatest" % "2.2.4" % "test" // Uncomment to use Akka //libraryDependencies += "com.typesafe.akka" %% "akka-actor" % "2.3.11" project/build.properties Activator-generated Properties #Thu Jul 30 14:55:12 JST 2015 template.uuid=e17acfbb-1ff5-41f5-b8cf-2c40be6a8340 sbt.version=0.13.8 # sbtのバージョンを固定できる
  11. 11. build.sbt vs project/Build.scala • sbt 0.12の時代は、マルチプロジェクトのために Build.scalaを利用していた。 • sbt 0.13からは、build.sbtだけでマルチプロジェク トに対応できるようになった。加えてval, def も定 義できるようになった。 • Build.scalaは積極的に使わなくなった?
  12. 12. sbt compile $ sbt [info] Loading project definition from /Users/cw-junichi/temp/sbt-simple/project [info] Updating {file:/Users/cw-junichi/temp/sbt-simple/project/}sbt-simple-build... [info] Resolving org.fusesource.jansi#jansi;1.4 ... [info] Done updating. [info] Set current project to sbt-simple (in build file:/Users/cw-junichi/temp/sbt- simple/)
 > compile [info] Updating {file:/Users/cw-junichi/temp/sbt-simple/}sbt-simple... [info] Resolving jline#jline;2.12.1 ... [info] Done updating. [info] Compiling 1 Scala source to /Users/cw-junichi/temp/sbt-simple/target/ scala-2.11/classes... [success] Total time: 3 s, completed 2015/07/30 15:37:49 $ sbt compile [info] Loading project definition from /Users/cw-junichi/temp/sbt-simple/project [info] Set current project to sbt-simple (in build file:/Users/cw-junichi/temp/ sbt-simple/) [info] Updating {file:/Users/cw-junichi/temp/sbt-simple/}sbt-simple... [info] Resolving jline#jline;2.12.1 ... [info] Done updating. [info] Compiling 1 Scala source to /Users/cw-junichi/temp/sbt-simple/target/ scala-2.11/classes... [success] Total time: 3 s, completed 2015/07/30 15:38:19
  13. 13. sbt run package com.example object Hello { def main(args: Array[String]): Unit = { println("Hello, world!") } } $ sbt run [info] Loading project definition from /Users/cw-junichi/temp/sbt-simple/ project [info] Set current project to sbt-simple (in build file:/Users/cw-junichi/ temp/sbt-simple/) [info] Running com.example.Hello Hello, world! [success] Total time: 0 s, completed 2015/07/30 15:40:02
  14. 14. sbt test import org.scalatest._ class HelloSpec extends FlatSpec with Matchers { "Hello" should "have tests" in { true should === (true) } } $ sbt test [info] Loading project definition from /Users/cw-junichi/temp/sbt-simple/project [info] Set current project to sbt-simple (in build file:/Users/cw-junichi/temp/sbt-simple/) [info] Compiling 1 Scala source to /Users/cw-junichi/temp/sbt-simple/target/scala-2.11/ test-classes... [info] HelloSpec: [info] Hello [info] - should have tests [info] Run completed in 294 milliseconds. [info] Total number of tests run: 1 [info] Suites: completed 1, aborted 0 [info] Tests: succeeded 1, failed 0, canceled 0, ignored 0, pending 0 [info] All tests passed. [success] Total time: 4 s, completed 2015/07/30 15:42:53
  15. 15. IDEAでそのまま読み込めます
  16. 16. マルチプロジェクト lazy val commonSettings = Seq( organization := "com.example", version := "0.1.0", scalaVersion := "2.11.4" ) lazy val core = (project in file("core")). settings(commonSettings: _*). settings( // other settings ) lazy val util = (project in file("util")). settings(commonSettings: _*). settings( // other settings ) lazy val root = (project in file(".")). aggregate(util, core)
  17. 17. プラグイン addSbtPlugin("com.typesafe.sbt" % "sbt-scalariform" % "1.3.0") Add following to project/plugins.sbt scalariformSettings Add following to build.sbt $ sbt scalariformFormat $ sbt compile [info] Loading project definition from /Users/cw-junichi/temp/sbt-simple/project [info] Set current project to sbt-simple (in build file:/Users/cw-junichi/temp/sbt-simple/) [info] Updating {file:/Users/cw-junichi/temp/sbt-simple/}sbt-simple... [info] Formatting 1 Scala source {file:/Users/cw-junichi/temp/sbt-simple/}sbt-simple(compile) ... [info] Resolving org.scala-lang#scala-library;2.11.7 ... [info] Reformatted 1 Scala source {file:/Users/cw-junichi/temp/sbt-simple/}sbt-simple(compile). [info] Resolving jline#jline;2.12.1 ... [info] Done updating. [success] Total time: 1 s, completed 2015/07/30 16:24:30 Execute plugin’s function.
  18. 18. 実践
  19. 19. コマンド def hello = Command.command("hello") { state => println("Hello") state } commands ++= Seq(hello) コマンドの定義 $ sbt hello
 [info] Loading project definition from /Users/cw-junichi/temp/sbt-simple/ project [info] Set current project to sbt-simple (in build file:/Users/cw-junichi/ temp/sbt-simple/) Hello コマンド名で呼び出す
  20. 20. sbtで使えるキー • キーには3種類ある • SettingKey[T] • 一度だけ値が計算されるキー(値はプロジェクトの読み込 み時に計算され、保存される) • TaskKey[T] • 毎回再計算されるタスクを呼び出す、副作用を伴う可能性 のある値のキー。 • InputKey[T] • コマンドラインの引数を入力として受け取るタスクのキー。 • 組み込みキー • import sbt.Keys._
  21. 21. セッティング lazy val message = settingKey[String]("message")
 message := “hello” セッティングキーを定義して、セッティングに対して値を割り当てる $ sbt message
 [info] Loading project definition from /Users/cw-junichi/temp/sbt-simple/ project [info] Set current project to sbt-simple (in build file:/Users/cw-junichi/ temp/sbt-simple/) [info] hello セッティングキー名で呼び出す
  22. 22. タスク lazy val hello = taskKey[Unit]("An example task")
 hello := { println(“Hello!") } タスクキーを定義して、タスクに対して関数を割り当てる $ sbt hello
 [info] Loading project definition from /Users/cw-junichi/temp/sbt-simple/ project [info] Set current project to sbt-simple (in build file:/Users/cw-junichi/ temp/sbt-simple/) Hello! [success] Total time: 0 s, completed 2015/07/31 10:51:11 タスクキー名で呼び出す
  23. 23. タスクからセッティングを参照する lazy val message = settingKey[String]("message") lazy val say = taskKey[Unit]("say task") message := "hello" say := { println(message.value) } セッティングキー名.valueで割当られている値を参照する $ sbt say [info] Loading project definition from /Users/cw-junichi/temp/sbt-simple/ project [info] Set current project to sbt-simple (in build file:/Users/cw-junichi/ temp/sbt-simple/) hello [success] Total time: 0 s, completed 2015/07/31 12:04:46
  24. 24. タスクの結果を得る lazy val message = settingKey[String]("message") lazy val modifier = taskKey[String]("modifier task") lazy val display = taskKey[Unit]("display task") message := "hello" modifier := { "{{{" + message.value + "}}}" } display := { println(modifier.value) } タスクキー.valueでタスクの結果を得ることができる $ sbt display [info] Loading project definition from /Users/cw-junichi/temp/sbt-simple/ project [info] Set current project to sbt-simple (in build file:/Users/cw-junichi/ temp/sbt-simple/) {{{hello}}} [success] Total time: 0 s, completed 2015/08/01 7:43:23
  25. 25. タスクの実行意味論 val startServer = taskKey[Unit]("start server") val stopServer = taskKey[Unit]("stop server") val sampleIntTask = taskKey[Int]("A sample int task.") val sampleStringTask = taskKey[String]("A sample string task.") startServer := { println("starting...") Thread.sleep(500) } stopServer := { println("stopping...") Thread.sleep(500) } sampleIntTask := { startServer.value val sum = 1 + 2 println("sum: " + sum) stopServer.value // THIS WON'T WORK sum } sampleStringTask := { startServer.value val s = sampleIntTask .value.toString println("s: " + s) s } $ sbt sampleStringTask [info] Loading project definition from / Users/cw-junichi/temp/sbt-simple/project [info] Set current project to sbt-simple (in build file:/Users/cw-junichi/temp/sbt- simple/) stopping... starting... sum: 3 s: 3 [success] Total time: 1 s, completed 2015/07/31 12:40:28 sampleIntTask := { ServerUtil.startServer try { val sum = 1 + 2 println("sum: " + sum) sum } finally { ServerUtil.stopServer } }
  26. 26. スコープ • マルチプロジェクトで、各プロジェクトにおいて 同じキーが別の値を取ることができる • メインとテストのソースで異なるようにコンパイ ルしたければ、compileキーは別の値と取ること ができる • つまり、キーとスコープによって値が決定され る • スコープには、プロジェクト、コンフィグレーショ ン、タスクがある。
  27. 27. タスクのスコープ lazy val say = taskKey[Unit](“say task”)
 
 lazy val message = settingKey[String]("message") lazy val modifier = taskKey[String]("modifier task") lazy val display = taskKey[Unit]("display task") message in say := "hello" modifier in say := { "{{{" + (message in say).value + "}}}" } display in say := { println((modifier in say).value) } $ sbt say::display [info] Loading project definition from /Users/cw-junichi/temp/sbt-simple/ project [info] Set current project to sbt-simple (in build file:/Users/cw-junichi/ temp/sbt-simple/) {{{hello}}} [success] Total time: 0 s, completed 2015/08/01 7:45:33
  28. 28. sbt plugin sbtPlugin := true
 
 name := """sbt-simple-plugin""" version := "1.0" scalaVersion := “2.10.5" libraryDependencies += "org.scalatest" %% "scalatest" % "2.2.4" % "test" sbtPlugin := trueとするだけ、他は通常のsbtプロジェクトと同じ
  29. 29. chatwork/sbt-docker
  30. 30. chatwork/sbt-docker $ sbt docker::build [info] Loading project definition from /Users/cw-junichi/sbt-docker/src/sbt-test/sbt-docker/simple/project [info] Set current project to simple (in build file:/Users/cw-junichi/sbt-docker/src/sbt-test/sbt-docker/simple/) [info] generated docker file from template file. dockerTemplate = /Users/cw-junichi/sbt-docker/src/sbt-test/sbt-docker/simple/ docker/Dockerfile.ftl, templateContext = Map(name -> simple, version -> 0.1-SNAPSHOT), dockerfile = /Users/cw-junichi/sbt- docker/src/sbt-test/sbt-docker/simple/docker/Dockerfile [info] Set(/Users/cw-junichi/sbt-docker/src/sbt-test/sbt-docker/simple/target/docker/Dockerfile, /Users/cw-junichi/sbt-docker/ src/sbt-test/sbt-docker/simple/target/docker/bin/echo.sh) [info] userName = , emailAddress = [info] Step 0 : FROM busybox [info] ---> 8c2e06607696 [info] Step 1 : ADD bin/echo.sh / [info] ---> f7bffdfc573c [info] Removing intermediate container f6a8e236bf34 [info] Step 2 : CMD sh /echo.sh simple-0.1-SNAPSHOT [info] ---> Running in d761354df21a [info] ---> 7ae24d96c42c [info] Removing intermediate container d761354df21a [info] Successfully built 7ae24d96c42c [info] docker build, imageId = 7ae24d96c42c [success] Total time: 3 s, completed 2015/07/31 13:58:46 docker build docker buildの前にアプリケーションをbuildできる。
  31. 31. ディレクトリ構成 "## build.sbt "## project $   "## build.properties $   "## plugins.sbt $   "## scripted.sbt "## release.sbt "## scripted.sbt "## src $   "## main $   $   "## resources $   $   $   &## logback.xml $   $   &## scala $   $   &## com $   $   &## chatwork $   $   &## sbt $   $   &## docker $   $   "## BuildOptions.scala $   $   "## DockerfileBuilder.scala $   $   "## SbtDocker.scala $   $   "## SbtDockerKeys.scala $   $   &## SbtDockerPlugin.scala $   &## sbt-test $   &## sbt-docker $   &## simple $   "## build.sbt $   "## docker $   $   "## Dockerfile.ftl $   $   &## bin $   $   &## echo.sh $   &## project $      &## plugins.sbt &## version.sbt
  32. 32. build.sbt import scalariform.formatter.preferences._ scalaVersion := "2.10.5" sonatypeProfileName := "com.chatwork" organization := "com.chatwork" publishMavenStyle := true publishArtifact in Test := false pomIncludeRepository := { _ => false } pomExtra := { <url>https://github.com/chatwork/sbt-docker</url> <licenses> <license> <name>The MIT License</name> <url>http://opensource.org/licenses/MIT</url> </license> </licenses> <scm> <url>git@github.com:chatwork/sbt-docker.git</url> <connection>scm:git:github.com/chatwork/sbt-docker</ connection> <developerConnection>scm:git:git@github.com:chatwork/ sbt-docker.git</developerConnection> </scm> <developers> <developer> <id>cw-junichikato</id> <name>Junichi Kato</name> </developer> </developers> } name := "sbt-docker" sbtPlugin := true
 
 resolvers ++= Seq( "Sonatype OSS Snapshot Repository" at "https:// oss.sonatype.org/content/repositories/snapshots/", "Sonatype OSS Release Repository" at "https:// oss.sonatype.org/content/repositories/releases/", "Typesafe Releases" at "http://repo.typesafe.com/ typesafe/releases/" )
 
 libraryDependencies ++= Seq( "com.spotify" % "docker-client" % "2.7.7", "ch.qos.logback" % "logback-classic" % "1.1.3", "org.slf4j" % "slf4j-api" % "1.7.12", "org.freemarker" % "freemarker" % "2.3.14" ) scalariformSettings ScalariformKeys.preferences := ScalariformKeys.preferences.value .setPreference(AlignParameters, true) .setPreference(AlignSingleLineCaseStatements, true) .setPreference(DoubleIndentClassDeclaration, true) .setPreference(PreserveDanglingCloseParenthesis, true) .setPreference(MultilineScaladocCommentsStartOnFirstLin e, false) credentials <<= Def.task { val ivyCredentials = (baseDirectory in LocalRootProject).value / ".credentials" val result = Credentials(ivyCredentials) :: Nil result }
  33. 33. project/plugins.sbt logLevel := Level.Warn addSbtPlugin("org.xerial.sbt" % "sbt-sonatype" % "0.5.0") addSbtPlugin("com.jsuereth" % "sbt-pgp" % "1.0.0") addSbtPlugin("com.typesafe.sbt" % "sbt-scalariform" % "1.3.0") addSbtPlugin("com.github.gseitz" % "sbt-release" % "1.0.0")
  34. 34. プラグイン本体 package com.chatwork.sbt.docker import sbt.Keys._ import sbt._ import sbt.plugins.IvyPlugin object SbtDockerPlugin extends AutoPlugin { override def trigger = allRequirements override def requires: Plugins = IvyPlugin object autoImport extends SbtDockerKeys import SbtDocker._ import SbtDockerKeys._ override def projectSettings: Seq[Def.Setting[_]] = Seq( name in docker := (name in thisProjectRef).value, sourceDirectory in docker := baseDirectory.value / "docker", buildDirectory in docker := baseDirectory.value / "target" / "docker", sourceFiles in docker := Seq(), login in docker := false, emailAddress in docker := "", userName in docker := "", password in docker := "", buildOptions in docker := Set.empty[BuildOptions.Value], build in docker <<= dockerBuildTask dependsOn (copySourceFiles in docker), copySourceFiles in docker <<= copySourceFilesTask, dockerfileTemplate in docker := (sourceDirectory in docker).value / "Dockerfile.ftl", dockerfile in docker := (sourceDirectory in docker).value / "Dockerfile", templateContext in docker := Map( "name" -> (name in thisProjectRef).value, "version" -> (version in thisProjectRef).value ), generateDockerfile in docker <<= generateDockerfileTask, push in docker <<= dockerPushTask, pull in docker <<= dockerPullTask, list in docker <<= dockerListImagesTask, start in docker <<= dockerStartTask dependsOn (copySourceFiles in docker), startAndWait in docker <<= dockerStartAndWaitTask dependsOn (copySourceFiles in docker) ) }
  35. 35. Key関係 val build = taskKey[Option[String]]("build") val buildOptions = settingKey[Set[BuildOptions.Value]] ("build-options") val buildDirectory = settingKey[File]("build- directory") // --- val dockerfileTemplate = settingKey[File]("dockerfile- template") val dockerfile = settingKey[File]("dockerfile") val templateContext = settingKey[Map[String, String]] ("template-context") val generateDockerfile = taskKey[File]("generate- dockerfile") // --- val push = taskKey[Unit]("push") val pull = taskKey[Unit]("pull") val list = taskKey[Unit]("list") val start = taskKey[Option[Future[String]]]("start") val startAndWait = taskKey[Unit]("start-and-wait") } package com.chatwork.sbt.docker import sbt._ import scala.concurrent.Future object SbtDockerKeys extends SbtDockerKeys trait SbtDockerKeys { val docker = taskKey[Unit]("docker") val login = settingKey[Boolean]("login") val emailAddress = settingKey[String]("email- address") val userName = settingKey[String]("user-name") val password = settingKey[String]("password") // --- val sourceFiles = taskKey[Seq[(File, String)]] ("source-files") val copySourceFiles = taskKey[Set[File]] ("copy-source-files") // ---
  36. 36. タスクの定義 trait SbtDocker { def dockerBuildTask: Def.Initialize[Task[Option[String]]] = Def.task { val logger = streams.value.log val sut = dockerClient.value val workDir = (buildDirectory in docker).value.toPath val repositoryName = (name in docker).value val bo = (buildOptions in docker).value.map(toBuildParameter) Try { val result = sut.build(workDir, repositoryName, progressHandler(logger) { pm => Some(pm.stream()) }, bo.toArray: _*) logger.info(s"docker build, imageId = $result") Some(result) }.recover { case ex: DockerException => logger.error(ex.toString) None }.get } }
  37. 37. AutoPlugin • これまでは • project/plugins.sbtにプラグインを追加 • build.sbtにプラグイン固有のセッティングを追加 • AutoPluginでは、タスク、セッティングなどのビルド定義を自動的に追加できる • 明示的に利用するプラグインを指定できる。 • (project in file(“.”)).enablePlugins(a, b).disablePlugins(c) • requires • 依存しているプラグイン(AutoPlugin)を指定できる • trigger • プラグインが動作する条件。allRequirementsは、すべての依存プラグインが 使えるようになってから動作できるようにする。 • autoImport • importの自動化。autoImportメンバー内に存在するものはimport宣言なしで 利用できるようになる
  38. 38. お知らせ
  39. 39. ありがとうございました

×