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 a convenient method to compute the highest level of compatibility of all the sub-projects aggregated by a project #186

Merged
merged 1 commit into from
Nov 30, 2023
Merged
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
Original file line number Diff line number Diff line change
Expand Up @@ -90,4 +90,12 @@ object Compatibility {
}
}

// Ordering from the least compatible to the most compatible
implicit val ordering: Ordering[Compatibility] =
Ordering.by {
case Compatibility.None => 0
case Compatibility.BinaryCompatible => 1
case Compatibility.BinaryAndSourceCompatible => 3
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -35,4 +35,40 @@ object SbtVersionPolicyPlugin extends AutoPlugin {
SbtVersionPolicySettings.findIssuesSettings ++
SbtVersionPolicySettings.skipSettings

/**
* Compute the highest compatibility level satisfied by all the projects aggregated by the
* project this task is applied to.
* This is useful to know the overall level of compatibility of a multi-module project.
* On every aggregated project, it invokes `versionPolicyAssessCompatibility` and keeps
* the first result only (ie, it assumes that that task assessed the compatibility with
* the latest release only).
*/
val aggregatedAssessedCompatibilityWithLatestRelease: Def.Initialize[Task[Compatibility]] =
Def.taskDyn {
import autoImport.versionPolicyAssessCompatibility
val log = Keys.streams.value.log
// Take all the projects aggregated by this project
val aggregatedProjects = Keys.thisProject.value.aggregate

// Compute the highest compatibility level that is satisfied by all the aggregated projects
val maxCompatibility: Compatibility = Compatibility.BinaryAndSourceCompatible
aggregatedProjects.foldLeft(Def.task { maxCompatibility }) { (highestCompatibilityTask, project) =>
Def.task {
val highestCompatibility = highestCompatibilityTask.value
val compatibilities = (project / versionPolicyAssessCompatibility).value
// The most common case is to assess the compatibility with the latest release,
// so we look at the first element only and discard the others
compatibilities.headOption match {
case Some((_, compatibility)) =>
log.debug(s"Compatibility of aggregated project ${project.project} is ${compatibility}")
// Take the lowest of both
Compatibility.ordering.min(highestCompatibility, compatibility)
case None =>
log.debug(s"Unable to assess the compatibility level of the aggregated project ${project.project}")
highestCompatibility
}
}
}
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
// A project with two modules (“a” and “b”) and a root module that aggregates them

val v1_a =
project
.settings(
name := "aggregated-test-a",
version := "1.0.0",
)

val v1_b =
project
.settings(
name := "aggregated-test-b",
version := "1.0.0",
)

val v1_root =
project
.settings(
name := "aggregated-test-root",
publish / skip := true,
)
.aggregate(v1_a, v1_b)


// First round of evolutions
// No changes in v2_a
val v2_a =
project
.settings(
name := "aggregated-test-a",
version := "1.0.0+n",
)

// Small changes that don’t break the binary and source compatibility
val v2_b =
project
.settings(
name := "aggregated-test-b",
version := "1.0.0+n",
)

val v2_root =
project
.settings(
name := "aggregated-test-root",
publish / skip := true,
TaskKey[Unit]("check") := {
val compatibility = SbtVersionPolicyPlugin.aggregatedAssessedCompatibilityWithLatestRelease.value
assert(compatibility == Compatibility.BinaryAndSourceCompatible)
}
)
.aggregate(v2_a, v2_b)


// Second round of evolutions
// Introduction of a public member
val v3_a =
project
.settings(
name := "aggregated-test-a",
version := "1.0.0+n",
)

// No changes
val v3_b =
project
.settings(
name := "aggregated-test-b",
version := "1.0.0+n",
)

val v3_root =
project
.settings(
name := "aggregated-test-root",
publish / skip := true,
TaskKey[Unit]("check") := {
val compatibility = SbtVersionPolicyPlugin.aggregatedAssessedCompatibilityWithLatestRelease.value
assert(compatibility == Compatibility.BinaryCompatible)
}
)
.aggregate(v3_a, v3_b)

// Third round of evolutions
// Introduction of a public member
val v4_a =
project
.settings(
name := "aggregated-test-a",
version := "1.0.0+n",
)

// Removal of a public member
val v4_b =
project
.settings(
name := "aggregated-test-b",
version := "1.0.0+n",
)

val v4_root =
project
.settings(
name := "aggregated-test-root",
publish / skip := true,
TaskKey[Unit]("check") := {
val compatibility = SbtVersionPolicyPlugin.aggregatedAssessedCompatibilityWithLatestRelease.value
assert(compatibility == Compatibility.None)
}
)
.aggregate(v4_a, v4_b)


Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
addSbtPlugin("ch.epfl.scala" % "sbt-version-policy" % sys.props("plugin.version"))
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
> v1_root/publishLocal

> v2_root/check

> v3_root/check

> v4_root/check
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package pkg

object A {

val x: Int = 42

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package pkg

object B {

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package pkg

object A {

val x: Int = 42

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package pkg

object B {

private val b: String = "b"

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package pkg

object A {

val x: Int = 42

val y: Int = 0

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package pkg

object B {

private val b: String = "b"

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package pkg

object A {

val x: Int = 42

val y: Int = 0

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
package pkg

object C
Loading