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
9 changes: 5 additions & 4 deletions docs/gha.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,10 +71,11 @@ Any and all settings which affect the behavior of the generative plugin should b
- `githubWorkflowUseSbtThinClient` : `Boolean` – Controls whether or not the `--client` option will be added to `sbt` command invocations, accelerating build times (default: `true` for sbt ≥ 1.4, `false` otherwise)
- `githubWorkflowIncludeClean` : `Boolean` – Controls whether to include the clean.yml file (default: `true`)
- `githubWorkflowDependencyPatterns` : `Seq[String]` – A list of file globs which dictate where dependency information is stored. This is conventionally just `**/*.sbt` and `project/build.properties`. If you store dependency information in some *other* file (for example, `project/Versions.scala`), then you should add a glob which matches that file in this setting. This is used for determining the appropriate cache keys for the Ivy and Coursier caches.
- `githubWorkflowTargetBranches` : `Seq[String]` – A list of globs which will match branches and tags for `push` and `pull-request` event types to trigger the **ci.yml** workflow. Defaults to `[*]`.
- `githubWorkflowTargetTags` : `Seq[String]` – A list of globs which will match tags and tags for `push` event types to trigger the **ci.yml** workflow. Defaults to `[]`.
- `githubWorkflowTargetPaths` : `Paths` – Paths which will match modified files for `push` and `pull_request` event types to trigger the **ci.yml** workflow. May be `Paths.None`, `Paths.Include(patterns)`, or `Paths.Ignore(patterns)`. `Paths.Include` may include negative patterns. Defaults to `Paths.None`.
- `githubWorkflowPREventTypes` : `Seq[PREventType]` – A list of event types which will be used to determine which Pull Request events trigger the **ci.yml** workflow. This follows GitHub's defaults: `[opened, synchronize, reopened]`.
- `githubWorkflowTriggers`: WorkflowTriggers – Specifify the push, pull_request, and merge_group triggers in the on: section of the ci.yml workflow. By default this is derived from githubWorkflowTargetBranches, githubWorkflowTargetTags, githubWorkflowTargetPaths, and githubWorkflowPREventTypes. Setting it explicitly takes precedence, and those four keys are then ignored. This is the only way to express triggers they can't, such as a merge_group trigger (for GitHub merge queues) or branches-ignore/paths-ignore filters. Constructed via WorkflowTriggers, PushTrigger, PullRequestTrigger, and MergeGroupTrigger (with MergeGroupEventType for merge-group activity types).
- `githubWorkflowTargetBranches` : `Seq[String]` – A list of globs which will match branches and tags for `push` and `pull-request` event types to trigger the **ci.yml** workflow. Defaults to `[*]`. (Ignored if githubWorkflowTriggers is set directly.)
- `githubWorkflowTargetTags` : `Seq[String]` – A list of globs which will match tags and tags for `push` event types to trigger the **ci.yml** workflow. Defaults to `[]`. (Ignored if githubWorkflowTriggers is set directly.)
- `githubWorkflowTargetPaths` : `Paths` – Paths which will match modified files for `push` and `pull_request` event types to trigger the **ci.yml** workflow. May be `Paths.None`, `Paths.Include(patterns)`, or `Paths.Ignore(patterns)`. `Paths.Include` may include negative patterns. Defaults to `Paths.None`. (Ignored if githubWorkflowTriggers is set directly.)
- `githubWorkflowPREventTypes` : `Seq[PREventType]` – A list of event types which will be used to determine which Pull Request events trigger the **ci.yml** workflow. This follows GitHub's defaults: `[opened, synchronize, reopened]`. (Ignored if githubWorkflowTriggers is set directly.)
- `githubWorkflowArtifactUpload` : `Boolean` – Controls whether or not to upload target directories in the event that multiple jobs are running sequentially. Can be set on a per-project basis. Defaults to `true`.
- `githubWorkflowJobSetup` : `Seq[WorkflowStep]` – The automatically-generated checkout, setup, and cache steps which are common to all jobs which touch the build (default: autogenerated)
- `githubWorkflowEnv` : `Map[String, String]` – An environment which is global to the entire **ci.yml** workflow. Defaults to `Map("GITHUB_TOKEN" -> "${{ secrets.GITHUB_TOKEN }}")` since it's so commonly needed.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,9 @@ trait GenerativeKeys {

