@@ -5,12 +5,55 @@ import (
55	"flag" 
66	"fmt" 
77	"os" 
8+ 	"sync" 
89
910	"github.com/contriboss/gemfile-go/gemfile" 
1011	"github.com/contriboss/gemfile-go/lockfile" 
1112	"github.com/contriboss/ore-light/internal/registry" 
1213)
1314
15+ // versionCheckResult holds the result of checking a gem's latest version 
16+ type  versionCheckResult  struct  {
17+ 	gemName        string 
18+ 	latestVersion  string 
19+ 	err            error 
20+ }
21+ 
22+ // checkVersionsParallel fetches latest versions for multiple gems in parallel 
23+ // Uses 10 concurrent workers (similar to Bundler's approach but more conservative) 
24+ func  checkVersionsParallel (ctx  context.Context , client  * registry.Client , gemNames  []string , verbose  bool ) map [string ]versionCheckResult  {
25+ 	results  :=  make (map [string ]versionCheckResult )
26+ 	resultsMu  :=  sync.Mutex {}
27+ 
28+ 	// Limit concurrent requests to 10 (respectful to rubygems.org) 
29+ 	semaphore  :=  make (chan  struct {}, 10 )
30+ 	var  wg  sync.WaitGroup 
31+ 
32+ 	for  _ , gemName  :=  range  gemNames  {
33+ 		wg .Add (1 )
34+ 		go  func (name  string ) {
35+ 			defer  wg .Done ()
36+ 
37+ 			// Acquire semaphore 
38+ 			semaphore  <-  struct {}{}
39+ 			defer  func () { <- semaphore  }()
40+ 
41+ 			versions , err  :=  client .GetGemVersions (ctx , name )
42+ 
43+ 			resultsMu .Lock ()
44+ 			if  err  !=  nil  {
45+ 				results [name ] =  versionCheckResult {gemName : name , err : err }
46+ 			} else  if  len (versions ) >  0  {
47+ 				results [name ] =  versionCheckResult {gemName : name , latestVersion : versions [0 ]}
48+ 			}
49+ 			resultsMu .Unlock ()
50+ 		}(gemName )
51+ 	}
52+ 
53+ 	wg .Wait ()
54+ 	return  results 
55+ }
56+ 
1457// RunOutdated implements the ore outdated command 
1558func  RunOutdated (args  []string ) error  {
1659	fs  :=  flag .NewFlagSet ("outdated" , flag .ContinueOnError )
@@ -65,41 +108,41 @@ func RunOutdated(args []string) error {
65108		fmt .Println ("🔍 Checking for outdated gems..." )
66109	}
67110
68- 	outdatedCount  :=  0 
69- 	checkedCount  :=  0 
111+ 	// Collect gem names 
112+ 	gemNames  :=  make ([]string , len (lock .GemSpecs ))
113+ 	for  i , spec  :=  range  lock .GemSpecs  {
114+ 		gemNames [i ] =  spec .Name 
115+ 	}
70116
71- 	// Check each gem in lockfile for updates 
117+ 	// Check all versions in parallel 
118+ 	results  :=  checkVersionsParallel (ctx , client , gemNames , * verbose )
119+ 
120+ 	// Process results and display outdated gems 
121+ 	outdatedCount  :=  0 
72122	for  _ , spec  :=  range  lock .GemSpecs  {
73- 		checkedCount ++ 
74- 		if  * verbose  &&  checkedCount % 10  ==  0  {
75- 			fmt .Printf ("   Checked %d/%d gems...\n " , checkedCount , len (lock .GemSpecs ))
76- 		}
123+ 		result  :=  results [spec .Name ]
77124
78- 		versions , err  :=  client .GetGemVersions (ctx , spec .Name )
79- 		if  err  !=  nil  {
125+ 		if  result .err  !=  nil  {
80126			if  * verbose  {
81- 				fmt .Fprintf (os .Stderr , "Warning: Could not fetch versions for %s: %v\n " , spec .Name , err )
127+ 				fmt .Fprintf (os .Stderr , "Warning: Could not fetch versions for %s: %v\n " , spec .Name , result . err )
82128			}
83129			continue 
84130		}
85131
86- 		if  len ( versions )  ==  0  {
132+ 		if  result . latestVersion  ==  ""  {
87133			continue 
88134		}
89135
90- 		// Latest version is first in the list 
91- 		latestVersion  :=  versions [0 ]
92- 
93136		// Compare versions 
94- 		if  latestVersion  !=  spec .Version  {
137+ 		if  result . latestVersion  !=  spec .Version  {
95138			outdatedCount ++ 
96139			constraint  :=  constraints [spec .Name ]
97140			if  constraint  ==  ""  {
98141				constraint  =  "(no constraint)" 
99142			}
100143
101144			fmt .Printf ("  * %s (newest %s, installed %s, requested %s)\n " ,
102- 				spec .Name , latestVersion , spec .Version , constraint )
145+ 				spec .Name , result . latestVersion , spec .Version , constraint )
103146		}
104147	}
105148
0 commit comments