Skip to content

Commit 3e7096c

Browse files
committed
Add on-resolve-failure input
1 parent f77b648 commit 3e7096c

File tree

17 files changed

+176
-81
lines changed

17 files changed

+176
-81
lines changed

action.yml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,13 @@ inputs:
1616
Example: `foo_2.13 bar_2.13`
1717
required: false
1818
default: ''
19+
on-resolve-failure:
20+
description: |
21+
Either 'error' or 'warning'.
22+
When a dependency resolution failure happens, if 'error' the job will fail and will not submit the snapshot.
23+
If 'warning', the job will ignore the failing modules and submit the snapshot.
24+
required: false
25+
default: error
1926
token:
2027
description: GitHub Personal Access Token (PAT). Defaults to PAT provided by Action runner.
2128
required: false

dist/index.js

Lines changed: 11 additions & 7 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

dist/index.js.map

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

sbt-plugin/src/main/contraband/input.contra

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,17 @@ package ch.epfl.scala
33
@codecPackage("ch.epfl.scala")
44
@fullCodec("JsonProtocol")
55

6+
enum OnFailure {
7+
error
8+
warning
9+
}
10+
611
## Input of the githubSubmitDependencyGraph command
712
type SubmitInput {
8-
## A set of modules to ignore, a module is the artifact ID of a project on a particular scala version
9-
## Typically it would be something like foo_2.13
13+
onResolveFailure: ch.epfl.scala.OnFailure
14+
15+
## A set of modules to ignore.
16+
## The name of module is composed of the name of the project and its binary version.
17+
## Example: foo_2.13
1018
ignoredModules: [String]
1119
}

sbt-plugin/src/main/scala/ch/epfl/scala/GithubDependencyGraphPlugin.scala

