1
1
package ch .epfl .scala
2
2
3
- import java .nio .charset .StandardCharsets
4
3
import java .nio .file .Paths
5
4
import java .time .Instant
6
5
@@ -22,44 +21,35 @@ import sjsonnew.shaded.scalajson.ast.unsafe.JValue
22
21
import sjsonnew .support .scalajson .unsafe .{Parser => JsonParser , _ }
23
22
24
23
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"
34
30
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."
42
33
43
- val internalCommand = s " ${command}Internal "
44
- val internalOnly = " internal usage only"
34
+ def usage (command : String ): String = s """ $command {"projects":[], "scalaVersions":[]} """
45
35
46
36
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)
49
40
)
50
41
51
42
private lazy val http : HttpClient = Gigahorse .http(Gigahorse .config)
52
43
53
- private def inputParser (state : State ): Parser [SubmitInput ] =
44
+ private def inputParser (state : State ): Parser [DependencySnapshotInput ] =
54
45
Parsers .any.* .map { raw =>
55
46
JsonParser
56
47
.parseFromString(raw.mkString)
57
- .flatMap(Converter .fromJson[SubmitInput ])
48
+ .flatMap(Converter .fromJson[DependencySnapshotInput ])
58
49
.get
59
50
}.failOnException
60
51
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 = {
63
53
val loadedBuild = state.setting(Keys .loadedBuild)
64
54
// all project refs that have a Scala version
65
55
val projectRefs = loadedBuild.allProjectRefs
@@ -78,65 +68,69 @@ class SubmitDependencyGraph(
78
68
state.log.info(s " Resolving snapshot of $buildFile" )
79
69
80
70
val initState = state
81
- .put(githubSubmitInputKey , input)
71
+ .put(githubDependencySnapshotInputKey , input)
82
72
.put(githubBuildFile, githubapi.FileInfo (buildFile.toString))
83
73
.put(githubManifestsKey, Map .empty[String , Manifest ])
84
74
.put(githubProjectsKey, projectRefs)
85
75
86
76
val storeAllManifests = scalaVersions.flatMap { scalaVersion =>
87
77
Seq (s " ++ $scalaVersion" , s " Global/ ${githubStoreDependencyManifests.key} $scalaVersion" )
88
78
}
89
- val commands = storeAllManifests :+ internalCommand
79
+ val commands = storeAllManifests :+ GenerateInternal
90
80
commands.toList ::: initState
91
81
}
92
82
93
- private def submitInternal (state : State ): State = {
83
+ private def generateInternal (state : State ): State = {
94
84
val snapshot = githubDependencySnapshot(state)
95
- val snapshotUrl = s " ${githubApiUrl()}/repos/ ${githubRepository()}/dependency-graph/snapshots "
96
-
97
85
val snapshotJson = CompactPrinter (Converter .toJsonUnsafe(snapshot))
98
-
99
86
val snapshotJsonFile = IO .withTemporaryFile(" dependency-snapshot-" , " .json" , keepFile = true ) { file =>
100
87
IO .write(file, snapshotJson)
101
88
state.log.info(s " Dependency snapshot written to ${file.getAbsolutePath}" )
102
89
file
103
90
}
91
+ setGithubOutputs(" snapshot-json-path" -> snapshotJsonFile.getAbsolutePath)
92
+ state.put(githubSnapshotFileKey, snapshotJsonFile)
93
+ }
104
94
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"
126
102
)
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
131
125
}
126
+
127
+ result.get
132
128
}
133
129
134
130
// 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 )
140
134
141
135
private def getSnapshot (httpResp : FullResponse ): Try [SnapshotResponse ] =
142
136
httpResp.status match {
@@ -182,32 +176,32 @@ class SubmitDependencyGraph(
182
176
}
183
177
184
178
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 {
211
180
throw new MessageOnlyException (s " Missing environment variable $name. This task must run in a Github Action. " )
212
181
}
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)
213
207
}
0 commit comments