Skip to content

User Stories: AutoPlugins

kenji yoshida edited this page Mar 11, 2014 · 2 revisions

This document details the desired workflow/use cases of the sbt project natures feature. This is a loosely specified, poorly defined set of "stories" (or terminal outputs) that we hope show our goal towards improving the consumption and definition of plugins within the sbt ecosystem.

Story: User adds pgp plugin to the build.

User opens project/plugins.sbt and adds the line:

// Was here before.
addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.3.0")

addSbtPlugin("com.typesafe.sbt" % "sbt-pgp" % "1.0")

The user's build.sbt file is unchanged. The pgp plugin's required natures should be detected on any default/play projects and added to these projects automatically.

Story: User wants to aggregate tasks on a project, but not have that project actually have any behavior.

User takes his existing build.sbt file:

val lib = project

val web = project.addPlugins(Web, PlayPlugin).dependsOn(lib) // scala is default

And declares the root project to be a "non-ivy" project which aggregates the others:

val lib = project

val web = project.addPlugins(Web, PlayPlugin).dependsOn(lib)

val root = Project("root", file(".")).disablePlugins(IvyModule).aggregate(lib,web)

User Story: User wants to create an sbt plugin that enhances play projects.

  1. The user declares a dependency from his plugin to the play plugin:

Note: This is in build.sbt, not project/plugins.sbt

name := "my-play-build-plugin"

organization := "my.company"

version := "1.0"

sbtPlugin := true

addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.3.0")
  1. The user declares his plugin settings via the AutoPlugin interface. This will ensure that the PlayPlugin is enabled for a project before adding its own settings.

src/main/scala/MyPlugin.scala:

import play.PlayPlugin

object MyPlugin extends sbt.AutoPlugin {
  def select = PlayPlugin

  // These will only be added to projects that have the play plugin settings added as well.
  def projectSettings = Seq(sbt.Keys.prompt := "awesome is enabled >")
}

User Story: User attempt to use a plugin which is not configured correctly.

  1. User add the sbt-pgp plugin to project/plugins.sbt:
addSbtPlugin("com.typesafe.sbt" % "sbt-pgp" % "1.0")

User attempts to call a pgp task from a project where it is not enabled:

sbt>  publishSigned
error: publishSigned is not defined on project 'root'.
       This tasks is defined by the 'com.typesafe.sbt.SbtPgp' plugin but was not enabled.
       To enable, please add the following plugin(s):
       * sbt.plugins.IvyModule

User Story: user wants to create a C++ workflow that does not include the Java workflow

  1. User creates a 'root' plugin representing the core C++ features:
object CppPlugin extends RootPlugin {
   override def projectSettings = ...
   override def globalSettings = ...
}
  1. Create a helper project instantiation.
def cppProject(name: String, dir: File): Project =
    Project(name,file).disablePlugins(plugins.JvmModule).addPlugins(CppPlugin)

User Story: User wants to ensure the plugins contributing tasks can have users control the order.

Currently the order of plugin defined in project/plugins.sbt will be respected, except that plugins which depend on other plugins will have their settings added after their dependencies.

In addition, you can use the following pattern:

We have a pipeline of tasks to perform in an order:

case class PipelineStage(priority: Int, task: Task[Unit])

object PipelinerPlugin extends RootPlugin {
   val rawPipeline = settingKey[Seq[PipelineStage]]
   val pipeline = taskKey[Unit]
   override val projectSettings = Seq(
    rawPipeline := Nil,
    pipeline := (Def.taskDyn {
      rawPipeline.value.sortBy(_.priority).map(_.task).join
    }).value
   )
}

object PipelineStageExamplePlugin extends AutoPlugin {
  val stageExamplePriority = settingKey[Int]("my priority")
  val stageExampleAction = taskKey[Unit]("my task to do stuff")

  def select = PipelinerPlugin

  override val projectSettings = Seq(
     stageExamplePriority := 1,
     stageExampleAction := println("THIS is an example stage")
     rawPipeline += stageExamplePriority.value -> stageExampleAction.toTask.value
  )
}