mirror of
https://github.com/go-gitea/gitea.git
synced 2025-10-12 03:36:07 +00:00
Move archive function to repo_model and gitrepo (#35514)
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
// Copyright 2025 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package git
|
||||
package repo
|
||||
|
||||
import (
|
||||
"testing"
|
@@ -11,7 +11,6 @@ import (
|
||||
"time"
|
||||
|
||||
"code.gitea.io/gitea/models/db"
|
||||
"code.gitea.io/gitea/modules/git"
|
||||
"code.gitea.io/gitea/modules/timeutil"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
|
||||
@@ -27,11 +26,46 @@ const (
|
||||
ArchiverReady // it's ready
|
||||
)
|
||||
|
||||
// ArchiveType archive types
|
||||
type ArchiveType int
|
||||
|
||||
const (
|
||||
ArchiveUnknown ArchiveType = iota
|
||||
ArchiveZip // 1
|
||||
ArchiveTarGz // 2
|
||||
ArchiveBundle // 3
|
||||
)
|
||||
|
||||
// String converts an ArchiveType to string: the extension of the archive file without prefix dot
|
||||
func (a ArchiveType) String() string {
|
||||
switch a {
|
||||
case ArchiveZip:
|
||||
return "zip"
|
||||
case ArchiveTarGz:
|
||||
return "tar.gz"
|
||||
case ArchiveBundle:
|
||||
return "bundle"
|
||||
}
|
||||
return "unknown"
|
||||
}
|
||||
|
||||
func SplitArchiveNameType(s string) (string, ArchiveType) {
|
||||
switch {
|
||||
case strings.HasSuffix(s, ".zip"):
|
||||
return strings.TrimSuffix(s, ".zip"), ArchiveZip
|
||||
case strings.HasSuffix(s, ".tar.gz"):
|
||||
return strings.TrimSuffix(s, ".tar.gz"), ArchiveTarGz
|
||||
case strings.HasSuffix(s, ".bundle"):
|
||||
return strings.TrimSuffix(s, ".bundle"), ArchiveBundle
|
||||
}
|
||||
return s, ArchiveUnknown
|
||||
}
|
||||
|
||||
// RepoArchiver represents all archivers
|
||||
type RepoArchiver struct { //revive:disable-line:exported
|
||||
ID int64 `xorm:"pk autoincr"`
|
||||
RepoID int64 `xorm:"index unique(s)"`
|
||||
Type git.ArchiveType `xorm:"unique(s)"`
|
||||
ID int64 `xorm:"pk autoincr"`
|
||||
RepoID int64 `xorm:"index unique(s)"`
|
||||
Type ArchiveType `xorm:"unique(s)"`
|
||||
Status ArchiverStatus
|
||||
CommitID string `xorm:"VARCHAR(64) unique(s)"`
|
||||
CreatedUnix timeutil.TimeStamp `xorm:"INDEX NOT NULL created"`
|
||||
@@ -56,15 +90,15 @@ func repoArchiverForRelativePath(relativePath string) (*RepoArchiver, error) {
|
||||
if err != nil {
|
||||
return nil, util.NewInvalidArgumentErrorf("invalid storage path: invalid repo id")
|
||||
}
|
||||
commitID, archiveType := git.SplitArchiveNameType(parts[2])
|
||||
if archiveType == git.ArchiveUnknown {
|
||||
commitID, archiveType := SplitArchiveNameType(parts[2])
|
||||
if archiveType == ArchiveUnknown {
|
||||
return nil, util.NewInvalidArgumentErrorf("invalid storage path: invalid archive type")
|
||||
}
|
||||
return &RepoArchiver{RepoID: repoID, CommitID: commitID, Type: archiveType}, nil
|
||||
}
|
||||
|
||||
// GetRepoArchiver get an archiver
|
||||
func GetRepoArchiver(ctx context.Context, repoID int64, tp git.ArchiveType, commitID string) (*RepoArchiver, error) {
|
||||
func GetRepoArchiver(ctx context.Context, repoID int64, tp ArchiveType, commitID string) (*RepoArchiver, error) {
|
||||
var archiver RepoArchiver
|
||||
has, err := db.GetEngine(ctx).Where("repo_id=?", repoID).And("`type`=?", tp).And("commit_id=?", commitID).Get(&archiver)
|
||||
if err != nil {
|
||||
|
@@ -12,14 +12,12 @@ import (
|
||||
"net/url"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"code.gitea.io/gitea/modules/git/gitcmd"
|
||||
"code.gitea.io/gitea/modules/proxy"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
)
|
||||
|
||||
// GPGSettings represents the default GPG settings for this repository
|
||||
@@ -242,43 +240,3 @@ func GetLatestCommitTime(ctx context.Context, repoPath string) (time.Time, error
|
||||
commitTime := strings.TrimSpace(stdout)
|
||||
return time.Parse("Mon Jan _2 15:04:05 2006 -0700", commitTime)
|
||||
}
|
||||
|
||||
// CreateBundle create bundle content to the target path
|
||||
func (repo *Repository) CreateBundle(ctx context.Context, commit string, out io.Writer) error {
|
||||
tmp, cleanup, err := setting.AppDataTempDir("git-repo-content").MkdirTempRandom("gitea-bundle")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer cleanup()
|
||||
|
||||
env := append(os.Environ(), "GIT_OBJECT_DIRECTORY="+filepath.Join(repo.Path, "objects"))
|
||||
_, _, err = gitcmd.NewCommand("init", "--bare").RunStdString(ctx, &gitcmd.RunOpts{Dir: tmp, Env: env})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, _, err = gitcmd.NewCommand("reset", "--soft").AddDynamicArguments(commit).RunStdString(ctx, &gitcmd.RunOpts{Dir: tmp, Env: env})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, _, err = gitcmd.NewCommand("branch", "-m", "bundle").RunStdString(ctx, &gitcmd.RunOpts{Dir: tmp, Env: env})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
tmpFile := filepath.Join(tmp, "bundle")
|
||||
_, _, err = gitcmd.NewCommand("bundle", "create").AddDynamicArguments(tmpFile, "bundle", "HEAD").RunStdString(ctx, &gitcmd.RunOpts{Dir: tmp, Env: env})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fi, err := os.Open(tmpFile)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer fi.Close()
|
||||
|
||||
_, err = io.Copy(out, fi)
|
||||
return err
|
||||
}
|
||||
|
@@ -1,75 +0,0 @@
|
||||
// Copyright 2015 The Gogs Authors. All rights reserved.
|
||||
// Copyright 2020 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package git
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"code.gitea.io/gitea/modules/git/gitcmd"
|
||||
)
|
||||
|
||||
// ArchiveType archive types
|
||||
type ArchiveType int
|
||||
|
||||
const (
|
||||
ArchiveUnknown ArchiveType = iota
|
||||
ArchiveZip // 1
|
||||
ArchiveTarGz // 2
|
||||
ArchiveBundle // 3
|
||||
)
|
||||
|
||||
// String converts an ArchiveType to string: the extension of the archive file without prefix dot
|
||||
func (a ArchiveType) String() string {
|
||||
switch a {
|
||||
case ArchiveZip:
|
||||
return "zip"
|
||||
case ArchiveTarGz:
|
||||
return "tar.gz"
|
||||
case ArchiveBundle:
|
||||
return "bundle"
|
||||
}
|
||||
return "unknown"
|
||||
}
|
||||
|
||||
func SplitArchiveNameType(s string) (string, ArchiveType) {
|
||||
switch {
|
||||
case strings.HasSuffix(s, ".zip"):
|
||||
return strings.TrimSuffix(s, ".zip"), ArchiveZip
|
||||
case strings.HasSuffix(s, ".tar.gz"):
|
||||
return strings.TrimSuffix(s, ".tar.gz"), ArchiveTarGz
|
||||
case strings.HasSuffix(s, ".bundle"):
|
||||
return strings.TrimSuffix(s, ".bundle"), ArchiveBundle
|
||||
}
|
||||
return s, ArchiveUnknown
|
||||
}
|
||||
|
||||
// CreateArchive create archive content to the target path
|
||||
func (repo *Repository) CreateArchive(ctx context.Context, format ArchiveType, target io.Writer, usePrefix bool, commitID string) error {
|
||||
if format.String() == "unknown" {
|
||||
return fmt.Errorf("unknown format: %v", format)
|
||||
}
|
||||
|
||||
cmd := gitcmd.NewCommand("archive")
|
||||
if usePrefix {
|
||||
cmd.AddOptionFormat("--prefix=%s", filepath.Base(strings.TrimSuffix(repo.Path, ".git"))+"/")
|
||||
}
|
||||
cmd.AddOptionFormat("--format=%s", format.String())
|
||||
cmd.AddDynamicArguments(commitID)
|
||||
|
||||
var stderr strings.Builder
|
||||
err := cmd.Run(ctx, &gitcmd.RunOpts{
|
||||
Dir: repo.Path,
|
||||
Stdout: target,
|
||||
Stderr: &stderr,
|
||||
})
|
||||
if err != nil {
|
||||
return gitcmd.ConcatenateError(err, stderr.String())
|
||||
}
|
||||
return nil
|
||||
}
|
81
modules/gitrepo/archive.go
Normal file
81
modules/gitrepo/archive.go
Normal file
@@ -0,0 +1,81 @@
|
||||
// Copyright 2025 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package gitrepo
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"code.gitea.io/gitea/modules/git/gitcmd"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
)
|
||||
|
||||
// CreateArchive create archive content to the target path
|
||||
func CreateArchive(ctx context.Context, repo Repository, format string, target io.Writer, usePrefix bool, commitID string) error {
|
||||
if format == "unknown" {
|
||||
return fmt.Errorf("unknown format: %v", format)
|
||||
}
|
||||
|
||||
cmd := gitcmd.NewCommand("archive")
|
||||
if usePrefix {
|
||||
cmd.AddOptionFormat("--prefix=%s", filepath.Base(strings.TrimSuffix(repo.RelativePath(), ".git"))+"/")
|
||||
}
|
||||
cmd.AddOptionFormat("--format=%s", format)
|
||||
cmd.AddDynamicArguments(commitID)
|
||||
|
||||
var stderr strings.Builder
|
||||
err := cmd.Run(ctx, &gitcmd.RunOpts{
|
||||
Dir: repoPath(repo),
|
||||
Stdout: target,
|
||||
Stderr: &stderr,
|
||||
})
|
||||
if err != nil {
|
||||
return gitcmd.ConcatenateError(err, stderr.String())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// CreateBundle create bundle content to the target path
|
||||
func CreateBundle(ctx context.Context, repo Repository, commit string, out io.Writer) error {
|
||||
tmp, cleanup, err := setting.AppDataTempDir("git-repo-content").MkdirTempRandom("gitea-bundle")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer cleanup()
|
||||
|
||||
env := append(os.Environ(), "GIT_OBJECT_DIRECTORY="+filepath.Join(repoPath(repo), "objects"))
|
||||
_, _, err = gitcmd.NewCommand("init", "--bare").RunStdString(ctx, &gitcmd.RunOpts{Dir: tmp, Env: env})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, _, err = gitcmd.NewCommand("reset", "--soft").AddDynamicArguments(commit).RunStdString(ctx, &gitcmd.RunOpts{Dir: tmp, Env: env})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, _, err = gitcmd.NewCommand("branch", "-m", "bundle").RunStdString(ctx, &gitcmd.RunOpts{Dir: tmp, Env: env})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
tmpFile := filepath.Join(tmp, "bundle")
|
||||
_, _, err = gitcmd.NewCommand("bundle", "create").AddDynamicArguments(tmpFile, "bundle", "HEAD").RunStdString(ctx, &gitcmd.RunOpts{Dir: tmp, Env: env})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fi, err := os.Open(tmpFile)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer fi.Close()
|
||||
|
||||
_, err = io.Copy(out, fi)
|
||||
return err
|
||||
}
|
@@ -7,13 +7,13 @@ import (
|
||||
"errors"
|
||||
"net/http"
|
||||
|
||||
"code.gitea.io/gitea/modules/git"
|
||||
repo_model "code.gitea.io/gitea/models/repo"
|
||||
"code.gitea.io/gitea/services/context"
|
||||
archiver_service "code.gitea.io/gitea/services/repository/archiver"
|
||||
)
|
||||
|
||||
func serveRepoArchive(ctx *context.APIContext, reqFileName string) {
|
||||
aReq, err := archiver_service.NewRequest(ctx.Repo.Repository.ID, ctx.Repo.GitRepo, reqFileName)
|
||||
aReq, err := archiver_service.NewRequest(ctx.Repo.Repository, ctx.Repo.GitRepo, reqFileName)
|
||||
if err != nil {
|
||||
if errors.Is(err, archiver_service.ErrUnknownArchiveFormat{}) {
|
||||
ctx.APIError(http.StatusBadRequest, err)
|
||||
@@ -24,18 +24,18 @@ func serveRepoArchive(ctx *context.APIContext, reqFileName string) {
|
||||
}
|
||||
return
|
||||
}
|
||||
archiver_service.ServeRepoArchive(ctx.Base, ctx.Repo.Repository, ctx.Repo.GitRepo, aReq)
|
||||
archiver_service.ServeRepoArchive(ctx.Base, aReq)
|
||||
}
|
||||
|
||||
func DownloadArchive(ctx *context.APIContext) {
|
||||
var tp git.ArchiveType
|
||||
var tp repo_model.ArchiveType
|
||||
switch ballType := ctx.PathParam("ball_type"); ballType {
|
||||
case "tarball":
|
||||
tp = git.ArchiveTarGz
|
||||
tp = repo_model.ArchiveTarGz
|
||||
case "zipball":
|
||||
tp = git.ArchiveZip
|
||||
tp = repo_model.ArchiveZip
|
||||
case "bundle":
|
||||
tp = git.ArchiveBundle
|
||||
tp = repo_model.ArchiveBundle
|
||||
default:
|
||||
ctx.APIError(http.StatusBadRequest, "Unknown archive type: "+ballType)
|
||||
return
|
||||
|
@@ -364,7 +364,7 @@ func RedirectDownload(ctx *context.Context) {
|
||||
|
||||
// Download an archive of a repository
|
||||
func Download(ctx *context.Context) {
|
||||
aReq, err := archiver_service.NewRequest(ctx.Repo.Repository.ID, ctx.Repo.GitRepo, ctx.PathParam("*"))
|
||||
aReq, err := archiver_service.NewRequest(ctx.Repo.Repository, ctx.Repo.GitRepo, ctx.PathParam("*"))
|
||||
if err != nil {
|
||||
if errors.Is(err, archiver_service.ErrUnknownArchiveFormat{}) {
|
||||
ctx.HTTPError(http.StatusBadRequest, err.Error())
|
||||
@@ -375,7 +375,7 @@ func Download(ctx *context.Context) {
|
||||
}
|
||||
return
|
||||
}
|
||||
archiver_service.ServeRepoArchive(ctx.Base, ctx.Repo.Repository, ctx.Repo.GitRepo, aReq)
|
||||
archiver_service.ServeRepoArchive(ctx.Base, aReq)
|
||||
}
|
||||
|
||||
// InitiateDownload will enqueue an archival request, as needed. It may submit
|
||||
@@ -388,7 +388,7 @@ func InitiateDownload(ctx *context.Context) {
|
||||
})
|
||||
return
|
||||
}
|
||||
aReq, err := archiver_service.NewRequest(ctx.Repo.Repository.ID, ctx.Repo.GitRepo, ctx.PathParam("*"))
|
||||
aReq, err := archiver_service.NewRequest(ctx.Repo.Repository, ctx.Repo.GitRepo, ctx.PathParam("*"))
|
||||
if err != nil {
|
||||
ctx.HTTPError(http.StatusBadRequest, "invalid archive request")
|
||||
return
|
||||
@@ -398,7 +398,7 @@ func InitiateDownload(ctx *context.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
archiver, err := repo_model.GetRepoArchiver(ctx, aReq.RepoID, aReq.Type, aReq.CommitID)
|
||||
archiver, err := repo_model.GetRepoArchiver(ctx, aReq.Repo.ID, aReq.Type, aReq.CommitID)
|
||||
if err != nil {
|
||||
ctx.ServerError("archiver_service.StartArchive", err)
|
||||
return
|
||||
|
@@ -33,8 +33,8 @@ import (
|
||||
// This is entirely opaque to external entities, though, and mostly used as a
|
||||
// handle elsewhere.
|
||||
type ArchiveRequest struct {
|
||||
RepoID int64
|
||||
Type git.ArchiveType
|
||||
Repo *repo_model.Repository
|
||||
Type repo_model.ArchiveType
|
||||
CommitID string
|
||||
|
||||
archiveRefShortName string // the ref short name to download the archive, for example: "master", "v1.0.0", "commit id"
|
||||
@@ -74,20 +74,20 @@ func (e RepoRefNotFoundError) Is(err error) bool {
|
||||
// NewRequest creates an archival request, based on the URI. The
|
||||
// resulting ArchiveRequest is suitable for being passed to Await()
|
||||
// if it's determined that the request still needs to be satisfied.
|
||||
func NewRequest(repoID int64, repo *git.Repository, archiveRefExt string) (*ArchiveRequest, error) {
|
||||
func NewRequest(repo *repo_model.Repository, gitRepo *git.Repository, archiveRefExt string) (*ArchiveRequest, error) {
|
||||
// here the archiveRefShortName is not a clear ref, it could be a tag, branch or commit id
|
||||
archiveRefShortName, archiveType := git.SplitArchiveNameType(archiveRefExt)
|
||||
if archiveType == git.ArchiveUnknown {
|
||||
archiveRefShortName, archiveType := repo_model.SplitArchiveNameType(archiveRefExt)
|
||||
if archiveType == repo_model.ArchiveUnknown {
|
||||
return nil, ErrUnknownArchiveFormat{archiveRefExt}
|
||||
}
|
||||
|
||||
// Get corresponding commit.
|
||||
commitID, err := repo.ConvertToGitID(archiveRefShortName)
|
||||
commitID, err := gitRepo.ConvertToGitID(archiveRefShortName)
|
||||
if err != nil {
|
||||
return nil, RepoRefNotFoundError{RefShortName: archiveRefShortName}
|
||||
}
|
||||
|
||||
r := &ArchiveRequest{RepoID: repoID, archiveRefShortName: archiveRefShortName, Type: archiveType}
|
||||
r := &ArchiveRequest{Repo: repo, archiveRefShortName: archiveRefShortName, Type: archiveType}
|
||||
r.CommitID = commitID.String()
|
||||
return r, nil
|
||||
}
|
||||
@@ -105,7 +105,7 @@ func (aReq *ArchiveRequest) GetArchiveName() string {
|
||||
// context is cancelled/times out a started archiver will still continue to run
|
||||
// in the background.
|
||||
func (aReq *ArchiveRequest) Await(ctx context.Context) (*repo_model.RepoArchiver, error) {
|
||||
archiver, err := repo_model.GetRepoArchiver(ctx, aReq.RepoID, aReq.Type, aReq.CommitID)
|
||||
archiver, err := repo_model.GetRepoArchiver(ctx, aReq.Repo.ID, aReq.Type, aReq.CommitID)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("models.GetRepoArchiver: %w", err)
|
||||
}
|
||||
@@ -130,7 +130,7 @@ func (aReq *ArchiveRequest) Await(ctx context.Context) (*repo_model.RepoArchiver
|
||||
case <-ctx.Done():
|
||||
return nil, ctx.Err()
|
||||
case <-poll.C:
|
||||
archiver, err = repo_model.GetRepoArchiver(ctx, aReq.RepoID, aReq.Type, aReq.CommitID)
|
||||
archiver, err = repo_model.GetRepoArchiver(ctx, aReq.Repo.ID, aReq.Type, aReq.CommitID)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("repo_model.GetRepoArchiver: %w", err)
|
||||
}
|
||||
@@ -143,17 +143,19 @@ func (aReq *ArchiveRequest) Await(ctx context.Context) (*repo_model.RepoArchiver
|
||||
|
||||
// Stream satisfies the ArchiveRequest being passed in. Processing
|
||||
// will occur directly in this routine.
|
||||
func (aReq *ArchiveRequest) Stream(ctx context.Context, gitRepo *git.Repository, w io.Writer) error {
|
||||
if aReq.Type == git.ArchiveBundle {
|
||||
return gitRepo.CreateBundle(
|
||||
func (aReq *ArchiveRequest) Stream(ctx context.Context, w io.Writer) error {
|
||||
if aReq.Type == repo_model.ArchiveBundle {
|
||||
return gitrepo.CreateBundle(
|
||||
ctx,
|
||||
aReq.Repo,
|
||||
aReq.CommitID,
|
||||
w,
|
||||
)
|
||||
}
|
||||
return gitRepo.CreateArchive(
|
||||
return gitrepo.CreateArchive(
|
||||
ctx,
|
||||
aReq.Type,
|
||||
aReq.Repo,
|
||||
aReq.Type.String(),
|
||||
w,
|
||||
setting.Repository.PrefixArchiveFiles,
|
||||
aReq.CommitID,
|
||||
@@ -167,10 +169,10 @@ func (aReq *ArchiveRequest) Stream(ctx context.Context, gitRepo *git.Repository,
|
||||
// being returned for completion, as it may be different than the one they passed
|
||||
// in.
|
||||
func doArchive(ctx context.Context, r *ArchiveRequest) (*repo_model.RepoArchiver, error) {
|
||||
ctx, _, finished := process.GetManager().AddContext(ctx, fmt.Sprintf("ArchiveRequest[%d]: %s", r.RepoID, r.GetArchiveName()))
|
||||
ctx, _, finished := process.GetManager().AddContext(ctx, fmt.Sprintf("ArchiveRequest[%s]: %s", r.Repo.FullName(), r.GetArchiveName()))
|
||||
defer finished()
|
||||
|
||||
archiver, err := repo_model.GetRepoArchiver(ctx, r.RepoID, r.Type, r.CommitID)
|
||||
archiver, err := repo_model.GetRepoArchiver(ctx, r.Repo.ID, r.Type, r.CommitID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -183,7 +185,7 @@ func doArchive(ctx context.Context, r *ArchiveRequest) (*repo_model.RepoArchiver
|
||||
}
|
||||
} else {
|
||||
archiver = &repo_model.RepoArchiver{
|
||||
RepoID: r.RepoID,
|
||||
RepoID: r.Repo.ID,
|
||||
Type: r.Type,
|
||||
CommitID: r.CommitID,
|
||||
Status: repo_model.ArchiverGenerating,
|
||||
@@ -215,28 +217,18 @@ func doArchive(ctx context.Context, r *ArchiveRequest) (*repo_model.RepoArchiver
|
||||
_ = rd.Close()
|
||||
}()
|
||||
done := make(chan error, 1) // Ensure that there is some capacity which will ensure that the goroutine below can always finish
|
||||
repo, err := repo_model.GetRepositoryByID(ctx, archiver.RepoID)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("archiver.LoadRepo failed: %w", err)
|
||||
}
|
||||
|
||||
gitRepo, err := gitrepo.OpenRepository(ctx, repo)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer gitRepo.Close()
|
||||
|
||||
go func(done chan error, w *io.PipeWriter, archiveReq *ArchiveRequest, gitRepo *git.Repository) {
|
||||
go func(done chan error, w *io.PipeWriter, archiveReq *ArchiveRequest) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
done <- fmt.Errorf("%v", r)
|
||||
}
|
||||
}()
|
||||
|
||||
err := archiveReq.Stream(ctx, gitRepo, w)
|
||||
err := archiveReq.Stream(ctx, w)
|
||||
_ = w.CloseWithError(err)
|
||||
done <- err
|
||||
}(done, w, r, gitRepo)
|
||||
}(done, w, r)
|
||||
|
||||
// TODO: add lfs data to zip
|
||||
// TODO: add submodule data to zip
|
||||
@@ -347,20 +339,20 @@ func DeleteRepositoryArchives(ctx context.Context) error {
|
||||
return storage.Clean(storage.RepoArchives)
|
||||
}
|
||||
|
||||
func ServeRepoArchive(ctx *gitea_context.Base, repo *repo_model.Repository, gitRepo *git.Repository, archiveReq *ArchiveRequest) {
|
||||
func ServeRepoArchive(ctx *gitea_context.Base, archiveReq *ArchiveRequest) {
|
||||
// Add nix format link header so tarballs lock correctly:
|
||||
// https://github.com/nixos/nix/blob/56763ff918eb308db23080e560ed2ea3e00c80a7/doc/manual/src/protocols/tarball-fetcher.md
|
||||
ctx.Resp.Header().Add("Link", fmt.Sprintf(`<%s/archive/%s.%s?rev=%s>; rel="immutable"`,
|
||||
repo.APIURL(),
|
||||
archiveReq.Repo.APIURL(),
|
||||
archiveReq.CommitID,
|
||||
archiveReq.Type.String(),
|
||||
archiveReq.CommitID,
|
||||
))
|
||||
downloadName := repo.Name + "-" + archiveReq.GetArchiveName()
|
||||
downloadName := archiveReq.Repo.Name + "-" + archiveReq.GetArchiveName()
|
||||
|
||||
if setting.Repository.StreamArchives {
|
||||
httplib.ServeSetHeaders(ctx.Resp, &httplib.ServeHeaderOptions{Filename: downloadName})
|
||||
if err := archiveReq.Stream(ctx, gitRepo, ctx.Resp); err != nil && !ctx.Written() {
|
||||
if err := archiveReq.Stream(ctx, ctx.Resp); err != nil && !ctx.Written() {
|
||||
log.Error("Archive %v streaming failed: %v", archiveReq, err)
|
||||
ctx.HTTPError(http.StatusInternalServerError)
|
||||
}
|
||||
|
@@ -29,47 +29,47 @@ func TestArchive_Basic(t *testing.T) {
|
||||
contexttest.LoadGitRepo(t, ctx)
|
||||
defer ctx.Repo.GitRepo.Close()
|
||||
|
||||
bogusReq, err := NewRequest(ctx.Repo.Repository.ID, ctx.Repo.GitRepo, firstCommit+".zip")
|
||||
bogusReq, err := NewRequest(ctx.Repo.Repository, ctx.Repo.GitRepo, firstCommit+".zip")
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, bogusReq)
|
||||
assert.Equal(t, firstCommit+".zip", bogusReq.GetArchiveName())
|
||||
|
||||
// Check a series of bogus requests.
|
||||
// Step 1, valid commit with a bad extension.
|
||||
bogusReq, err = NewRequest(ctx.Repo.Repository.ID, ctx.Repo.GitRepo, firstCommit+".unknown")
|
||||
bogusReq, err = NewRequest(ctx.Repo.Repository, ctx.Repo.GitRepo, firstCommit+".unknown")
|
||||
assert.Error(t, err)
|
||||
assert.Nil(t, bogusReq)
|
||||
|
||||
// Step 2, missing commit.
|
||||
bogusReq, err = NewRequest(ctx.Repo.Repository.ID, ctx.Repo.GitRepo, "dbffff.zip")
|
||||
bogusReq, err = NewRequest(ctx.Repo.Repository, ctx.Repo.GitRepo, "dbffff.zip")
|
||||
assert.Error(t, err)
|
||||
assert.Nil(t, bogusReq)
|
||||
|
||||
// Step 3, doesn't look like branch/tag/commit.
|
||||
bogusReq, err = NewRequest(ctx.Repo.Repository.ID, ctx.Repo.GitRepo, "db.zip")
|
||||
bogusReq, err = NewRequest(ctx.Repo.Repository, ctx.Repo.GitRepo, "db.zip")
|
||||
assert.Error(t, err)
|
||||
assert.Nil(t, bogusReq)
|
||||
|
||||
bogusReq, err = NewRequest(ctx.Repo.Repository.ID, ctx.Repo.GitRepo, "master.zip")
|
||||
bogusReq, err = NewRequest(ctx.Repo.Repository, ctx.Repo.GitRepo, "master.zip")
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, bogusReq)
|
||||
assert.Equal(t, "master.zip", bogusReq.GetArchiveName())
|
||||
|
||||
bogusReq, err = NewRequest(ctx.Repo.Repository.ID, ctx.Repo.GitRepo, "test/archive.zip")
|
||||
bogusReq, err = NewRequest(ctx.Repo.Repository, ctx.Repo.GitRepo, "test/archive.zip")
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, bogusReq)
|
||||
assert.Equal(t, "test-archive.zip", bogusReq.GetArchiveName())
|
||||
|
||||
// Now two valid requests, firstCommit with valid extensions.
|
||||
zipReq, err := NewRequest(ctx.Repo.Repository.ID, ctx.Repo.GitRepo, firstCommit+".zip")
|
||||
zipReq, err := NewRequest(ctx.Repo.Repository, ctx.Repo.GitRepo, firstCommit+".zip")
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, zipReq)
|
||||
|
||||
tgzReq, err := NewRequest(ctx.Repo.Repository.ID, ctx.Repo.GitRepo, firstCommit+".tar.gz")
|
||||
tgzReq, err := NewRequest(ctx.Repo.Repository, ctx.Repo.GitRepo, firstCommit+".tar.gz")
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, tgzReq)
|
||||
|
||||
secondReq, err := NewRequest(ctx.Repo.Repository.ID, ctx.Repo.GitRepo, secondCommit+".bundle")
|
||||
secondReq, err := NewRequest(ctx.Repo.Repository, ctx.Repo.GitRepo, secondCommit+".bundle")
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, secondReq)
|
||||
|
||||
@@ -89,7 +89,7 @@ func TestArchive_Basic(t *testing.T) {
|
||||
// Sleep two seconds to make sure the queue doesn't change.
|
||||
time.Sleep(2 * time.Second)
|
||||
|
||||
zipReq2, err := NewRequest(ctx.Repo.Repository.ID, ctx.Repo.GitRepo, firstCommit+".zip")
|
||||
zipReq2, err := NewRequest(ctx.Repo.Repository, ctx.Repo.GitRepo, firstCommit+".zip")
|
||||
assert.NoError(t, err)
|
||||
// This zipReq should match what's sitting in the queue, as we haven't
|
||||
// let it release yet. From the consumer's point of view, this looks like
|
||||
@@ -104,12 +104,12 @@ func TestArchive_Basic(t *testing.T) {
|
||||
// Now we'll submit a request and TimedWaitForCompletion twice, before and
|
||||
// after we release it. We should trigger both the timeout and non-timeout
|
||||
// cases.
|
||||
timedReq, err := NewRequest(ctx.Repo.Repository.ID, ctx.Repo.GitRepo, secondCommit+".tar.gz")
|
||||
timedReq, err := NewRequest(ctx.Repo.Repository, ctx.Repo.GitRepo, secondCommit+".tar.gz")
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, timedReq)
|
||||
doArchive(t.Context(), timedReq)
|
||||
|
||||
zipReq2, err = NewRequest(ctx.Repo.Repository.ID, ctx.Repo.GitRepo, firstCommit+".zip")
|
||||
zipReq2, err = NewRequest(ctx.Repo.Repository, ctx.Repo.GitRepo, firstCommit+".zip")
|
||||
assert.NoError(t, err)
|
||||
// Now, we're guaranteed to have released the original zipReq from the queue.
|
||||
// Ensure that we don't get handed back the released entry somehow, but they
|
||||
|
Reference in New Issue
Block a user