Skip to content

Commit 4fe4156

Browse files
Mic92lf-wxiaoguang
authored
Add an immutable tarball link to archive download headers for Nix (#31139)
This allows `nix flake metadata` and nix in general to lock a *branch* tarball link in a manner that causes it to fetch the correct commit even if the branch is updated with a newer version. Co-authored-by: Jade Lovelace <[email protected]> Co-authored-by: wxiaoguang <[email protected]>
1 parent 1e3c4d8 commit 4fe4156

File tree

3 files changed

+23
-0
lines changed

3 files changed

+23
-0
lines changed

routers/api/v1/repo/file.go

+6
Original file line numberDiff line numberDiff line change
@@ -319,6 +319,12 @@ func archiveDownload(ctx *context.APIContext) {
319319
func download(ctx *context.APIContext, archiveName string, archiver *repo_model.RepoArchiver) {
320320
downloadName := ctx.Repo.Repository.Name + "-" + archiveName
321321

322+
// Add nix format link header so tarballs lock correctly:
323+
// https://github.com/nixos/nix/blob/56763ff918eb308db23080e560ed2ea3e00c80a7/doc/manual/src/protocols/tarball-fetcher.md
324+
ctx.Resp.Header().Add("Link", fmt.Sprintf(`<%s/archive/%s.tar.gz?rev=%s>; rel="immutable"`,
325+
ctx.Repo.Repository.APIURL(),
326+
archiver.CommitID, archiver.CommitID))
327+
322328
rPath := archiver.RelativePath()
323329
if setting.RepoArchive.Storage.MinioConfig.ServeDirect {
324330
// If we have a signed url (S3, object storage), redirect to this directly.

routers/web/repo/repo.go

+6
Original file line numberDiff line numberDiff line change
@@ -484,6 +484,12 @@ func Download(ctx *context.Context) {
484484
func download(ctx *context.Context, archiveName string, archiver *repo_model.RepoArchiver) {
485485
downloadName := ctx.Repo.Repository.Name + "-" + archiveName
486486

487+
// Add nix format link header so tarballs lock correctly:
488+
// https://github.com/nixos/nix/blob/56763ff918eb308db23080e560ed2ea3e00c80a7/doc/manual/src/protocols/tarball-fetcher.md
489+
ctx.Resp.Header().Add("Link", fmt.Sprintf(`<%s/archive/%s.tar.gz?rev=%s>; rel="immutable"`,
490+
ctx.Repo.Repository.APIURL(),
491+
archiver.CommitID, archiver.CommitID))
492+
487493
rPath := archiver.RelativePath()
488494
if setting.RepoArchive.Storage.MinioConfig.ServeDirect {
489495
// If we have a signed url (S3, object storage), redirect to this directly.

tests/integration/api_repo_archive_test.go

+11
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import (
88
"io"
99
"net/http"
1010
"net/url"
11+
"regexp"
1112
"testing"
1213

1314
auth_model "code.gitea.io/gitea/models/auth"
@@ -39,6 +40,16 @@ func TestAPIDownloadArchive(t *testing.T) {
3940
assert.NoError(t, err)
4041
assert.Len(t, bs, 266)
4142

43+
// Must return a link to a commit ID as the "immutable" archive link
44+
linkHeaderRe := regexp.MustCompile(`^<(https?://.*/api/v1/repos/user2/repo1/archive/[a-f0-9]+\.tar\.gz.*)>; rel="immutable"$`)
45+
m := linkHeaderRe.FindStringSubmatch(resp.Header().Get("Link"))
46+
assert.NotEmpty(t, m[1])
47+
resp = MakeRequest(t, NewRequest(t, "GET", m[1]).AddTokenAuth(token), http.StatusOK)
48+
bs2, err := io.ReadAll(resp.Body)
49+
assert.NoError(t, err)
50+
// The locked URL should give the same bytes as the non-locked one
51+
assert.EqualValues(t, bs, bs2)
52+
4253
link, _ = url.Parse(fmt.Sprintf("/api/v1/repos/%s/%s/archive/master.bundle", user2.Name, repo.Name))
4354
resp = MakeRequest(t, NewRequest(t, "GET", link.String()).AddTokenAuth(token), http.StatusOK)
4455
bs, err = io.ReadAll(resp.Body)

0 commit comments

Comments
 (0)