@@ -21,7 +21,8 @@ internal class AppHostRpcTarget(
21
21
IServiceProvider serviceProvider ,
22
22
IDistributedApplicationEventing eventing ,
23
23
PublishingActivityProgressReporter activityReporter ,
24
- IHostApplicationLifetime lifetime
24
+ IHostApplicationLifetime lifetime ,
25
+ DistributedApplicationOptions options
25
26
)
26
27
{
27
28
public async IAsyncEnumerable < ( string Id , string StatusText , bool IsComplete , bool IsError ) > GetPublishingActivitiesAsync ( [ EnumeratorCancellation ] CancellationToken cancellationToken )
@@ -101,6 +102,25 @@ public Task<long> PingAsync(long timestamp, CancellationToken cancellationToken)
101
102
102
103
public Task < ( string BaseUrlWithLoginToken , string ? CodespacesUrlWithLoginToken ) > GetDashboardUrlsAsync ( )
103
104
{
105
+ return GetDashboardUrlsAsync ( CancellationToken . None ) ;
106
+ }
107
+
108
+ public async Task < ( string BaseUrlWithLoginToken , string ? CodespacesUrlWithLoginToken ) > GetDashboardUrlsAsync ( CancellationToken cancellationToken )
109
+ {
110
+ if ( ! options . DashboardEnabled )
111
+ {
112
+ logger . LogError ( "Dashboard URL requested but dashboard is disabled." ) ;
113
+ throw new InvalidOperationException ( "Dashboard URL requested but dashboard is disabled." ) ;
114
+ }
115
+
116
+ // Wait for the dashboard to be healthy before we return the URL. This is to avoid
117
+ // a race condition when using Codespaces or devcontainers where the dashboard URL
118
+ // is displayed before the dashboard port forwarding is actually configured. It is
119
+ // also a point of friction to show the URL before the dashboard is ready to be used
120
+ // when using Devcontainers/Codespaces because people think that something isn't working
121
+ // when in fact they just need to refresh the page.
122
+ await resourceNotificationService . WaitForResourceHealthyAsync ( KnownResourceNames . AspireDashboard , cancellationToken ) . ConfigureAwait ( false ) ;
123
+
104
124
var dashboardOptions = serviceProvider . GetService < IOptions < DashboardOptions > > ( ) ;
105
125
106
126
if ( dashboardOptions is null )
@@ -122,11 +142,11 @@ public Task<long> PingAsync(long timestamp, CancellationToken cancellationToken)
122
142
123
143
if ( baseUrlWithLoginToken == codespacesUrlWithLoginToken )
124
144
{
125
- return Task . FromResult < ( string , string ? ) > ( ( baseUrlWithLoginToken , null ) ) ;
145
+ return ( baseUrlWithLoginToken , null ) ;
126
146
}
127
147
else
128
148
{
129
- return Task . FromResult ( ( baseUrlWithLoginToken , codespacesUrlWithLoginToken ) ) ;
149
+ return ( baseUrlWithLoginToken , codespacesUrlWithLoginToken ) ;
130
150
}
131
151
}
132
152
0 commit comments