From 3c68399eb065bae6100c343025691cae7f15d9f5 Mon Sep 17 00:00:00 2001 From: JakobDev Date: Sun, 23 Feb 2025 09:23:25 +0000 Subject: [PATCH] feat(ui): show link to download directory (#4736) This adds links to download a directory as archive. This can be useful if you e.g. just want to download a assets directory instead of the full source tree. The logic already exists in the backend, so only the frontend had been changed. ![grafik](https://codeberg.org/attachments/bee69268-ed03-4a05-8505-3d11e977a82c) Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/4736 Reviewed-by: 0ko <0ko@noreply.codeberg.org> Co-authored-by: JakobDev Co-committed-by: JakobDev --- templates/repo/home.tmpl | 11 ++++++ tests/integration/repo_archive_test.go | 51 ++++++++++++++++++++++++++ 2 files changed, 62 insertions(+) diff --git a/templates/repo/home.tmpl b/templates/repo/home.tmpl index 7bf4ee4a8e..fdcf1b8193 100644 --- a/templates/repo/home.tmpl +++ b/templates/repo/home.tmpl @@ -144,6 +144,17 @@ {{svg "octicon-history" 16 "tw-mr-2"}}{{ctx.Locale.Tr "repo.file_history"}} + {{if not $.DisableDownloadSourceArchives}} + + {{end}} {{end}} diff --git a/tests/integration/repo_archive_test.go b/tests/integration/repo_archive_test.go index 75fe78eeed..277ead4a28 100644 --- a/tests/integration/repo_archive_test.go +++ b/tests/integration/repo_archive_test.go @@ -4,10 +4,17 @@ package integration import ( + "archive/tar" + "compress/gzip" + "fmt" "io" "net/http" + "net/url" "testing" + repo_model "code.gitea.io/gitea/models/repo" + "code.gitea.io/gitea/models/unittest" + user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/test" "code.gitea.io/gitea/routers" @@ -32,3 +39,47 @@ func TestRepoDownloadArchive(t *testing.T) { assert.Empty(t, resp.Header().Get("Content-Encoding")) assert.Len(t, bs, 320) } + +func TestRepoDownloadArchiveSubdir(t *testing.T) { + onGiteaRun(t, func(*testing.T, *url.URL) { + defer test.MockVariableValue(&setting.EnableGzip, true)() + defer test.MockVariableValue(&web.GzipMinSize, 10)() + defer test.MockVariableValue(&testWebRoutes, routers.NormalRoutes())() + + repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}) + user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}) + + // Create a subdirectory + err := createOrReplaceFileInBranch(user, repo, "subdir/test.txt", "master", "Test") + require.NoError(t, err) + + t.Run("Frontend", func(t *testing.T) { + resp := MakeRequest(t, NewRequestf(t, "GET", "/%s/src/branch/master/subdir", repo.FullName()), http.StatusOK) + + assert.Contains(t, resp.Body.String(), fmt.Sprintf("/%s/archive/master:subdir.zip", repo.FullName())) + assert.Contains(t, resp.Body.String(), fmt.Sprintf("/%s/archive/master:subdir.tar.gz", repo.FullName())) + }) + + t.Run("Backend", func(t *testing.T) { + resp := MakeRequest(t, NewRequestf(t, "GET", "/%s/archive/master:subdir.tar.gz", repo.FullName()), http.StatusOK) + + uncompressedStream, err := gzip.NewReader(resp.Body) + require.NoError(t, err) + + tarReader := tar.NewReader(uncompressedStream) + + header, err := tarReader.Next() + require.NoError(t, err) + assert.Equal(t, tar.TypeDir, int32(header.Typeflag)) + assert.Equal(t, fmt.Sprintf("%s/", repo.Name), header.Name) + + header, err = tarReader.Next() + require.NoError(t, err) + assert.Equal(t, tar.TypeReg, int32(header.Typeflag)) + assert.Equal(t, fmt.Sprintf("%s/test.txt", repo.Name), header.Name) + + _, err = tarReader.Next() + assert.Equal(t, io.EOF, err) + }) + }) +}