Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion modules/gitrepo/archive.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import (
)

// 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 {
func CreateArchive(ctx context.Context, repo Repository, format string, target io.Writer, usePrefix bool, commitID string, paths []string) error {
if format == "unknown" {
return fmt.Errorf("unknown format: %v", format)
}
Expand All @@ -27,6 +27,9 @@ func CreateArchive(ctx context.Context, repo Repository, format string, target i
}
cmd.AddOptionFormat("--format=%s", format)
cmd.AddDynamicArguments(commitID)
if len(paths) > 0 {
cmd.AddDynamicArguments(paths...)
}

var stderr strings.Builder
if err := RunCmd(ctx, repo, cmd.WithStdout(target).WithStderr(&stderr)); err != nil {
Expand Down
11 changes: 8 additions & 3 deletions routers/api/v1/repo/download.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,15 @@ package repo
import (
"errors"
"net/http"
"strings"

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, ctx.Repo.GitRepo, reqFileName)
func serveRepoArchive(ctx *context.APIContext, reqFileName string, path []string) {
aReq, err := archiver_service.NewRequest(ctx.Repo.Repository, ctx.Repo.GitRepo, reqFileName, path)
if err != nil {
if errors.Is(err, archiver_service.ErrUnknownArchiveFormat{}) {
ctx.APIError(http.StatusBadRequest, err)
Expand All @@ -40,5 +41,9 @@ func DownloadArchive(ctx *context.APIContext) {
ctx.APIError(http.StatusBadRequest, "Unknown archive type: "+ballType)
return
}
serveRepoArchive(ctx, ctx.PathParam("*")+"."+tp.String())
var paths []string
if ctx.FormString("paths") != "" {
paths = strings.Split(ctx.FormString("paths"), ",")
}
serveRepoArchive(ctx, ctx.PathParam("*")+"."+tp.String(), paths)
}
13 changes: 11 additions & 2 deletions routers/api/v1/repo/file.go
Original file line number Diff line number Diff line change
Expand Up @@ -273,13 +273,22 @@ func GetArchive(ctx *context.APIContext) {
// description: the git reference for download with attached archive format (e.g. master.zip)
// type: string
// required: true
// - name: paths
// in: query
// type: array
// items:
// type: string
// description: subpath of the repository to download
// responses:
// 200:
// description: success
// "404":
// "$ref": "#/responses/notFound"

serveRepoArchive(ctx, ctx.PathParam("*"))
var paths []string
if ctx.FormString("paths") != "" {
paths = strings.Split(ctx.FormString("paths"), ",")
}
serveRepoArchive(ctx, ctx.PathParam("*"), paths)
Comment thread
TheFox0x7 marked this conversation as resolved.
Outdated
}

// GetEditorconfig get editor config of a repository
Expand Down
4 changes: 2 additions & 2 deletions routers/web/repo/repo.go
Original file line number Diff line number Diff line change
Expand Up @@ -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, ctx.Repo.GitRepo, ctx.PathParam("*"))
aReq, err := archiver_service.NewRequest(ctx.Repo.Repository, ctx.Repo.GitRepo, ctx.PathParam("*"), ctx.FormStrings("paths"))
if err != nil {
if errors.Is(err, archiver_service.ErrUnknownArchiveFormat{}) {
ctx.HTTPError(http.StatusBadRequest, err.Error())
Expand All @@ -388,7 +388,7 @@ func InitiateDownload(ctx *context.Context) {
})
return
}
aReq, err := archiver_service.NewRequest(ctx.Repo.Repository, ctx.Repo.GitRepo, ctx.PathParam("*"))
aReq, err := archiver_service.NewRequest(ctx.Repo.Repository, ctx.Repo.GitRepo, ctx.PathParam("*"), ctx.FormStrings("paths"))
if err != nil {
ctx.HTTPError(http.StatusBadRequest, "invalid archive request")
return
Expand Down
6 changes: 4 additions & 2 deletions services/repository/archiver/archiver.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ type ArchiveRequest struct {
Repo *repo_model.Repository
Type repo_model.ArchiveType
CommitID string
Paths []string

archiveRefShortName string // the ref short name to download the archive, for example: "master", "v1.0.0", "commit id"
}
Expand Down Expand Up @@ -74,7 +75,7 @@ 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(repo *repo_model.Repository, gitRepo *git.Repository, archiveRefExt string) (*ArchiveRequest, error) {
func NewRequest(repo *repo_model.Repository, gitRepo *git.Repository, archiveRefExt string, paths []string) (*ArchiveRequest, error) {
// here the archiveRefShortName is not a clear ref, it could be a tag, branch or commit id
archiveRefShortName, archiveType := repo_model.SplitArchiveNameType(archiveRefExt)
if archiveType == repo_model.ArchiveUnknown {
Expand All @@ -87,7 +88,7 @@ func NewRequest(repo *repo_model.Repository, gitRepo *git.Repository, archiveRef
return nil, RepoRefNotFoundError{RefShortName: archiveRefShortName}
}

r := &ArchiveRequest{Repo: repo, archiveRefShortName: archiveRefShortName, Type: archiveType}
r := &ArchiveRequest{Repo: repo, archiveRefShortName: archiveRefShortName, Type: archiveType, Paths: paths}
r.CommitID = commitID.String()
return r, nil
}
Expand Down Expand Up @@ -159,6 +160,7 @@ func (aReq *ArchiveRequest) Stream(ctx context.Context, w io.Writer) error {
w,
setting.Repository.PrefixArchiveFiles,
aReq.CommitID,
aReq.Paths,
)
}

Expand Down
24 changes: 12 additions & 12 deletions services/repository/archiver/archiver_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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, ctx.Repo.GitRepo, firstCommit+".zip")
bogusReq, err := NewRequest(ctx.Repo.Repository, ctx.Repo.GitRepo, firstCommit+".zip", nil)
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, ctx.Repo.GitRepo, firstCommit+".unknown")
bogusReq, err = NewRequest(ctx.Repo.Repository, ctx.Repo.GitRepo, firstCommit+".unknown", nil)
assert.Error(t, err)
assert.Nil(t, bogusReq)

// Step 2, missing commit.
bogusReq, err = NewRequest(ctx.Repo.Repository, ctx.Repo.GitRepo, "dbffff.zip")
bogusReq, err = NewRequest(ctx.Repo.Repository, ctx.Repo.GitRepo, "dbffff.zip", nil)
assert.Error(t, err)
assert.Nil(t, bogusReq)

// Step 3, doesn't look like branch/tag/commit.
bogusReq, err = NewRequest(ctx.Repo.Repository, ctx.Repo.GitRepo, "db.zip")
bogusReq, err = NewRequest(ctx.Repo.Repository, ctx.Repo.GitRepo, "db.zip", nil)
assert.Error(t, err)
assert.Nil(t, bogusReq)

bogusReq, err = NewRequest(ctx.Repo.Repository, ctx.Repo.GitRepo, "master.zip")
bogusReq, err = NewRequest(ctx.Repo.Repository, ctx.Repo.GitRepo, "master.zip", nil)
assert.NoError(t, err)
assert.NotNil(t, bogusReq)
assert.Equal(t, "master.zip", bogusReq.GetArchiveName())

bogusReq, err = NewRequest(ctx.Repo.Repository, ctx.Repo.GitRepo, "test/archive.zip")
bogusReq, err = NewRequest(ctx.Repo.Repository, ctx.Repo.GitRepo, "test/archive.zip", nil)
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, ctx.Repo.GitRepo, firstCommit+".zip")
zipReq, err := NewRequest(ctx.Repo.Repository, ctx.Repo.GitRepo, firstCommit+".zip", nil)
assert.NoError(t, err)
assert.NotNil(t, zipReq)

tgzReq, err := NewRequest(ctx.Repo.Repository, ctx.Repo.GitRepo, firstCommit+".tar.gz")
tgzReq, err := NewRequest(ctx.Repo.Repository, ctx.Repo.GitRepo, firstCommit+".tar.gz", nil)
assert.NoError(t, err)
assert.NotNil(t, tgzReq)

secondReq, err := NewRequest(ctx.Repo.Repository, ctx.Repo.GitRepo, secondCommit+".bundle")
secondReq, err := NewRequest(ctx.Repo.Repository, ctx.Repo.GitRepo, secondCommit+".bundle", nil)
assert.NoError(t, err)
assert.NotNil(t, secondReq)

Expand All @@ -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, ctx.Repo.GitRepo, firstCommit+".zip")
zipReq2, err := NewRequest(ctx.Repo.Repository, ctx.Repo.GitRepo, firstCommit+".zip", nil)
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
Expand All @@ -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, ctx.Repo.GitRepo, secondCommit+".tar.gz")
timedReq, err := NewRequest(ctx.Repo.Repository, ctx.Repo.GitRepo, secondCommit+".tar.gz", nil)
assert.NoError(t, err)
assert.NotNil(t, timedReq)
doArchive(t.Context(), timedReq)

