@@ -49,10 +49,11 @@ type FetchResult struct {
4949
5050// FetchOptions contains options for fetch operation
5151type FetchOptions struct {
52- Force bool // Force re-download all repos
53- Region string // Region for mirror selection (default|china)
54- Parallel int // Number of parallel workers
55- Retry int // Number of retry attempts
52+ Force bool // Force re-download all repos
53+ Region string // Region for mirror selection (default|china)
54+ Parallel int // Number of parallel workers
55+ Retry int // Number of retry attempts
56+ BestEffort bool // Allow a partial result when at least one repository succeeds
5657}
5758
5859// Fetcher handles repository metadata fetching
@@ -62,6 +63,7 @@ type Fetcher struct {
6263 force bool
6364 maxWorkers int
6465 retries int
66+ bestEffort bool
6567}
6668
6769// NewFetcher creates a new repository fetcher
@@ -93,6 +95,7 @@ func NewFetcher(opts FetchOptions) *Fetcher {
9395 force : opts .Force ,
9496 maxWorkers : opts .Parallel ,
9597 retries : opts .Retry ,
98+ bestEffort : opts .BestEffort ,
9699 }
97100}
98101
@@ -116,48 +119,49 @@ func (f *Fetcher) FetchAll(ctx context.Context) error {
116119 results := f .fetchConcurrent (ctx , repos )
117120
118121 // Process results
119- var updated , skipped , failed int
120- var errors []string
122+ report := RunReport {Operation : "fetch" , Total : len (repos )}
121123
122124 for _ , result := range results {
123125 if result .Error != nil {
124- failed ++
125- errors = append (errors , fmt .Sprintf ("%s: %v" , result .Repository .ID , result .Error ))
126+ report .AddFailure (result .Repository .ID , result .Error )
126127 continue
127128 }
128129
129130 if result .Updated {
130131 if err := f .saveMetadata (ctx , result ); err != nil {
131- failed ++
132- errors = append (errors , fmt .Sprintf ("%s: save failed: %v" , result .Repository .ID , err ))
132+ report .AddFailure (result .Repository .ID , fmt .Errorf ("save metadata: %w" , err ))
133133 } else {
134- updated ++
134+ report . Succeeded ++
135135 logrus .Debugf ("Updated %s (%s)" , result .Repository .ID , formatBytes (int64 (len (result .Data ))))
136136 }
137137 } else {
138- skipped ++
138+ report . Skipped ++
139139 logrus .Debugf ("Skipped %s (not modified)" , result .Repository .ID )
140140 }
141141 }
142142
143- // Update status timestamp
144- if updated > 0 {
145- if err := f .updateFetchTime (ctx ); err != nil {
146- logrus .Warnf ("Failed to update fetch time: %v" , err )
147- }
148- }
149-
150143 // Log summary
151- logrus .Infof ("Fetch complete: %d updated, %d skipped, %d failed" , updated , skipped , failed )
144+ logrus .Infof ("Fetch complete: %d updated, %d skipped, %d failed" ,
145+ report .Succeeded , report .Skipped , report .Failed ())
152146
153147 // Report errors
154- for _ , errMsg := range errors {
155- logrus .Error ( errMsg )
148+ for _ , failure := range report . Failures {
149+ logrus .Errorf ( "%s: %v" , failure . Item , failure . Err )
156150 }
157151
158- // Fail if all repositories failed
159- if failed == len (repos ) {
160- return fmt .Errorf ("all repositories failed to fetch" )
152+ if err := report .Err (f .bestEffort ); err != nil {
153+ return err
154+ }
155+ if report .Failed () > 0 {
156+ logrus .Warnf ("best-effort fetch accepted %d failed repositories" , report .Failed ())
157+ }
158+
159+ // Advance the accepted-run timestamp only after strict/best-effort policy
160+ // has approved the result.
161+ if report .Succeeded > 0 {
162+ if err := f .updateFetchTime (ctx ); err != nil {
163+ logrus .Warnf ("Failed to update fetch time: %v" , err )
164+ }
161165 }
162166
163167 return nil
@@ -195,8 +199,7 @@ func (f *Fetcher) loadRepositories(ctx context.Context) ([]*RepoMetadata, error)
195199 & repo .CachedSize ,
196200 )
197201 if err != nil {
198- logrus .Warnf ("Failed to scan repository: %v" , err )
199- continue
202+ return nil , fmt .Errorf ("scan repository metadata: %w" , err )
200203 }
201204 repos = append (repos , repo )
202205 }
0 commit comments