Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add plugin for generating .mergify.yml config #190

Merged
merged 24 commits into from
Mar 1, 2022
Merged
Show file tree
Hide file tree
Changes from 11 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
4 changes: 2 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -77,11 +77,11 @@ jobs:

- name: Make target directories
if: github.event_name != 'pull_request' && (startsWith(github.ref, 'refs/tags/v') || github.ref == 'refs/heads/series/0.4')
run: mkdir -p github/target github-actions/target kernel/target versioning/target ci-release/target target .js/target mdocs/target site/target ci-signing/target mima/target .jvm/target .native/target no-publish/target sonatype/target ci/target sonatype-ci-release/target core/target settings/target project/target
run: mkdir -p github/target github-actions/target kernel/target versioning/target ci-release/target target .js/target mdocs/target site/target ci-signing/target mergify/target mima/target .jvm/target .native/target no-publish/target sonatype/target ci/target sonatype-ci-release/target core/target settings/target project/target

- name: Compress target directories
if: github.event_name != 'pull_request' && (startsWith(github.ref, 'refs/tags/v') || github.ref == 'refs/heads/series/0.4')
run: tar cf targets.tar github/target github-actions/target kernel/target versioning/target ci-release/target target .js/target mdocs/target site/target ci-signing/target mima/target .jvm/target .native/target no-publish/target sonatype/target ci/target sonatype-ci-release/target core/target settings/target project/target
run: tar cf targets.tar github/target github-actions/target kernel/target versioning/target ci-release/target target .js/target mdocs/target site/target ci-signing/target mergify/target mima/target .jvm/target .native/target no-publish/target sonatype/target ci/target sonatype-ci-release/target core/target settings/target project/target

- name: Upload target directories
if: github.event_name != 'pull_request' && (startsWith(github.ref, 'refs/tags/v') || github.ref == 'refs/heads/series/0.4')
Expand Down
19 changes: 19 additions & 0 deletions .mergify.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# This file was automatically generated by sbt-typelevel-mergify using the
# mergifyGenerate task. You should add and commit this file to
# your git repository. It goes without saying that you shouldn't edit
# this file by hand! Instead, if you wish to make changes, you should
# change your sbt build configuration to revise the mergify configuration
# to meet your needs, then regenerate this file.

pull_request_rules:
- name: merge scala-steward's PRs
conditions:
- author=scala-steward
- or:
- body~=labels:.*early-semver-patch
- body~=labels:.*early-semver-minor
- status-success=Build and Test (ubuntu-latest, 2.12.15, temurin@8, rootJVM)
- '#approved-reviews-by>=1'
Comment on lines +9 to +16
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@fthomas if you have a chance, would appreciate your eyes on this. Are these labels the right configuration for checking steward PRs? Thanks! :)

actions:
- merge: {}

12 changes: 12 additions & 0 deletions build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,16 @@ ThisBuild / developers := List(
tlGitHubDev("djspiewak", "Daniel Spiewak")
)

ThisBuild / mergifyStewardConfig ~= { _.map(_.copy(mergeMinors = true)) }
ThisBuild / mergifySuccessConditions += MergifyCondition.Custom("#approved-reviews-by>=1")
Comment on lines +14 to +15
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

mergeMinors, because I'm dog-fooding. But I actually don't trust any update to upstream sbt plugins is not going to break things, patch or not ...


lazy val root = tlCrossRootProject.aggregate(
kernel,
noPublish,
settings,
github,
githubActions,
mergify,
versioning,
mima,
sonatype,
Expand Down Expand Up @@ -66,6 +70,14 @@ lazy val githubActions = project
name := "sbt-typelevel-github-actions"
)

lazy val mergify = project
.in(file("mergify"))
.enablePlugins(SbtPlugin)
.settings(
name := "sbt-typelevel-mergify"
)
.dependsOn(githubActions)

