Skip to content

Commit 45f6c20

Browse files
committed
Stream repo zip/tar.gz/bundle achives by default
1 parent 84812e4 commit 45f6c20

File tree

5 files changed

+92
-0
lines changed

5 files changed

+92
-0
lines changed

custom/conf/app.example.ini

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1083,6 +1083,9 @@ LEVEL = Info
10831083
;; This feature is experimental, not fully tested, and may be changed in the future
10841084
;ALLOW_FORK_INTO_SAME_OWNER = false
10851085

1086+
;; Repository archives (tar.gz, zip and bundle) are streamed when this is enabled instead of being cached
1087+
;STREAM_ARCHIVES = true
1088+
10861089
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
10871090
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
10881091
;[repository.editor]

modules/setting/repository.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ var (
5353
DisableDownloadSourceArchives bool
5454
AllowForkWithoutMaximumLimit bool
5555
AllowForkIntoSameOwner bool
56+
StreamArchives bool
5657

5758
// Repository editor settings
5859
Editor struct {
@@ -167,6 +168,7 @@ var (
167168
DisableStars: false,
168169
DefaultBranch: "main",
169170
AllowForkWithoutMaximumLimit: true,
171+
StreamArchives: true,
170172

171173
// Repository editor settings
172174
Editor: struct {

routers/api/v1/repo/file.go

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -307,6 +307,29 @@ func archiveDownload(ctx *context.APIContext) {
307307
return
308308
}
309309

310+
if setting.Repository.StreamArchives {
311+
312+
downloadName := ctx.Repo.Repository.Name + "-" + aReq.GetArchiveName()
313+
314+
// Add nix format link header so tarballs lock correctly:
315+
// https://github.com/nixos/nix/blob/56763ff918eb308db23080e560ed2ea3e00c80a7/doc/manual/src/protocols/tarball-fetcher.md
316+
ctx.Resp.Header().Add("Link", fmt.Sprintf(`<%s/archive/%s.%s?rev=%s>; rel="immutable"`,
317+
ctx.Repo.Repository.APIURL(),
318+
aReq.CommitID,
319+
aReq.Type.String(),
320+
aReq.CommitID,
321+
))
322+
323+
ctx.SetServeHeaders(&context.ServeHeaderOptions{
324+
Filename: downloadName,
325+
})
326+
327+
if err := aReq.Stream(ctx, ctx.Resp); err != nil && !ctx.Written() {
328+
ctx.APIErrorInternal(err)
329+
}
330+
return
331+
}
332+
310333
archiver, err := aReq.Await(ctx)
311334
if err != nil {
312335
ctx.APIErrorInternal(err)

routers/web/repo/repo.go

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -377,6 +377,28 @@ func Download(ctx *context.Context) {
377377
return
378378
}
379379

380+
if setting.Repository.StreamArchives {
381+
downloadName := ctx.Repo.Repository.Name + "-" + aReq.GetArchiveName()
382+
383+
// Add nix format link header so tarballs lock correctly:
384+
// https://github.com/nixos/nix/blob/56763ff918eb308db23080e560ed2ea3e00c80a7/doc/manual/src/protocols/tarball-fetcher.md
385+
ctx.Resp.Header().Add("Link", fmt.Sprintf(`<%s/archive/%s.%s?rev=%s>; rel="immutable"`,
386+
ctx.Repo.Repository.APIURL(),
387+
aReq.CommitID,
388+
aReq.Type.String(),
389+
aReq.CommitID,
390+
))
391+
392+
ctx.SetServeHeaders(&context.ServeHeaderOptions{
393+
Filename: downloadName,
394+
})
395+
396+
if err := aReq.Stream(ctx, ctx.Resp); err != nil && !ctx.Written() {
397+
ctx.ServerError("archiver.StreamArchive", err)
398+
}
399+
return
400+
}
401+
380402
archiver, err := aReq.Await(ctx)
381403
if err != nil {
382404
ctx.ServerError("archiver.Await", err)
@@ -423,6 +445,12 @@ func download(ctx *context.Context, archiveName string, archiver *repo_model.Rep
423445
// a request that's already in-progress, but the archiver service will just
424446
// kind of drop it on the floor if this is the case.
425447
func InitiateDownload(ctx *context.Context) {
448+
if setting.Repository.StreamArchives {
449+
ctx.JSON(http.StatusOK, map[string]any{
450+
"complete": true,
451+
})
452+
return
453+
}
426454
aReq, err := archiver_service.NewRequest(ctx.Repo.Repository.ID, ctx.Repo.GitRepo, ctx.PathParam("*"))
427455
if err != nil {
428456
ctx.HTTPError(http.StatusBadRequest, "invalid archive request")

services/repository/archiver/archiver.go

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,42 @@ func (aReq *ArchiveRequest) Await(ctx context.Context) (*repo_model.RepoArchiver
138138
}
139139
}
140140

141+
// Stream satisfies the ArchiveRequest being passed in. Processing
142+
// will occur directly in this routine.
143+
func (aReq *ArchiveRequest) Stream(ctx context.Context, w io.Writer) error {
144+
ctx, _, finished := process.GetManager().AddContext(ctx, fmt.Sprintf("ArchiveRequest[%d]: %s", aReq.RepoID, aReq.GetArchiveName()))
145+
defer finished()
146+
147+
repo, err := repo_model.GetRepositoryByID(ctx, aReq.RepoID)
148+
if err != nil {
149+
return fmt.Errorf("archiver.LoadRepo failed: %w", err)
150+
}
151+
152+
gitRepo, err := gitrepo.OpenRepository(ctx, repo)
153+
if err != nil {
154+
return err
155+
}
156+
defer gitRepo.Close()
157+
158+
if aReq.Type == git.ArchiveBundle {
159+
err = gitRepo.CreateBundle(
160+
ctx,
161+
aReq.CommitID,
162+
w,
163+
)
164+
} else {
165+
err = gitRepo.CreateArchive(
166+
ctx,
167+
aReq.Type,
168+
w,
169+
setting.Repository.PrefixArchiveFiles,
170+
aReq.CommitID,
171+
)
172+
}
173+
174+
return err
175+
}
176+
141177
// doArchive satisfies the ArchiveRequest being passed in. Processing
142178
// will occur in a separate goroutine, as this phase may take a while to
143179
// complete. If the archive already exists, doArchive will not do

0 commit comments

Comments
 (0)