@@ -24,8 +24,15 @@ package actions
2424// PUT: http://localhost:3000/twirp/github.actions.results.api.v1.ArtifactService/UploadArtifact?sig=mO7y35r4GyjN7fwg0DTv3-Fv1NDXD84KLEgLpoPOtDI=&expires=2024-01-23+21%3A48%3A37.20833956+%2B0100+CET&artifactName=test&taskID=75&comp=block 
2525// 1.3. Continue Upload Zip Content to Blobstorage (unauthenticated request), repeat until everything is uploaded 
2626// PUT: http://localhost:3000/twirp/github.actions.results.api.v1.ArtifactService/UploadArtifact?sig=mO7y35r4GyjN7fwg0DTv3-Fv1NDXD84KLEgLpoPOtDI=&expires=2024-01-23+21%3A48%3A37.20833956+%2B0100+CET&artifactName=test&taskID=75&comp=appendBlock 
27- // 1.4. Unknown xml payload to Blobstorage (unauthenticated request), ignored for now 
27+ // 1.4. BlockList xml payload to Blobstorage (unauthenticated request) 
28+ // Files of about 800MB are parallel in parallel and / or out of order, this file is needed to enshure the correct order 
2829// PUT: http://localhost:3000/twirp/github.actions.results.api.v1.ArtifactService/UploadArtifact?sig=mO7y35r4GyjN7fwg0DTv3-Fv1NDXD84KLEgLpoPOtDI=&expires=2024-01-23+21%3A48%3A37.20833956+%2B0100+CET&artifactName=test&taskID=75&comp=blockList 
30+ // Request 
31+ // <?xml version="1.0" encoding="UTF-8" standalone="yes"?> 
32+ // <BlockList> 
33+ // 	<Latest>blockId1</Latest> 
34+ // 	<Latest>blockId2</Latest> 
35+ // </BlockList> 
2936// 1.5. FinalizeArtifact 
3037// Post: /twirp/github.actions.results.api.v1.ArtifactService/FinalizeArtifact 
3138// Request 
@@ -82,6 +89,7 @@ import (
8289	"crypto/hmac" 
8390	"crypto/sha256" 
8491	"encoding/base64" 
92+ 	"encoding/xml" 
8593	"fmt" 
8694	"io" 
8795	"net/http" 
@@ -152,31 +160,34 @@ func ArtifactsV4Routes(prefix string) *web.Route {
152160	return  m 
153161}
154162
155- func  (r  artifactV4Routes ) buildSignature (endp , expires , artifactName  string , taskID  int64 ) []byte  {
163+ func  (r  artifactV4Routes ) buildSignature (endp , expires , artifactName  string , taskID ,  artifactID  int64 ) []byte  {
156164	mac  :=  hmac .New (sha256 .New , setting .GetGeneralTokenSigningSecret ())
157165	mac .Write ([]byte (endp ))
158166	mac .Write ([]byte (expires ))
159167	mac .Write ([]byte (artifactName ))
160168	mac .Write ([]byte (fmt .Sprint (taskID )))
169+ 	mac .Write ([]byte (fmt .Sprint (artifactID )))
161170	return  mac .Sum (nil )
162171}
163172
164- func  (r  artifactV4Routes ) buildArtifactURL (ctx  * ArtifactContext , endp , artifactName  string , taskID  int64 ) string  {
173+ func  (r  artifactV4Routes ) buildArtifactURL (ctx  * ArtifactContext , endp , artifactName  string , taskID ,  artifactID  int64 ) string  {
165174	expires  :=  time .Now ().Add (60  *  time .Minute ).Format ("2006-01-02 15:04:05.999999999 -0700 MST" )
166175	uploadURL  :=  strings .TrimSuffix (httplib .GuessCurrentAppURL (ctx ), "/" ) +  strings .TrimSuffix (r .prefix , "/" ) + 
167- 		"/"  +  endp  +  "?sig="  +  base64 .URLEncoding .EncodeToString (r .buildSignature (endp , expires , artifactName , taskID )) +  "&expires="  +  url .QueryEscape (expires ) +  "&artifactName="  +  url .QueryEscape (artifactName ) +  "&taskID="  +  fmt .Sprint (taskID )
176+ 		"/"  +  endp  +  "?sig="  +  base64 .URLEncoding .EncodeToString (r .buildSignature (endp , expires , artifactName , taskID ,  artifactID )) +  "&expires="  +  url .QueryEscape (expires ) +  "&artifactName="  +  url .QueryEscape (artifactName ) +  "&taskID="  +  fmt .Sprint (taskID )  +   "&artifactID="   +   fmt . Sprint ( artifactID )
168177	return  uploadURL 
169178}
170179
171180func  (r  artifactV4Routes ) verifySignature (ctx  * ArtifactContext , endp  string ) (* actions.ActionTask , string , bool ) {
172181	rawTaskID  :=  ctx .Req .URL .Query ().Get ("taskID" )
182+ 	rawArtifactID  :=  ctx .Req .URL .Query ().Get ("artifactID" )
173183	sig  :=  ctx .Req .URL .Query ().Get ("sig" )
174184	expires  :=  ctx .Req .URL .Query ().Get ("expires" )
175185	artifactName  :=  ctx .Req .URL .Query ().Get ("artifactName" )
176186	dsig , _  :=  base64 .URLEncoding .DecodeString (sig )
177187	taskID , _  :=  strconv .ParseInt (rawTaskID , 10 , 64 )
188+ 	artifactID , _  :=  strconv .ParseInt (rawArtifactID , 10 , 64 )
178189
179- 	expecedsig  :=  r .buildSignature (endp , expires , artifactName , taskID )
190+ 	expecedsig  :=  r .buildSignature (endp , expires , artifactName , taskID ,  artifactID )
180191	if  ! hmac .Equal (dsig , expecedsig ) {
181192		log .Error ("Error unauthorized" )
182193		ctx .Error (http .StatusUnauthorized , "Error unauthorized" )
@@ -271,6 +282,8 @@ func (r *artifactV4Routes) createArtifact(ctx *ArtifactContext) {
271282		return 
272283	}
273284	artifact .ContentEncoding  =  ArtifactV4ContentEncoding 
285+ 	artifact .FileSize  =  0 
286+ 	artifact .FileCompressedSize  =  0 
274287	if  err  :=  actions .UpdateArtifactByID (ctx , artifact .ID , artifact ); err  !=  nil  {
275288		log .Error ("Error UpdateArtifactByID: %v" , err )
276289		ctx .Error (http .StatusInternalServerError , "Error UpdateArtifactByID" )
@@ -279,7 +292,7 @@ func (r *artifactV4Routes) createArtifact(ctx *ArtifactContext) {
279292
280293	respData  :=  CreateArtifactResponse {
281294		Ok :              true ,
282- 		SignedUploadUrl : r .buildArtifactURL (ctx , "UploadArtifact" , artifactName , ctx .ActionTask .ID ),
295+ 		SignedUploadUrl : r .buildArtifactURL (ctx , "UploadArtifact" , artifactName , ctx .ActionTask .ID ,  artifact . ID ),
283296	}
284297	r .sendProtbufBody (ctx , & respData )
285298}
@@ -293,38 +306,77 @@ func (r *artifactV4Routes) uploadArtifact(ctx *ArtifactContext) {
293306	comp  :=  ctx .Req .URL .Query ().Get ("comp" )
294307	switch  comp  {
295308	case  "block" , "appendBlock" :
296- 		// get artifact by name 
297- 		artifact , err  :=  r .getArtifactByName (ctx , task .Job .RunID , artifactName )
298- 		if  err  !=  nil  {
299- 			log .Error ("Error artifact not found: %v" , err )
300- 			ctx .Error (http .StatusNotFound , "Error artifact not found" )
301- 			return 
309+ 		blockid  :=  ctx .Req .URL .Query ().Get ("blockid" )
310+ 		if  blockid  ==  ""  {
311+ 			// get artifact by name 
312+ 			artifact , err  :=  r .getArtifactByName (ctx , task .Job .RunID , artifactName )
313+ 			if  err  !=  nil  {
314+ 				log .Error ("Error artifact not found: %v" , err )
315+ 				ctx .Error (http .StatusNotFound , "Error artifact not found" )
316+ 				return 
317+ 			}
318+ 
319+ 			_ , err  =  appendUploadChunk (r .fs , ctx , artifact , artifact .FileSize , ctx .Req .ContentLength , artifact .RunID )
320+ 			if  err  !=  nil  {
321+ 				log .Error ("Error runner api getting task: task is not running" )
322+ 				ctx .Error (http .StatusInternalServerError , "Error runner api getting task: task is not running" )
323+ 				return 
324+ 			}
325+ 			artifact .FileCompressedSize  +=  ctx .Req .ContentLength 
326+ 			artifact .FileSize  +=  ctx .Req .ContentLength 
327+ 			if  err  :=  actions .UpdateArtifactByID (ctx , artifact .ID , artifact ); err  !=  nil  {
328+ 				log .Error ("Error UpdateArtifactByID: %v" , err )
329+ 				ctx .Error (http .StatusInternalServerError , "Error UpdateArtifactByID" )
330+ 				return 
331+ 			}
332+ 		} else  {
333+ 			_ , err  :=  r .fs .Save (fmt .Sprintf ("tmpv4%d/block-%d-%d-%s" , task .Job .RunID , task .Job .RunID , ctx .Req .ContentLength , base64 .URLEncoding .EncodeToString ([]byte (blockid ))), ctx .Req .Body , - 1 )
334+ 			if  err  !=  nil  {
335+ 				log .Error ("Error runner api getting task: task is not running" )
336+ 				ctx .Error (http .StatusInternalServerError , "Error runner api getting task: task is not running" )
337+ 				return 
338+ 			}
302339		}
303- 
304- 		if  comp  ==  "block"  {
305- 			artifact .FileSize  =  0 
306- 			artifact .FileCompressedSize  =  0 
307- 		}
308- 
309- 		_ , err  =  appendUploadChunk (r .fs , ctx , artifact , artifact .FileSize , ctx .Req .ContentLength , artifact .RunID )
340+ 		ctx .JSON (http .StatusCreated , "appended" )
341+ 	case  "blocklist" :
342+ 		rawArtifactID  :=  ctx .Req .URL .Query ().Get ("artifactID" )
343+ 		artifactID , _  :=  strconv .ParseInt (rawArtifactID , 10 , 64 )
344+ 		_ , err  :=  r .fs .Save (fmt .Sprintf ("tmpv4%d/%d-%d-blocklist" , task .Job .RunID , task .Job .RunID , artifactID ), ctx .Req .Body , - 1 )
310345		if  err  !=  nil  {
311346			log .Error ("Error runner api getting task: task is not running" )
312347			ctx .Error (http .StatusInternalServerError , "Error runner api getting task: task is not running" )
313348			return 
314349		}
315- 		artifact .FileCompressedSize  +=  ctx .Req .ContentLength 
316- 		artifact .FileSize  +=  ctx .Req .ContentLength 
317- 		if  err  :=  actions .UpdateArtifactByID (ctx , artifact .ID , artifact ); err  !=  nil  {
318- 			log .Error ("Error UpdateArtifactByID: %v" , err )
319- 			ctx .Error (http .StatusInternalServerError , "Error UpdateArtifactByID" )
320- 			return 
321- 		}
322- 		ctx .JSON (http .StatusCreated , "appended" )
323- 	case  "blocklist" :
324350		ctx .JSON (http .StatusCreated , "created" )
325351	}
326352}
327353
354+ type  BlockList  struct  {
355+ 	Latest  []string  `xml:"Latest"` 
356+ }
357+ 
358+ type  Latest  struct  {
359+ 	Value  string  `xml:",chardata"` 
360+ }
361+ 
362+ func  (r  * artifactV4Routes ) readBlockList (runID , artifactID  int64 ) (* BlockList , error ) {
363+ 	blockListName  :=  fmt .Sprintf ("tmpv4%d/%d-%d-blocklist" , runID , runID , artifactID )
364+ 	s , err  :=  r .fs .Open (blockListName )
365+ 	if  err  !=  nil  {
366+ 		return  nil , err 
367+ 	}
368+ 
369+ 	xdec  :=  xml .NewDecoder (s )
370+ 	blockList  :=  & BlockList {}
371+ 	err  =  xdec .Decode (blockList )
372+ 
373+ 	delerr  :=  r .fs .Delete (blockListName )
374+ 	if  delerr  !=  nil  {
375+ 		log .Warn ("Failed to delete blockList %s: %v" , blockListName , delerr )
376+ 	}
377+ 	return  blockList , err 
378+ }
379+ 
328380func  (r  * artifactV4Routes ) finalizeArtifact (ctx  * ArtifactContext ) {
329381	var  req  FinalizeArtifactRequest 
330382
@@ -343,18 +395,34 @@ func (r *artifactV4Routes) finalizeArtifact(ctx *ArtifactContext) {
343395		ctx .Error (http .StatusNotFound , "Error artifact not found" )
344396		return 
345397	}
346- 	chunkMap , err  :=  listChunksByRunID (r .fs , runID )
398+ 
399+ 	var  chunks  []* chunkFileItem 
400+ 	blockList , err  :=  r .readBlockList (runID , artifact .ID )
347401	if  err  !=  nil  {
348- 		log .Error ("Error merge chunks: %v" , err )
349- 		ctx .Error (http .StatusInternalServerError , "Error merge chunks" )
350- 		return 
351- 	}
352- 	chunks , ok  :=  chunkMap [artifact .ID ]
353- 	if  ! ok  {
354- 		log .Error ("Error merge chunks" )
355- 		ctx .Error (http .StatusInternalServerError , "Error merge chunks" )
356- 		return 
402+ 		log .Warn ("Failed to read BlockList, fallback to old behavior: %v" , err )
403+ 		chunkMap , err  :=  listChunksByRunID (r .fs , runID )
404+ 		if  err  !=  nil  {
405+ 			log .Error ("Error merge chunks: %v" , err )
406+ 			ctx .Error (http .StatusInternalServerError , "Error merge chunks" )
407+ 			return 
408+ 		}
409+ 		chunks , ok  =  chunkMap [artifact .ID ]
410+ 		if  ! ok  {
411+ 			log .Error ("Error merge chunks" )
412+ 			ctx .Error (http .StatusInternalServerError , "Error merge chunks" )
413+ 			return 
414+ 		}
415+ 	} else  {
416+ 		chunks , err  =  listChunksByRunIDV4 (r .fs , runID , artifact .ID , blockList )
417+ 		if  err  !=  nil  {
418+ 			log .Error ("Error merge chunks: %v" , err )
419+ 			ctx .Error (http .StatusInternalServerError , "Error merge chunks" )
420+ 			return 
421+ 		}
422+ 		artifact .FileSize  =  chunks [len (chunks )- 1 ].End  +  1 
423+ 		artifact .FileCompressedSize  =  chunks [len (chunks )- 1 ].End  +  1 
357424	}
425+ 
358426	checksum  :=  "" 
359427	if  req .Hash  !=  nil  {
360428		checksum  =  req .Hash .Value 
@@ -455,7 +523,7 @@ func (r *artifactV4Routes) getSignedArtifactURL(ctx *ArtifactContext) {
455523		}
456524	}
457525	if  respData .SignedUrl  ==  ""  {
458- 		respData .SignedUrl  =  r .buildArtifactURL (ctx , "DownloadArtifact" , artifactName , ctx .ActionTask .ID )
526+ 		respData .SignedUrl  =  r .buildArtifactURL (ctx , "DownloadArtifact" , artifactName , ctx .ActionTask .ID ,  artifact . ID )
459527	}
460528	r .sendProtbufBody (ctx , & respData )
461529}
0 commit comments