zipReq2, err = NewRequest(ctx.Repo.Repository, ctx.Repo.GitRepo, firstCommit+".zip")
zipReq2, err = NewRequest(ctx.Repo.Repository, ctx.Repo.GitRepo, firstCommit+".zip", nil)
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
Expand Down
3 changes: 3 additions & 0 deletions templates/repo/view_content.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,9 @@
<a class="item" data-clipboard-text="{{.Repository.Link}}/src/commit/{{.CommitID}}/{{PathEscapeSegments .TreePath}}" data-clipboard-text-type="url">
{{svg "octicon-link" 16}}{{ctx.Locale.Tr "repo.file_copy_permalink"}}
</a>
<a class="item muted archive-link" href="{{$.RepoLink}}/archive/{{PathEscapeSegments $.RefFullName.ShortName}}.zip?paths={{PathEscapeSegments .TreePath}}" rel="nofollow">{{svg "octicon-file-zip"}} {{ctx.Locale.Tr "repo.download_zip"}}</a>
<a class="item muted archive-link" href="{{$.RepoLink}}/archive/{{PathEscapeSegments $.RefFullName.ShortName}}.tar.gz?paths={{PathEscapeSegments .TreePath}}" rel="nofollow">{{svg "octicon-file-zip"}} {{ctx.Locale.Tr "repo.download_tar"}}</a>
<a class="item muted archive-link" href="{{$.RepoLink}}/archive/{{PathEscapeSegments $.RefFullName.ShortName}}.bundle?paths={{PathEscapeSegments .TreePath}}" rel="nofollow">{{svg "octicon-package"}} {{ctx.Locale.Tr "repo.download_bundle"}}</a>
{{if and (.Permission.CanWrite ctx.Consts.RepoUnitTypeCode) (not .Repository.IsArchived) (not $isTreePathRoot)}}
<div class="divider"></div>
<a class="item tw-text-danger" href="{{.RepoLink}}/_delete/{{.BranchName | PathEscapeSegments}}/{{.TreePath | PathEscapeSegments}}">
Expand Down
9 changes: 9 additions & 0 deletions templates/swagger/v1_json.tmpl

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.