From 1df8f916918ab2b027d94dfcd97617cfd9fe6ad1 Mon Sep 17 00:00:00 2001 From: Giteabot Date: Tue, 30 Jun 2026 10:17:29 -0700 Subject: [PATCH] fix(archiver): use serializable repo-archive queue payload (#38273) (#38283) Backport #38273 by @Vinod-OAI After upgrading from 1.25.x to 1.26.x, `repo-archive` workers can fail to unmarshal queued items: ``` Failed to unmarshal item from queue "repo-archive": json: unable to unmarshal into Go convert.Conversion within "/Repo/Units/0/Config": cannot derive concrete type for nil interface with finite type set ``` `ArchiveRequest` started embedding `*repo_model.Repository` in 1.26, which does not round-trip through the JSON queue. This change stores a minimal `archiveQueueItem` (`RepoID`, `Type`, `CommitID`, `Paths`) in `repo-archive` and loads the repository in the worker. `UnmarshalJSON` accepts legacy payloads that used `RepoID` or embedded `Repo.id`. Fixes #38272 Co-authored-by: Vinod-OAI Co-authored-by: bircni --- services/repository/archiver/archiver.go | 50 ++++++++++++++++--- services/repository/archiver/archiver_test.go | 18 +++++++ 2 files changed, 62 insertions(+), 6 deletions(-) diff --git a/services/repository/archiver/archiver.go b/services/repository/archiver/archiver.go index 2b78118556..b73d466731 100644 --- a/services/repository/archiver/archiver.go +++ b/services/repository/archiver/archiver.go @@ -42,6 +42,38 @@ type ArchiveRequest struct { archiveRefShortName string // the ref short name to download the archive, for example: "master", "v1.0.0", "commit id" } +type archiveQueueItem struct { + RepoID int64 `json:"RepoID"` + Type repo_model.ArchiveType `json:"Type"` + CommitID string `json:"CommitID"` + Paths []string `json:"Paths,omitempty"` + ArchiveRefShortName string `json:"ArchiveRefShortName,omitempty"` +} + +func (aReq *ArchiveRequest) toQueueItem() *archiveQueueItem { + return &archiveQueueItem{ + RepoID: aReq.Repo.ID, + Type: aReq.Type, + CommitID: aReq.CommitID, + Paths: aReq.Paths, + ArchiveRefShortName: aReq.archiveRefShortName, + } +} + +func (item *archiveQueueItem) toArchiveRequest(ctx context.Context) (*ArchiveRequest, error) { + repo, err := repo_model.GetRepositoryByID(ctx, item.RepoID) + if err != nil { + return nil, err + } + return &ArchiveRequest{ + Repo: repo, + Type: item.Type, + CommitID: item.CommitID, + Paths: item.Paths, + archiveRefShortName: item.ArchiveRefShortName, + }, nil +} + // 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. @@ -227,13 +259,18 @@ func doArchive(ctx context.Context, r *ArchiveRequest) (*repo_model.RepoArchiver return archiver, nil } -var archiverQueue *queue.WorkerPoolQueue[*ArchiveRequest] +var archiverQueue *queue.WorkerPoolQueue[*archiveQueueItem] // Init initializes archiver func Init(ctx context.Context) error { - handler := func(items ...*ArchiveRequest) []*ArchiveRequest { - for _, archiveReq := range items { - log.Trace("ArchiverData Process: %#v", archiveReq) + handler := func(items ...*archiveQueueItem) []*archiveQueueItem { + for _, item := range items { + log.Trace("ArchiverData Process: %#v", item) + archiveReq, err := item.toArchiveRequest(ctx) + if err != nil { + log.Error("Archive repo %d: %v", item.RepoID, err) + continue + } if archiver, err := doArchive(ctx, archiveReq); err != nil { log.Error("Archive %v failed: %v", archiveReq, err) } else { @@ -254,14 +291,15 @@ func Init(ctx context.Context) error { // StartArchive push the archive request to the queue func StartArchive(request *ArchiveRequest) error { - has, err := archiverQueue.Has(request) + item := request.toQueueItem() + has, err := archiverQueue.Has(item) if err != nil { return err } if has { return nil } - return archiverQueue.Push(request) + return archiverQueue.Push(item) } func deleteOldRepoArchiver(ctx context.Context, archiver *repo_model.RepoArchiver) error { diff --git a/services/repository/archiver/archiver_test.go b/services/repository/archiver/archiver_test.go index 16a4750a8b..8829ac1b79 100644 --- a/services/repository/archiver/archiver_test.go +++ b/services/repository/archiver/archiver_test.go @@ -7,7 +7,9 @@ import ( "testing" "time" + repo_model "gitea.dev/models/repo" "gitea.dev/models/unittest" + "gitea.dev/modules/json" "gitea.dev/modules/util" "gitea.dev/services/contexttest" @@ -21,6 +23,22 @@ func TestMain(m *testing.M) { unittest.MainTest(m) } +func TestArchiveQueueItemJSON(t *testing.T) { + orig := &archiveQueueItem{ + RepoID: 7, + Type: repo_model.ArchiveZip, + CommitID: "abc123", + Paths: []string{"agents"}, + ArchiveRefShortName: "main", + } + bs, err := json.Marshal(orig) + require.NoError(t, err) + + var decoded archiveQueueItem + require.NoError(t, json.Unmarshal(bs, &decoded)) + assert.Equal(t, *orig, decoded) +} + func TestArchive_Basic(t *testing.T) { assert.NoError(t, unittest.PrepareTestDatabase())