Skip to content

Commit 793c0c0

Browse files
committed
Add concurrency support
Fixes #79
1 parent 532c2b5 commit 793c0c0

File tree

5 files changed

+145
-8
lines changed

5 files changed

+145
-8
lines changed
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
/*
2+
* Copyright 2020-2021 Daniel Spiewak
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package sbtghactions
18+
19+
case class Concurrency(
20+
group: String,
21+
cancelInProgress: Option[Boolean] = None,
22+
)

src/main/scala/sbtghactions/GenerativeKeys.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ trait GenerativeKeys {
3636
lazy val githubWorkflowSbtCommand = settingKey[String]("The command which invokes sbt (default: sbt)")
3737
lazy val githubWorkflowUseSbtThinClient = settingKey[Boolean]("Whether to use sbt's native thin client, default is false since this can cause issues (see https://github.com/sbt/sbt/issues/6468)")
3838
lazy val githubWorkflowIncludeClean = settingKey[Boolean]("Whether to include the clean.yml file (default: true)")
39+
lazy val githubWorkflowConcurrency = settingKey[Option[Concurrency]]("Use concurrency to ensure that only a single workflow within the same concurrency group will run at a time. (default: None)")
3940

4041
lazy val githubWorkflowBuildMatrixFailFast = settingKey[Option[Boolean]]("Whether or not to enable the fail-fast strategy (default: None/Enabled)")
4142
lazy val githubWorkflowBuildMatrixAdditions = settingKey[Map[String, List[String]]]("A map of additional matrix dimensions for the build job. Each list should be non-empty. (default: {})")

src/main/scala/sbtghactions/GenerativePlugin.scala

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,9 @@ object GenerativePlugin extends AutoPlugin {
7070
type PermissionValue = sbtghactions.PermissionValue
7171
val PermissionValue = sbtghactions.PermissionValue
7272

73+
type Concurrency = sbtghactions.Concurrency
74+
val Concurrency = sbtghactions.Concurrency
75+
7376
type Graalvm = sbtghactions.Graalvm
7477
val Graalvm = sbtghactions.Graalvm
7578
}
@@ -172,6 +175,18 @@ object GenerativePlugin extends AutoPlugin {
172175
s"(startsWith($target, 'refs/heads/') && endsWith($target, '$name'))"
173176
}
174177

178+
def compileConcurrency(concurrency: Concurrency): String =
179+
concurrency.cancelInProgress match {
180+
case Some(value) =>
181+
val fields = s"""group: ${wrap(concurrency.group)}
182+
|cancel-in-progress: ${wrap(value.toString)}""".stripMargin
183+
s"""concurrency:
184+
|${indent(fields, 1)}""".stripMargin
185+
186+
case None =>
187+
s"concurrency: ${wrap(concurrency.group)}"
188+
}
189+
175190
def compileEnvironment(environment: JobEnvironment): String =
176191
environment.url match {
177192
case Some(url) =>
@@ -350,6 +365,9 @@ ${indent(rendered.mkString("\n"), 1)}"""
350365

351366
val renderedCond = job.cond.map(wrap).map("\nif: " + _).getOrElse("")
352367

368+
val renderedConcurrency =
369+
job.concurrency.map(compileConcurrency).map("\n" + _).getOrElse("")
370+
353371
val renderedContainer = job.container match {
354372
case Some(JobContainer(image, credentials, env, volumes, ports, options)) =>
355373
if (credentials.isEmpty && env.isEmpty && volumes.isEmpty && ports.isEmpty && options.isEmpty) {
@@ -480,7 +498,7 @@ strategy:${renderedFailFast}
480498
os:${compileList(job.oses, 3)}
481499
scala:${compileList(job.scalas, 3)}
482500
java:${compileList(job.javas.map(_.render), 3)}${renderedMatrices}
483-
runs-on: ${runsOn}${renderedEnvironment}${renderedContainer}${renderedTimeout}${renderedPerm}${renderedEnv}
501+
runs-on: ${runsOn}${renderedEnvironment}${renderedContainer}${renderedTimeout}${renderedPerm}${renderedEnv}${renderedConcurrency}
484502
steps:
485503
${indent(job.steps.map(compileStep(_, sbt, job.sbtStepPreamble, declareShell = declareShell)).mkString("\n\n"), 1)}"""
486504

@@ -495,6 +513,7 @@ ${indent(job.steps.map(compileStep(_, sbt, job.sbtStepPreamble, declareShell = d
495513
prEventTypes: List[PREventType],
496514
permissions: Option[Permissions],
497515
env: Map[String, String],
516+
concurrency: Option[Concurrency],
498517
jobs: List[WorkflowJob],
499518
sbt: String)
500519
: String = {
@@ -510,6 +529,9 @@ ${indent(job.steps.map(compileStep(_, sbt, job.sbtStepPreamble, declareShell = d
510529
else
511530
renderedPermissionsPre + "\n\n"
512531

532+
val renderedConcurrency =
533+
concurrency.map(compileConcurrency).map(_ + "\n\n").getOrElse("")
534+
513535
val renderedTypesPre = prEventTypes.map(compilePREventType).mkString("[", ", ", "]")
514536
val renderedTypes = if (prEventTypes.sortBy(_.toString) == PREventType.Defaults)
515537
""
@@ -546,7 +568,7 @@ on:
546568
push:
547569
branches: [${branches.map(wrap).mkString(", ")}]$renderedTags$renderedPaths
548570

549-
${renderedPerm}${renderedEnv}jobs:
571+
${renderedPerm}${renderedEnv}${renderedConcurrency}jobs:
550572
${indent(jobs.map(compileJob(_, sbt)).mkString("\n\n"), 1)}
551573
"""
552574
}
@@ -557,7 +579,7 @@ ${indent(jobs.map(compileJob(_, sbt)).mkString("\n\n"), 1)}
557579
// This is currently set to false because of https://github.com/sbt/sbt/issues/6468. When a new SBT version is
558580
// released that fixes this issue then check for that SBT version (or higher) and set to true.
559581
githubWorkflowUseSbtThinClient := false,
560-
582+
githubWorkflowConcurrency := None,
561583
githubWorkflowBuildMatrixFailFast := None,
562584
githubWorkflowBuildMatrixAdditions := Map(),
563585
githubWorkflowBuildMatrixInclusions := Seq(),
@@ -783,6 +805,7 @@ ${indent(jobs.map(compileJob(_, sbt)).mkString("\n\n"), 1)}
783805
githubWorkflowPREventTypes.value.toList,
784806
githubWorkflowPermissions.value,
785807
githubWorkflowEnv.value,
808+
githubWorkflowConcurrency.value,
786809
githubWorkflowGeneratedCI.value.toList,
787810
sbt)
788811
}

src/main/scala/sbtghactions/WorkflowJob.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,4 +37,5 @@ final case class WorkflowJob(
3737
runsOnExtraLabels: List[String] = List(),
3838
container: Option[JobContainer] = None,
3939
environment: Option[JobEnvironment] = None,
40+
concurrency: Option[Concurrency] = None,
4041
timeout: Option[FiniteDuration] = None)

src/test/scala/sbtghactions/GenerativePluginSpec.scala

Lines changed: 95 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ class GenerativePluginSpec extends Specification {
4747
|${" " * 2}
4848
|""".stripMargin
4949

50-
compileWorkflow("test", List("main"), Nil, Paths.None, PREventType.Defaults, None, Map(), Nil, "sbt") mustEqual expected
50+
compileWorkflow("test", List("main"), Nil, Paths.None, PREventType.Defaults, None, Map(), None, Nil, "sbt") mustEqual expected
5151
}
5252

5353
"produce the appropriate skeleton around a zero-job workflow with non-empty tags" in {
@@ -65,7 +65,7 @@ class GenerativePluginSpec extends Specification {
6565
|${" " * 2}
6666
|""".stripMargin
6767

68-
compileWorkflow("test", List("main"), List("howdy"), Paths.None, PREventType.Defaults, None, Map(), Nil, "sbt") mustEqual expected
68+
compileWorkflow("test", List("main"), List("howdy"), Paths.None, PREventType.Defaults, None, Map(), None, Nil, "sbt") mustEqual expected
6969
}
7070

7171
"respect non-default pr types" in {
@@ -83,7 +83,7 @@ class GenerativePluginSpec extends Specification {
8383
|${" " * 2}
8484
|""".stripMargin
8585

86-
compileWorkflow("test", List("main"), Nil, Paths.None, List(PREventType.ReadyForReview, PREventType.ReviewRequested, PREventType.Opened), None, Map(), Nil, "sbt") mustEqual expected
86+
compileWorkflow("test", List("main"), Nil, Paths.None, List(PREventType.ReadyForReview, PREventType.ReviewRequested, PREventType.Opened), None, Map(), None, Nil, "sbt") mustEqual expected
8787
}
8888

8989
"compile a one-job workflow targeting multiple branch patterns with a environment variables" in {
@@ -126,6 +126,7 @@ class GenerativePluginSpec extends Specification {
126126
))),
127127
Map(
128128
"GITHUB_TOKEN" -> s"$${{ secrets.GITHUB_TOKEN }}"),
129+
None,
129130
List(
130131
WorkflowJob(
131132
"build",
@@ -176,6 +177,7 @@ class GenerativePluginSpec extends Specification {
176177
PREventType.Defaults,
177178
None,
178179
Map(),
180+
None,
179181
List(
180182
WorkflowJob(
181183
"build",
@@ -221,6 +223,7 @@ class GenerativePluginSpec extends Specification {
221223
PREventType.Defaults,
222224
None,
223225
Map(),
226+
None,
224227
List(
225228
WorkflowJob(
226229
"build",
@@ -273,6 +276,7 @@ class GenerativePluginSpec extends Specification {
273276
PREventType.Defaults,
274277
None,
275278
Map(),
279+
None,
276280
List(
277281
WorkflowJob(
278282
"build",
@@ -305,7 +309,7 @@ class GenerativePluginSpec extends Specification {
305309
|${" " * 2}
306310
|""".stripMargin
307311

308-
compileWorkflow("test", List("main"), Nil, Paths.Include(List("**.scala", "**.sbt")), PREventType.Defaults, None, Map(), Nil, "sbt") mustEqual expected
312+
compileWorkflow("test", List("main"), Nil, Paths.Include(List("**.scala", "**.sbt")), PREventType.Defaults, None, Map(), None, Nil, "sbt") mustEqual expected
309313
}
310314

311315
"render ignored paths on pull_request and push" in {
@@ -324,7 +328,69 @@ class GenerativePluginSpec extends Specification {
324328
|${" " * 2}
325329
|""".stripMargin
326330

327-
compileWorkflow("test", List("main"), Nil, Paths.Ignore(List("docs/**")), PREventType.Defaults, None, Map(), Nil, "sbt") mustEqual expected
331+
compileWorkflow("test", List("main"), Nil, Paths.Ignore(List("docs/**")), PREventType.Defaults, None, Map(), None, Nil, "sbt") mustEqual expected
332+
}
333+
334+
"render shorthand form for concurrency without specific cancel-in-progress property" in {
335+
val expected = header + s"""
336+
|name: test
337+
|
338+
|on:
339+
| pull_request:
340+
| branches: [main]
341+
| push:
342+
| branches: [main]
343+
|
344+
|concurrency: test-group
345+
|
346+
|jobs:
347+
|${" " * 2}
348+
|""".stripMargin
349+
350+
compileWorkflow(
351+
"test",
352+
List("main"),
353+
Nil,
354+
Paths.None,
355+
PREventType.Defaults,
356+
None,
357+
Map(),
358+
Some(Concurrency("test-group")),
359+
Nil,
360+
"sbt",
361+
) mustEqual expected
362+
}
363+
364+
"render extended form for concurrency with specific cancel-in-progress property" in {
365+
val expected = header + s"""
366+
|name: test
367+
|
368+
|on:
369+
| pull_request:
370+
| branches: [main]
371+
| push:
372+
| branches: [main]
373+
|
374+
|concurrency:
375+
| group: test-group
376+
| cancel-in-progress: true
377+
|
378+
|jobs:
379+
|${" " * 2}
380+
|""".stripMargin
381+
382+
compileWorkflow(
383+
"test",
384+
List("main"),
385+
Nil,
386+
Paths.None,
387+
PREventType.Defaults,
388+
None,
389+
Map(),
390+
Some(Concurrency("test-group", Some(true))),
391+
Nil,
392+
"sbt",
393+
) mustEqual expected
328394
}
329395
}
330396

@@ -820,6 +886,30 @@ class GenerativePluginSpec extends Specification {
820886
runs-on: $${{ matrix.os }}
821887
timeout-minutes: 60
822888

889+
steps:
890+
- run: csbt ci-release"""
891+
}
892+
893+
"compile a job with concurrency" in {
894+
val results = compileJob(
895+
WorkflowJob(
896+
"publish",
897+
"Publish Release",
898+
List(
899+
WorkflowStep.Sbt(List("ci-release"))),
900+
concurrency = Some(Concurrency("test-group")),
901+
),
902+
"csbt")
903+
904+
results mustEqual s"""publish:
905+
name: Publish Release
906+
strategy:
907+
matrix:
908+
os: [ubuntu-latest]
909+
scala: [2.13.10]
910+
java: [zulu@8]
911+
runs-on: $${{ matrix.os }}
912+
concurrency: test-group
823913
steps:
824914
- run: csbt ci-release"""
825915
}

0 commit comments

Comments
 (0)