@@ -146,13 +146,12 @@ pub fn to_body(bot: &Bot, state: &AppState, fleet: &Fleet) -> BotBody {
146146 }
147147}
148148
149- async fn with_version ( mut body : BotBody , bot : & Bot , state : & AppState ) -> BotBody {
150- body. version = running_version ( state, bot) . await ;
149+ async fn with_version ( mut body : BotBody , bot : & Bot , state : & AppState , wait : bool ) -> BotBody {
150+ body. version = running_version ( state, bot, wait ) . await ;
151151 body
152152}
153153
154- async fn running_version ( state : & AppState , bot : & Bot ) -> Option < String > {
155- let releases = crate :: panel:: versions:: release_index ( & state. cfg . bot_image ) . await ;
154+ async fn running_version ( state : & AppState , bot : & Bot , wait : bool ) -> Option < String > {
156155 let lookup = bot
157156 . image_id
158157 . as_deref ( )
@@ -163,6 +162,35 @@ async fn running_version(state: &AppState, bot: &Bot) -> Option<String> {
163162 . local_image_labels ( lookup)
164163 . await
165164 . unwrap_or_default ( ) ;
165+ let repo_digests = if bot
166+ . image
167+ . as_deref ( )
168+ . is_none_or ( crate :: panel:: updates:: is_content_digest_ref)
169+ {
170+ state
171+ . docker
172+ . local_image_digests ( lookup)
173+ . await
174+ . unwrap_or_default ( )
175+ } else {
176+ Vec :: new ( )
177+ } ;
178+ let source = crate :: panel:: versions:: release_lookup_image (
179+ bot. image . as_deref ( ) ,
180+ & repo_digests,
181+ & state. cfg . bot_image ,
182+ ) ;
183+ let needed_sha = crate :: panel:: versions:: release_sha_hint ( bot. image . as_deref ( ) , & labels) ;
184+ let releases = if wait {
185+ crate :: panel:: versions:: release_index_await (
186+ & source,
187+ needed_sha. as_deref ( ) ,
188+ crate :: panel:: versions:: DETAIL_RELEASE_WAIT ,
189+ )
190+ . await
191+ } else {
192+ crate :: panel:: versions:: release_index ( & source, needed_sha. as_deref ( ) )
193+ } ;
166194 crate :: panel:: versions:: version_for_image ( & releases, bot. image . as_deref ( ) , & labels)
167195}
168196
@@ -182,7 +210,7 @@ pub async fn list(State(state): State<AppState>) -> Result<Response, ApiError> {
182210 // fleet page stays alphabetical even if that ever changes.
183211 let mut bots = Vec :: new ( ) ;
184212 for b in fleet. bots ( ) {
185- bots. push ( with_version ( to_body ( b, & state, & fleet) , b, & state) . await ) ;
213+ bots. push ( with_version ( to_body ( b, & state, & fleet) , b, & state, false ) . await ) ;
186214 }
187215 bots. sort_by ( |a, b| a. name . cmp ( & b. name ) ) ;
188216 Ok ( Json ( FleetBody {
@@ -198,7 +226,7 @@ pub async fn show(
198226 Path ( name) : Path < String > ,
199227) -> Result < Response , ApiError > {
200228 let ( bot, fleet) = state. bot_and_fleet ( & name) . await ?;
201- Ok ( Json ( with_version ( to_body ( & bot, & state, & fleet) , & bot, & state) . await ) . into_response ( ) )
229+ Ok ( Json ( with_version ( to_body ( & bot, & state, & fleet) , & bot, & state, true ) . await ) . into_response ( ) )
202230}
203231
204232/// A lifecycle action's result. Carries the bot's new state so the UI doesn't
@@ -217,7 +245,7 @@ async fn action_response(
217245) -> Result < Response , ApiError > {
218246 let ( bot, fleet) = state. bot_and_fleet ( name) . await ?;
219247 Ok ( Json ( ActionBody {
220- bot : with_version ( to_body ( & bot, state, & fleet) , & bot, state) . await ,
248+ bot : with_version ( to_body ( & bot, state, & fleet) , & bot, state, true ) . await ,
221249 message,
222250 } )
223251 . into_response ( ) )
@@ -1703,7 +1731,7 @@ pub async fn migrate_layout(
17031731
17041732 let ( fresh, fresh_fleet) = state. bot_and_fleet ( & name) . await ?;
17051733 Ok ( Json ( serde_json:: json!( {
1706- "bot" : with_version( to_body( & fresh, & state, & fresh_fleet) , & fresh, & state) . await ,
1734+ "bot" : with_version( to_body( & fresh, & state, & fresh_fleet) , & fresh, & state, true ) . await ,
17071735 "message" : report. message( ) ,
17081736 "movedFiles" : report. moved,
17091737 "ledgersRecovered" : report. ledgers_recovered,
@@ -1867,6 +1895,33 @@ mod tests {
18671895 assert_eq ! ( v[ "version" ] , "v0.1.226" , "{body}" ) ;
18681896 }
18691897
1898+ #[ tokio:: test]
1899+ async fn a_forked_bot_resolves_releases_from_its_own_repository ( ) {
1900+ let h = harness ( "bot-version-fork" ) ;
1901+ seed_panel_bot ( & h, "bot-a" ) ;
1902+ h. docker
1903+ . set_container_image ( "stitch-bot-a" , "ghcr.io/someone/fork:sha-24e9192" ) ;
1904+ crate :: panel:: versions:: set_test_release_index_for_repo (
1905+ "textile-protocol/textile-stitch" ,
1906+ std:: collections:: HashMap :: from ( [ ( "24e9192" . into ( ) , "v0.1.226" . into ( ) ) ] ) ,
1907+ ) ;
1908+ crate :: panel:: versions:: set_test_release_index_for_repo (
1909+ "someone/fork" ,
1910+ std:: collections:: HashMap :: from ( [ ( "24e9192" . into ( ) , "v9.9.9" . into ( ) ) ] ) ,
1911+ ) ;
1912+ struct ResetReleases ;
1913+ impl Drop for ResetReleases {
1914+ fn drop ( & mut self ) {
1915+ crate :: panel:: versions:: clear_test_release_index ( ) ;
1916+ }
1917+ }
1918+ let _reset = ResetReleases ;
1919+ let ( status, body) = h. get ( "/api/bots/bot-a" ) . await ;
1920+ assert_eq ! ( status, StatusCode :: OK , "{body}" ) ;
1921+ let v = Harness :: parse ( & body) ;
1922+ assert_eq ! ( v[ "version" ] , "v9.9.9" , "{body}" ) ;
1923+ }
1924+
18701925 #[ tokio:: test]
18711926 async fn an_empty_host_lists_no_bots ( ) {
18721927 let h = harness ( "empty" ) ;
0 commit comments