Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Split out impl to plugin + lib, so others can piggyback this for their own defaults plugins #2

Merged
merged 6 commits into from
Nov 15, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
79 changes: 75 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,11 @@
[![GitHub Release](https://img.shields.io/github/v/release/laserdisc-io/sbt-laserdisc-defaults)](https://github.com/laserdisc-io/sbt-laserdisc-defaults/releases/latest)

A plugin to reduce the boilerplate in many of the simpler laserdisc projects (but can be used in any sbt project).
It auto-configures things like sbt & scala versioning, cross compiling, scalafmt & git configuration and more.
It auto-configures things like sbt & scala versioning, cross compiling, scalafmt & git configuration and more.

See [what it does](#what-it-does) for full details.
The plugin can be used as-is, or extended into your own custom plugin, picking and choosing the defaults you like!

## Usage
## Direct Usage

Add the following to `project/plugins.sbt` :

Expand All @@ -29,6 +29,7 @@ lazy val root = (project in file("."))
> **Note**
> Any settings in your `build.sbt` **override** those set by this plugin.


## What it Does

When SBT loads your project, the [LaserDiscDefaultsPlugin](src/main/scala/laserdisc/sbt/LaserDiscDefaultsPlugin.scala) will automatically perform the following:
Expand All @@ -39,6 +40,8 @@ When SBT loads your project, the [LaserDiscDefaultsPlugin](src/main/scala/laserd
* [sbt-git](https://github.com/sbt/sbt-git) - automatic versioning based on the current commit hash/tag
* [sbt-native-packager](https://github.com/sbt/sbt-native-packager) -

### Categories

**Apply [Core](src/main/scala/laserdisc/sbt/category/Core.scala) Settings**

* Apply a bunch of default values:
Expand Down Expand Up @@ -103,7 +106,75 @@ When SBT loads your project, the [LaserDiscDefaultsPlugin](src/main/scala/laserd

* Fails the `dist` task if [CODEOWNERS](https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/customizing-your-repository/about-code-owners) file is missing or empty.

## Releasing
## Extending Your Own

This SBT build comprises two modules:

* [plugin](plugin)<br>
* This module builds and publishes the `sbt-laserdisc-defaults` SBT plugin.
* There is minimal code in this module, just the [concrete implementation](./plugin/src/main/scala/laserdisc/sbt/LaserDiscDefaultsPlugin.scala) of the shared code (next)

* [plugin-shared](plugin-shared)<br>
* All the logic of the plugin is here, rolling up under an extendable `LaserDiscDefaultsPluginBase` abstract implementation.
* This library is published as a dependency JAR `io.laserdisc:sbt-laserdisc-defaults-shared` for custom implementations to extend.

To build your own sbt plugin:

1. Define an SBT plugin project, with a dependency on the shared library, and enable all the relevant plugins:
```sbt
lazy val root = project
.in(file("."))
.settings(
sbtPlugin := true,
organization := "com.modaoperandi",
name := "sbt-moda-defaults",
addSbtPlugin("io.laserdisc" % "sbt-laserdisc-defaults-shared" % "<version>")
.... etc ...
```
2. Then, create your implementation of [`LaserDiscDefaultsPluginBase`](./plugin-shared/src/main/scala/laserdisc/sbt/LaserDiscDefaultsPluginBase.scala). The `sbt-laserdisc-defaults` plugin does this, so look [at the source](./plugin/src/main/scala/laserdisc/sbt/LaserDiscDefaultsPlugin.scala) for the actual code, but at a high level, it looks like this:
```scala

object LaserDiscDefaultsPlugin extends LaserDiscDefaultsPluginBase {

// define the settings keys with your desired naming strategy
object autoImport {
lazy val laserdiscFailOnWarn = settingKey[Boolean](Compiler.FailOnWarnKeyDesc)
lazy val laserdiscCompileTarget = settingKey[CompileTarget](Compiler.CompileTargetKeyDesc)
// .. etc ..
}

// define the publishing settings everyone who uses this plugin should have
private val laserdiscDefaults = new GithubPublishDefaults {
override def githubOrg: String = "springfield-nuclear"
override def orgName: String = "Springfield Nuclear Power Plant"
override def groupId: String = "com.simpsonsarchive.nuclear"
override def licenseCheck: LicenseCheck = LicenseRequired
}

// for context if logging/errors is necessary (use https://github.com/sbt/sbt-buildinfo)
override implicit val pluginCtx: PluginContext = PluginContext(
pluginName = PluginBuildInfo.name,
pluginVersion = PluginBuildInfo.version,
pluginHomepage = "https://github.com/springfield-nuclear/springfield-nuclear-plant"
)

// select all the category implementation you want (or create your own), passing in the key defs from above
override val categories: Seq[DefaultsCategory] = Seq(
Publishing(laserdiscDefaults, laserdiscPublishDefaults, laserdiscRepoName),
Compiler(laserdiscFailOnWarn, laserdiscCompileTarget),
Standards(),
Core() // keep last, so the warning message about defaults being used shows first
)

}


```

And that's it!


## Releasing This Plugin

Draft [a new release](https://github.com/laserdisc-io/sbt-laserdisc-defaults/releases/new), ensuring the format of the release follows the `v1.2.3` format (note the `v` prefix), and the appropriate [Github Action](.github/workflows/release.yaml) will publish version `1.2.3` (without the v) to sonatype.

Expand Down
85 changes: 46 additions & 39 deletions build.sbt
Original file line number Diff line number Diff line change
@@ -1,35 +1,60 @@
ThisBuild / organization := "io.laserdisc"
ThisBuild / organizationName := "LaserDisc"

// these are plugins that are needed by the projects that use _this_ plugin (see `LaserDiscDefaultsPlugin.requires`)
addSbtPlugin("org.scalameta" % "sbt-scalafmt" % "2.5.2")
addSbtPlugin("com.github.sbt" % "sbt-git" % "2.1.0")
addSbtPlugin("com.github.sbt" % "sbt-native-packager" % "1.10.4")
addSbtPlugin("com.thoughtworks.sbt-api-mappings" % "sbt-api-mappings" % "3.0.2")
ThisBuild / scalaVersion := "2.12.20"

lazy val root = project
.in(file("."))
lazy val root = (project in file("."))
.aggregate(`plugin`, `plugin-shared`)
.settings(
scalaVersion := "2.12.20",
sbtPlugin := true,
name := "sbt-laserdisc-defaults",
moduleName := "sbt-laserdisc-defaults",
description := "SBT defaults for LaserDisc projects",
compileSettings,
testSettings,
publishSettings,
dependencies,
name := "sbt-laserdisc-defaults-root",
addCommandAlias("format", ";scalafmtAll;scalafmtSbt"),
addCommandAlias("checkFormat", ";scalafmtCheckAll;scalafmtSbtCheck"),
addCommandAlias("build", ";checkFormat;clean;scripted"), // note: `scripted` to invoke plugin tests
addCommandAlias("release", ";build;publish")
)
.enablePlugins(SbtPlugin, JavaAppPackaging, ScalafmtPlugin, BuildInfoPlugin, GitVersioning)

lazy val plugin = project
.in(file("plugin"))
.settings(
name := "sbt-laserdisc-defaults",
moduleName := "sbt-laserdisc-defaults",
description := "SBT defaults for LaserDisc projects",
buildInfoObject := "PluginBuildInfo",
buildInfoPackage := "laserdisc.sbt",
scriptedLaunchOpts ++=
Seq(
"-Xmx1024M",
"--add-opens=java.base/java.util=ALL-UNNAMED", // to support sbt-dotenv
"--add-opens=java.base/java.lang=ALL-UNNAMED", // to support sbt-dotenv
s"-Dplugin.version=${version.value}",
s"-Dplugin.project.rootdir=${(ThisBuild / baseDirectory).value.absolutePath}",
s"-Dsbt.boot.directory=${file(sys.props("user.home")) / ".sbt" / "boot"}" // https://github.com/sbt/sbt/issues/3469
),
scriptedBufferLog := false, // set to true to suppress detailed scripted test output
compileSettings,
publishSettings
)
.dependsOn(`plugin-shared`)
.enablePlugins(BuildInfoPlugin, SbtPlugin)

lazy val `plugin-shared` = project
.in(file("plugin-shared"))
.settings(
name := "sbt-laserdisc-defaults-shared",
compileSettings,
publishSettings,
Compile / resourceGenerators += FileTemplates.copyToResources, // crucial for templating - see function comment
addSbtPlugin("org.scalameta" % "sbt-scalafmt" % "2.5.2"),
addSbtPlugin("com.github.sbt" % "sbt-git" % "2.1.0"),
addSbtPlugin("com.github.sbt" % "sbt-native-packager" % "1.10.4"),
addSbtPlugin("com.thoughtworks.sbt-api-mappings" % "sbt-api-mappings" % "3.0.2"),
libraryDependencies ++= Seq(
"org.apache.maven" % "maven-artifact" % "3.9.9"
)
)
.enablePlugins(SbtPlugin, JavaAppPackaging, ScalafmtPlugin, GitPlugin)

def compileSettings = Seq(
Compile / resourceGenerators += FileTemplates.copyToResources,
buildInfoObject := "PluginInfo",
buildInfoPackage := "laserdisc.sbt.defaults",
scalacOptions ++= Seq(
"-encoding",
"UTF-8", // source files are in UTF-8
Expand All @@ -45,25 +70,12 @@ def compileSettings = Seq(
)
)

def testSettings = Seq(
scriptedLaunchOpts ++=
Seq(
"-Xmx1024M",
"--add-opens=java.base/java.util=ALL-UNNAMED", // to support sbt-dotenv
"--add-opens=java.base/java.lang=ALL-UNNAMED", // to support sbt-dotenv
s"-Dplugin.version=${version.value}",
s"-Dplugin.project.rootdir=${(ThisBuild / baseDirectory).value.absolutePath}",
s"-Dsbt.boot.directory=${file(sys.props("user.home")) / ".sbt" / "boot"}" // https://github.com/sbt/sbt/issues/3469
),
scriptedBufferLog := false // set to true to suppress detailed scripted test output
)

lazy val publishSettings = Seq(
Test / publishArtifact := false,
pomIncludeRepository := (_ => false),
organization := "io.laserdisc",
homepage := Some(url("http://laserdisc.io/sbt-laserdisc-defaults")),
developers := List(Developer("barryoneill", "Barry O'Neill", "", url("https://github.com/barryoneill"))),
publishMavenStyle := true,
scmInfo := Some(
ScmInfo(
url("https://github.com/laserdisc-io/sbt-laserdisc-defaults/tree/master"),
Expand All @@ -72,8 +84,3 @@ lazy val publishSettings = Seq(
),
licenses := Seq("MIT" -> url("https://raw.githubusercontent.com/laserdisc-io/sbt-laserdisc-defaults/master/LICENSE"))
)

// local dependencies for this plugin
def dependencies = libraryDependencies ++= Seq(
"org.apache.maven" % "maven-artifact" % "3.9.9"
)
19 changes: 19 additions & 0 deletions plugin-shared/src/main/scala/laserdisc/sbt/CompileTarget.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package laserdisc.sbt

/** This enumeration allows the plugin user to specify whether their project
* will compile for Scala 2, Scala 3, or cross-compile for both.
*/
sealed abstract class CompileTarget(val defaultScalaVersion: String, val crossVersions: Seq[String])
object CompileTarget {

val Scala2Version: String = "2.13.15"
val Scala3Version: String = "3.3.4"

final case object Scala2Only extends CompileTarget(Scala2Version, List(Scala2Version))

final case object Scala3Only extends CompileTarget(Scala3Version, List(Scala3Version))

// cross compiles, but defaults to scala 3 (affects mostly IDE auto-config)
final case object Scala2And3 extends CompileTarget(Scala3Version, List(Scala2Version, Scala3Version))

}
Original file line number Diff line number Diff line change
@@ -1,11 +1,17 @@
package laserdisc.sbt

import sbt.{Def, Keys}
import sbt.{Def, Keys, MessageOnlyException}

/** A means of breaking the various defaults into logical groups but keeping the structure consistent
*/
trait DefaultsCategory {

def fail(msg: String): Nothing = throw new MessageOnlyException(msg)

def fail(msg: String, cause: Throwable): Nothing = throw new MessageOnlyException(
s"$msg [${cause.getClass.getSimpleName}:${cause.getMessage}]"
)

protected[this] val logger = Keys.sLog

/** @return build level settings to be applied
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package laserdisc.sbt

import com.github.sbt.git.GitVersioning
import com.typesafe.sbt.packager.archetypes.JavaAppPackaging
import org.scalafmt.sbt.ScalafmtPlugin
import sbt.*

/** Base class for concrete plugin implementnations
*/
abstract class LaserDiscDefaultsPluginBase extends AutoPlugin {

/** Plugin impls should provide information about themselves (e.g. name, version)
* so that log messages and errors can appropriately be associated with the plugin
*/
implicit val pluginCtx: PluginContext

/** Plugin impls can pick and choose [[DefaultsCategory]] implementations
*/
val categories: Seq[DefaultsCategory]

/* These are the plugins we wish users of our plugin to automatically have. You will need to
* add the appropriate addSbtPlugin to **build.sbt** to do this (as opposed to having them in the
* standard project/plugins.sbt, which defines the plugins needed to compile _this_ project)*/
override def requires: Plugins = plugins.JvmPlugin && ScalafmtPlugin && GitVersioning && JavaAppPackaging

// we're forcing the plugins to be present above, but still, we define them all as requirements
override def trigger: PluginTrigger = allRequirements

// apply these to the whole build (i.e. `ThisBuild / whatever..`)
override lazy val buildSettings: Seq[Def.Setting[?]] = categories.flatMap(_.buildSettings)

// apply these settings to every 'project'
override lazy val projectSettings: Seq[Def.Setting[?]] = categories.flatMap(_.projectSettings)

}
12 changes: 12 additions & 0 deletions plugin-shared/src/main/scala/laserdisc/sbt/PluginContext.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package laserdisc.sbt

/** Used by plugins to identify themselves (for log messages, errors etc)
* @param pluginName The identity of the plugin, e.g. `sbt-foo-defaults`
* @param pluginVersion The version of the plugin (e.g. from https://github.com/sbt/sbt-buildinfo )
* @param pluginHomepage The URL of the homepage for this plugin
*/
case class PluginContext(
pluginName: String,
pluginVersion: String,
pluginHomepage: String
)
Loading
Loading