forked from http4s/http4s
/
Http4sPlugin.scala
357 lines (322 loc) · 18.4 KB
/
Http4sPlugin.scala
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
package org.http4s.build
import com.lucidchart.sbt.scalafmt.ScalafmtCorePlugin
import com.lucidchart.sbt.scalafmt.ScalafmtCorePlugin.autoImport._
import com.timushev.sbt.updates.UpdatesPlugin.autoImport._ // autoImport vs. UpdateKeys necessary here for implicit
import com.typesafe.sbt.SbtGit.git
import com.typesafe.sbt.SbtPgp.autoImport._
import com.typesafe.sbt.git.JGit
import com.typesafe.sbt.pgp.PgpKeys.publishSigned
import com.typesafe.tools.mima.core.{DirectMissingMethodProblem, ProblemFilters}
import com.typesafe.tools.mima.plugin.MimaPlugin
import com.typesafe.tools.mima.plugin.MimaPlugin.autoImport._
import java.lang.{Runtime => JRuntime}
import sbt.Keys._
import sbt._
import sbtrelease.ReleasePlugin.autoImport._
import sbtrelease.ReleaseStateTransformations._
import sbtrelease._
object Http4sPlugin extends AutoPlugin {
object autoImport {
val isTravisBuild = settingKey[Boolean]("true if this build is running as either a PR or a release build within Travis CI")
val http4sMimaVersion = settingKey[Option[String]]("Version to target for MiMa compatibility")
val http4sPublish = settingKey[Boolean]("Is this a publishing build?")
val http4sApiVersion = taskKey[(Int, Int)]("API version of http4s")
val http4sJvmTarget = taskKey[String]("JVM target")
val http4sBuildData = taskKey[Unit]("Export build metadata for Hugo")
}
import autoImport._
override def trigger = allRequirements
override def requires = MimaPlugin && ScalafmtCorePlugin
override lazy val buildSettings = Seq(
// Many steps only run on one build. We distinguish the primary build from
// secondary builds by the Travis build number.
isTravisBuild := sys.env.get("TRAVIS").isDefined,
// Publishing to gh-pages and sonatype only done from select branches and
// never from pull requests.
http4sPublish := {
sys.env.get("TRAVIS").contains("true") &&
sys.env.get("TRAVIS_PULL_REQUEST").contains("false") &&
sys.env.get("TRAVIS_REPO_SLUG").contains("http4s/http4s") &&
sys.env.get("TEST").contains("publish")
},
ThisBuild / http4sApiVersion := (ThisBuild / version).map {
case VersionNumber(Seq(major, minor, _*), _, _) => (major.toInt, minor.toInt)
}.value,
git.remoteRepo := "git@github.com:http4s/http4s.git"
) ++ signingSettings
override lazy val projectSettings: Seq[Setting[_]] = Seq(
// scalaVersion := (sys.env.get("TRAVIS_SCALA_VERSION") orElse sys.env.get("SCALA_VERSION") getOrElse "2.12.8"),
// crossScalaVersions := List("2.11.12", "2.12.8", "2.13.0-M5"),
// Rig will take care of this on production builds. We haven't fully
// implemented that machinery yet, so we're going to live without this
// one for now.
scalacOptions -= "-Xcheckinit",
// Getting some spurious unreachable code warnings in 2.13.0-M5
scalacOptions -= {
CrossVersion.partialVersion(scalaVersion.value) match {
case Some((2, 13)) =>
"-Xfatal-warnings"
case _ =>
"I DON'T EXIST I'M WORKING AROUND NOT BEING ABLE TO CALL scalaVersion.value FROM ~="
}
},
// https://github.com/tkawachi/sbt-doctest/issues/102
Test / compile / scalacOptions -= "-Ywarn-unused:params",
scalacOptions ++= {
CrossVersion.partialVersion(scalaVersion.value) match {
case Some((2, minor)) if minor >= 12 =>
Seq("-Ybackend-parallelism", math.min(JRuntime.getRuntime.availableProcessors, 16).toString)
case _ =>
Seq.empty
},
},
http4sMimaVersion := {
version.value match {
case VersionNumber(Seq(major, minor, patch), _, _) if patch.toInt > 0 =>
Some(s"$major.$minor.${patch.toInt - 1}")
case _ =>
None
}
},
mimaFailOnProblem := http4sMimaVersion.value.isDefined,
mimaPreviousArtifacts := (http4sMimaVersion.value.map {
case "0.20.5" => "0.20.4" // cursed release
case v => v
}.map {
organization.value % s"${moduleName.value}_${scalaBinaryVersion.value}" % _
}).toSet,
mimaBinaryIssueFilters ++= Seq(
ProblemFilters.exclude[DirectMissingMethodProblem]("org.http4s.client.blaze.BlazeClientBuilder.this"),
ProblemFilters.exclude[DirectMissingMethodProblem]("org.http4s.client.blaze.Http1Support.this")
),
libraryDependencies += compilerPlugin(
CrossVersion.binaryScalaVersion(scalaVersion.value) match {
case "2.13.0-M5" => "org.spire-math" %% "kind-projector" % "0.9.9" cross CrossVersion.binary
case _ => "org.typelevel" %% "kind-projector" % "0.10.0"
}
),
addCompilerPlugin("com.olegpy" %% "better-monadic-for" % "0.3.0-M4"),
scalafmtVersion := "1.5.1",
Test / scalafmt := {
(Compile / scalafmt).value
(Test / scalafmt).value
()
},
Test / scalafmt / test := {
(Compile / scalafmt / test).value
(Test / scalafmt / test).value
()
},
http4sBuildData := {
val dest = target.value / "hugo-data" / "build.toml"
val (major, minor) = http4sApiVersion.value
val releases = latestPerMinorVersion(baseDirectory.value)
.map { case ((major, minor), v) => s""""$major.$minor" = "${v.string}""""}
.mkString("\n")
// Would be more elegant if `[versions.http4s]` was nested, but then
// the index lookups in `shortcodes/version.html` get complicated.
val buildData: String =
s"""
|[versions]
|"http4s.api" = "$major.$minor"
|"http4s.current" = "${version.value}"
|"http4s.doc" = "${docExampleVersion(version.value)}"
|circe = "${circeJawn.revision}"
|cryptobits = "${cryptobits.revision}"
|"argonaut-shapeless_6.2" = "1.2.0-M6"
|
|[releases]
|$releases
""".stripMargin
IO.write(dest, buildData)
},
dependencyUpdatesFilter -= moduleFilter(organization = "javax.servlet"), // servlet-4.0 is not yet supported by jetty-9 or tomcat-9, so don't accidentally depend on its new features
dependencyUpdatesFilter -= moduleFilter(organization = "org.scalacheck"), // scalacheck-1.14 is incompatible with cats-laws-1.1
dependencyUpdatesFilter -= moduleFilter(organization = "org.specs2"), // specs2-4.2 is incompatible with scalacheck-1.13
dependencyUpdatesFilter -= moduleFilter(organization = "org.typelevel", name = "discipline"), // discipline-0.10 is incompatible with scalacheck-1.13
dependencyUpdatesFilter -= moduleFilter(organization = "org.typelevel", name = "cats-effect"),
dependencyUpdatesFilter -= moduleFilter(organization = "org.typelevel", name = "cats-effect-laws"),
) ++ releaseSettings
val releaseSettings = Seq(
// Reset a couple sbt-release defaults that rig changed
releaseVersion := { ver =>
Version(ver).map(v =>
v.copy(qualifier = v.qualifier.map(_.replaceAllLiterally("-SNAPSHOT", "")))
.string
).getOrElse(versionFormatError(ver))
},
releaseTagName := s"v${if (releaseUseGlobalVersion.value) (ThisBuild / version).value else version.value}",
releasePublishArtifactsAction := Def.taskDyn {
if (isSnapshot.value) publish
else publishSigned
}.value,
releaseProcess := {
implicit class StepSyntax(val step: ReleaseStep) {
def when(cond: Boolean) =
if (cond) step else ReleaseStep(identity)
}
implicit class StateFCommand(val step: State => State) {
def when(cond: Boolean) =
StepSyntax(step).when(cond)
}
val release = !isSnapshot.value
val publishable = http4sPublish.value
Seq(
checkSnapshotDependencies.when(release),
inquireVersions.when(release),
setReleaseVersion.when(release),
tagRelease.when(publishable && release),
runClean,
releaseStepCommandAndRemaining("+mimaReportBinaryIssues"),
releaseStepCommandAndRemaining("""sonatypeOpen "Release via Travis""""),
releaseStepCommandAndRemaining("+publishSigned").when(publishable),
releaseStepCommand("sonatypeReleaseAll").when(publishable && release),
setNextVersion.when(publishable && release),
commitNextVersion.when(publishable && release),
pushChanges.when(publishable && release),
// We need a superfluous final step to ensure exit code
// propagation from failed steps above.
//
// https://github.com/sbt/sbt-release/issues/95
releaseStepCommand("show core/version")
)
}
)
val signingSettings = Seq(
useGpg := false,
usePgpKeyHex("42FAD8A85B13261D"),
pgpPublicRing := baseDirectory.value / "project" / ".gnupg" / "pubring.gpg",
pgpSecretRing := baseDirectory.value / "project" / ".gnupg" / "secring.gpg",
pgpPassphrase := sys.env.get("PGP_PASS").map(_.toArray),
)
def extractApiVersion(version: String) = {
val VersionExtractor = """(\d+)\.(\d+)\..*""".r
version match {
case VersionExtractor(major, minor) => (major.toInt, minor.toInt)
}
}
def extractDocsPrefix(version: String) =
extractApiVersion(version).productIterator.mkString("/v", ".", "")
/**
* @return the version we want to document, for example in tuts,
* given the version being built.
*
* For snapshots after a stable release, return the previous stable
* release. For snapshots of 0.16.0 and 0.17.0, return the latest
* milestone. Otherwise, just return the current version.
*/
def docExampleVersion(currentVersion: String) = {
val MilestoneVersionExtractor = """(0).(16|17).(0)a?-SNAPSHOT""".r
val latestMilestone = "M1"
val VersionExtractor = """(\d+)\.(\d+)\.(\d+).*""".r
currentVersion match {
case MilestoneVersionExtractor(major, minor, patch) =>
s"${major.toInt}.${minor.toInt}.${patch.toInt}-$latestMilestone"
case VersionExtractor(major, minor, patch) if patch.toInt > 0 =>
s"${major.toInt}.${minor.toInt}.${patch.toInt - 1}"
case _ =>
currentVersion
}
}
def latestPerMinorVersion(file: File): Map[(Int, Int), Version] =
JGit(file).tags.collect {
case ref if ref.getName.startsWith("refs/tags/v") =>
Version(ref.getName.substring("refs/tags/v".size))
}.foldLeft(Map.empty[(Int, Int), Version]) {
case (m, Some(v)) =>
def toMinor(v: Version) = (v.major, v.subversions.headOption.getOrElse(0))
def patch(v: Version) = v.subversions.drop(1).headOption.getOrElse(0)
// M before RC before final
def milestone(v: Version) = v.qualifier match {
case Some(q) if q.startsWith("-M") => (0, q.substring(2).toInt)
case Some(q) if q.startsWith("-RC") => (1, q.substring(3).toInt)
case None => (2, 0)
}
val versionOrdering: Ordering[Version] =
Ordering[(Int, (Int, Int))].on(v => (patch(v), milestone(v)))
val key = toMinor(v)
val max = m.get(key).fold(v) { v0 => versionOrdering.max(v, v0) }
m.updated(key, max)
case (m, None) => m
}
def addAlpnPath(attList: Keys.Classpath): Seq[String] = {
for {
file <- attList.map(_.data)
path = file.getAbsolutePath if path.contains("jetty") && path.contains("alpn-boot")
} yield {
println(s"Adding Alpn classes to boot classpath: $path")
"-Xbootclasspath/p:" + path
}
}
// Borrowed from the cats build
def priorTo2_13(scalaVersion: String): Boolean =
CrossVersion.partialVersion(scalaVersion) match {
case Some((2, minor)) if minor < 13 => true
case _ => false
}
def scalacheckVersion(scalaVersion: String): String =
if (priorTo2_13(scalaVersion)) "1.13.5" else "1.14.0"
def disciplineVersion(scalaVersion: String): String =
if (priorTo2_13(scalaVersion)) "0.9.0" else "0.11.0"
def specs2Version(scalaVersion: String): String =
if (priorTo2_13(scalaVersion)) "4.1.0" else "4.4.1"
def fs2Version(scalaVersion: String): String =
if (priorTo2_13(scalaVersion)) "1.0.5" else "1.0.4"
lazy val alpnBoot = "org.mortbay.jetty.alpn" % "alpn-boot" % "8.1.13.v20181017"
lazy val argonaut = "io.argonaut" %% "argonaut" % "6.2.3"
lazy val asyncHttpClient = "org.asynchttpclient" % "async-http-client" % "2.8.1"
lazy val blaze = "org.http4s" %% "blaze-http" % "0.14.7"
lazy val boopickle = "io.suzaku" %% "boopickle" % "1.3.1"
lazy val cats = "org.typelevel" %% "cats-core" % "1.6.1"
lazy val catsEffect = "org.typelevel" %% "cats-effect" % "1.3.1"
lazy val catsEffectLaws = "org.typelevel" %% "cats-effect-laws" % catsEffect.revision
lazy val catsKernelLaws = "org.typelevel" %% "cats-kernel-laws" % cats.revision
lazy val catsLaws = "org.typelevel" %% "cats-laws" % cats.revision
lazy val circeGeneric = "io.circe" %% "circe-generic" % circeJawn.revision
lazy val circeJawn = "io.circe" %% "circe-jawn" % "0.11.1"
lazy val circeLiteral = "io.circe" %% "circe-literal" % circeJawn.revision
lazy val circeParser = "io.circe" %% "circe-parser" % circeJawn.revision
lazy val circeTesting = "io.circe" %% "circe-testing" % circeJawn.revision
lazy val cryptobits = "org.reactormonk" %% "cryptobits" % "1.2"
lazy val dropwizardMetricsCore = "io.dropwizard.metrics" % "metrics-core" % "4.1.0"
lazy val dropwizardMetricsJson = "io.dropwizard.metrics" % "metrics-json" % dropwizardMetricsCore.revision
def discipline(sv: String) = "org.typelevel" %% "discipline" % disciplineVersion(sv)
def fs2Io(sv: String) = "co.fs2" %% "fs2-io" % fs2Version(sv)
def fs2ReactiveStreams(sv: String) = "co.fs2" %% "fs2-reactive-streams" % fs2Version(sv)
lazy val javaxServletApi = "javax.servlet" % "javax.servlet-api" % "3.1.0"
lazy val jawnFs2 = "org.http4s" %% "jawn-fs2" % "0.14.2"
lazy val jawnJson4s = "org.typelevel" %% "jawn-json4s" % "0.14.1"
lazy val jawnPlay = "org.typelevel" %% "jawn-play" % "0.14.1"
lazy val jettyClient = "org.eclipse.jetty" % "jetty-client" % "9.4.19.v20190610"
lazy val jettyRunner = "org.eclipse.jetty" % "jetty-runner" % jettyServer.revision
lazy val jettyServer = "org.eclipse.jetty" % "jetty-server" % "9.4.19.v20190610"
lazy val jettyServlet = "org.eclipse.jetty" % "jetty-servlet" % jettyServer.revision
lazy val json4sCore = "org.json4s" %% "json4s-core" % "3.6.5"
lazy val json4sJackson = "org.json4s" %% "json4s-jackson" % json4sCore.revision
lazy val json4sNative = "org.json4s" %% "json4s-native" % json4sCore.revision
lazy val jspApi = "javax.servlet.jsp" % "javax.servlet.jsp-api" % "2.3.3" // YourKit hack
lazy val log4s = "org.log4s" %% "log4s" % "1.7.0"
lazy val logbackClassic = "ch.qos.logback" % "logback-classic" % "1.2.3"
lazy val mockito = "org.mockito" % "mockito-core" % "2.27.0"
lazy val okhttp = "com.squareup.okhttp3" % "okhttp" % "3.14.2"
lazy val playJson = "com.typesafe.play" %% "play-json" % "2.7.2"
lazy val prometheusClient = "io.prometheus" % "simpleclient" % "0.6.0"
lazy val prometheusCommon = "io.prometheus" % "simpleclient_common" % prometheusClient.revision
lazy val prometheusHotspot = "io.prometheus" % "simpleclient_hotspot" % prometheusClient.revision
lazy val parboiled = "org.http4s" %% "parboiled" % "1.0.1"
lazy val quasiquotes = "org.scalamacros" %% "quasiquotes" % "2.1.0"
def scalacheck(sv:String) = "org.scalacheck" %% "scalacheck" % scalacheckVersion(sv)
def scalaCompiler(so: String, sv: String) = so % "scala-compiler" % sv
def scalaReflect(so: String, sv: String) = so % "scala-reflect" % sv
lazy val scalatagsApi = "com.lihaoyi" %% "scalatags" % "0.6.8"
lazy val scalaXml = "org.scala-lang.modules" %% "scala-xml" % "1.1.1"
def specs2Core(sv: String) = "org.specs2" %% "specs2-core" % specs2Version(sv)
def specs2Matcher(sv: String) = "org.specs2" %% "specs2-matcher" % specs2Version(sv)
def specs2MatcherExtra(sv: String) = "org.specs2" %% "specs2-matcher-extra" % specs2Version(sv)
def specs2Scalacheck(sv: String) = "org.specs2" %% "specs2-scalacheck" % specs2Version(sv)
lazy val tomcatCatalina = "org.apache.tomcat" % "tomcat-catalina" % "9.0.22"
lazy val tomcatCoyote = "org.apache.tomcat" % "tomcat-coyote" % tomcatCatalina.revision
lazy val treeHugger = "com.eed3si9n" %% "treehugger" % "0.4.3"
lazy val twirlApi = "com.typesafe.play" %% "twirl-api" % "1.4.0"
lazy val vault = "io.chrisdavenport" %% "vault" % "1.0.0"
}