Skip to content

Commit a4fb5be

Browse files
fuxiaoheiGiteaBot
authored andcommitted
Fix uploaded artifacts should be overwritten (go-gitea#28726)
Fix `Uploaded artifacts should be overwritten` go-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.
1 parent f492385 commit a4fb5be

File tree

3 files changed

+104
-3
lines changed

3 files changed

+104
-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

+89
Original file line numberDiff line numberDiff line change
@@ -290,3 +290,92 @@ 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+
AddTokenAuth("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+
AddTokenAuth("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+
AddTokenAuth("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+
}).AddTokenAuth("8061e833a55f6fc0157c98b883e91fcfeeb1a71a")
328+
resp := MakeRequest(t, req, http.StatusOK)
329+
var uploadResp uploadArtifactResponse
330+
DecodeJSON(t, resp, &uploadResp)
331+
332+
idx := strings.Index(uploadResp.FileContainerResourceURL, "/api/actions_pipeline/_apis/pipelines/")
333+
url := uploadResp.FileContainerResourceURL[idx:] + "?itemPath=artifact/abc.txt"
334+
body := strings.Repeat("B", 4096)
335+
req = NewRequestWithBody(t, "PUT", url, strings.NewReader(body)).
336+
AddTokenAuth("8061e833a55f6fc0157c98b883e91fcfeeb1a71a").
337+
SetHeader("Content-Range", "bytes 0-4095/4096").
338+
SetHeader("x-tfs-filelength", "4096").
339+
SetHeader("x-actions-results-md5", "wUypcJFeZCK5T6r4lfqzqg==") // base64(md5(body))
340+
MakeRequest(t, req, http.StatusOK)
341+
342+
// confirm artifact upload
343+
req = NewRequest(t, "PATCH", "/api/actions_pipeline/_apis/pipelines/workflows/791/artifacts?artifactName=artifact").
344+
AddTokenAuth("8061e833a55f6fc0157c98b883e91fcfeeb1a71a")
345+
MakeRequest(t, req, http.StatusOK)
346+
}
347+
348+
{
349+
// download artifact again, it should 4096 B
350+
req := NewRequest(t, "GET", "/api/actions_pipeline/_apis/pipelines/workflows/791/artifacts").
351+
AddTokenAuth("8061e833a55f6fc0157c98b883e91fcfeeb1a71a")
352+
resp := MakeRequest(t, req, http.StatusOK)
353+
var listResp listArtifactsResponse
354+
DecodeJSON(t, resp, &listResp)
355+
356+
var uploadedItem listArtifactsResponseItem
357+
for _, item := range listResp.Value {
358+
if item.Name == "artifact" {
359+
uploadedItem = item
360+
break
361+
}
362+
}
363+
assert.Equal(t, uploadedItem.Name, "artifact")
364+
365+
idx := strings.Index(uploadedItem.FileContainerResourceURL, "/api/actions_pipeline/_apis/pipelines/")
366+
url := uploadedItem.FileContainerResourceURL[idx+1:] + "?itemPath=artifact"
367+
req = NewRequest(t, "GET", url).
368+
AddTokenAuth("8061e833a55f6fc0157c98b883e91fcfeeb1a71a")
369+
resp = MakeRequest(t, req, http.StatusOK)
370+
var downloadResp downloadArtifactResponse
371+
DecodeJSON(t, resp, &downloadResp)
372+
373+
idx = strings.Index(downloadResp.Value[0].ContentLocation, "/api/actions_pipeline/_apis/pipelines/")
374+
url = downloadResp.Value[0].ContentLocation[idx:]
375+
req = NewRequest(t, "GET", url).
376+
AddTokenAuth("8061e833a55f6fc0157c98b883e91fcfeeb1a71a")
377+
resp = MakeRequest(t, req, http.StatusOK)
378+
body := strings.Repeat("B", 4096)
379+
assert.Equal(t, resp.Body.String(), body)
380+
}
381+
}

0 commit comments

Comments
 (0)