Skip to content

Commit 37ec421

Browse files
committed
Split in two commands and adapt tests
1 parent 973a085 commit 37ec421

File tree

19 files changed

+159
-115
lines changed

19 files changed

+159
-115
lines changed

.github/workflows/ci.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ jobs:
1515
working-directory: sbt-plugin
1616
steps:
1717
- uses: actions/checkout@v3
18-
- uses: coursier/setup-action@v1.2.0-M3
18+
- uses: coursier/setup-action@v1.3.5
1919
with:
2020
apps: scalafmt sbt
2121
- run: scalafmt --test
@@ -43,7 +43,7 @@ jobs:
4343
jvm: ${{ matrix.jvm }}
4444
apps: sbt
4545
- run: sbt test
46-
- run: sbt "scripted dependency-manifest/*"
46+
- run: sbt "scripted dependency-manifest/* generate-snapshot/*"
4747
- run: sbt "scripted submit-snapshot/*"
4848
if: github.event_name == 'push' || github.event.pull_request.head.repo.owner.login == 'scalacenter'
4949

dist/index.js

Lines changed: 1 addition & 1 deletion
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: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,8 @@ enum OnFailure {
88
warning
99
}
1010

11-
## Input of the githubSubmitDependencyGraph command
12-
type SubmitInput {
11+
## Input of the githubGenerateSnapshot command
12+
type DependencySnapshotInput {
1313
onResolveFailure: ch.epfl.scala.OnFailure
1414

1515
## A set of modules to ignore.

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

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,10 +27,12 @@ object GithubDependencyGraphPlugin extends AutoPlugin {
2727
.map(_.toConfigRef)
2828

2929
object autoImport {
30-
val githubSubmitInputKey: AttributeKey[SubmitInput] = AttributeKey("githubSubmitInput")
30+
val githubSubmitInputKey: AttributeKey[DependencySnapshotInput] = AttributeKey("githubSubmitInput")
3131
val githubBuildFile: AttributeKey[githubapi.FileInfo] = AttributeKey("githubBuildFile")
3232
val githubManifestsKey: AttributeKey[Map[String, githubapi.Manifest]] = AttributeKey("githubDependencyManifests")
3333
val githubProjectsKey: AttributeKey[Seq[ProjectRef]] = AttributeKey("githubProjectRefs")
34+
val githubSnapshotFileKey: AttributeKey[File] = AttributeKey("githubSnapshotFile")
35+
3436
val githubDependencyManifest: TaskKey[Option[githubapi.Manifest]] = taskKey(
3537
"The dependency manifest of the project"
3638
)
Lines changed: 80 additions & 86 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
package ch.epfl.scala
22

3-
import java.nio.charset.StandardCharsets
43
import java.nio.file.Paths
54
import java.time.Instant
65

@@ -22,44 +21,35 @@ import sjsonnew.shaded.scalajson.ast.unsafe.JValue
2221
import sjsonnew.support.scalajson.unsafe.{Parser => JsonParser, _}
2322

2423
object SubmitDependencyGraph {
25-
val Submit = "githubSubmitDependencyGraph"
26-
val brief = "Submit the dependency graph to Github Dependency API."
27-
val detail = "Submit the dependency graph of a set of projects and scala versions to Github Dependency API"
28-
val Generate = "generateDependencyGraph"
29-
val briefGenerate = "Generate the dependency graph"
30-
val detailGenerate = "Generate the dependency graph of a set of projects and scala versions"
31-
val commands = new SubmitDependencyGraph(true, Generate, briefGenerate, detailGenerate).commands ++
32-
new SubmitDependencyGraph(false, Submit, brief, detail).commands
33-
}
24+
val Generate = "githubGenerateSnapshot"
25+
private val GenerateUsage = s"""$Generate {"projects":[], "scalaVersions":[]}"""
26+
private val GenerateDetail = "Generate the dependency graph of a set of projects and scala versions"
27+
28+
private val GenerateInternal = s"${Generate}Internal"
29+
private val InternalOnly = "internal usage only"
3430

35-
class SubmitDependencyGraph(
36-
val local: Boolean,
37-
val command: String,
38-
val brief: String,
39-
val detail: String
40-
) {
41-
val usage: String = s"""$command {"projects":[], "scalaVersions":[]}"""
31+
val Submit = "githubSubmitSnapshot"
32+
private val SubmitDetail = "Submit the dependency graph to Github Dependency API."
4233

43-
val internalCommand = s"${command}Internal"
44-
val internalOnly = "internal usage only"
34+
def usage(command: String): String = s"""$command {"projects":[], "scalaVersions":[]}"""
4535

4636
val commands: Seq[Command] = Seq(
47-
Command(command, (usage, brief), detail)(inputParser)(submit),
48-
Command.command(internalCommand, internalOnly, internalOnly)(submitInternal)
37+
Command(Generate, (GenerateUsage, GenerateDetail), GenerateDetail)(inputParser)(generate),
38+
Command.command(GenerateInternal, InternalOnly, InternalOnly)(generateInternal),
39+
Command.command(Submit, SubmitDetail, SubmitDetail)(submit)
4940
)
5041

5142
private lazy val http: HttpClient = Gigahorse.http(Gigahorse.config)
5243

53-
private def inputParser(state: State): Parser[SubmitInput] =
44+
private def inputParser(state: State): Parser[DependencySnapshotInput] =
5445
Parsers.any.*.map { raw =>
5546
JsonParser
5647
.parseFromString(raw.mkString)
57-
.flatMap(Converter.fromJson[SubmitInput])
48+
.flatMap(Converter.fromJson[DependencySnapshotInput])
5849
.get
5950
}.failOnException
6051

61-
private def submit(state: State, input: SubmitInput): State = {
62-
checkGithubEnv() // fail fast if the Github CI environment is incomplete
52+
private def generate(state: State, input: DependencySnapshotInput): State = {
6353
val loadedBuild = state.setting(Keys.loadedBuild)
6454
// all project refs that have a Scala version
6555
val projectRefs = loadedBuild.allProjectRefs
@@ -86,57 +76,61 @@ class SubmitDependencyGraph(
8676
val storeAllManifests = scalaVersions.flatMap { scalaVersion =>
8777
Seq(s"++$scalaVersion", s"Global/${githubStoreDependencyManifests.key} $scalaVersion")
8878
}
89-
val commands = storeAllManifests :+ internalCommand
79+
val commands = storeAllManifests :+ GenerateInternal
9080
commands.toList ::: initState
9181
}
9282

93-
private def submitInternal(state: State): State = {
83+
private def generateInternal(state: State): State = {
9484
val snapshot = githubDependencySnapshot(state)
95-
val snapshotUrl = s"${githubApiUrl()}/repos/${githubRepository()}/dependency-graph/snapshots"
96-
9785
val snapshotJson = CompactPrinter(Converter.toJsonUnsafe(snapshot))
98-
9986
val snapshotJsonFile = IO.withTemporaryFile("dependency-snapshot-", ".json", keepFile = true) { file =>
10087
IO.write(file, snapshotJson)
10188
state.log.info(s"Dependency snapshot written to ${file.getAbsolutePath}")
10289
file
10390
}
91+
setGithubOutputs("snapshot-json-path" -> snapshotJsonFile.getAbsolutePath)
92+
state.put(githubSnapshotFileKey, snapshotJsonFile)
93+
}
10494

105-
if (local) state
106-
else {
107-
108-
val request = Gigahorse
109-
.url(snapshotUrl)
110-
.post(snapshotJson, StandardCharsets.UTF_8)
111-
.addHeaders(
112-
"Content-Type" -> "application/json",
113-
"Authorization" -> s"token ${githubToken()}"
114-
)
115-
116-
state.log.info(s"Submiting dependency snapshot of job ${snapshot.job} to $snapshotUrl")
117-
val result = for {
118-
httpResp <- Try(Await.result(http.processFull(request), Duration.Inf))
119-
snapshot <- getSnapshot(httpResp)
120-
} yield {
121-
state.log.info(s"Submitted successfully as $snapshotUrl/${snapshot.id}")
122-
setGithubOutputs(
123-
"submission-id" -> s"${snapshot.id}",
124-
"submission-api-url" -> s"${snapshotUrl}/${snapshot.id}",
125-
"snapshot-json-path" -> snapshotJsonFile.getAbsolutePath
95+
def submit(state: State): State = {
96+
checkGithubEnv() // fail if the Github CI environment
97+
val snapshotJsonFile = state
98+
.get(githubSnapshotFileKey)
99+
.getOrElse(
100+
throw new MessageOnlyException(
101+
"Missing snapshot file. This command must execute after the githubGenerateSnapshot command"
126102
)
127-
state
128-
}
129-
130-
result.get
103+
)
104+
val snapshotUrl = s"${githubApiUrl()}/repos/${githubRepository()}/dependency-graph/snapshots"
105+
val job = githubJob()
106+
val request = Gigahorse
107+
.url(snapshotUrl)
108+
.post(snapshotJsonFile)
109+
.addHeaders(
110+
"Content-Type" -> "application/json",
111+
"Authorization" -> s"token ${githubToken()}"
112+
)
113+
114+
state.log.info(s"Submitting dependency snapshot of job $job to $snapshotUrl")
115+
val result = for {
116+
httpResp <- Try(Await.result(http.processFull(request), Duration.Inf))
117+
snapshot <- getSnapshot(httpResp)
118+
} yield {
119+
state.log.info(s"Submitted successfully as $snapshotUrl/${snapshot.id}")
120+
setGithubOutputs(
121+
"submission-id" -> s"${snapshot.id}",
122+
"submission-api-url" -> s"${snapshotUrl}/${snapshot.id}"
123+
)
124+
state
131125
}
126+
127+
result.get
132128
}
133129

134130
// https://docs.github.com/en/actions/using-workflows/workflow-commands-for-github-actions#setting-an-output-parameter
135-
private def setGithubOutputs(outputs: (String, String)*): Unit = IO.writeLines(
136-
file(githubOutput),
137-
outputs.toSeq.map { case (name, value) => s"${name}=${value}" },
138-
append = true
139-
)
131+
private def setGithubOutputs(outputs: (String, String)*): Unit =
132+
for (output <- githubOutput())
133+
IO.writeLines(output, outputs.map { case (name, value) => s"${name}=${value}" }, append = true)
140134

141135
private def getSnapshot(httpResp: FullResponse): Try[SnapshotResponse] =
142136
httpResp.status match {
@@ -182,32 +176,32 @@ class SubmitDependencyGraph(
182176
}
183177

184178
private def checkGithubEnv(): Unit = {
185-
githubWorkspace()
186-
githubWorkflow()
187-
githubJobName()
188-
githubAction()
189-
githubRunId()
190-
githubSha()
191-
githubRef()
192-
githubApiUrl()
193-
githubRepository()
194-
githubToken()
195-
}
196-
197-
private def githubWorkspace(): String = githubCIEnv("GITHUB_WORKSPACE")
198-
private def githubWorkflow(): String = githubCIEnv("GITHUB_WORKFLOW")
199-
private def githubJobName(): String = githubCIEnv("GITHUB_JOB")
200-
private def githubAction(): String = githubCIEnv("GITHUB_ACTION")
201-
private def githubRunId(): String = githubCIEnv("GITHUB_RUN_ID")
202-
private def githubSha(): String = githubCIEnv("GITHUB_SHA")
203-
private def githubRef(): String = githubCIEnv("GITHUB_REF")
204-
private def githubApiUrl(): String = githubCIEnv("GITHUB_API_URL")
205-
private def githubRepository(): String = githubCIEnv("GITHUB_REPOSITORY")
206-
private def githubToken(): String = githubCIEnv("GITHUB_TOKEN")
207-
private def githubOutput(): String = githubCIEnv("GITHUB_OUTPUT")
208-
209-
private def githubCIEnv(name: String): String =
210-
Properties.envOrNone(name).orElse(Some("").find(_ => local)).getOrElse {
179+
def check(name: String): Unit = Properties.envOrNone(name).orElse {
211180
throw new MessageOnlyException(s"Missing environment variable $name. This task must run in a Github Action.")
212181
}
182+
check("GITHUB_WORKSPACE")
183+
check("GITHUB_WORKFLOW")
184+
check("GITHUB_JOB")
185+
check("GITHUB_ACTION")
186+
check("GITHUB_RUN_ID")
187+
check("GITHUB_SHA")
188+
check("GITHUB_REF")
189+
check("GITHUB_API_URL")
190+
check("GITHUB_REPOSITORY")
191+
check("GITHUB_TOKEN")
192+
check("GITHUB_OUTPUT")
193+
}
194+
195+
private def githubWorkspace(): String = Properties.envOrElse("GITHUB_WORKSPACE", "")
196+
private def githubWorkflow(): String = Properties.envOrElse("GITHUB_WORKFLOW", "")
197+
private def githubJobName(): String = Properties.envOrElse("GITHUB_JOB", "")
198+
private def githubAction(): String = Properties.envOrElse("GITHUB_ACTION", "")
199+
private def githubRunId(): String = Properties.envOrElse("GITHUB_RUN_ID", "")
200+
private def githubSha(): String = Properties.envOrElse("GITHUB_SHA", "")
201+
private def githubRef(): String = Properties.envOrElse("GITHUB_REF", "")
202+
203+
private def githubApiUrl(): String = Properties.envOrElse("GITHUB_API_URL", "")
204+
private def githubRepository(): String = Properties.envOrElse("GITHUB_REPOSITORY", "")
205+
private def githubToken(): String = Properties.envOrElse("GITHUB_TOKEN", "")
206+
private def githubOutput(): Option[File] = Properties.envOrNone("GITHUB_OUTPUT").map(file)
213207
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
> 'githubGenerateSnapshot {}'
2+
> Global / checkManifests 4
3+
4+
> 'githubGenerateSnapshot {"ignoredModules":["a_2.13", "b_2.13"]}'
5+
> Global / checkManifests 2
6+
7+
> 'githubGenerateSnapshot {"ignoredModules":["a_2.12", "a_2.13", "b_2.13"]}'
8+
> Global / checkManifests 1
9+
10+
> 'githubGenerateSnapshot {"ignoredModules":[]}'
11+
> Global / checkManifests 4
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
-> 'githubGenerateSnapshot {}'
2+
-> 'Global / githubGenerateSnapshot {"onResolveFailure": "error"}'
3+
4+
> 'githubGenerateSnapshot {"onResolveFailure": "warning"}'
5+
> Global / checkManifests 1

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

Lines changed: 0 additions & 12 deletions
This file was deleted.

sbt-plugin/src/sbt-test/submit-snapshot/resolve-failure/test

Lines changed: 0 additions & 5 deletions
This file was deleted.
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
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+
crossScalaVersions := Seq(
21+
"2.12.16",
22+
"2.13.8",
23+
"3.1.3"
24+
),
25+
libraryDependencies ++= Seq(
26+
// a dependency with many classifiers
27+
("org.lwjgl" % "lwjgl" % "3.3.1")
28+
.classifier("natives-windows")
29+
.classifier("natives-linux")
30+
.classifier("natives-macos")
31+
)
32+
)
33+
34+
// b is not cross-compiled
35+
// but we should still be able to resolve the manifests of the build on 2.12.16 and 3.1.3
36+
// this pattern is taken from scalameta/metals where metals, not cross-compiled, depends on mtags
37+
// which is cross-compiled
38+
val b = project
39+
.in(file("b"))
40+
.settings(
41+
scalaVersion := "2.13.8"
42+
)
43+
.dependsOn(a)
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: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
-> 'githubSubmitSnapshot'
2+
> 'githubGenerateSnapshot {}'
3+
> 'githubSubmitSnapshot'

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

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,16 +29,16 @@ class JsonProtocolTests extends FunSuite {
2929
test("decode empty input") {
3030
import ch.epfl.scala.JsonProtocol._
3131
val raw = Parser.parseUnsafe("{}")
32-
val obtained = Converter.fromJson[SubmitInput](raw).get
33-
val expected = SubmitInput(None, Vector.empty, Vector.empty)
32+
val obtained = Converter.fromJson[DependencySnapshotInput](raw).get
33+
val expected = DependencySnapshotInput(None, Vector.empty, Vector.empty)
3434
assertEquals(obtained, expected)
3535
}
3636

3737
test("decode input with onResolveFailure: warning") {
3838
import ch.epfl.scala.JsonProtocol._
3939
val raw = Parser.parseUnsafe("""{"onResolveFailure": "warning"}""")
40-
val obtained = Converter.fromJson[SubmitInput](raw).get
41-
val expected = SubmitInput(Some(OnFailure.warning), Vector.empty, Vector.empty)
40+
val obtained = Converter.fromJson[DependencySnapshotInput](raw).get
41+
val expected = DependencySnapshotInput(Some(OnFailure.warning), Vector.empty, Vector.empty)
4242
assertEquals(obtained, expected)
4343
}
4444
}

src/main.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ async function run(): Promise<void> {
5858
}
5959

6060
process.env['GITHUB_TOKEN'] = token
61-
await cli.exec('sbt', ['--batch', `githubSubmitDependencyGraph ${JSON.stringify(input)}`], {
61+
await cli.exec('sbt', ['--batch', `githubGenerateSnapshot ${JSON.stringify(input)}; githubSubmitSnapshot`], {
6262
cwd: workingDir,
6363
})
6464
} catch (error) {

0 commit comments

Comments
 (0)