mirror of https://github.com/sbt/sbt.git
143 lines
4.4 KiB
Markdown
143 lines
4.4 KiB
Markdown
|
|
Tech stack
|
||
|
|
==========
|
||
|
|
|
||
|
|
This page explains some of the libraries and tooling used in the code base.
|
||
|
|
|
||
|
|
Testing
|
||
|
|
-------
|
||
|
|
|
||
|
|
See the previous sections for test libraries:
|
||
|
|
|
||
|
|
- [Unit tests](04_unit_tests.md)
|
||
|
|
- [scripted tests](05_scripted_tests.md)
|
||
|
|
|
||
|
|
sbt
|
||
|
|
---
|
||
|
|
|
||
|
|
The main features of sbt, such as compilation and testing are implemented
|
||
|
|
using the sbt's settings and task DSL as well as its submodules like IO.
|
||
|
|
|
||
|
|
Generally this means that user-facing and immutable configurations
|
||
|
|
should be expressed as settings, and side effects should be wrapped in a task.
|
||
|
|
|
||
|
|
Coursier for dependency management
|
||
|
|
----------------------------------
|
||
|
|
|
||
|
|
Since sbt 1.3, we have switched its library dependency management engine
|
||
|
|
from Apache Ivy to [Coursier][coursier].
|
||
|
|
|
||
|
|
Contraband for datatype
|
||
|
|
-----------------------
|
||
|
|
|
||
|
|
[Contraband][contraband] is a datatype description language that can generate:
|
||
|
|
|
||
|
|
- pseudo case classes in Scala or Java
|
||
|
|
- JSON bindings
|
||
|
|
|
||
|
|
```graphql
|
||
|
|
## Position in a text document expressed as zero-based line and zero-based character offset.
|
||
|
|
## A position is between two characters like an 'insert' cursor in a editor.
|
||
|
|
type Position {
|
||
|
|
## Line position in a document (zero-based).
|
||
|
|
line: Long!
|
||
|
|
|
||
|
|
## Character offset on a line in a document (zero-based).
|
||
|
|
character: Long!
|
||
|
|
}
|
||
|
|
|
||
|
|
## A range in a text document expressed as (zero-based) start and end positions. A range is comparable to a selection in an editor.
|
||
|
|
## Therefore the end position is exclusive.
|
||
|
|
type Range {
|
||
|
|
## The range's start position.
|
||
|
|
start: sbt.internal.bsp.Position!
|
||
|
|
|
||
|
|
## The range's end position.
|
||
|
|
end: sbt.internal.bsp.Position!
|
||
|
|
}
|
||
|
|
````
|
||
|
|
|
||
|
|
Unlike Scala's case classes, Contraband's pseudo case class can evolve over time without breaking binary compatibility of the generated Scala code.
|
||
|
|
|
||
|
|
sjson-new, Jawn, and SLIP-28 ScalaJson for JSON
|
||
|
|
-----------------------------------------------
|
||
|
|
|
||
|
|
For JSON serialization, sbt uses [sjson-new][sjson-new], a typeclass-based JSON codec library.
|
||
|
|
It offers `JsonFormat[A1]` typeclass, which lets you define how a datatype should be
|
||
|
|
broken down, but it does not specify which JSON AST should be used (backend-independent).
|
||
|
|
If you used Contraband for data, it can generate sjson-new codec for you.
|
||
|
|
|
||
|
|
Jawn is a fast JSON parsing library, that's also backend-independent.
|
||
|
|
|
||
|
|
As the concrete JSON AST, sbt adopts SLIP-28 [ScalaJSON][scalajson], a reference implementation of
|
||
|
|
a proposed effort to create a standard JSON AST. We also forked and shaded it, so it wouldn't clash
|
||
|
|
if the API changed over time.
|
||
|
|
|
||
|
|
Gigahorse for HTTP
|
||
|
|
------------------
|
||
|
|
|
||
|
|
[Gigahorse][gigahorse] is a backend-independent HTTP library sbt uses for some features.
|
||
|
|
As the concete backend we use Apache HttpClient 5.x.
|
||
|
|
|
||
|
|
```scala
|
||
|
|
private val http = {
|
||
|
|
val defaultHttpRequestTimeout = 2.minutes
|
||
|
|
|
||
|
|
val gigahorseConfig = Gigahorse.config
|
||
|
|
.withRequestTimeout(defaultHttpRequestTimeout)
|
||
|
|
.withReadTimeout(defaultHttpRequestTimeout)
|
||
|
|
|
||
|
|
Gigahorse.http(gigahorseConfig)
|
||
|
|
}
|
||
|
|
val req = Gigahorse
|
||
|
|
.url(s"${baseUrl}/publisher/upload?$q")
|
||
|
|
.post(
|
||
|
|
MultipartFormBody(
|
||
|
|
FormPart("bundle", bundleZipPath.toFile())
|
||
|
|
)
|
||
|
|
)
|
||
|
|
|
||
|
|
http.run(reqTransform(req), Gigahorse.asString)
|
||
|
|
|
||
|
|
http.close()
|
||
|
|
```
|
||
|
|
|
||
|
|
Putting them all together
|
||
|
|
-------------------------
|
||
|
|
|
||
|
|
Sonatype Publisher API combines Contraband, Jawn, sjson-new, and Gigahorse to access Sonatype's RESTful API.
|
||
|
|
First, one of its HTTP API is described as a Contraband type:
|
||
|
|
|
||
|
|
```graphql
|
||
|
|
type PublisherStatus {
|
||
|
|
deploymentId: String!
|
||
|
|
deploymentName: String!
|
||
|
|
deploymentState: sbt.internal.sona.DeploymentState!
|
||
|
|
purls: [String]
|
||
|
|
# Optional errors.
|
||
|
|
errors: sjsonnew.shaded.scalajson.ast.unsafe.JValue
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
Next, the HTTP endpoint is described as a Gigahorse request.
|
||
|
|
Finally the HTTP body is transformed via Jawn and sjson-new asynchronously in `http.run(...)`.
|
||
|
|
|
||
|
|
```scala
|
||
|
|
/**
|
||
|
|
* https://central.sonatype.org/publish/publish-portal-api/#verify-status-of-the-deployment
|
||
|
|
*/
|
||
|
|
private def deploymentStatusF(deploymentId: String): Future[PublisherStatus] = {
|
||
|
|
val req = Gigahorse
|
||
|
|
.url(s"${baseUrl}/publisher/status")
|
||
|
|
.addQueryString("id" -> deploymentId)
|
||
|
|
.post("", StandardCharsets.UTF_8)
|
||
|
|
http.run(reqTransform(req), SonaClient.asPublisherStatus)
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
[coursier]: https://github.com/coursier/coursier
|
||
|
|
[contraband]: https://www.scala-sbt.org/contraband/
|
||
|
|
[sjson-new]: https://github.com/eed3si9n/sjson-new
|
||
|
|
[scalajson]: https://github.com/sbt/scalajson
|
||
|
|
[jawn]: https://github.com/typelevel/jawn
|
||
|
|
[gigahorse]: https://eed3si9n.com/gigahorse/
|