Skip to content

Commit

Permalink
Make ReadinessLevel's configurable (#34)
Browse files Browse the repository at this point in the history
  • Loading branch information
mdedetrich authored Mar 22, 2022
1 parent 095881f commit 04799ce
Show file tree
Hide file tree
Showing 7 changed files with 141 additions and 91 deletions.
92 changes: 79 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,19 +53,6 @@ project-info {
url: "https://gitter.im/akka/alpakka-kafka"
}
]
levels: [
{
readiness: Supported
since: "2018-11-22"
since-version: "0.22"
}
{
readiness: Incubating
since: "2018-08-22"
since-version: "0.16"
note: "Community rocks this module"
}
]
}
}
```
Expand All @@ -83,6 +70,85 @@ The quick brown fox...
```
[Example](https://github.com/lightbend/sbt-paradox-project-info/blob/master/src/sbt-test/project-info/happy-path/src/main/paradox/index.md)

### Readiness Levels

Readiness levels is a convenient way to show the support for different projects. In order to use readiness levels you
first need to specify what they mean and how to render them, i.e.

```sbt
import com.lightbend.paradox.projectinfo._
import com.lightbend.paradox.projectinfo.ParadoxProjectInfoPluginKeys._

readinessLevels ++= Map(
"Supported" -> new ReadinessLevel {
val name = "<b>This project is supported</b>"
},
"NotSupported" -> new ReadinessLevel {
val name = "<b>This project is not supported</b>"
}
)
```

Then in your `project/project-info.conf` you can specify these readiness levels using the `levels` config path, i.e.

```hocon
project-info {
# version is overridden from the `projectInfoVersion` key (which defaults to sbt's project version)
version: "current"
scala-versions: ["2.12", "2.13"]
jdk-versions: ["OpenJDK 8"]
core {
title: "The core project"
// if undefined, sbt's crossScalaVersions are used
scala-versions: ${project-info.scala-versions}
jdk-versions: ${project-info.jdk-versions}
jpms-name: "alpakka.core"
snapshots: {
text: "Snapshots are available"
url: "snapshots.html"
new-tab: false
}
issues: {
url: "https://github.com/lightbend/sbt-paradox-project-info/issues"
text: "Github issues"
}
release-notes: {
url: "https://github.com/lightbend/sbt-paradox-project-info/releases"
text: "Github releases"
}
api-docs: [
{
text: "Scaladoc"
url: "https://developer.lightbend.com/docs/api/alpakka/"${project-info.version}"/akka/stream/alpakka/index.html"
}
]
forums: [
{
text: "Lightbend Discuss"
url: "https://discuss.lightbend.com/c/akka/"
}
{
text: "akka/alpakka-kafka Gitter channel"
url: "https://gitter.im/akka/alpakka-kafka"
}
]
levels: [
{
readiness: Supported
since: "2018-11-22"
since-version: "0.22"
}
{
readiness: NotSupported
since: "2018-08-22"
since-version: "0.16"
note: "Alpha level of module"
}
]
}
}
```

## License

The license is Apache 2.0, see LICENSE.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,8 @@ object ParadoxProjectInfoPlugin extends AutoPlugin {
override def projectSettings: Seq[Setting[_]] = projectInfoSettings(Compile)

override def globalSettings: Seq[Setting[_]] = Seq(
projectInfoVersion := version.value
projectInfoVersion := version.value,
readinessLevels := Map.empty
)

def projectInfoSettings(config: Configuration): Seq[Setting[_]] =
Expand All @@ -44,9 +45,10 @@ object ParadoxProjectInfoPlugin extends AutoPlugin {
Def.task {
val s = state.value
Seq { _: Writer.Context
val f = new File((LocalRootProject / baseDirectory).value, "project/project-info.conf")
val extracted = Project.extract(s)
val rootVersion = extracted.get(projectInfoVersion)
val f = new File((LocalRootProject / baseDirectory).value, "project/project-info.conf")
val extracted = Project.extract(s)
val rootVersion = projectInfoVersion.value
val readinessLevelsMap = readinessLevels.value
val config = if (f.exists()) {
ConfigFactory
.parseFile(f)
Expand All @@ -63,7 +65,7 @@ object ParadoxProjectInfoPlugin extends AutoPlugin {
val projectName =
try extracted.get(project / name)
catch {
case e: Exception =>
case _: Exception =>
throw new RuntimeException(
s"couldn't read sbt setting `$projectId / name`, does the projectId exist?"
)
Expand All @@ -78,7 +80,7 @@ object ParadoxProjectInfoPlugin extends AutoPlugin {
extracted.get(project / crossScalaVersions).toList
)
}
new ProjectInfoDirective(config, sbtDetails)
new ProjectInfoDirective(config, sbtDetails, readinessLevelsMap)
}
}
}.value
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ import sbt._

trait ParadoxProjectInfoPluginKeys {
val projectInfoVersion = settingKey[String]("The version/revision propagated into `project-info.version`.")
val readinessLevels =
settingKey[Map[String, ReadinessLevel]]("The referenced readiness-levels when using levels")
}

object ParadoxProjectInfoPluginKeys extends ParadoxProjectInfoPluginKeys
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,16 @@ import com.typesafe.config.Config
import org.pegdown.Printer
import org.pegdown.ast.{DirectiveNode, Visitor}

class ProjectInfoDirective(config: Config, moduleToSbtValues: String => SbtValues)
extends LeafBlockDirective("project-info") {
class ProjectInfoDirective(
config: Config,
moduleToSbtValues: String => SbtValues,
readinessLevelsMap: Map[String, ReadinessLevel]
) extends LeafBlockDirective("project-info") {
def render(node: DirectiveNode, visitor: Visitor, printer: Printer): Unit = {
val projectId = node.attributes.value("projectId")
if (config.hasPath(projectId)) {
val module = config.getConfig(projectId)
val data = SbtAndProjectInfo(moduleToSbtValues(projectId), ProjectInfo(projectId, module))
val data = SbtAndProjectInfo(moduleToSbtValues(projectId), ProjectInfo(projectId, readinessLevelsMap, module))
ProjectInfoDirective.renderInfo(printer, data)
} else throw new RuntimeException(s"project-info.conf does not contain `$projectId`")
}
Expand Down
54 changes: 13 additions & 41 deletions src/main/scala/com/lightbend/paradox/projectinfo/model.scala
Original file line number Diff line number Diff line change
Expand Up @@ -35,41 +35,8 @@ case class SbtValues(
crossScalaVersions: immutable.Seq[String]
)

trait ReadinessLevel { def name: String }
object ReadinessLevel {
private def glossary(anchor: String, label: String): String =
s"""<a href="https://developer.lightbend.com/docs/introduction/getting-help/support-terminology.html#$anchor" target="_blank" rel="noopener">$label</a>""".stripMargin

case object Supported extends ReadinessLevel {
val name =
s"""${glossary(
"supported",
"Supported"
)}, <a href="https://www.lightbend.com/lightbend-subscription" target="_blank" rel="noopener">Lightbend Subscription</a> provides support"""
}
case object Certified extends ReadinessLevel {
val name =
s"""${glossary("certified", "Certified")} by <a href="https://www.lightbend.com/" target="_blank">Lightbend</a>"""
}
case object Incubating extends ReadinessLevel {
val name = glossary("incubating", "Incubating")
}
case object CommunityDriven extends ReadinessLevel {
val name = glossary("community-driven", "Community-driven")
}
case object EndOfLife extends ReadinessLevel {
val name =
s"${glossary("eol", "End-of-Life")}, it is not recommended to use this project any more."
}

def fromString(s: String): ReadinessLevel = s match {
case "Supported" => Supported
case "Certified" => Certified
case "Incubating" => Incubating
case "CommunityDriven" => CommunityDriven
case "EndOfLife" => EndOfLife
case other => throw new IllegalArgumentException(s"unknown readiness level: $other")
}
trait ReadinessLevel {
def name: String
}

case class Link(url: String, text: Option[String], newTab: Boolean = true)
Expand All @@ -94,9 +61,9 @@ case class Level(

object Level {

def apply(c: Config): Level = {
def apply(c: Config, readinessLevelsMap: Map[String, ReadinessLevel]): Level = {
import Util.ExtendedConfig
val ml = c.getReadinessLevel("readiness")
val ml = c.getReadinessLevel("readiness", readinessLevelsMap)
val since = c.getLocalDate("since")
val sinceVersion = c.getString("since-version")
val ends = c.getOption("ends", _.getLocalDate(_))
Expand All @@ -122,7 +89,7 @@ case class ProjectInfo(
object ProjectInfo {
import Util.ExtendedConfig

def apply(name: String, c: Config): ProjectInfo = {
def apply(name: String, readinessLevelsMap: Map[String, ReadinessLevel], c: Config): ProjectInfo = {
val title = c.getOption("title", _.getString(_)).getOrElse(name)
val scalaVersions =
if (c.hasPath("scala-versions")) c.getStringList("scala-versions").asScala.toList
Expand All @@ -137,7 +104,8 @@ object ProjectInfo {
val levels = c
.getOption(
"levels",
(config, string) => for { item <- config.getObjectList(string).asScala.toList } yield Level(item.toConfig)
(config, string) =>
for { item <- config.getObjectList(string).asScala.toList } yield Level(item.toConfig, readinessLevelsMap)
)
.getOrElse(List.empty)

Expand All @@ -163,8 +131,12 @@ object Util {
private val dateFormat = DateTimeFormatter.ISO_LOCAL_DATE

implicit class ExtendedConfig(c: Config) {
def getReadinessLevel(path: String): ReadinessLevel = ReadinessLevel.fromString(c.getString(path))
def getLocalDate(path: String): LocalDate = LocalDate.parse(c.getString(path), dateFormat)
def getReadinessLevel(path: String, readinessLevelsMap: Map[String, ReadinessLevel]): ReadinessLevel = {
val value = c.getString(path)
readinessLevelsMap.getOrElse(value, throw new Exception(s"No configured readinessLevels with value:$value"))
}

def getLocalDate(path: String): LocalDate = LocalDate.parse(c.getString(path), dateFormat)
def getOption[T](path: String, read: (Config, String) => T): Option[T] =
if (c.hasPath(path)) Some(read(c, path)) else None
def getOptionalBoolean(path: String, defaultValue: Boolean): Boolean =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,9 @@ class ProjectInfoSpec extends AnyWordSpec with Matchers {
|}
""".stripMargin
val c = ConfigFactory.parseString(in).getConfig("level")
Level(c) should be(Level(ReadinessLevel.Incubating, LocalDate.of(2018, 11, 22), "0.12", None, None))
Level(c, SampleReadinessLevels.values) should be(
Level(SampleReadinessLevels.Incubating, LocalDate.of(2018, 11, 22), "0.12", None, None)
)
}
}

Expand All @@ -51,7 +53,7 @@ class ProjectInfoSpec extends AnyWordSpec with Matchers {
|}
""".stripMargin
val c = ConfigFactory.parseString(in).getConfig("core")
ProjectInfo("core", c) should be(
ProjectInfo("core", SampleReadinessLevels.values, c) should be(
ProjectInfo(
"core",
"core",
Expand Down Expand Up @@ -93,7 +95,7 @@ class ProjectInfoSpec extends AnyWordSpec with Matchers {
val c = ConfigFactory.parseString(in).getConfig("project-info")
val name = "core"
val conf = c.getConfig(name)
ProjectInfo(name, conf) should be(
ProjectInfo(name, SampleReadinessLevels.values, conf) should be(
ProjectInfo(
"core",
title = "The core project",
Expand All @@ -107,7 +109,7 @@ class ProjectInfoSpec extends AnyWordSpec with Matchers {
snapshots = None,
levels = List(
Level(
ReadinessLevel.Incubating,
SampleReadinessLevels.Incubating,
LocalDate.of(2018, 11, 22),
"0.18",
None,
Expand Down Expand Up @@ -150,7 +152,7 @@ class ProjectInfoSpec extends AnyWordSpec with Matchers {
val c = ConfigFactory.parseString(in).resolve().getConfig("project-info")
val name = "core"
val conf = c.getConfig(name)
ProjectInfo(name, conf) should be(
ProjectInfo(name, SampleReadinessLevels.values, conf) should be(
ProjectInfo(
"core",
title = "The core project",
Expand All @@ -163,9 +165,9 @@ class ProjectInfoSpec extends AnyWordSpec with Matchers {
releaseNotes = None,
snapshots = None,
levels = List(
Level(ReadinessLevel.Supported, LocalDate.of(2018, 12, 12), "0.21", None, None),
Level(SampleReadinessLevels.Supported, LocalDate.of(2018, 12, 12), "0.21", None, None),
Level(
ReadinessLevel.Incubating,
SampleReadinessLevels.Incubating,
LocalDate.of(2018, 11, 22),
"0.18",
None,
Expand Down Expand Up @@ -212,7 +214,7 @@ class ProjectInfoSpec extends AnyWordSpec with Matchers {
val c = ConfigFactory.parseString(in).resolve().getConfig("project-info")
val name = "core"
val conf = c.getConfig(name)
ProjectInfo(name, conf) should be(
ProjectInfo(name, SampleReadinessLevels.values, conf) should be(
ProjectInfo(
"core",
title = "The core project",
Expand All @@ -234,7 +236,7 @@ class ProjectInfoSpec extends AnyWordSpec with Matchers {
releaseNotes = None,
snapshots = None,
levels = List(
Level(ReadinessLevel.Supported, LocalDate.of(2018, 12, 12), "0.21", None, None)
Level(SampleReadinessLevels.Supported, LocalDate.of(2018, 12, 12), "0.21", None, None)
)
)
)
Expand Down Expand Up @@ -276,7 +278,7 @@ class ProjectInfoSpec extends AnyWordSpec with Matchers {
val c = ConfigFactory.parseString(in).resolve().getConfig("project-info")
val name = "core"
val conf = c.getConfig(name)
ProjectInfo(name, conf) should be(
ProjectInfo(name, SampleReadinessLevels.values, conf) should be(
ProjectInfo(
"core",
title = "The core project",
Expand All @@ -292,7 +294,7 @@ class ProjectInfoSpec extends AnyWordSpec with Matchers {
releaseNotes = None,
snapshots = None,
levels = List(
Level(ReadinessLevel.Supported, LocalDate.of(2018, 12, 12), "0.21", None, None)
Level(SampleReadinessLevels.Supported, LocalDate.of(2018, 12, 12), "0.21", None, None)
)
)
)
Expand Down Expand Up @@ -324,7 +326,7 @@ class ProjectInfoSpec extends AnyWordSpec with Matchers {
val c = ConfigFactory.parseString(in).resolve().getConfig("project-info")
val name = "core"
val conf = c.getConfig(name)
ProjectInfo(name, conf) should be(
ProjectInfo(name, SampleReadinessLevels.values, conf) should be(
ProjectInfo(
"core",
title = "The core project",
Expand All @@ -343,7 +345,7 @@ class ProjectInfoSpec extends AnyWordSpec with Matchers {
)
),
levels = List(
Level(ReadinessLevel.Supported, LocalDate.of(2018, 12, 12), "0.21", None, None)
Level(SampleReadinessLevels.Supported, LocalDate.of(2018, 12, 12), "0.21", None, None)
)
)
)
Expand Down
Loading

0 comments on commit 04799ce

Please sign in to comment.