@@ -56,7 +56,13 @@ ThisBuild / githubWorkflowBuild ~= { steps =>
5656 commands = List (" headerCheckAll" ),
5757 name = Some (" Check Headers" )
5858 ),
59+ WorkflowStep .Sbt (
60+ commands = List (" checkTestData" , " genTestData" ),
61+ name = Some (" Check and build the test data" )
62+ ),
5963 WorkflowStep .Run (
64+ // The scripts have to exist before this: compose mounts target/testdata into the
65+ // containers, and a database only reads its init directory on a first start.
6066 commands = List (" docker compose up --force-recreate -d --wait --quiet-pull" ),
6167 name = Some (" Start up test databases" )
6268 )
@@ -91,6 +97,10 @@ ThisBuild / githubWorkflowAddedJobs +=
9197 sbtStepPreamble = Nil ,
9298 steps = githubWorkflowJobSetup.value.toList ++
9399 List (
100+ WorkflowStep .Sbt (
101+ commands = List (" genTestData" ),
102+ name = Some (" Build the test data" )
103+ ),
94104 WorkflowStep .Run (
95105 commands = List (" docker compose up --force-recreate -d --wait --quiet-pull" ),
96106 name = Some (" Start up test databases" )
@@ -102,6 +112,11 @@ ThisBuild / githubWorkflowAddedJobs +=
102112
103113ThisBuild / tlSitePublishBranch := Some (" main" )
104114
115+ lazy val genTestData =
116+ taskKey[Unit ](" Build the container init scripts from the shared test data" )
117+ lazy val checkTestData =
118+ taskKey[Unit ](" Check every dataset's scripts and CSVs against each other" )
119+ lazy val newDataset = inputKey[Unit ](" Create an empty dataset directory: newDataset <name>" )
105120lazy val allUp = taskKey[Unit ](" Start all docker compose services" )
106121lazy val allStop = taskKey[Unit ](" Stop all docker compose services" )
107122lazy val pgUp = taskKey[Unit ](" Start Postgres" )
@@ -111,18 +126,50 @@ lazy val oracleStop = taskKey[Unit]("Stop Oracle")
111126lazy val mssqlUp = taskKey[Unit ](" Start SQL Server" )
112127lazy val mssqlStop = taskKey[Unit ](" Stop SQL Server" )
113128
114- ThisBuild / allUp := runDocker(" docker compose up -d --wait --quiet-pull" )
115- ThisBuild / allStop := runDocker(" docker compose stop" )
116- ThisBuild / pgUp := runDocker(" docker compose up -d --wait --quiet-pull postgres" )
117- ThisBuild / pgStop := runDocker(" docker compose stop postgres" )
118- ThisBuild / oracleUp := runDocker(" docker compose up -d --wait --quiet-pull oracle" )
119- ThisBuild / oracleStop := runDocker(" docker compose stop oracle" )
120- ThisBuild / mssqlUp := runDocker(" docker compose up -d --wait --quiet-pull mssql" )
121- ThisBuild / mssqlStop := runDocker(" docker compose stop mssql" )
122-
123- def runDocker (cmd : String ): Unit = {
124- require(cmd.! == 0 , s " docker indicated an error " )
129+ ThisBuild / genTestData := GenTestData (buildRoot)
130+ ThisBuild / checkTestData := {
131+ val problems = GenTestData .check(buildRoot)
132+ val log = streams.value.log
133+ problems.foreach(log.error(_))
134+ if (problems.nonEmpty) sys.error(s " ${problems.size} problems in testdata " )
125135}
136+ ThisBuild / newDataset := NewDataset (buildRoot, Def .spaceDelimited(" <name>" ).parsed)
137+
138+ // An input task is evaluated once per aggregated project, so without this newDataset creates the
139+ // directory and then fails three times saying it exists. Plain tasks resolve to one scoped key
140+ // and run once already.
141+ ThisBuild / newDataset / aggregate := false
142+ ThisBuild / allUp := dockerUp()
143+ ThisBuild / allStop := dockerStop()
144+ ThisBuild / pgUp := dockerUp(" postgres" )
145+ ThisBuild / pgStop := dockerStop(" postgres" )
146+ ThisBuild / oracleUp := dockerUp(" oracle" )
147+ ThisBuild / oracleStop := dockerStop(" oracle" )
148+ ThisBuild / mssqlUp := dockerUp(" mssql" )
149+ ThisBuild / mssqlStop := dockerStop(" mssql" )
150+
151+ // The compose file is named relatively, so docker is already run from the build root; the
152+ // generated init scripts are written relative to the same place.
153+ def buildRoot : File = file(" ." ).getAbsoluteFile
154+
155+ /**
156+ * Starts the named services, or every service when named none.
157+ */
158+ def dockerUp (services : String * ): Unit = {
159+ // A container only reads its init scripts the first time it starts, so they have to be
160+ // current before anything brings one up.
161+ GenTestData (buildRoot)
162+ runDocker(" docker compose up -d --wait --quiet-pull" , services)
163+ }
164+
165+ /**
166+ * Stops the named services, or every service when named none.
167+ */
168+ def dockerStop (services : String * ): Unit =
169+ runDocker(" docker compose stop" , services)
170+
171+ def runDocker (cmd : String , services : Seq [String ]): Unit =
172+ require((cmd +: services).mkString(" " ).! == 0 , s " docker indicated an error " )
126173
127174lazy val commonSettings = Seq (
128175 // scalacOptions --= Seq("-Wunused:params", "-Wunused:imports", "-Wunused:patvars", "-Wdead-code", "-Wunused:locals", "-Wunused:privates", "-Wunused:implicits"),
@@ -295,8 +342,7 @@ lazy val doobiepg = project
295342 name := " grackle-doobie-pg" ,
296343 Test / fork := true ,
297344 Test / parallelExecution := false ,
298- Test / testOptions += Tests
299- .Setup (_ => runDocker(" docker compose up -d --wait --quiet-pull postgres" )),
345+ Test / testOptions += Tests .Setup (_ => dockerUp(" postgres" )),
300346 libraryDependencies ++= Seq (
301347 " org.typelevel" %% " doobie-postgres-circe" % doobieVersion,
302348 // Pin transitive Postgres JDBC driver to >= 42.7.11 (CVE-2026-42198 / GHSA-98qh-xjc8-98pq)
@@ -314,8 +360,7 @@ lazy val doobieoracle = project
314360 name := " grackle-doobie-oracle" ,
315361 Test / fork := true ,
316362 Test / parallelExecution := false ,
317- Test / testOptions += Tests
318- .Setup (_ => runDocker(" docker compose up -d --wait --quiet-pull oracle" )),
363+ Test / testOptions += Tests .Setup (_ => dockerUp(" oracle" )),
319364 libraryDependencies ++= Seq (
320365 " com.oracle.database.jdbc" % " ojdbc8" % oracleDriverVersion
321366 )
@@ -334,8 +379,7 @@ lazy val doobiemssql = project
334379 // mssql-jdbc binds a zone-naive java.sql.Timestamp using the ambient JVM zone, so MSSQL
335380 // datetime tests fail off-UTC unless pinned.
336381 Test / javaOptions += " -Duser.timezone=UTC" ,
337- Test / testOptions += Tests
338- .Setup (_ => runDocker(" docker compose up -d --wait --quiet-pull mssql" )),
382+ Test / testOptions += Tests .Setup (_ => dockerUp(" mssql" )),
339383 libraryDependencies ++= Seq (
340384 " com.microsoft.sqlserver" % " mssql-jdbc" % mssqlDriverVersion
341385 )
@@ -359,8 +403,7 @@ lazy val skunk = crossProject(JVMPlatform, JSPlatform, NativePlatform)
359403 )
360404 .jvmSettings(
361405 Test / fork := true ,
362- Test / testOptions += Tests .Setup (_ =>
363- runDocker(" docker compose up -d --wait --quiet-pull postgres" )),
406+ Test / testOptions += Tests .Setup (_ => dockerUp(" postgres" )),
364407 libraryDependencies ++= Seq (
365408 " ch.qos.logback" % " logback-classic" % logbackVersion % " test"
366409 )
0 commit comments