lazy val versioning = project
.in(file("versioning"))
.enablePlugins(SbtPlugin)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -606,7 +606,7 @@ ${indent(jobs.map(compileJob(_, sbt)).mkString("\n\n"), 1)}
githubWorkflowGeneratedDownloadSteps := {
val extraKeys = githubWorkflowArtifactDownloadExtraKeys.value
val additions = githubWorkflowBuildMatrixAdditions.value
val matrix = additions.map {
val matrixAdds = additions.map {
case (key, values) =>
if (extraKeys(key))
key -> values // we want to iterate over all values
Expand All @@ -615,25 +615,23 @@ ${indent(jobs.map(compileJob(_, sbt)).mkString("\n\n"), 1)}
}

val keys = "scala" :: additions.keys.toList.sorted
val oses = githubWorkflowOSes.value.toList
val scalas = githubWorkflowScalaVersions.value.toList
val exclusions = githubWorkflowBuildMatrixExclusions.value
val javas = githubWorkflowJavaVersions.value.toList
val exclusions = githubWorkflowBuildMatrixExclusions.value.toList

// we build the list of artifacts, by iterating over all combinations of keys
val artifacts = matrix
.toList
.sortBy(_._1)
.map(_._2)
.foldLeft(scalas.map(List(_))) { (artifacts, values) =>
for {
artifact <- artifacts
value <- values
} yield artifact :+ value
} // then, we filter artifacts for keys that are excluded from the matrix
.filterNot { artifact =>
val job = keys.zip(artifact).toMap
exclusions.exists { // there is an exclude that matches the current job
case MatrixExclude(matching) => matching.toSet.subsetOf(job.toSet)
}
val artifacts =
expandMatrix(
oses,
scalas,
javas,
matrixAdds,
Nil,
exclusions
).map {
case _ :: scala :: _ :: tail => scala :: tail
case _ => sys.error("Bug generating artifact download steps") // shouldn't happen
}

if (githubWorkflowArtifactUpload.value) {
Expand Down Expand Up @@ -862,7 +860,37 @@ ${indent(jobs.map(compileJob(_, sbt)).mkString("\n\n"), 1)}
}
)

private[gha] def diff(expected: String, actual: String): String = {
private[sbt] def expandMatrix(
oses: List[String],
scalas: List[String],
javas: List[JavaSpec],
matrixAdds: Map[String, List[String]],
includes: List[MatrixInclude],
excludes: List[MatrixExclude]
): List[List[String]] = {
val keys = "os" :: "scala" :: "java" :: matrixAdds.keys.toList.sorted
val matrix =
matrixAdds + ("os" -> oses) + ("scala" -> scalas) + ("java" -> javas.map(_.render))

// expand the matrix
keys
.foldLeft(List(List.empty[String])) { (cells, key) =>
val values = matrix.getOrElse(key, Nil)
cells.flatMap { cell => values.map(v => cell ::: v :: Nil) }
}
.filterNot { cell => // remove the excludes
val job = keys.zip(cell).toMap
excludes.exists { // there is an exclude that matches the current job
case MatrixExclude(matching) => matching.toSet.subsetOf(job.toSet)
}
} ::: includes.map { // add the includes
case MatrixInclude(matching, additions) =>
// yoloing here, but let's wait for the bug report
keys.map(matching) ::: additions.values.toList
}
}

private[sbt] def diff(expected: String, actual: String): String = {
val expectedLines = expected.split("\n", -1)
val actualLines = actual.split("\n", -1)
val (lines, _) =
Expand Down
1 change: 1 addition & 0 deletions mergify/build.sbt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
libraryDependencies += "io.circe" %% "circe-yaml" % "0.14.1"
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
/*
* 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.mergify

import io.circe.Encoder
import io.circe.Json
import io.circe.syntax._

sealed abstract class MergifyAction

object MergifyAction {

implicit def encoder: Encoder[MergifyAction] = Encoder.instance {
case merge: Merge => merge.asJson
case label: Label => label.asJson
case _ => sys.error("should not happen")
}

final case class Merge(
method: Option[String] = None,
rebaseFallback: Option[String] = None,
commitMessageTemplate: Option[String] = None
) extends MergifyAction

object Merge {
implicit def encoder: Encoder[Merge] =
Encoder
.forProduct3("method", "rebase_fallback", "commit_message_template") { (m: Merge) =>
(m.method, m.rebaseFallback, m.commitMessageTemplate)
}
.mapJson(m => Json.obj("merge" -> m))
}

final case class Label(
add: List[String] = Nil,
remove: List[String] = Nil,
removeAll: Option[Boolean] = None
) extends MergifyAction

object Label {
implicit def encoder: Encoder[Label] =
Encoder
.forProduct3("add", "remove", "remove_all") { (l: Label) =>
(l.add, l.remove, l.removeAll)
}
.mapJson(l => Json.obj("label" -> l))
}

private[this] object Dummy extends MergifyAction
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This trick lets us keep MergifyAction sealed while being able to compatibly add additional cases to the ADT, since users will never be able to exhaustively pattern match.


}
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
/*
* 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.mergify

import io.circe.Encoder
import io.circe.syntax._

sealed abstract class MergifyCondition

object MergifyCondition {
implicit def encoder: Encoder[MergifyCondition] = Encoder.instance {
case custom: Custom => custom.asJson
case and: And => and.asJson
case or: Or => or.asJson
case _ => sys.error("shouldn't happen")
}

final case class Custom(condition: String) extends MergifyCondition
object Custom {
implicit def encoder: Encoder[Custom] = Encoder.encodeString.contramap(_.condition)
}

final case class And(conditions: List[MergifyCondition]) extends MergifyCondition
object And {
implicit def encoder: Encoder[And] = Encoder.forProduct1("and")(_.conditions)
}

final case class Or(conditions: List[MergifyCondition]) extends MergifyCondition
object Or {
implicit def encoder: Encoder[Or] = Encoder.forProduct1("or")(_.conditions)
}

private[this] final object Dummy extends MergifyCondition // break exhaustivity checking
}
Loading