@@ -59,6 +59,7 @@ type pkgChecksum struct {
5959var (
6060 RepoCfg RepoConfig
6161 RepoCfgs []RepoConfig // Support for multiple repositories
62+ UserRepoCfgs []RepoConfig // Populated from UserRepo packageRepositories
6263 PkgChecksum []pkgChecksum
6364 GzHref string
6465 Architecture string
@@ -68,14 +69,32 @@ var (
6869 urlExistenceCacheMu sync.Mutex
6970 urlExistenceCache map [string ]bool
7071 urlExistenceCacheLoaded bool
72+
73+ packageListURLCacheMu sync.Mutex
74+ packageListURLCache map [string ]string
75+ packageListURLCacheLoaded bool
7176)
7277
7378const urlExistenceCacheFileName = "url_exists_cache.json"
79+ const packageListURLCacheFileName = "package_list_url_cache.json"
7480
7581func urlExistenceCacheFilePath () string {
7682 return filepath .Join (config .TempDir (), "builds" , urlExistenceCacheFileName )
7783}
7884
85+ func packageListURLCacheFilePath () string {
86+ return filepath .Join (config .TempDir (), "builds" , packageListURLCacheFileName )
87+ }
88+
89+ func packageListURLCacheKey (baseURL , codename , arch , component string ) string {
90+ return strings .Join ([]string {
91+ strings .TrimSpace (baseURL ),
92+ strings .TrimSpace (codename ),
93+ strings .TrimSpace (arch ),
94+ strings .TrimSpace (component ),
95+ }, "|" )
96+ }
97+
7998func loadURLExistenceCacheLocked () {
8099 if urlExistenceCacheLoaded {
81100 return
@@ -129,6 +148,63 @@ func saveURLExistenceToCache(url string, exists bool) {
129148 }
130149}
131150
151+ func loadPackageListURLCacheLocked () {
152+ if packageListURLCacheLoaded {
153+ return
154+ }
155+ packageListURLCacheLoaded = true
156+ packageListURLCache = make (map [string ]string )
157+
158+ cacheFile := packageListURLCacheFilePath ()
159+ data , err := os .ReadFile (cacheFile )
160+ if err != nil {
161+ return
162+ }
163+
164+ if err := json .Unmarshal (data , & packageListURLCache ); err != nil {
165+ packageListURLCache = make (map [string ]string )
166+ }
167+ }
168+
169+ func getPackageListURLFromCache (baseURL , codename , arch , component string ) (string , bool ) {
170+ packageListURLCacheMu .Lock ()
171+ defer packageListURLCacheMu .Unlock ()
172+
173+ loadPackageListURLCacheLocked ()
174+ url , ok := packageListURLCache [packageListURLCacheKey (baseURL , codename , arch , component )]
175+ if ! ok || strings .TrimSpace (url ) == "" {
176+ return "" , false
177+ }
178+ return url , true
179+ }
180+
181+ func savePackageListURLToCache (baseURL , codename , arch , component , packageListURL string ) {
182+ log := logger .Logger ()
183+
184+ packageListURLCacheMu .Lock ()
185+ defer packageListURLCacheMu .Unlock ()
186+
187+ loadPackageListURLCacheLocked ()
188+ key := packageListURLCacheKey (baseURL , codename , arch , component )
189+ packageListURLCache [key ] = packageListURL
190+
191+ cacheFile := packageListURLCacheFilePath ()
192+ if err := os .MkdirAll (filepath .Dir (cacheFile ), 0755 ); err != nil {
193+ log .Warnf ("failed to create package-list URL cache directory: %v" , err )
194+ return
195+ }
196+
197+ data , err := json .Marshal (packageListURLCache )
198+ if err != nil {
199+ log .Warnf ("failed to marshal package-list URL cache: %v" , err )
200+ return
201+ }
202+
203+ if err := os .WriteFile (cacheFile , data , 0644 ); err != nil {
204+ log .Warnf ("failed to persist package-list URL cache: %v" , err )
205+ }
206+ }
207+
132208func extractDebPackageNameFromFile (fileName string ) string {
133209 base := strings .TrimSuffix (fileName , ".deb" )
134210 if idx := strings .Index (base , "_" ); idx > 0 {
@@ -244,7 +320,7 @@ func isDebRequirementInCache(
244320}
245321
246322func debMetadataBuildPaths () []string {
247- buildPaths := make ([]string , 0 , 1 + len (RepoCfgs ))
323+ buildPaths := make ([]string , 0 , 1 + len (RepoCfgs )+ len ( UserRepoCfgs ) )
248324 seen := make (map [string ]struct {})
249325 add := func (path string ) {
250326 path = strings .TrimSpace (path )
@@ -262,6 +338,9 @@ func debMetadataBuildPaths() []string {
262338 for _ , rc := range RepoCfgs {
263339 add (rc .BuildPath )
264340 }
341+ for _ , rc := range UserRepoCfgs {
342+ add (rc .BuildPath )
343+ }
265344
266345 return buildPaths
267346}
@@ -504,10 +583,13 @@ func BuildRepoConfigs(userRepoList []Repository, arch string) ([]RepoConfig, err
504583 return userRepo , nil
505584}
506585
507- func UserPackages () ([]ospackage.PackageInfo , error ) {
508-
509- log := logger .Logger ()
510- log .Infof ("fetching packages from %s" , "user package list" )
586+ // initializeUserRepoCfgs builds the UserRepoCfgs from UserRepo without fetching package metadata.
587+ // This is called early to ensure UserRepoCfgs is available before cache checks.
588+ func initializeUserRepoCfgs () error {
589+ // UserRepoCfgs is already initialized, skip
590+ if len (UserRepoCfgs ) > 0 {
591+ return nil
592+ }
511593
512594 var repoList []Repository
513595 repoGroup := "custrepo"
@@ -528,18 +610,32 @@ func UserPackages() ([]ospackage.PackageInfo, error) {
528610 })
529611 }
530612
531- // If no valid repositories were found (all were placeholders), return empty package list
613+ // If no valid repositories were found (all were placeholders), nothing to do
532614 if len (repoList ) == 0 {
533- return []ospackage. PackageInfo {}, nil
615+ return nil
534616 }
535617
536618 userRepo , err := BuildRepoConfigs (repoList , Architecture )
537619 if err != nil {
538- return nil , fmt .Errorf ("building user repo configs failed: %w" , err )
620+ return fmt .Errorf ("building user repo configs failed: %w" , err )
621+ }
622+ UserRepoCfgs = userRepo
623+
624+ return nil
625+ }
626+
627+ func UserPackages () ([]ospackage.PackageInfo , error ) {
628+
629+ log := logger .Logger ()
630+ log .Infof ("fetching packages from %s" , "user package list" )
631+
632+ // Initialize UserRepoCfgs early if not already done
633+ if err := initializeUserRepoCfgs (); err != nil {
634+ return nil , err
539635 }
540636
541637 var allUserPackages []ospackage.PackageInfo
542- for _ , rpItx := range userRepo {
638+ for _ , rpItx := range UserRepoCfgs {
543639
544640 userPkgs , err := ParseRepositoryMetadata (rpItx .PkgPrefix , rpItx .PkgList , rpItx .ReleaseFile , rpItx .ReleaseSign , rpItx .PbGPGKey , rpItx .BuildPath , rpItx .Arch , rpItx .AllowPackages )
545641 if err != nil {
@@ -777,6 +873,13 @@ func DownloadPackagesComplete(pkgList []string, destDir, dotFile string, pkgSour
777873 var downloadPkgList []string
778874
779875 log := logger .Logger ()
876+
877+ // Initialize user repo configs early so they are available for cache metadata lookup
878+ if err := initializeUserRepoCfgs (); err != nil {
879+ log .Warnf ("Failed to initialize user repo configs: %v" , err )
880+ // Continue anyway; user repos are optional
881+ }
882+
780883 absDestDir , err := filepath .Abs (destDir )
781884 if err != nil {
782885 return downloadPkgList , nil , fmt .Errorf ("resolving cache directory: %w" , err )
0 commit comments