@@ -7,9 +7,13 @@ package repo
77import (
88 "errors"
99 "fmt"
10+ "math/rand"
1011 "net/http"
12+ "os"
1113 "slices"
14+ "strconv"
1215 "strings"
16+ "time"
1317
1418 "code.gitea.io/gitea/models"
1519 "code.gitea.io/gitea/models/db"
@@ -19,6 +23,7 @@ import (
1923 repo_model "code.gitea.io/gitea/models/repo"
2024 "code.gitea.io/gitea/models/unit"
2125 user_model "code.gitea.io/gitea/models/user"
26+ "code.gitea.io/gitea/modules/annex"
2227 "code.gitea.io/gitea/modules/base"
2328 "code.gitea.io/gitea/modules/cache"
2429 "code.gitea.io/gitea/modules/context"
@@ -30,6 +35,7 @@ import (
3035 api "code.gitea.io/gitea/modules/structs"
3136 "code.gitea.io/gitea/modules/util"
3237 "code.gitea.io/gitea/modules/web"
38+ "code.gitea.io/gitea/routers/common"
3339 "code.gitea.io/gitea/services/convert"
3440 "code.gitea.io/gitea/services/forms"
3541 repo_service "code.gitea.io/gitea/services/repository"
@@ -429,13 +435,39 @@ func Download(ctx *context.Context) {
429435 return
430436 }
431437
432- archiver , err := aReq . Await ( ctx )
438+ err = archiver_service . StartArchive ( aReq )
433439 if err != nil {
434- ctx .ServerError ("archiver.Await " , err )
440+ ctx .ServerError ("archiver_service.StartArchive " , err )
435441 return
436442 }
437443
438- download (ctx , aReq .GetArchiveName (), archiver )
444+ for {
445+ archiver , err := repo_model .GetRepoArchiver (ctx , aReq .RepoID , aReq .Type , aReq .CommitID )
446+ if err != nil {
447+ ctx .ServerError ("repo_model.GetRepoArchiver" , err )
448+ return
449+ }
450+ if archiver != nil && archiver .Status == repo_model .ArchiverReady {
451+ download (ctx , aReq .GetArchiveName (), archiver )
452+ return
453+ }
454+ if archiver != nil && archiver .Status == repo_model .ArchiverGenerating {
455+ filePath := os .TempDir () + "/" + strconv .FormatInt (archiver .RepoID , 10 ) + "-" + archiver .CommitID + "." + archiver .Type .String ()
456+ f , err := os .Open (filePath )
457+ if err != nil {
458+ // The archiver might have finished in-between repo_model.GetRepoArchiver and os.Open, retry
459+ continue
460+ }
461+ defer f .Close ()
462+ r := annex .NewUntilFileDeletedReader (filePath , f )
463+ downloadName := ctx .Repo .Repository .Name + "-" + aReq .GetArchiveName ()
464+ // TODO: figure out how to serve the file with an approximate size of the resulting archive
465+ common .ServeContentByReader (ctx .Base , downloadName , - 1 , r )
466+ return
467+ }
468+ // archiver was nil, retry after 0 to 1 seconds
469+ time .Sleep (time .Duration (rand .Intn (int (time .Second ))))
470+ }
439471}
440472
441473func download (ctx * context.Context , archiveName string , archiver * repo_model.RepoArchiver ) {
@@ -465,43 +497,6 @@ func download(ctx *context.Context, archiveName string, archiver *repo_model.Rep
465497 })
466498}
467499
468- // InitiateDownload will enqueue an archival request, as needed. It may submit
469- // a request that's already in-progress, but the archiver service will just
470- // kind of drop it on the floor if this is the case.
471- func InitiateDownload (ctx * context.Context ) {
472- uri := ctx .Params ("*" )
473- aReq , err := archiver_service .NewRequest (ctx .Repo .Repository .ID , ctx .Repo .GitRepo , uri )
474- if err != nil {
475- ctx .ServerError ("archiver_service.NewRequest" , err )
476- return
477- }
478- if aReq == nil {
479- ctx .Error (http .StatusNotFound )
480- return
481- }
482-
483- archiver , err := repo_model .GetRepoArchiver (ctx , aReq .RepoID , aReq .Type , aReq .CommitID )
484- if err != nil {
485- ctx .ServerError ("archiver_service.StartArchive" , err )
486- return
487- }
488- if archiver == nil || archiver .Status != repo_model .ArchiverReady {
489- if err := archiver_service .StartArchive (aReq ); err != nil {
490- ctx .ServerError ("archiver_service.StartArchive" , err )
491- return
492- }
493- }
494-
495- var completed bool
496- if archiver != nil && archiver .Status == repo_model .ArchiverReady {
497- completed = true
498- }
499-
500- ctx .JSON (http .StatusOK , map [string ]any {
501- "complete" : completed ,
502- })
503- }
504-
505500// SearchRepo repositories via options
506501func SearchRepo (ctx * context.Context ) {
507502 opts := & repo_model.SearchRepoOptions {
0 commit comments