Skip to content

Commit 0fefe8a

Browse files
authored
chore: check server version on sign-in and launch (#43)
Updates CredentialModel to have an additional state `Unknown` during startup while credentials are validated in the background asynchronously (15s timeout). While loading credentials on startup, the tray app shows a loading message. While updating credentials, we now check that the server version falls in our expected range. The sign in page now uses a WinUI 3 Dialog to show errors. Closes #42
1 parent be51a7b commit 0fefe8a

31 files changed

+1098
-152
lines changed

.github/workflows/ci.yaml

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ on:
1111
jobs:
1212
fmt:
1313
runs-on: windows-latest
14+
timeout-minutes: 10
1415
steps:
1516
- uses: actions/checkout@v4
1617
- name: Setup dotnet
@@ -26,6 +27,7 @@ jobs:
2627

2728
test:
2829
runs-on: windows-latest
30+
timeout-minutes: 10
2931
steps:
3032
- uses: actions/checkout@v4
3133
- name: Setup dotnet
@@ -34,13 +36,36 @@ jobs:
3436
dotnet-version: 8.0.x
3537
cache: true
3638
cache-dependency-path: '**/packages.lock.json'
39+
- name: Install Windows App SDK Runtime
40+
shell: pwsh
41+
run: |
42+
$ErrorActionPreference = "Stop"
43+
44+
$filename = ".\WindowsAppRuntimeInstall-x64.exe"
45+
$url = "https://download.microsoft.com/download/7a3a6a44-b07e-4ca5-8b63-2de185769dbc/WindowsAppRuntimeInstall-x64.exe" # 1.6.5 (1.6.250205002)
46+
& curl.exe --progress-bar --show-error --fail --location --output $filename $url
47+
if ($LASTEXITCODE -ne 0) { throw "Failed to download Windows App SDK" }
48+
49+
$process = Start-Process -FilePath $filename -ArgumentList "--quiet --force" -NoNewWindow -Wait -PassThru
50+
if ($process.ExitCode -ne 0) { throw "Failed to install Windows App SDK: exit code is $($process.ExitCode)" }
3751
- name: dotnet restore
3852
run: dotnet restore --locked-mode
3953
- name: dotnet test
40-
run: dotnet test --no-restore
54+
run: dotnet test --no-restore --blame-hang --blame-hang-dump-type full --blame-hang-timeout 2m -p:Platform=x64
55+
- name: Upload test binaries and TestResults
56+
if: failure()
57+
uses: actions/upload-artifact@v4
58+
with:
59+
name: test-results
60+
retention-days: 1
61+
path: |
62+
./**/bin
63+
./**/obj
64+
./**/TestResults
4165
4266
build:
4367
runs-on: windows-latest
68+
timeout-minutes: 10
4469
steps:
4570
- uses: actions/checkout@v4
4671
- name: Setup dotnet

.github/workflows/release.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ permissions:
1818
jobs:
1919
release:
2020
runs-on: ${{ github.repository_owner == 'coder' && 'windows-latest-16-cores' || 'windows-latest' }}
21+
timeout-minutes: 15
2122

2223
steps:
2324
- uses: actions/checkout@v4

App/App.xaml.cs

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
using System;
2+
using System.Threading;
23
using System.Threading.Tasks;
4+
using Coder.Desktop.App.Models;
35
using Coder.Desktop.App.Services;
46
using Coder.Desktop.App.ViewModels;
57
using Coder.Desktop.App.Views;
@@ -26,6 +28,7 @@ public App()
2628
services.AddTransient<SignInWindow>();
2729

2830
// TrayWindow views and view models
31+
services.AddTransient<TrayWindowLoadingPage>();
2932
services.AddTransient<TrayWindowDisconnectedViewModel>();
3033
services.AddTransient<TrayWindowDisconnectedPage>();
3134
services.AddTransient<TrayWindowLoginRequiredViewModel>();
@@ -45,17 +48,29 @@ public async Task ExitApplication()
4548
{
4649
_handleWindowClosed = false;
4750
Exit();
48-
var rpcManager = _services.GetRequiredService<IRpcController>();
51+
var rpcController = _services.GetRequiredService<IRpcController>();
4952
// TODO: send a StopRequest if we're connected???
50-
await rpcManager.DisposeAsync();
53+
await rpcController.DisposeAsync();
5154
Environment.Exit(0);
5255
}
5356

5457
protected override void OnLaunched(LaunchActivatedEventArgs args)
5558
{
56-
var trayWindow = _services.GetRequiredService<TrayWindow>();
59+
// Start connecting to the manager in the background.
60+
var rpcController = _services.GetRequiredService<IRpcController>();
61+
if (rpcController.GetState().RpcLifecycle == RpcLifecycle.Disconnected)
62+
// Passing in a CT with no cancellation is desired here, because
63+
// the named pipe open will block until the pipe comes up.
64+
_ = rpcController.Reconnect(CancellationToken.None);
65+
66+
// Load the credentials in the background. Even though we pass a CT
67+
// with no cancellation, the method itself will impose a timeout on the
68+
// HTTP portion.
69+
var credentialManager = _services.GetRequiredService<ICredentialManager>();
70+
_ = credentialManager.LoadCredentials(CancellationToken.None);
5771

5872
// Prevent the TrayWindow from closing, just hide it.
73+
var trayWindow = _services.GetRequiredService<TrayWindow>();
5974
trayWindow.Closed += (sender, args) =>
6075
{
6176
if (!_handleWindowClosed) return;

App/Models/CredentialModel.cs

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,24 @@ namespace Coder.Desktop.App.Models;
22

33
public enum CredentialState
44
{
5+
// Unknown means "we haven't checked yet"
6+
Unknown,
7+
8+
// Invalid means "we checked and there's either no saved credentials or they are not valid"
59
Invalid,
10+
11+
// Valid means "we checked and there are saved credentials and they are valid"
612
Valid,
713
}
814

915
public class CredentialModel
1016
{
11-
public CredentialState State { get; set; } = CredentialState.Invalid;
17+
public CredentialState State { get; init; } = CredentialState.Unknown;
18+
19+
public string? CoderUrl { get; init; }
20+
public string? ApiToken { get; init; }
1221

13-
public string? CoderUrl { get; set; }
14-
public string? ApiToken { get; set; }
22+
public string? Username { get; init; }
1523

1624
public CredentialModel Clone()
1725
{
@@ -20,6 +28,7 @@ public CredentialModel Clone()
2028
State = State,
2129
CoderUrl = CoderUrl,
2230
ApiToken = ApiToken,
31+
Username = Username,
2332
};
2433
}
2534
}

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy