Skip to content

Commit ddd5d07

Browse files
fuxiaoheisilverwind
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 ea45dfc commit ddd5d07

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
@@ -186,7 +186,14 @@ func mergeChunksForArtifact(ctx *ArtifactContext, chunks []*chunkFileItem, st st
186186
}()
187187

188188
// save storage path to artifact
189-
log.Debug("[artifact] merge chunks to artifact: %d, %s", artifact.ID, storagePath)
189+
log.Debug("[artifact] merge chunks to artifact: %d, %s, old:%s", artifact.ID, storagePath, artifact.StoragePath)
190+
// if artifact is already uploaded, delete the old file
191+
if artifact.StoragePath != "" {
192+
if err := st.Delete(artifact.StoragePath); err != nil {
193+
log.Warn("Error deleting old artifact: %s, %v", artifact.StoragePath, err)
194+
}
195+
}
196+
190197
artifact.StoragePath = storagePath
191198
artifact.Status = int64(actions.ArtifactStatusUploadConfirmed)
192199
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
@@ -287,3 +287,92 @@ func TestActionsArtifactUploadWithRetentionDays(t *testing.T) {
287287
AddTokenAuth("8061e833a55f6fc0157c98b883e91fcfeeb1a71a")
288288
MakeRequest(t, req, http.StatusOK)
289289
}
290+
291+
func TestActionsArtifactOverwrite(t *testing.T) {
292+
defer tests.PrepareTestEnv(t)()
293+
294+
{
295+
// download old artifact uploaded by tests above, it should 1024 A
296+
req := NewRequest(t, "GET", "/api/actions_pipeline/_apis/pipelines/workflows/791/artifacts").
297+
AddTokenAuth("8061e833a55f6fc0157c98b883e91fcfeeb1a71a")
298+
resp := MakeRequest(t, req, http.StatusOK)
299+
var listResp listArtifactsResponse
300+
DecodeJSON(t, resp, &listResp)
301+
302+
idx := strings.Index(listResp.Value[0].FileContainerResourceURL, "/api/actions_pipeline/_apis/pipelines/")
303+
url := listResp.Value[0].FileContainerResourceURL[idx+1:] + "?itemPath=artifact"
304+
req = NewRequest(t, "GET", url).
305+
AddTokenAuth("8061e833a55f6fc0157c98b883e91fcfeeb1a71a")
306+
resp = MakeRequest(t, req, http.StatusOK)
307+
var downloadResp downloadArtifactResponse
308+
DecodeJSON(t, resp, &downloadResp)
309+
310+
idx = strings.Index(downloadResp.Value[0].ContentLocation, "/api/actions_pipeline/_apis/pipelines/")
311+
url = downloadResp.Value[0].ContentLocation[idx:]
312+
req = NewRequest(t, "GET", url).
313+
AddTokenAuth("8061e833a55f6fc0157c98b883e91fcfeeb1a71a")
314+
resp = MakeRequest(t, req, http.StatusOK)
315+
body := strings.Repeat("A", 1024)
316+
assert.Equal(t, resp.Body.String(), body)
317+
}
318+
319+
{
320+
// upload same artifact, it uses 4096 B
321+
req := NewRequestWithJSON(t, "POST", "/api/actions_pipeline/_apis/pipelines/workflows/791/artifacts", getUploadArtifactRequest{
322+
Type: "actions_storage",
323+
Name: "artifact",
324+
}).AddTokenAuth("8061e833a55f6fc0157c98b883e91fcfeeb1a71a")
325+
resp := MakeRequest(t, req, http.StatusOK)
326+
var uploadResp uploadArtifactResponse
327+
DecodeJSON(t, resp, &uploadResp)
328+
329+
idx := strings.Index(uploadResp.FileContainerResourceURL, "/api/actions_pipeline/_apis/pipelines/")
330+
url := uploadResp.FileContainerResourceURL[idx:] + "?itemPath=artifact/abc.txt"
331+
body := strings.Repeat("B", 4096)
332+
req = NewRequestWithBody(t, "PUT", url, strings.NewReader(body)).
333+
AddTokenAuth("8061e833a55f6fc0157c98b883e91fcfeeb1a71a").
334+
SetHeader("Content-Range", "bytes 0-4095/4096").
335+
SetHeader("x-tfs-filelength", "4096").
336+
SetHeader("x-actions-results-md5", "wUypcJFeZCK5T6r4lfqzqg==") // base64(md5(body))
337+
MakeRequest(t, req, http.StatusOK)
338+
339+
// confirm artifact upload
340+
req = NewRequest(t, "PATCH", "/api/actions_pipeline/_apis/pipelines/workflows/791/artifacts?artifactName=artifact").
341+
AddTokenAuth("8061e833a55f6fc0157c98b883e91fcfeeb1a71a")
342+
MakeRequest(t, req, http.StatusOK)
343+
}
344+
345+
{
346+
// download artifact again, it should 4096 B
347+
req := NewRequest(t, "GET", "/api/actions_pipeline/_apis/pipelines/workflows/791/artifacts").
348+
AddTokenAuth("8061e833a55f6fc0157c98b883e91fcfeeb1a71a")
349+
resp := MakeRequest(t, req, http.StatusOK)
350+
var listResp listArtifactsResponse
351+
DecodeJSON(t, resp, &listResp)
352+
353+
var uploadedItem listArtifactsResponseItem
354+
for _, item := range listResp.Value {
355+
if item.Name == "artifact" {
356+
uploadedItem = item
357+
break
358+
}
359+
}
360+
assert.Equal(t, uploadedItem.Name, "artifact")
361+
362+
idx := strings.Index(uploadedItem.FileContainerResourceURL, "/api/actions_pipeline/_apis/pipelines/")
363+
url := uploadedItem.FileContainerResourceURL[idx+1:] + "?itemPath=artifact"
364+
req = NewRequest(t, "GET", url).
365+
AddTokenAuth("8061e833a55f6fc0157c98b883e91fcfeeb1a71a")
366+
resp = MakeRequest(t, req, http.StatusOK)
367+
var downloadResp downloadArtifactResponse
368+
DecodeJSON(t, resp, &downloadResp)
369+
370+
idx = strings.Index(downloadResp.Value[0].ContentLocation, "/api/actions_pipeline/_apis/pipelines/")
371+
url = downloadResp.Value[0].ContentLocation[idx:]
372+
req = NewRequest(t, "GET", url).
373+
AddTokenAuth("8061e833a55f6fc0157c98b883e91fcfeeb1a71a")
374+
resp = MakeRequest(t, req, http.StatusOK)
375+
body := strings.Repeat("B", 4096)
376+
assert.Equal(t, resp.Body.String(), body)
377+
}
378+
}

0 commit comments

Comments
 (0)