Files
gitea/services/migrations/github_issue_recovery.go
T
9cd06bf57f fix(migrations): wait out GitHub rate limits and tolerate failed patch downloads (#40012)
Large GitHub migrations failed once the API budget ran out. A rejected
request never updated the downloader's rate state, so go-github refused
every retry locally.

- Record the rate limit from error responses too, so the next request
waits for the reset
- Retry repository-wide comment pages like the other downloader calls
- Stop `waitAndPickClient` spinning when the rate refresh fails
- Skip reaction requests when the reaction rollup reports none
- Log a failed patch download instead of failing the migration

---------

Co-authored-by: bircni <bircni@icloud.com>
Co-authored-by: silverwind <me@silverwind.io>
Co-authored-by: Giteabot <teabot@gitea.io>
2026-10-31 17:31:51 +01:00

71 lines
1.8 KiB
Go

// Copyright 2026 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package migrations
import (
"context"
"fmt"
base "gitea.dev/modules/migration"
)
type githubIssueRecoveryUploader struct {
base.Uploader
downloader *GithubDownloaderV3
issues map[int64]struct{}
}
func wrapGithubIssueRecoveryUploader(uploader base.Uploader, downloader base.Downloader) base.Uploader {
if retry, ok := downloader.(*base.RetryDownloader); ok {
downloader = retry.Downloader
}
github, ok := downloader.(*GithubDownloaderV3)
if !ok {
return uploader
}
return &githubIssueRecoveryUploader{
Uploader: uploader,
downloader: github,
issues: make(map[int64]struct{}),
}
}
func (g *githubIssueRecoveryUploader) CreateIssues(ctx context.Context, issues ...*base.Issue) error {
if err := g.Uploader.CreateIssues(ctx, issues...); err != nil {
return err
}
for _, issue := range issues {
g.issues[issue.Number] = struct{}{}
}
return nil
}
func (g *githubIssueRecoveryUploader) CreatePullRequests(ctx context.Context, prs ...*base.PullRequest) error {
if err := g.Uploader.CreatePullRequests(ctx, prs...); err != nil {
return err
}
for _, pr := range prs {
g.issues[pr.Number] = struct{}{}
}
return nil
}
func (g *githubIssueRecoveryUploader) CreateComments(ctx context.Context, comments ...*base.Comment) error {
for _, comment := range comments {
if _, ok := g.issues[comment.IssueIndex]; ok {
continue
}
issue, err := g.downloader.GetIssue(ctx, comment.IssueIndex)
if err != nil {
return fmt.Errorf("recover missing IssueIndex %d: %w", comment.IssueIndex, err)
}
if err := g.Uploader.CreateIssues(ctx, issue); err != nil {
return err
}
g.issues[issue.Number] = struct{}{}
}
return g.Uploader.CreateComments(ctx, comments...)
}