-
Notifications
You must be signed in to change notification settings - Fork 360
/
Copy pathContinuousIntegration.scala
126 lines (113 loc) · 4.86 KB
/
ContinuousIntegration.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
import Testing._
import sbt.Keys._
import sbt._
import sbt.io.Path._
import scala.sys.process._
object ContinuousIntegration {
lazy val ciSettings: Seq[Setting[_]] = List(
srcCiResources := sourceDirectory.value / "ci" / "resources",
targetCiResources := target.value / "ci" / "resources",
envFile := srcCiResources.value / "env.temp", // generated by resources/acquire_b2c_token.sh
vaultToken := userHome / ".vault-token",
copyCiResources := {
IO.copyDirectory(srcCiResources.value, targetCiResources.value)
},
renderCiResources := {
minnieKenny.toTask("").value
copyCiResources.value
val log = streams.value.log
if (!vaultToken.value.exists()) {
sys.error(
s"""The vault token file "${vaultToken.value}" does not exist. Be sure to login using the instructions """ +
"""on https://hub.docker.com/r/broadinstitute/dsde-toolbox/ under "Authenticating to vault"."""
)
}
if (vaultToken.value.isDirectory) {
sys.error(s"""The vault token file "${vaultToken.value}" should not be a directory.""")
}
// Only include the local file argument if the file exists (local development w/ acquire_b2c_token.sh)
// Don't include it otherwise (CI/CD and other development)
val localEnvFileArgs = if (envFile.value.exists()) List("-e", s"ENV_FILE=${envFile.value}") else List()
val cmd: List[String] = List.concat(
List(
"docker",
"run",
"--rm",
"-v",
s"${vaultToken.value}:/root/.vault-token",
"-v",
s"${srcCiResources.value}:${srcCiResources.value}",
"-v",
s"${targetCiResources.value}:${targetCiResources.value}"
),
localEnvFileArgs,
List(
"-e",
"ENVIRONMENT=not_used",
"-e",
s"INPUT_PATH=${srcCiResources.value}",
"-e",
s"OUT_PATH=${targetCiResources.value}",
"broadinstitute/dsde-toolbox:dev",
"render-templates.sh"
)
)
val result = cmd ! log
if (result != 0) {
sys.error(
"Vault rendering failed. Please double check for errors above and see the setup instructions on " +
"https://hub.docker.com/r/broadinstitute/dsde-toolbox/"
)
}
}
)
def aggregateSettings(rootProject: Project): Seq[Setting[_]] = List(
// Before compiling, check if the expected projects are aggregated so that they will be compiled-and-tested too.
Compile / compile := {
streams.value.log // make sure logger is loaded
validateAggregatedProjects(rootProject, state.value)
(Compile / compile).value
}
)
private val copyCiResources: TaskKey[Unit] = taskKey[Unit](s"Copy CI resources.")
private val renderCiResources: TaskKey[Unit] = taskKey[Unit](s"Render CI resources with Hashicorp Vault.")
private val srcCiResources: SettingKey[File] = settingKey[File]("Source directory for CI resources")
private val targetCiResources: SettingKey[File] = settingKey[File]("Target directory for CI resources")
private val vaultToken: SettingKey[File] = settingKey[File]("File with the vault token")
private val envFile: SettingKey[File] =
settingKey[File]("File with the environment variables needed to render CI resources.")
/**
* For "reasons" these projects are excluded from the root aggregation in build.sbt.
*/
private val unaggregatedProjects = Map.empty
/**
* Get the list of projects defined in build.sbt excluding the passed in root project.
*/
private def getBuildSbtNames(rootProject: Project, state: State): Set[String] = {
val extracted = Project.extract(state)
extracted.structure.units.flatMap { case (_, loadedBuildUnit) =>
loadedBuildUnit.defined.keys
}.toSet - rootProject.id
}
/**
* Validates that projects are aggregated.
*/
private def validateAggregatedProjects(rootProject: Project, state: State): Unit = {
// Get the list of projects explicitly aggregated
val projectReferences: Seq[ProjectReference] = rootProject.aggregate
val localProjectReferences = projectReferences collect { case localProject: LocalProject =>
localProject
}
val aggregatedNames = localProjectReferences.map(_.project).toSet
val buildSbtNames = getBuildSbtNames(rootProject, state)
val missingNames = buildSbtNames.diff(aggregatedNames ++ unaggregatedProjects.keySet).toList.sorted
if (missingNames.nonEmpty) {
sys.error(s"There are projects defined in build.sbt that are not aggregated: ${missingNames.mkString(", ")}")
}
val falseNames = unaggregatedProjects.filterKeys(aggregatedNames.contains)
if (falseNames.nonEmpty) {
val reasons = falseNames.map { case (name, reason) => s" ${name}: ${reason}" }.mkString("\n")
sys.error(s"There are projects aggregated in build.sbt that shouldn't be:\n$reasons")
}
}
}