Lines changed: 62 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,9 @@ object GithubDependencyGraphPlugin extends AutoPlugin {
2828
val githubSubmitInputKey: AttributeKey[SubmitInput] = AttributeKey("githubSubmitInput")
2929
val githubManifestsKey: AttributeKey[Map[String, githubapi.Manifest]] = AttributeKey("githubDependencyManifests")
3030
val githubProjectsKey: AttributeKey[Seq[ProjectRef]] = AttributeKey("githubProjectRefs")
31-
val githubDependencyManifest: TaskKey[githubapi.Manifest] = taskKey("The dependency manifest of the project")
31+
val githubDependencyManifest: TaskKey[Option[githubapi.Manifest]] = taskKey(
32+
"The dependency manifest of the project"
33+
)
3234
val githubStoreDependencyManifests: InputKey[StateTransform] =
3335
inputKey("Store the dependency manifests of all projects of a Scala version in the attribute map.")
3436
.withRank(KeyRanks.DTask)
@@ -74,6 +76,7 @@ object GithubDependencyGraphPlugin extends AutoPlugin {
7476
.map(ref => (ref / githubDependencyManifest).?)
7577
.join
7678
.value
79+
.flatten
7780
.collect { case Some(manifest) => (manifest.name, manifest) }
7881
.toMap
7982
StateTransform { state =>
@@ -95,7 +98,7 @@ object GithubDependencyGraphPlugin extends AutoPlugin {
9598
!ignored
9699
}
97100

98-
private def manifestTask: Def.Initialize[Task[Manifest]] = Def.task {
101+
private def manifestTask: Def.Initialize[Task[Option[Manifest]]] = Def.task {
99102
// updateFull is needed to have information about callers and reconstruct dependency tree
100103
val reportResult = Keys.updateFull.result.value
101104
val projectID = Keys.projectID.value
@@ -105,66 +108,74 @@ object GithubDependencyGraphPlugin extends AutoPlugin {
105108
val allDirectDependencies = Keys.allDependencies.value
106109
val baseDirectory = Keys.baseDirectory.value
107110
val logger = Keys.streams.value.log
111+
val onResolveFailure = Keys.state.value.get(githubSubmitInputKey).flatMap(_.onResolveFailure)
108112

109113
def getReference(module: ModuleID): String =
110114
crossVersion(module)
111115
.withConfigurations(None)
112116
.withExtraAttributes(Map.empty)
113117
.toString
114118

115-
val alreadySeen = mutable.Set[String]()
116-
val moduleReports = mutable.Buffer[(ModuleReport, ConfigRef)]()
117-
val allDependencies = mutable.Buffer[(String, String)]()
118-
119-
val report = reportResult match {
119+
reportResult match {
120120
case Inc(cause) =>
121121
val moduleName = crossVersion(projectID).name
122-
logger.error(s"Failed to resolve the dependencies of $moduleName")
123-
throw cause
124-
case Value(report) => report
125-
}
126-
127-
for {
128-
configReport <- report.configurations
129-
moduleReport <- configReport.modules
130-
moduleRef = getReference(moduleReport.module)
131-
if !moduleReport.evicted && !alreadySeen.contains(moduleRef)
132-
} {
133-
alreadySeen += moduleRef
134-
moduleReports += (moduleReport -> configReport.configuration)
135-
for (caller <- moduleReport.callers)
136-
allDependencies += (getReference(caller.caller) -> moduleRef)
137-
}
138-
139-
val allDependenciesMap: Map[String, Vector[String]] = allDependencies.view
140-
.groupBy(_._1)
141-
.mapValues {
142-
_.map { case (_, dep) => dep }.toVector
143-
}
144-
val allDirectDependenciesRefs: Set[String] = allDirectDependencies.map(getReference).toSet
145-
146-
val resolved =
147-
for ((moduleReport, configRef) <- moduleReports)
148-
yield {
149-
val moduleRef = getReference(moduleReport.module)
150-
val packageUrl = formatPackageUrl(moduleReport)
151-
val dependencies = allDependenciesMap.getOrElse(moduleRef, Vector.empty)
152-
val relationship =
153-
if (allDirectDependenciesRefs.contains(moduleRef)) DependencyRelationship.direct
154-
else DependencyRelationship.indirect
155-
val scope =
156-
if (isRuntime(configRef)) DependencyScope.runtime
157-
else DependencyScope.development
158-
val metadata = Map("config" -> JString(configRef.name))
159-
val node = DependencyNode(packageUrl, metadata, Some(relationship), Some(scope), dependencies)
160-
(moduleRef -> node)
122+
val message = s"Failed to resolve the dependencies of $moduleName"
123+
onResolveFailure match {
124+
case Some(OnFailure.warning) =>
125+
logger.warn(message)
126+
None
127+
case _ =>
128+
logger.error(message)
129+
throw cause
130+
}
131+
case Value(report) =>
132+
val alreadySeen = mutable.Set[String]()
133+
val moduleReports = mutable.Buffer[(ModuleReport, ConfigRef)]()
134+
val allDependencies = mutable.Buffer[(String, String)]()
135+
136+
for {
137+
configReport <- report.configurations
138+
moduleReport <- configReport.modules
139+
moduleRef = getReference(moduleReport.module)
140+
if !moduleReport.evicted && !alreadySeen.contains(moduleRef)
141+
} {
142+
alreadySeen += moduleRef
143+
moduleReports += (moduleReport -> configReport.configuration)
144+
for (caller <- moduleReport.callers)
145+
allDependencies += (getReference(caller.caller) -> moduleRef)
161146
}
162147

163-
val projectModuleRef = getReference(projectID)
164-
// TODO: find exact build file for this project
165-
val file = githubapi.FileInfo("build.sbt")
166-
val metadata = Map("baseDirectory" -> JString(baseDirectory.toString))
167-
githubapi.Manifest(projectModuleRef, file, metadata, resolved.toMap)
148+
val allDependenciesMap: Map[String, Vector[String]] = allDependencies.view
149+
.groupBy(_._1)
150+
.mapValues {
151+
_.map { case (_, dep) => dep }.toVector
152+
}
153+
val allDirectDependenciesRefs: Set[String] = allDirectDependencies.map(getReference).toSet
154+
155+
val resolved =
156+
for ((moduleReport, configRef) <- moduleReports)
157+
yield {
158+
val moduleRef = getReference(moduleReport.module)
159+
val packageUrl = formatPackageUrl(moduleReport)
160+
val dependencies = allDependenciesMap.getOrElse(moduleRef, Vector.empty)
161+
val relationship =
162+
if (allDirectDependenciesRefs.contains(moduleRef)) DependencyRelationship.direct
163+
else DependencyRelationship.indirect
164+
val scope =
165+
if (isRuntime(configRef)) DependencyScope.runtime
166+
else DependencyScope.development
167+
val metadata = Map("config" -> JString(configRef.name))
168+
val node = DependencyNode(packageUrl, metadata, Some(relationship), Some(scope), dependencies)
169+
(moduleRef -> node)
170+
}
171+
172+
val projectModuleRef = getReference(projectID)
173+
// TODO: find exact build file for this project
174+
val file = githubapi.FileInfo("build.sbt")
175+
val metadata = Map("baseDirectory" -> JString(baseDirectory.toString))
176+
val manifest = githubapi.Manifest(projectModuleRef, file, metadata, resolved.toMap)
177+
Some(manifest)
178+
}
168179
}
169180

170181
private def formatPackageUrl(moduleReport: ModuleReport): String = {

sbt-plugin/src/sbt-test/dependency-manifest/coursier-manifest/build.sbt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ lazy val p1 = project
2323
"org.scalatest" %% "scalatest" % "3.2.2" % Test
2424
),
2525
checkManifest := {
26-
val manifest = githubDependencyManifest.value
26+
val manifest = githubDependencyManifest.value.get
2727
assert(manifest.name == "ch.epfl.scala:p1_2.12:1.2.0-SNAPSHOT")
2828

2929
// all dependencies are defined
@@ -58,7 +58,7 @@ lazy val p2 = project
5858
"com.typesafe.akka" %% "akka-http" % "10.2.8"
5959
),
6060
checkManifest := {
61-
val manifest = githubDependencyManifest.value
61+
val manifest = githubDependencyManifest.value.get
6262
assert(manifest.name == "ch.epfl.scala:p2_2.12:1.2.0-SNAPSHOT")
6363

6464
// all dependencies are defined

sbt-plugin/src/sbt-test/dependency-manifest/default-scala-manifest/build.sbt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ lazy val p1 = project
1717
.in(file("p1"))
1818
.settings(
1919
checkManifest := {
20-
val manifest = githubDependencyManifest.value
20+
val manifest = githubDependencyManifest.value.get
2121
assert(manifest.name == "ch.epfl.scala:p1_2.12:1.2.0-SNAPSHOT")
2222

2323
// all dependencies are defined

sbt-plugin/src/sbt-test/dependency-manifest/ivy-manifest/build.sbt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ lazy val p1 = project
2323
"org.scalatest" %% "scalatest" % "3.2.2" % Test
2424
),
2525
checkManifest := {
26-
val manifest = githubDependencyManifest.value
26+
val manifest = githubDependencyManifest.value.get
2727
assert(manifest.name == "ch.epfl.scala:p1_2.12:1.2.0-SNAPSHOT")
2828

2929
// all dependencies are defined
@@ -58,7 +58,7 @@ lazy val p2 = project
5858
"com.typesafe.akka" %% "akka-http" % "10.2.8"
5959
),
6060
checkManifest := {
61-
val manifest = githubDependencyManifest.value
61+
val manifest = githubDependencyManifest.value.get
6262
assert(manifest.name == "ch.epfl.scala:p2_2.12:1.2.0-SNAPSHOT")
6363

6464
// all dependencies are defined

sbt-plugin/src/sbt-test/dependency-manifest/package-urls/build.sbt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ lazy val p1 = project
2626
.classifier("natives-macos")
2727
),
2828
checkManifest := {
29-
val manifest = githubDependencyManifest.value
29+
val manifest = githubDependencyManifest.value.get
3030

3131
checkDependency(manifest, "com.google.inject:guice:4.0")(
3232
expectedPackageUrl = "pkg:maven/com.google.inject/[email protected]?packaging=no_aop"

sbt-plugin/src/sbt-test/dependency-manifest/scala3-manifest/build.sbt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ lazy val p1 = project
2020
"io.circe" %% "circe-core" % "0.14.1"
2121
),
2222
checkManifest := {
23-
val manifest = githubDependencyManifest.value
23+
val manifest = githubDependencyManifest.value.get
2424
assert(manifest.name == "ch.epfl.scala:p1_3:1.2.0-SNAPSHOT")
2525

2626
// all dependencies are defined

sbt-plugin/src/sbt-test/submit-snapshot/ci-submit/build.sbt

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ val b = project
4242
)
4343
.dependsOn(a)
4444

45-
checkManifests := {
45+
Global / checkManifests := {
4646
val logger = streams.value.log
4747
val expectedSize: Int = (Space ~> NatBasic).parsed
4848
val manifests = state.value.get(githubManifestsKey).getOrElse {
@@ -54,4 +54,3 @@ checkManifests := {
5454
s"expected $expectedSize manifests, found ${manifests.size}"
5555
)
5656
}
57-
checkManifests / aggregate := false
Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11
> 'githubSubmitDependencyGraph {}'
2-
> checkManifests 4
2+
> Global / checkManifests 4
33

44
> 'githubSubmitDependencyGraph {"ignoredModules":["a_2.13", "b_2.13"]}'
5-
> checkManifests 2
5+
> Global / checkManifests 2
66

77
> 'githubSubmitDependencyGraph {"ignoredModules":["a_2.12", "a_2.13", "b_2.13"]}'
8-
> checkManifests 1
8+
> Global / checkManifests 1
99

1010
> 'githubSubmitDependencyGraph {"ignoredModules":[]}'
11-
> checkManifests 4
11+
> Global / checkManifests 4
12+
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
import scala.util.Properties
2+
import sbt.internal.util.complete.Parsers._
3+
4+
val checkManifests = inputKey[Unit]("Check the number of manifests")
5+
6+
inThisBuild(
7+
Seq(
8+
organization := "ch.epfl.scala",
9+
version := "1.2.0-SNAPSHOT",
10+
// use Ivy because Coursier does not allow several classifier on the same dep
11+
useCoursier := false,
12+
scalaVersion := "2.13.8"
13+
)
14+
)
15+
16+
val a = project
17+
.in(file("."))
18+
.settings(
19+
scalaVersion := "2.13.8"
20+
)
21+
22+
// Update on b fails, because b on 2.12.16 depends on a on 2.13.8
23+
val b = project
24+
.in(file("b"))
25+
.settings(
26+
scalaVersion := "2.12.16"
27+
)
28+
.dependsOn(a)
29+
30+
Global / checkManifests := {
31+
val logger = streams.value.log
32+
val expectedSize: Int = (Space ~> NatBasic).parsed
33+
val manifests = state.value.get(githubManifestsKey).getOrElse {
34+
throw new MessageOnlyException(s"Not found ${githubManifestsKey.label} attribute")
35+
}
36+
logger.info(s"found ${manifests.size} manifests")
37+
assert(
38+
manifests.size == expectedSize,
39+
s"expected $expectedSize manifests, found ${manifests.size}"
40+
)
41+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
val pluginVersion = sys.props("plugin.version")
2+
3+
addSbtPlugin("ch.epfl.scala" % "sbt-github-dependency-submission" % pluginVersion)
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
-> 'githubSubmitDependencyGraph {}'
2+
-> 'Global / githubSubmitDependencyGraph {"onResolveFailure": "error"}'
3+
4+
> 'githubSubmitDependencyGraph {"onResolveFailure": "warning"}'
5+
> Global / checkManifests 1

sbt-plugin/src/test/scala/ch/epfl/scala/JsonProtocolTests.scala

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,15 @@ class JsonProtocolTests extends FunSuite {
3030
import ch.epfl.scala.JsonProtocol._
3131
val raw = Parser.parseUnsafe("{}")
3232
val obtained = Converter.fromJson[SubmitInput](raw).get
33-
val expected = SubmitInput(Vector.empty)
33+
val expected = SubmitInput(None, Vector.empty)
34+
assertEquals(obtained, expected)
35+
}
36+
37+
test("decode input with onResolveFailure: warning") {
38+
import ch.epfl.scala.JsonProtocol._
39+
val raw = Parser.parseUnsafe("""{"onResolveFailure": "warning"}""")
40+
val obtained = Converter.fromJson[SubmitInput](raw).get
41+
val expected = SubmitInput(Some(OnFailure.warning), Vector.empty)
3442
assertEquals(obtained, expected)
3543
}
3644
}

0 commit comments

Comments
 (0)