Skip to content

Commit d11597a

Browse files
fuxiaoheiearl-warren
authored andcommittedJan 31, 2024
Fix uploaded artifacts should be overwritten (#28726) backport v1.21 (#28832)
Backport go-gitea/gitea#28726 by @fuxiaohei Fix Uploaded artifacts should be overwritten go-gitea/gitea#28549 When upload different content to uploaded artifact, it checks that content size is not match in db record with previous artifact size, then the new artifact is refused. Now if it finds uploading content size is not matching db record when receiving chunks, it updates db records to follow the latest size value. (cherry picked from commit 7f0ce2d)
1 parent e262064 commit d11597a

File tree

3 files changed

+105
-3
lines changed

3 files changed

+105
-3
lines changed
 

‎routers/api/actions/artifacts.go

+7-2
Original file line numberDiff line numberDiff line change
@@ -257,8 +257,11 @@ func (ar artifactRoutes) uploadArtifact(ctx *ArtifactContext) {
257257
return
258258
}
259259

260-
// update artifact size if zero
261-
if artifact.FileSize == 0 || artifact.FileCompressedSize == 0 {
260+
// update artifact size if zero or not match, over write artifact size
261+
if artifact.FileSize == 0 ||
262+
artifact.FileCompressedSize == 0 ||
263+
artifact.FileSize != fileRealTotalSize ||
264+
artifact.FileCompressedSize != chunksTotalSize {
262265
artifact.FileSize = fileRealTotalSize
263266
artifact.FileCompressedSize = chunksTotalSize
264267
artifact.ContentEncoding = ctx.Req.Header.Get("Content-Encoding")
@@ -267,6 +270,8 @@ func (ar artifactRoutes) uploadArtifact(ctx *ArtifactContext) {
267270
ctx.Error(http.StatusInternalServerError, "Error update artifact")
268271
return
269272
}
273+
log.Debug("[artifact] update artifact size, artifact_id: %d, size: %d, compressed size: %d",
274+
artifact.ID, artifact.FileSize, artifact.FileCompressedSize)
270275
}
271276

272277
ctx.JSON(http.StatusOK, map[string]string{

‎routers/api/actions/artifacts_chunks.go

+8-1
Original file line numberDiff line numberDiff line change
@@ -182,7 +182,14 @@ func mergeChunksForArtifact(ctx *ArtifactContext, chunks []*chunkFileItem, st st
182182
}()
183183

184184
// save storage path to artifact
185-
log.Debug("[artifact] merge chunks to artifact: %d, %s", artifact.ID, storagePath)
185+
log.Debug("[artifact] merge chunks to artifact: %d, %s, old:%s", artifact.ID, storagePath, artifact.StoragePath)
186+
// if artifact is already uploaded, delete the old file
187+
if artifact.StoragePath != "" {
188+
if err := st.Delete(artifact.StoragePath); err != nil {
189+
log.Warn("Error deleting old artifact: %s, %v", artifact.StoragePath, err)
190+
}
191+
}
192+
186193
artifact.StoragePath = storagePath
187194
artifact.Status = int64(actions.ArtifactStatusUploadConfirmed)
188195
if err := actions.UpdateArtifactByID(ctx, artifact.ID, artifact); err != nil {

‎tests/integration/api_actions_artifact_test.go

+90
Original file line numberDiff line numberDiff line change
@@ -290,3 +290,93 @@ func TestActionsArtifactUploadWithRetentionDays(t *testing.T) {
290290
req = addTokenAuthHeader(req, "Bearer 8061e833a55f6fc0157c98b883e91fcfeeb1a71a")
291291
MakeRequest(t, req, http.StatusOK)
292292
}
293+
294+
func TestActionsArtifactOverwrite(t *testing.T) {
295+
defer tests.PrepareTestEnv(t)()
296+
297+
{
298+
// download old artifact uploaded by tests above, it should 1024 A
299+
req := NewRequest(t, "GET", "/api/actions_pipeline/_apis/pipelines/workflows/791/artifacts")
300+
req = addTokenAuthHeader(req, "Bearer 8061e833a55f6fc0157c98b883e91fcfeeb1a71a")
301+
resp := MakeRequest(t, req, http.StatusOK)
302+
var listResp listArtifactsResponse
303+
DecodeJSON(t, resp, &listResp)
304+
305+
idx := strings.Index(listResp.Value[0].FileContainerResourceURL, "/api/actions_pipeline/_apis/pipelines/")
306+
url := listResp.Value[0].FileContainerResourceURL[idx+1:] + "?itemPath=artifact"
307+
req = NewRequest(t, "GET", url)
308+
req = addTokenAuthHeader(req, "Bearer 8061e833a55f6fc0157c98b883e91fcfeeb1a71a")
309+
resp = MakeRequest(t, req, http.StatusOK)
310+
var downloadResp downloadArtifactResponse
311+
DecodeJSON(t, resp, &downloadResp)
312+
313+
idx = strings.Index(downloadResp.Value[0].ContentLocation, "/api/actions_pipeline/_apis/pipelines/")
314+
url = downloadResp.Value[0].ContentLocation[idx:]
315+
req = NewRequest(t, "GET", url)
316+
req = addTokenAuthHeader(req, "Bearer 8061e833a55f6fc0157c98b883e91fcfeeb1a71a")
317+
resp = MakeRequest(t, req, http.StatusOK)
318+
body := strings.Repeat("A", 1024)
319+
assert.Equal(t, resp.Body.String(), body)
320+
}
321+
322+
{
323+
// upload same artifact, it uses 4096 B
324+
req := NewRequestWithJSON(t, "POST", "/api/actions_pipeline/_apis/pipelines/workflows/791/artifacts", getUploadArtifactRequest{
325+
Type: "actions_storage",
326+
Name: "artifact",
327+
})
328+
req = addTokenAuthHeader(req, "Bearer 8061e833a55f6fc0157c98b883e91fcfeeb1a71a")
329+
resp := MakeRequest(t, req, http.StatusOK)
330+
var uploadResp uploadArtifactResponse
331+
DecodeJSON(t, resp, &uploadResp)
332+
333+
idx := strings.Index(uploadResp.FileContainerResourceURL, "/api/actions_pipeline/_apis/pipelines/")
334+
url := uploadResp.FileContainerResourceURL[idx:] + "?itemPath=artifact/abc.txt"
335+
body := strings.Repeat("B", 4096)
336+
req = NewRequestWithBody(t, "PUT", url, strings.NewReader(body))
337+
req = addTokenAuthHeader(req, "Bearer 8061e833a55f6fc0157c98b883e91fcfeeb1a71a")
338+
req.Header.Add("Content-Range", "bytes 0-4095/4096")
339+
req.Header.Add("x-tfs-filelength", "4096")
340+
req.Header.Add("x-actions-results-md5", "wUypcJFeZCK5T6r4lfqzqg==") // base64(md5(body))
341+
MakeRequest(t, req, http.StatusOK)
342+
343+
// confirm artifact upload
344+
req = NewRequest(t, "PATCH", "/api/actions_pipeline/_apis/pipelines/workflows/791/artifacts?artifactName=artifact")
345+
req = addTokenAuthHeader(req, "Bearer 8061e833a55f6fc0157c98b883e91fcfeeb1a71a")
346+
MakeRequest(t, req, http.StatusOK)
347+
}
348+
349+
{
350+
// download artifact again, it should 4096 B
351+
req := NewRequest(t, "GET", "/api/actions_pipeline/_apis/pipelines/workflows/791/artifacts")
352+
req = addTokenAuthHeader(req, "Bearer 8061e833a55f6fc0157c98b883e91fcfeeb1a71a")
353+
resp := MakeRequest(t, req, http.StatusOK)
354+
var listResp listArtifactsResponse
355+
DecodeJSON(t, resp, &listResp)
356+
357+
var uploadedItem listArtifactsResponseItem
358+
for _, item := range listResp.Value {
359+
if item.Name == "artifact" {
360+
uploadedItem = item
361+
break
362+
}
363+
}
364+
assert.Equal(t, uploadedItem.Name, "artifact")
365+
366+
idx := strings.Index(uploadedItem.FileContainerResourceURL, "/api/actions_pipeline/_apis/pipelines/")
367+
url := uploadedItem.FileContainerResourceURL[idx+1:] + "?itemPath=artifact"
368+
req = NewRequest(t, "GET", url)
369+
req = addTokenAuthHeader(req, "Bearer 8061e833a55f6fc0157c98b883e91fcfeeb1a71a")
370+
resp = MakeRequest(t, req, http.StatusOK)
371+
var downloadResp downloadArtifactResponse
372+
DecodeJSON(t, resp, &downloadResp)
373+
374+
idx = strings.Index(downloadResp.Value[0].ContentLocation, "/api/actions_pipeline/_apis/pipelines/")
375+
url = downloadResp.Value[0].ContentLocation[idx:]
376+
req = NewRequest(t, "GET", url)
377+
req = addTokenAuthHeader(req, "Bearer 8061e833a55f6fc0157c98b883e91fcfeeb1a71a")
378+
resp = MakeRequest(t, req, http.StatusOK)
379+
body := strings.Repeat("B", 4096)
380+
assert.Equal(t, resp.Body.String(), body)
381+
}
382+
}

0 commit comments

Comments
 (0)
Please sign in to comment.