Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions src/main/scala/sbtghactions/Concurrency.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
/*
* Copyright 2020-2021 Daniel Spiewak
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package sbtghactions

case class Concurrency(
group: String,
cancelInProgress: Option[Boolean] = None,
)
1 change: 1 addition & 0 deletions src/main/scala/sbtghactions/GenerativeKeys.scala
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ trait GenerativeKeys {
lazy val githubWorkflowSbtCommand = settingKey[String]("The command which invokes sbt (default: sbt)")
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)")
lazy val githubWorkflowIncludeClean = settingKey[Boolean]("Whether to include the clean.yml file (default: true)")
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)")

lazy val githubWorkflowBuildMatrixFailFast = settingKey[Option[Boolean]]("Whether or not to enable the fail-fast strategy (default: None/Enabled)")
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: {})")
Expand Down
103 changes: 90 additions & 13 deletions src/main/scala/sbtghactions/GenerativePlugin.scala
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,14 @@ object GenerativePlugin extends AutoPlugin {
type WorkflowStep = sbtghactions.WorkflowStep
val WorkflowStep = sbtghactions.WorkflowStep

type WorkflowAction = sbtghactions.WorkflowAction

type WorkflowSteps = sbtghactions.WorkflowSteps
val WorkflowSteps = sbtghactions.WorkflowSteps

type WorkflowApply = sbtghactions.WorkflowApply
val WorkflowApply = sbtghactions.WorkflowApply

type RefPredicate = sbtghactions.RefPredicate
val RefPredicate = sbtghactions.RefPredicate

Expand Down Expand Up @@ -70,6 +78,12 @@ object GenerativePlugin extends AutoPlugin {
type PermissionValue = sbtghactions.PermissionValue
val PermissionValue = sbtghactions.PermissionValue

type Concurrency = sbtghactions.Concurrency
val Concurrency = sbtghactions.Concurrency

type Secrets = sbtghactions.Secrets
val Secrets = sbtghactions.Secrets

type Graalvm = sbtghactions.Graalvm
val Graalvm = sbtghactions.Graalvm
}
Expand Down Expand Up @@ -172,6 +186,24 @@ object GenerativePlugin extends AutoPlugin {
s"(startsWith($target, 'refs/heads/') && endsWith($target, '$name'))"
}

def compileConcurrency(concurrency: Concurrency): String =
concurrency.cancelInProgress match {
case Some(value) =>
val fields = s"""group: ${wrap(concurrency.group)}
|cancel-in-progress: ${wrap(value.toString)}""".stripMargin
s"""concurrency:
|${indent(fields, 1)}""".stripMargin

case None =>
s"concurrency: ${wrap(concurrency.group)}"
}

def compileSecrets(secrets: Secrets): String =
secrets match {
case Secrets.Inherit => "secrets: inherit"
case Secrets.Explicit(secretMap) => compileEnv(secretMap, "secrets")
}

def compileEnvironment(environment: JobEnvironment): String =
environment.url match {
case Some(url) =>
Expand Down Expand Up @@ -340,6 +372,8 @@ ${indent(rendered.mkString("\n"), 1)}"""


def compileJob(job: WorkflowJob, sbt: String): String = {
val renderedName = s"""name: ${wrap(job.name)}"""

val renderedNeeds = if (job.needs.isEmpty)
""
else
Expand All @@ -350,6 +384,9 @@ ${indent(rendered.mkString("\n"), 1)}"""

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

val renderedConcurrency =
job.concurrency.map(compileConcurrency).map("\n" + _).getOrElse("")

val renderedContainer = job.container match {
case Some(JobContainer(image, credentials, env, volumes, ports, options)) =>
if (credentials.isEmpty && env.isEmpty && volumes.isEmpty && ports.isEmpty && options.isEmpty) {
Expand Down Expand Up @@ -467,24 +504,59 @@ ${indent(rendered.mkString("\n"), 1)}"""

val declareShell = job.oses.exists(_.contains("windows"))

val runsOn = if (job.runsOnExtraLabels.isEmpty)
s"$${{ matrix.os }}"
else
job.runsOnExtraLabels.mkString(s"""[ "$${{ matrix.os }}", """, ", ", " ]" )
val runsOn = job.action match {
case steps: WorkflowSteps if steps.runsOnExtraLabels.isEmpty =>
"\nruns-on: ${{ matrix.os }}"
case steps: WorkflowSteps =>
steps.runsOnExtraLabels.mkString(s"""\nruns-on: [ "$${{ matrix.os }}", """, ", ", " ]")
case _ =>
""
}

val renderedFailFast = job.matrixFailFast.fold("")("\n fail-fast: " + _)

val body = s"""name: ${wrap(job.name)}${renderedNeeds}${renderedCond}
strategy:${renderedFailFast}
val renderedStrategy = s"""\nstrategy:${renderedFailFast}
matrix:
os:${compileList(job.oses, 3)}
scala:${compileList(job.scalas, 3)}
java:${compileList(job.javas.map(_.render), 3)}${renderedMatrices}
runs-on: ${runsOn}${renderedEnvironment}${renderedContainer}${renderedTimeout}${renderedPerm}${renderedEnv}
steps:
${indent(job.steps.map(compileStep(_, sbt, job.sbtStepPreamble, declareShell = declareShell)).mkString("\n\n"), 1)}"""
java:${compileList(job.javas.map(_.render), 3)}"""

val renderedSteps = job.action match {
case steps: WorkflowSteps =>
"\nsteps:\n" +
indent(steps.steps.map(compileStep(_, sbt, steps.sbtStepPreamble, declareShell = declareShell)).mkString("\n\n"), 1)
case _ =>
""
}

s"${job.id}:\n${indent(body, 1)}"
val renderedUses = job.action match {
case apply: WorkflowApply =>
val renderedSecrets =
apply.secrets.map(compileSecrets).map("\n" + _).getOrElse("")

s"\nuses: ${apply.ref}${renderParams(apply.params)}${renderedSecrets}"
case _ =>
""
}

val content = List(
renderedName,
renderedNeeds,
renderedCond,
renderedStrategy,
renderedMatrices,
runsOn,
renderedEnvironment,
renderedContainer,
renderedTimeout,
renderedPerm,
renderedEnv,
renderedConcurrency,
renderedSteps,
renderedUses,
).reduce(_ ++ _)

s"${job.id}:\n${indent(content, 1)}"
}

def compileWorkflow(
Expand All @@ -495,6 +567,7 @@ ${indent(job.steps.map(compileStep(_, sbt, job.sbtStepPreamble, declareShell = d
prEventTypes: List[PREventType],
permissions: Option[Permissions],
env: Map[String, String],
concurrency: Option[Concurrency],
jobs: List[WorkflowJob],
sbt: String)
: String = {
Expand All @@ -510,6 +583,9 @@ ${indent(job.steps.map(compileStep(_, sbt, job.sbtStepPreamble, declareShell = d
else
renderedPermissionsPre + "\n\n"

val renderedConcurrency =
concurrency.map(compileConcurrency).map(_ + "\n\n").getOrElse("")

val renderedTypesPre = prEventTypes.map(compilePREventType).mkString("[", ", ", "]")
val renderedTypes = if (prEventTypes.sortBy(_.toString) == PREventType.Defaults)
""
Expand Down Expand Up @@ -546,7 +622,7 @@ on:
push:
branches: [${branches.map(wrap).mkString(", ")}]$renderedTags$renderedPaths

${renderedPerm}${renderedEnv}jobs:
${renderedPerm}${renderedEnv}${renderedConcurrency}jobs:
${indent(jobs.map(compileJob(_, sbt)).mkString("\n\n"), 1)}
"""
}
Expand All @@ -557,7 +633,7 @@ ${indent(jobs.map(compileJob(_, sbt)).mkString("\n\n"), 1)}
// This is currently set to false because of https://github.com/sbt/sbt/issues/6468. When a new SBT version is
// released that fixes this issue then check for that SBT version (or higher) and set to true.
githubWorkflowUseSbtThinClient := false,

githubWorkflowConcurrency := None,
githubWorkflowBuildMatrixFailFast := None,
githubWorkflowBuildMatrixAdditions := Map(),
githubWorkflowBuildMatrixInclusions := Seq(),
Expand Down Expand Up @@ -783,6 +859,7 @@ ${indent(jobs.map(compileJob(_, sbt)).mkString("\n\n"), 1)}
githubWorkflowPREventTypes.value.toList,
githubWorkflowPermissions.value,
githubWorkflowEnv.value,
githubWorkflowConcurrency.value,
githubWorkflowGeneratedCI.value.toList,
sbt)
}
Expand Down
24 changes: 24 additions & 0 deletions src/main/scala/sbtghactions/Secrets.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
/*
* Copyright 2020-2021 Daniel Spiewak
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package sbtghactions

sealed trait Secrets extends Product with Serializable

object Secrets {
case object Inherit extends Secrets
case class Explicit(secrets: Map[String, String]) extends Secrets
}
32 changes: 32 additions & 0 deletions src/main/scala/sbtghactions/WorkflowAction.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/*
* Copyright 2020-2021 Daniel Spiewak
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package sbtghactions

sealed trait WorkflowAction

final case class WorkflowSteps(
steps: List[WorkflowStep],
sbtStepPreamble: List[String] = List(),
runsOnExtraLabels: List[String] = List(),
) extends WorkflowAction


final case class WorkflowApply(
ref: String,
params: Map[String, String] = Map.empty,
secrets: Option[Secrets] = None,
) extends WorkflowAction
91 changes: 90 additions & 1 deletion src/main/scala/sbtghactions/WorkflowJob.scala
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,28 @@ package sbtghactions
import scala.concurrent.duration.FiniteDuration

final case class WorkflowJob(
id: String,
name: String,
action: WorkflowAction,
cond: Option[String],
permissions: Option[Permissions],
env: Map[String, String],
oses: List[String],
scalas: List[String],
javas: List[JavaSpec],
needs: List[String],
matrixFailFast: Option[Boolean],
matrixAdds: Map[String, List[String]],
matrixIncs: List[MatrixInclude],
matrixExcs: List[MatrixExclude],
container: Option[JobContainer],
environment: Option[JobEnvironment],
concurrency: Option[Concurrency],
timeout: Option[FiniteDuration],
)

object WorkflowJob {
def apply(
id: String,
name: String,
steps: List[WorkflowStep],
Expand All @@ -37,4 +59,71 @@ final case class WorkflowJob(
runsOnExtraLabels: List[String] = List(),
container: Option[JobContainer] = None,
environment: Option[JobEnvironment] = None,
timeout: Option[FiniteDuration] = None)
concurrency: Option[Concurrency] = None,
timeout: Option[FiniteDuration] = None
): WorkflowJob =
WorkflowJob(
id,
name,
action = WorkflowSteps(steps, sbtStepPreamble, runsOnExtraLabels),
cond,
permissions,
env,
oses,
scalas,
javas,
needs,
matrixFailFast,
matrixAdds,
matrixIncs,
matrixExcs,
container,
environment,
concurrency,
timeout
)

def use(
id: String,
name: String,
ref: String,
params: Map[String, String] = Map.empty,
secrets: Option[Secrets] = None,
cond: Option[String] = None,
permissions: Option[Permissions] = None,
env: Map[String, String] = Map(),
oses: List[String] = List("ubuntu-latest"),
scalas: List[String] = List("2.13.10"),
javas: List[JavaSpec] = List(JavaSpec.zulu("8")),
needs: List[String] = List(),
matrixFailFast: Option[Boolean] = None,
matrixAdds: Map[String, List[String]] = Map(),
matrixIncs: List[MatrixInclude] = List(),
matrixExcs: List[MatrixExclude] = List(),
container: Option[JobContainer] = None,
environment: Option[JobEnvironment] = None,
concurrency: Option[Concurrency] = None,
timeout: Option[FiniteDuration] = None,
): WorkflowJob =
WorkflowJob(
id,
name,
action = WorkflowApply(ref, params, secrets),
cond,
permissions,
env,
oses,
scalas,
javas,
needs,
matrixFailFast,
matrixAdds,
matrixIncs,
matrixExcs,
container,
environment,
concurrency,
timeout
)

}
Loading
Loading