11package  ch .epfl .scala 
22
3- import  java .nio .charset .StandardCharsets 
43import  java .nio .file .Paths 
54import  java .time .Instant 
65
@@ -22,44 +21,35 @@ import sjsonnew.shaded.scalajson.ast.unsafe.JValue
2221import  sjsonnew .support .scalajson .unsafe .{Parser  =>  JsonParser , _ }
2322
2423object  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" =  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 (" " =>  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" 
213207}
0 commit comments