3
3
4
4
package actions
5
5
6
- // Github Actions Artifacts API Simple Description
6
+ // GitHub Actions Artifacts API Simple Description
7
7
//
8
8
// 1. Upload artifact
9
9
// 1.1. Post upload url
@@ -63,7 +63,6 @@ package actions
63
63
64
64
import (
65
65
"compress/gzip"
66
- gocontext "context"
67
66
"crypto/md5"
68
67
"encoding/base64"
69
68
"errors"
@@ -92,9 +91,25 @@ const (
92
91
93
92
const artifactRouteBase = "/_apis/pipelines/workflows/{run_id}/artifacts"
94
93
95
- func ArtifactsRoutes (goctx gocontext.Context , prefix string ) * web.Route {
94
+ type artifactContextKeyType struct {}
95
+
96
+ var artifactContextKey = artifactContextKeyType {}
97
+
98
+ type ArtifactContext struct {
99
+ * context.Base
100
+
101
+ ActionTask * actions.ActionTask
102
+ }
103
+
104
+ func init () {
105
+ web.RegisterHandleTypeProvider [* ArtifactContext ](func (req * http.Request ) web.ResponseStatusProvider {
106
+ return req .Context ().Value (artifactContextKey ).(* ArtifactContext )
107
+ })
108
+ }
109
+
110
+ func ArtifactsRoutes (prefix string ) * web.Route {
96
111
m := web .NewRoute ()
97
- m .Use (withContexter ( goctx ))
112
+ m .Use (ArtifactContexter ( ))
98
113
99
114
r := artifactRoutes {
100
115
prefix : prefix ,
@@ -115,15 +130,14 @@ func ArtifactsRoutes(goctx gocontext.Context, prefix string) *web.Route {
115
130
return m
116
131
}
117
132
118
- // withContexter initializes a package context for a request.
119
- func withContexter (goctx gocontext.Context ) func (next http.Handler ) http.Handler {
133
+ func ArtifactContexter () func (next http.Handler ) http.Handler {
120
134
return func (next http.Handler ) http.Handler {
121
135
return http .HandlerFunc (func (resp http.ResponseWriter , req * http.Request ) {
122
- ctx := context.Context {
123
- Resp : context . NewResponse ( resp ),
124
- Data : map [ string ] interface {}{},
125
- }
126
- defer ctx .Close ( )
136
+ base , baseCleanUp := context .NewBaseContext ( resp , req )
137
+ defer baseCleanUp ()
138
+
139
+ ctx := & ArtifactContext { Base : base }
140
+ ctx .AppendContextValue ( artifactContextKey , ctx )
127
141
128
142
// action task call server api with Bearer ACTIONS_RUNTIME_TOKEN
129
143
// we should verify the ACTIONS_RUNTIME_TOKEN
@@ -132,23 +146,22 @@ func withContexter(goctx gocontext.Context) func(next http.Handler) http.Handler
132
146
ctx .Error (http .StatusUnauthorized , "Bad authorization header" )
133
147
return
134
148
}
149
+
135
150
authToken := strings .TrimPrefix (authHeader , "Bearer " )
136
151
task , err := actions .GetRunningTaskByToken (req .Context (), authToken )
137
152
if err != nil {
138
153
log .Error ("Error runner api getting task: %v" , err )
139
154
ctx .Error (http .StatusInternalServerError , "Error runner api getting task" )
140
155
return
141
156
}
142
- ctx .Data ["task" ] = task
143
157
144
- if err := task .LoadJob (goctx ); err != nil {
158
+ if err := task .LoadJob (req . Context () ); err != nil {
145
159
log .Error ("Error runner api getting job: %v" , err )
146
160
ctx .Error (http .StatusInternalServerError , "Error runner api getting job" )
147
161
return
148
162
}
149
163
150
- ctx .Req = context .WithContext (req , & ctx )
151
-
164
+ ctx .ActionTask = task
152
165
next .ServeHTTP (ctx .Resp , ctx .Req )
153
166
})
154
167
}
@@ -175,13 +188,8 @@ type getUploadArtifactResponse struct {
175
188
FileContainerResourceURL string `json:"fileContainerResourceUrl"`
176
189
}
177
190
178
- func (ar artifactRoutes ) validateRunID (ctx * context.Context ) (* actions.ActionTask , int64 , bool ) {
179
- task , ok := ctx .Data ["task" ].(* actions.ActionTask )
180
- if ! ok {
181
- log .Error ("Error getting task in context" )
182
- ctx .Error (http .StatusInternalServerError , "Error getting task in context" )
183
- return nil , 0 , false
184
- }
191
+ func (ar artifactRoutes ) validateRunID (ctx * ArtifactContext ) (* actions.ActionTask , int64 , bool ) {
192
+ task := ctx .ActionTask
185
193
runID := ctx .ParamsInt64 ("run_id" )
186
194
if task .Job .RunID != runID {
187
195
log .Error ("Error runID not match" )
@@ -192,7 +200,7 @@ func (ar artifactRoutes) validateRunID(ctx *context.Context) (*actions.ActionTas
192
200
}
193
201
194
202
// getUploadArtifactURL generates a URL for uploading an artifact
195
- func (ar artifactRoutes ) getUploadArtifactURL (ctx * context. Context ) {
203
+ func (ar artifactRoutes ) getUploadArtifactURL (ctx * ArtifactContext ) {
196
204
task , runID , ok := ar .validateRunID (ctx )
197
205
if ! ok {
198
206
return
@@ -220,7 +228,7 @@ func (ar artifactRoutes) getUploadArtifactURL(ctx *context.Context) {
220
228
221
229
// getUploadFileSize returns the size of the file to be uploaded.
222
230
// The raw size is the size of the file as reported by the header X-TFS-FileLength.
223
- func (ar artifactRoutes ) getUploadFileSize (ctx * context. Context ) (int64 , int64 , error ) {
231
+ func (ar artifactRoutes ) getUploadFileSize (ctx * ArtifactContext ) (int64 , int64 , error ) {
224
232
contentLength := ctx .Req .ContentLength
225
233
xTfsLength , _ := strconv .ParseInt (ctx .Req .Header .Get (artifactXTfsFileLengthHeader ), 10 , 64 )
226
234
if xTfsLength > 0 {
@@ -229,7 +237,7 @@ func (ar artifactRoutes) getUploadFileSize(ctx *context.Context) (int64, int64,
229
237
return contentLength , contentLength , nil
230
238
}
231
239
232
- func (ar artifactRoutes ) saveUploadChunk (ctx * context. Context ,
240
+ func (ar artifactRoutes ) saveUploadChunk (ctx * ArtifactContext ,
233
241
artifact * actions.ActionArtifact ,
234
242
contentSize , runID int64 ,
235
243
) (int64 , error ) {
@@ -273,7 +281,7 @@ func (ar artifactRoutes) saveUploadChunk(ctx *context.Context,
273
281
// The rules are from https://github.com/actions/toolkit/blob/main/packages/artifact/src/internal/path-and-artifact-name-validation.ts#L32
274
282
var invalidArtifactNameChars = strings .Join ([]string {"\\ " , "/" , "\" " , ":" , "<" , ">" , "|" , "*" , "?" , "\r " , "\n " }, "" )
275
283
276
- func (ar artifactRoutes ) uploadArtifact (ctx * context. Context ) {
284
+ func (ar artifactRoutes ) uploadArtifact (ctx * ArtifactContext ) {
277
285
_ , runID , ok := ar .validateRunID (ctx )
278
286
if ! ok {
279
287
return
@@ -341,7 +349,7 @@ func (ar artifactRoutes) uploadArtifact(ctx *context.Context) {
341
349
342
350
// comfirmUploadArtifact comfirm upload artifact.
343
351
// if all chunks are uploaded, merge them to one file.
344
- func (ar artifactRoutes ) comfirmUploadArtifact (ctx * context. Context ) {
352
+ func (ar artifactRoutes ) comfirmUploadArtifact (ctx * ArtifactContext ) {
345
353
_ , runID , ok := ar .validateRunID (ctx )
346
354
if ! ok {
347
355
return
@@ -364,7 +372,7 @@ type chunkItem struct {
364
372
Path string
365
373
}
366
374
367
- func (ar artifactRoutes ) mergeArtifactChunks (ctx * context. Context , runID int64 ) error {
375
+ func (ar artifactRoutes ) mergeArtifactChunks (ctx * ArtifactContext , runID int64 ) error {
368
376
storageDir := fmt .Sprintf ("tmp%d" , runID )
369
377
var chunks []* chunkItem
370
378
if err := ar .fs .IterateObjects (storageDir , func (path string , obj storage.Object ) error {
@@ -415,14 +423,20 @@ func (ar artifactRoutes) mergeArtifactChunks(ctx *context.Context, runID int64)
415
423
416
424
// use multiReader
417
425
readers := make ([]io.Reader , 0 , len (allChunks ))
418
- readerClosers := make ([]io.Closer , 0 , len (allChunks ))
426
+ closeReaders := func () {
427
+ for _ , r := range readers {
428
+ _ = r .(io.Closer ).Close () // it guarantees to be io.Closer by the following loop's Open function
429
+ }
430
+ readers = nil
431
+ }
432
+ defer closeReaders ()
433
+
419
434
for _ , c := range allChunks {
420
- reader , err := ar . fs . Open ( c . Path )
421
- if err != nil {
435
+ var readCloser io. ReadCloser
436
+ if readCloser , err = ar . fs . Open ( c . Path ); err != nil {
422
437
return fmt .Errorf ("open chunk error: %v, %s" , err , c .Path )
423
438
}
424
- readers = append (readers , reader )
425
- readerClosers = append (readerClosers , reader )
439
+ readers = append (readers , readCloser )
426
440
}
427
441
mergedReader := io .MultiReader (readers ... )
428
442
@@ -445,11 +459,6 @@ func (ar artifactRoutes) mergeArtifactChunks(ctx *context.Context, runID int64)
445
459
return fmt .Errorf ("merged file size is not equal to chunk length" )
446
460
}
447
461
448
- // close readers
449
- for _ , r := range readerClosers {
450
- r .Close ()
451
- }
452
-
453
462
// save storage path to artifact
454
463
log .Debug ("[artifact] merge chunks to artifact: %d, %s" , artifact .ID , storagePath )
455
464
artifact .StoragePath = storagePath
@@ -458,6 +467,8 @@ func (ar artifactRoutes) mergeArtifactChunks(ctx *context.Context, runID int64)
458
467
return fmt .Errorf ("update artifact error: %v" , err )
459
468
}
460
469
470
+ closeReaders () // close before delete
471
+
461
472
// drop chunks
462
473
for _ , c := range cs {
463
474
if err := ar .fs .Delete (c .Path ); err != nil {
@@ -479,21 +490,21 @@ type (
479
490
}
480
491
)
481
492
482
- func (ar artifactRoutes ) listArtifacts (ctx * context. Context ) {
493
+ func (ar artifactRoutes ) listArtifacts (ctx * ArtifactContext ) {
483
494
_ , runID , ok := ar .validateRunID (ctx )
484
495
if ! ok {
485
496
return
486
497
}
487
498
488
- artficats , err := actions .ListArtifactsByRunID (ctx , runID )
499
+ artifacts , err := actions .ListArtifactsByRunID (ctx , runID )
489
500
if err != nil {
490
501
log .Error ("Error getting artifacts: %v" , err )
491
502
ctx .Error (http .StatusInternalServerError , err .Error ())
492
503
return
493
504
}
494
505
495
- artficatsData := make ([]listArtifactsResponseItem , 0 , len (artficats ))
496
- for _ , a := range artficats {
506
+ artficatsData := make ([]listArtifactsResponseItem , 0 , len (artifacts ))
507
+ for _ , a := range artifacts {
497
508
artficatsData = append (artficatsData , listArtifactsResponseItem {
498
509
Name : a .ArtifactName ,
499
510
FileContainerResourceURL : ar .buildArtifactURL (runID , a .ID , "path" ),
@@ -517,7 +528,7 @@ type (
517
528
}
518
529
)
519
530
520
- func (ar artifactRoutes ) getDownloadArtifactURL (ctx * context. Context ) {
531
+ func (ar artifactRoutes ) getDownloadArtifactURL (ctx * ArtifactContext ) {
521
532
_ , runID , ok := ar .validateRunID (ctx )
522
533
if ! ok {
523
534
return
@@ -546,7 +557,7 @@ func (ar artifactRoutes) getDownloadArtifactURL(ctx *context.Context) {
546
557
ctx .JSON (http .StatusOK , respData )
547
558
}
548
559
549
- func (ar artifactRoutes ) downloadArtifact (ctx * context. Context ) {
560
+ func (ar artifactRoutes ) downloadArtifact (ctx * ArtifactContext ) {
550
561
_ , runID , ok := ar .validateRunID (ctx )
551
562
if ! ok {
552
563
return
0 commit comments