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,31 +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 usage : String = s """ $Submit {"projects":[], "scalaVersions":[]} """
27
- val brief = " Submit the dependency graph to Github Dependency API."
28
- val detail = " Submit the dependency graph of a set of projects and scala versions to Github Dependency API"
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"
29
27
30
- val SubmitInternal : String = s " ${Submit }Internal "
31
- val internalOnly = " internal usage only"
28
+ private val GenerateInternal = s " ${Generate }Internal "
29
+ private val InternalOnly = " internal usage only"
30
+
31
+ val Submit = " githubSubmitSnapshot"
32
+ private val SubmitDetail = " Submit the dependency graph to Github Dependency API."
33
+
34
+ def usage (command : String ): String = s """ $command {"projects":[], "scalaVersions":[]} """
32
35
33
36
val commands : Seq [Command ] = Seq (
34
- Command (Submit , (usage, brief), detail)(inputParser)(submit),
35
- Command .command(SubmitInternal , 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)
36
40
)
37
41
38
42
private lazy val http : HttpClient = Gigahorse .http(Gigahorse .config)
39
43
40
- private def inputParser (state : State ): Parser [SubmitInput ] =
44
+ private def inputParser (state : State ): Parser [DependencySnapshotInput ] =
41
45
Parsers .any.* .map { raw =>
42
46
JsonParser
43
47
.parseFromString(raw.mkString)
44
- .flatMap(Converter .fromJson[SubmitInput ])
48
+ .flatMap(Converter .fromJson[DependencySnapshotInput ])
45
49
.get
46
50
}.failOnException
47
51
48
- private def submit (state : State , input : SubmitInput ): State = {
49
- checkGithubEnv() // fail fast if the Github CI environment is incomplete
52
+ private def generate (state : State , input : DependencySnapshotInput ): State = {
50
53
val loadedBuild = state.setting(Keys .loadedBuild)
51
54
// all project refs that have a Scala version
52
55
val projectRefs = loadedBuild.allProjectRefs
@@ -65,48 +68,58 @@ object SubmitDependencyGraph {
65
68
state.log.info(s " Resolving snapshot of $buildFile" )
66
69
67
70
val initState = state
68
- .put(githubSubmitInputKey , input)
71
+ .put(githubSnapshotInputKey , input)
69
72
.put(githubBuildFile, githubapi.FileInfo (buildFile.toString))
70
73
.put(githubManifestsKey, Map .empty[String , Manifest ])
71
74
.put(githubProjectsKey, projectRefs)
72
75
73
76
val storeAllManifests = scalaVersions.flatMap { scalaVersion =>
74
77
Seq (s " ++ $scalaVersion" , s " Global/ ${githubStoreDependencyManifests.key} $scalaVersion" )
75
78
}
76
- val commands = storeAllManifests :+ SubmitInternal
79
+ val commands = storeAllManifests :+ GenerateInternal
77
80
commands.toList ::: initState
78
81
}
79
82
80
- private def submitInternal (state : State ): State = {
83
+ private def generateInternal (state : State ): State = {
81
84
val snapshot = githubDependencySnapshot(state)
82
- val snapshotUrl = s " ${githubApiUrl()}/repos/ ${githubRepository()}/dependency-graph/snapshots "
83
-
84
85
val snapshotJson = CompactPrinter (Converter .toJsonUnsafe(snapshot))
85
-
86
86
val snapshotJsonFile = IO .withTemporaryFile(" dependency-snapshot-" , " .json" , keepFile = true ) { file =>
87
87
IO .write(file, snapshotJson)
88
88
state.log.info(s " Dependency snapshot written to ${file.getAbsolutePath}" )
89
89
file
90
90
}
91
+ setGithubOutputs(" snapshot-json-path" -> snapshotJsonFile.getAbsolutePath)
92
+ state.put(githubSnapshotFileKey, snapshotJsonFile)
93
+ }
91
94
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"
102
+ )
103
+ )
104
+ val snapshotUrl = s " ${githubApiUrl()}/repos/ ${githubRepository()}/dependency-graph/snapshots "
105
+ val job = githubJob()
92
106
val request = Gigahorse
93
107
.url(snapshotUrl)
94
- .post(snapshotJson, StandardCharsets . UTF_8 )
108
+ .post(snapshotJsonFile )
95
109
.addHeaders(
96
110
" Content-Type" -> " application/json" ,
97
111
" Authorization" -> s " token ${githubToken()}"
98
112
)
99
113
100
- state.log.info(s " Submiting dependency snapshot of job ${snapshot. job} to $snapshotUrl" )
114
+ state.log.info(s " Submitting dependency snapshot of job $job to $snapshotUrl" )
101
115
val result = for {
102
116
httpResp <- Try (Await .result(http.processFull(request), Duration .Inf ))
103
117
snapshot <- getSnapshot(httpResp)
104
118
} yield {
105
119
state.log.info(s " Submitted successfully as $snapshotUrl/ ${snapshot.id}" )
106
120
setGithubOutputs(
107
121
" submission-id" -> s " ${snapshot.id}" ,
108
- " submission-api-url" -> s " ${snapshotUrl}/ ${snapshot.id}" ,
109
- " snapshot-json-path" -> snapshotJsonFile.getAbsolutePath
122
+ " submission-api-url" -> s " ${snapshotUrl}/ ${snapshot.id}"
110
123
)
111
124
state
112
125
}
@@ -115,11 +128,9 @@ object SubmitDependencyGraph {
115
128
}
116
129
117
130
// https://docs.github.com/en/actions/using-workflows/workflow-commands-for-github-actions#setting-an-output-parameter
118
- private def setGithubOutputs (outputs : (String , String )* ): Unit = IO .writeLines(
119
- file(githubOutput),
120
- outputs.toSeq.map { case (name, value) => s " ${name}= ${value}" },
121
- append = true
122
- )
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 )
123
134
124
135
private def getSnapshot (httpResp : FullResponse ): Try [SnapshotResponse ] =
125
136
httpResp.status match {
@@ -165,32 +176,32 @@ object SubmitDependencyGraph {
165
176
}
166
177
167
178
private def checkGithubEnv (): Unit = {
168
- githubWorkspace()
169
- githubWorkflow()
170
- githubJobName()
171
- githubAction()
172
- githubRunId()
173
- githubSha()
174
- githubRef()
175
- githubApiUrl()
176
- githubRepository()
177
- githubToken()
178
- }
179
-
180
- private def githubWorkspace (): String = githubCIEnv(" GITHUB_WORKSPACE" )
181
- private def githubWorkflow (): String = githubCIEnv(" GITHUB_WORKFLOW" )
182
- private def githubJobName (): String = githubCIEnv(" GITHUB_JOB" )
183
- private def githubAction (): String = githubCIEnv(" GITHUB_ACTION" )
184
- private def githubRunId (): String = githubCIEnv(" GITHUB_RUN_ID" )
185
- private def githubSha (): String = githubCIEnv(" GITHUB_SHA" )
186
- private def githubRef (): String = githubCIEnv(" GITHUB_REF" )
187
- private def githubApiUrl (): String = githubCIEnv(" GITHUB_API_URL" )
188
- private def githubRepository (): String = githubCIEnv(" GITHUB_REPOSITORY" )
189
- private def githubToken (): String = githubCIEnv(" GITHUB_TOKEN" )
190
- private def githubOutput (): String = githubCIEnv(" GITHUB_OUTPUT" )
191
-
192
- private def githubCIEnv (name : String ): String =
193
- Properties .envOrNone(name).getOrElse {
179
+ def check (name : String ): Unit = Properties .envOrNone(name).orElse {
194
180
throw new MessageOnlyException (s " Missing environment variable $name. This task must run in a Github Action. " )
195
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)
196
207
}
0 commit comments