Skip to content

Commit f491359

Browse files
committed
Add on-resolve-failure input
1 parent 2464c17 commit f491359

File tree

12 files changed

+173
-79
lines changed

12 files changed

+173
-79
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 & 52 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)
@@ -71,7 +73,7 @@ object GithubDependencyGraphPlugin extends AutoPlugin {
7173

7274
Def.task {
7375
val manifests: Map[String, Manifest] = projectRefs
74-
.map(ref => (ref / githubDependencyManifest).?)
76+
.map(ref => ref / githubDependencyManifest)
7577
.join
7678
.value
7779
.collect { case Some(manifest) => (manifest.name, manifest) }
@@ -95,7 +97,7 @@ object GithubDependencyGraphPlugin extends AutoPlugin {
9597
!ignored
9698
}
9799

98-
private def manifestTask: Def.Initialize[Task[Manifest]] = Def.task {
100+
private def manifestTask: Def.Initialize[Task[Option[Manifest]]] = Def.task {
99101
// updateFull is needed to have information about callers and reconstruct dependency tree
100102
val reportResult = Keys.updateFull.result.value
101103
val projectID = Keys.projectID.value
@@ -105,66 +107,74 @@ object GithubDependencyGraphPlugin extends AutoPlugin {
105107
val allDirectDependencies = Keys.allDependencies.value
106108
val baseDirectory = Keys.baseDirectory.value
107109
val logger = Keys.streams.value.log
110+
val onResolveFailure = Keys.state.value.attributes(githubSubmitInputKey).onResolveFailure
108111

109112
def getReference(module: ModuleID): String =
110113
crossVersion(module)
111114
.withConfigurations(None)
112115
.withExtraAttributes(Map.empty)
113116
.toString
114117

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 {
118+
reportResult match {
120119
case Inc(cause) =>
121120
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)
121+
val message = s"Failed to resolve the dependencies of $moduleName"
122+
onResolveFailure match {
123+
case Some(OnFailure.warning) =>
124+
logger.warn(message)
125+
None
126+
case _ =>
127+
logger.error(message)
128+
throw cause
129+
}
130+
case Value(report) =>
131+
val alreadySeen = mutable.Set[String]()
132+
val moduleReports = mutable.Buffer[(ModuleReport, ConfigRef)]()
133+
val allDependencies = mutable.Buffer[(String, String)]()
134+
135+
for {
136+
configReport <- report.configurations
137+
moduleReport <- configReport.modules
138+
moduleRef = getReference(moduleReport.module)
139+
if !moduleReport.evicted && !alreadySeen.contains(moduleRef)
140+
} {
141+
alreadySeen += moduleRef
142+
moduleReports += (moduleReport -> configReport.configuration)
143+
for (caller <- moduleReport.callers)
144+
allDependencies += (getReference(caller.caller) -> moduleRef)
161145
}
162146

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

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

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

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

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
1-
> 'githubSubmitDependencyGraph {}'
2-
> checkManifests 4
1+
> 'Global / githubSubmitDependencyGraph {}'
2+
> Global / checkManifests 4
33

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

7-
> 'githubSubmitDependencyGraph {"ignoredModules":["a_2.12", "a_2.13", "b_2.13"]}'
8-
> checkManifests 1
7+
> 'Global / githubSubmitDependencyGraph {"ignoredModules":["a_2.12", "a_2.13", "b_2.13"]}'
8+
> Global / checkManifests 1
9+
10+
> 'Global / githubSubmitDependencyGraph {"ignoredModules":[]}'
11+
> Global / checkManifests 4
912

10-
> 'githubSubmitDependencyGraph {"ignoredModules":[]}'
11-
> checkManifests 4
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+
-> 'Global / githubSubmitDependencyGraph {}'
2+
-> 'Global / githubSubmitDependencyGraph {"onResolveFailure": "error"}'
3+
4+
> 'Global / githubSubmitDependencyGraph {"onResolveFailure": "warning"}'
5+
> Global / checkManifests 1

0 commit comments

Comments
 (0)