mirror of
https://github.com/go-gitea/gitea.git
synced 2026-08-16 00:35:10 +00:00
fix(archiver): use serializable repo-archive queue payload (#38273)
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 <!-- Before submitting: - Target the `main` branch; release branches are for backports only. - Use a Conventional Commits title, e.g. `fix(repo): handle empty branch names`. - Read the contributing guidelines: https://github.com/go-gitea/gitea/blob/main/CONTRIBUTING.md - Documentation changes go to https://gitea.com/gitea/docs Describe your change below and link any issue it fixes. --> --------- Co-authored-by: bircni <bircni@icloud.com>
This commit is contained in:
@@ -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"
|
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
|
// NewRequest creates an archival request, based on the URI. The
|
||||||
// resulting ArchiveRequest is suitable for being passed to Await()
|
// resulting ArchiveRequest is suitable for being passed to Await()
|
||||||
// if it's determined that the request still needs to be satisfied.
|
// 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
|
return archiver, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
var archiverQueue *queue.WorkerPoolQueue[*ArchiveRequest]
|
var archiverQueue *queue.WorkerPoolQueue[*archiveQueueItem]
|
||||||
|
|
||||||
// Init initializes archiver
|
// Init initializes archiver
|
||||||
func Init(ctx context.Context) error {
|
func Init(ctx context.Context) error {
|
||||||
handler := func(items ...*ArchiveRequest) []*ArchiveRequest {
|
handler := func(items ...*archiveQueueItem) []*archiveQueueItem {
|
||||||
for _, archiveReq := range items {
|
for _, item := range items {
|
||||||
log.Trace("ArchiverData Process: %#v", archiveReq)
|
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 {
|
if archiver, err := doArchive(ctx, archiveReq); err != nil {
|
||||||
log.Error("Archive %v failed: %v", archiveReq, err)
|
log.Error("Archive %v failed: %v", archiveReq, err)
|
||||||
} else {
|
} else {
|
||||||
@@ -254,14 +291,15 @@ func Init(ctx context.Context) error {
|
|||||||
|
|
||||||
// StartArchive push the archive request to the queue
|
// StartArchive push the archive request to the queue
|
||||||
func StartArchive(request *ArchiveRequest) error {
|
func StartArchive(request *ArchiveRequest) error {
|
||||||
has, err := archiverQueue.Has(request)
|
item := request.toQueueItem()
|
||||||
|
has, err := archiverQueue.Has(item)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if has {
|
if has {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
return archiverQueue.Push(request)
|
return archiverQueue.Push(item)
|
||||||
}
|
}
|
||||||
|
|
||||||
func deleteOldRepoArchiver(ctx context.Context, archiver *repo_model.RepoArchiver) error {
|
func deleteOldRepoArchiver(ctx context.Context, archiver *repo_model.RepoArchiver) error {
|
||||||
|
|||||||
@@ -7,7 +7,9 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
repo_model "gitea.dev/models/repo"
|
||||||
"gitea.dev/models/unittest"
|
"gitea.dev/models/unittest"
|
||||||
|
"gitea.dev/modules/json"
|
||||||
"gitea.dev/modules/util"
|
"gitea.dev/modules/util"
|
||||||
"gitea.dev/services/contexttest"
|
"gitea.dev/services/contexttest"
|
||||||
|
|
||||||
@@ -21,6 +23,22 @@ func TestMain(m *testing.M) {
|
|||||||
unittest.MainTest(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) {
|
func TestArchive_Basic(t *testing.T) {
|
||||||
assert.NoError(t, unittest.PrepareTestDatabase())
|
assert.NoError(t, unittest.PrepareTestDatabase())
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user