lazy val githubWorkflowDependencyPatterns = settingKey[Seq[String]](
"A list of file globes within the project which affect dependency information (default: [**/*.sbt, project/build.properties])")
lazy val githubWorkflowTriggers = settingKey[WorkflowTriggers](
"The triggers to use for the workflow. The default is derived from githubWorkflowTargetBranches, githubWorkflowTargetTags, githubWorkflowTargetPaths, githubWorkflowPREventTypes. " +
"Setting this directly means the individual keys (githubWorkflowTargetBranches, etc.) are ignored.")
lazy val githubWorkflowTargetBranches = settingKey[Seq[String]](
"A list of branch patterns on which to trigger push and PR builds (default: [*])")
lazy val githubWorkflowTargetTags = settingKey[Seq[String]](
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,21 @@ object GenerativePlugin extends AutoPlugin {
type WorkflowJob = org.typelevel.sbt.gha.WorkflowJob
val WorkflowJob = org.typelevel.sbt.gha.WorkflowJob

type WorkflowTriggers = org.typelevel.sbt.gha.WorkflowTriggers
val WorkflowTriggers = org.typelevel.sbt.gha.WorkflowTriggers

type PushTrigger = org.typelevel.sbt.gha.PushTrigger
val PushTrigger = org.typelevel.sbt.gha.PushTrigger

type PullRequestTrigger = org.typelevel.sbt.gha.PullRequestTrigger
val PullRequestTrigger = org.typelevel.sbt.gha.PullRequestTrigger

type MergeGroupTrigger = org.typelevel.sbt.gha.MergeGroupTrigger
val MergeGroupTrigger = org.typelevel.sbt.gha.MergeGroupTrigger

type MergeGroupEventType = org.typelevel.sbt.gha.MergeGroupEventType
val MergeGroupEventType = org.typelevel.sbt.gha.MergeGroupEventType

type Concurrency = org.typelevel.sbt.gha.Concurrency
val Concurrency = org.typelevel.sbt.gha.Concurrency

Expand Down Expand Up @@ -147,6 +162,52 @@ object GenerativePlugin extends AutoPlugin {
}
}

def compileMergeGroupEventType(tpe: MergeGroupEventType): String =
tpe match {
case MergeGroupEventType.ChecksRequested => "checks_requested"
}

private def whenNonEmpty(items: List[String])(f: List[String] => String): String =
if (items.isEmpty) "" else f(items)

private def compilePushOrPullRequestTrigger(trigger: PushOrPullRequestTrigger): String = {
val branches = whenNonEmpty(trigger.branches)(b => s"\n branches:${compileList(b, 3)}")
val branchesIgnore =
whenNonEmpty(trigger.branchesIgnore)(b => s"\n branches-ignore:${compileList(b, 3)}")
val paths = whenNonEmpty(trigger.paths)(p => s"\n paths:${compileList(p, 3)}")
val pathsIgnore =
whenNonEmpty(trigger.pathsIgnore)(p => s"\n paths-ignore:${compileList(p, 3)}")
s"$branches$branchesIgnore$paths$pathsIgnore"
}

def compilePullRequestTrigger(pull: PullRequestTrigger): String = {
val base = compilePushOrPullRequestTrigger(pull)
val types =
whenNonEmpty(pull.types.map(compilePREventType))(t => s"\n types:${compileList(t, 3)}")
s"$base$types"
}

def compilePushTrigger(push: PushTrigger): String = {
val base = compilePushOrPullRequestTrigger(push)
val tags = whenNonEmpty(push.tags)(t => s"\n tags:${compileList(t, 3)}")
val tagsIgnore =
whenNonEmpty(push.tagsIgnore)(t => s"\n tags-ignore:${compileList(t, 3)}")
s"$base$tags$tagsIgnore"
}

def compileMergeGroupTrigger(merge: MergeGroupTrigger): String =
whenNonEmpty(merge.types.map(compileMergeGroupEventType))(t =>
s"\n types:${compileList(t, 3)}")

def compileWorkflowTrigger(trigger: WorkflowTriggers): String = {
val pullRequest =
trigger.pullRequest.fold("")(t => s"\n pull_request:${compilePullRequestTrigger(t)}")
val push = trigger.push.fold("")(t => s"\n push:${compilePushTrigger(t)}")
val mergeGroup =
trigger.mergeGroup.fold("")(t => s"\n merge_group:${compileMergeGroupTrigger(t)}")
s"on:$pullRequest$push$mergeGroup"
}

def compileRef(ref: Ref): String = ref match {
case Ref.Branch(name) => s"refs/heads/$name"
case Ref.Tag(name) => s"refs/tags/$name"
Expand Down Expand Up @@ -590,6 +651,23 @@ ${indent(job.steps.map(compileStep(_, sbt, job.sbtStepPreamble, declareShell = d
env: Map[String, String],
concurrency: Option[Concurrency],
jobs: List[WorkflowJob],
sbt: String): String =
compileWorkflow(
name,
createTriggers(branches, tags, paths, prEventTypes),
permissions,
env,
concurrency,
jobs,
sbt)

def compileWorkflow(
name: String,
triggers: WorkflowTriggers,
permissions: Option[Permissions],
env: Map[String, String],
concurrency: Option[Concurrency],
jobs: List[WorkflowJob],
sbt: String): String = {

val renderedPermissionsPre = compilePermissions(permissions)
Expand All @@ -608,29 +686,6 @@ ${indent(job.steps.map(compileStep(_, sbt, job.sbtStepPreamble, declareShell = d
val renderedConcurrency =
concurrency.map(compileConcurrency).map("\n" + _ + "\n\n").getOrElse("")

val renderedTypesPre = prEventTypes.map(compilePREventType).mkString("[", ", ", "]")
val renderedTypes =
if (prEventTypes.sortBy(_.toString) == PREventType.Defaults)
""
else
"\n" + indent("types: " + renderedTypesPre, 2)

val renderedTags =
if (tags.isEmpty)
""
else
s"""
tags: [${tags.map(wrap).mkString(", ")}]"""

val renderedPaths = paths match {
case Paths.None =>
""
case Paths.Include(paths) =>
"\n" + indent(s"""paths: [${paths.map(wrap).mkString(", ")}]""", 2)
case Paths.Ignore(paths) =>
"\n" + indent(s"""paths-ignore: [${paths.map(wrap).mkString(", ")}]""", 2)
}

s"""# This file was automatically generated by sbt-github-actions using the
# githubWorkflowGenerate task. You should add and commit this file to
# your git repository. It goes without saying that you shouldn't edit
Expand All @@ -640,17 +695,47 @@ ${indent(job.steps.map(compileStep(_, sbt, job.sbtStepPreamble, declareShell = d

name: ${wrap(name)}

on:
pull_request:
branches: [${branches.map(wrap).mkString(", ")}]$renderedTypes$renderedPaths
push:
branches: [${branches.map(wrap).mkString(", ")}]$renderedTags$renderedPaths
${compileWorkflowTrigger(triggers)}

${renderedPerm}${renderedEnv}${renderedConcurrency}jobs:
${indent(jobs.map(compileJob(_, sbt)).mkString("\n\n"), 1)}
"""
}

private def createTriggers(
branches: List[String],
tags: List[String],
paths: Paths,
prEventTypes: List[PREventType]): WorkflowTriggers = {
val (pathsInclude, pathsIgnore) = paths match {
case Paths.Include(ps) => (ps, Nil)
case Paths.Ignore(ps) => (Nil, ps)
case Paths.None => (Nil, Nil)
}

val types = {
if (prEventTypes.sortBy(_.toString) == PREventType.Defaults) Nil
else prEventTypes
}

WorkflowTriggers(
push = Some(
PushTrigger(
branches = branches,
tags = tags,
paths = pathsInclude,
pathsIgnore = pathsIgnore
)),
pullRequest = Some(
PullRequestTrigger(
branches = branches,
types = types,
paths = pathsInclude,
pathsIgnore = pathsIgnore
))
)
}

val settingDefaults = Seq(
githubWorkflowSbtCommand := "sbt",
githubWorkflowIncludeClean := true,
Expand All @@ -676,6 +761,13 @@ ${indent(jobs.map(compileJob(_, sbt)).mkString("\n\n"), 1)}
githubWorkflowPublishPostamble := Seq(),
githubWorkflowPublish := Seq(
WorkflowStep.Sbt(List("+publish"), name = Some("Publish project"))),

githubWorkflowTriggers := createTriggers(
githubWorkflowTargetBranches.value.toList,
githubWorkflowTargetTags.value.toList,
githubWorkflowTargetPaths.value,
githubWorkflowPREventTypes.value.toList
),
githubWorkflowPublishTargetBranches := Seq(RefPredicate.Equals(Ref.Branch("main"))),
githubWorkflowPublishCond := None,
githubWorkflowPublishTimeoutMinutes := None,
Expand Down Expand Up @@ -914,10 +1006,7 @@ ${indent(jobs.map(compileJob(_, sbt)).mkString("\n\n"), 1)}
private val generateCiContents = Def task {
compileWorkflow(
"Continuous Integration",
githubWorkflowTargetBranches.value.toList,
githubWorkflowTargetTags.value.toList,
githubWorkflowTargetPaths.value,
githubWorkflowPREventTypes.value.toList,
githubWorkflowTriggers.value,
githubWorkflowPermissions.value,
githubWorkflowEnv.value,
githubWorkflowConcurrency.value,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
/*
* Copyright 2022 Typelevel
*
* 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 org.typelevel.sbt.gha

sealed abstract class MergeGroupEventType

object MergeGroupEventType {
case object ChecksRequested extends MergeGroupEventType
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/*
* Copyright 2022 Typelevel
*
* 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 org.typelevel.sbt.gha

sealed abstract class MergeGroupTrigger {
def types: List[MergeGroupEventType]

def withTypes(types: List[MergeGroupEventType]): MergeGroupTrigger
}

object MergeGroupTrigger {
def apply(types: List[MergeGroupEventType] = Nil): MergeGroupTrigger =
Impl(types)

private final case class Impl(types: List[MergeGroupEventType]) extends MergeGroupTrigger {
override def withTypes(types: List[MergeGroupEventType]): MergeGroupTrigger =
copy(types = types)

override def productPrefix = "MergeGroupTrigger"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
/*
* Copyright 2022 Typelevel
*
* 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 org.typelevel.sbt.gha

sealed abstract class PullRequestTrigger extends PushOrPullRequestTrigger {
def types: List[PREventType]

def withTypes(types: List[PREventType]): PullRequestTrigger
}

object PullRequestTrigger {
def apply(
branches: List[String] = Nil,
branchesIgnore: List[String] = Nil,
paths: List[String] = Nil,
pathsIgnore: List[String] = Nil,
types: List[PREventType] = Nil
): PullRequestTrigger =
Impl(branches, branchesIgnore, paths, pathsIgnore, types)

private final case class Impl(
branches: List[String],
branchesIgnore: List[String],
paths: List[String],
pathsIgnore: List[String],
types: List[PREventType])
extends PullRequestTrigger {

override def withBranches(branches: List[String]): PullRequestTrigger =
copy(branches = branches)
override def withBranchesIgnore(branchesIgnore: List[String]): PullRequestTrigger =
copy(branchesIgnore = branchesIgnore)
override def withPaths(paths: List[String]): PullRequestTrigger = copy(paths = paths)
override def withPathsIgnore(pathsIgnore: List[String]): PullRequestTrigger =
copy(pathsIgnore = pathsIgnore)
override def withTypes(types: List[PREventType]): PullRequestTrigger =
copy(types = types)

override def productPrefix = "PullRequestTrigger"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/*
* Copyright 2022 Typelevel
*
* 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 org.typelevel.sbt.gha

trait PushOrPullRequestTrigger {
def branches: List[String]
def branchesIgnore: List[String]
def paths: List[String]
def pathsIgnore: List[String]

def withBranches(branches: List[String]): PushOrPullRequestTrigger
def withBranchesIgnore(branchesIgnore: List[String]): PushOrPullRequestTrigger
def withPaths(paths: List[String]): PushOrPullRequestTrigger
def withPathsIgnore(pathsIgnore: List[String]): PushOrPullRequestTrigger
}
Loading
Loading