Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore: check server version on sign-in and launch #43

Merged
merged 12 commits into from
Mar 11, 2025
27 changes: 26 additions & 1 deletion .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ on:
jobs:
fmt:
runs-on: windows-latest
timeout-minutes: 10
steps:
- uses: actions/checkout@v4
- name: Setup dotnet
Expand All @@ -26,6 +27,7 @@ jobs:

test:
runs-on: windows-latest
timeout-minutes: 10
steps:
- uses: actions/checkout@v4
- name: Setup dotnet
Expand All @@ -34,13 +36,36 @@ jobs:
dotnet-version: 8.0.x
cache: true
cache-dependency-path: '**/packages.lock.json'
- name: Install Windows App SDK Runtime
shell: pwsh
run: |
$ErrorActionPreference = "Stop"

$filename = ".\WindowsAppRuntimeInstall-x64.exe"
$url = "https://download.microsoft.com/download/7a3a6a44-b07e-4ca5-8b63-2de185769dbc/WindowsAppRuntimeInstall-x64.exe" # 1.6.5 (1.6.250205002)
& curl.exe --progress-bar --show-error --fail --location --output $filename $url
if ($LASTEXITCODE -ne 0) { throw "Failed to download Windows App SDK" }

$process = Start-Process -FilePath $filename -ArgumentList "--quiet --force" -NoNewWindow -Wait -PassThru
if ($process.ExitCode -ne 0) { throw "Failed to install Windows App SDK: exit code is $($process.ExitCode)" }
- name: dotnet restore
run: dotnet restore --locked-mode
- name: dotnet test
run: dotnet test --no-restore
run: dotnet test --no-restore --blame-hang --blame-hang-dump-type full --blame-hang-timeout 2m -p:Platform=x64
- name: Upload test binaries and TestResults
if: failure()
uses: actions/upload-artifact@v4
with:
name: test-results
retention-days: 1
path: |
./**/bin
./**/obj
./**/TestResults

build:
runs-on: windows-latest
timeout-minutes: 10
steps:
- uses: actions/checkout@v4
- name: Setup dotnet
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/release.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ permissions:
jobs:
release:
runs-on: ${{ github.repository_owner == 'coder' && 'windows-latest-16-cores' || 'windows-latest' }}
timeout-minutes: 15

steps:
- uses: actions/checkout@v4
Expand Down
21 changes: 18 additions & 3 deletions App/App.xaml.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
using System;
using System.Threading;
using System.Threading.Tasks;
using Coder.Desktop.App.Models;
using Coder.Desktop.App.Services;
using Coder.Desktop.App.ViewModels;
using Coder.Desktop.App.Views;
Expand All @@ -26,6 +28,7 @@ public App()
services.AddTransient<SignInWindow>();

// TrayWindow views and view models
services.AddTransient<TrayWindowLoadingPage>();
services.AddTransient<TrayWindowDisconnectedViewModel>();
services.AddTransient<TrayWindowDisconnectedPage>();
services.AddTransient<TrayWindowLoginRequiredViewModel>();
Expand All @@ -45,17 +48,29 @@ public async Task ExitApplication()
{
_handleWindowClosed = false;
Exit();
var rpcManager = _services.GetRequiredService<IRpcController>();
var rpcController = _services.GetRequiredService<IRpcController>();
// TODO: send a StopRequest if we're connected???
await rpcManager.DisposeAsync();
await rpcController.DisposeAsync();
Environment.Exit(0);
}

protected override void OnLaunched(LaunchActivatedEventArgs args)
{
var trayWindow = _services.GetRequiredService<TrayWindow>();
// Start connecting to the manager in the background.
var rpcController = _services.GetRequiredService<IRpcController>();
if (rpcController.GetState().RpcLifecycle == RpcLifecycle.Disconnected)
// Passing in a CT with no cancellation is desired here, because
// the named pipe open will block until the pipe comes up.
_ = rpcController.Reconnect(CancellationToken.None);

// Load the credentials in the background. Even though we pass a CT
// with no cancellation, the method itself will impose a timeout on the
// HTTP portion.
var credentialManager = _services.GetRequiredService<ICredentialManager>();
_ = credentialManager.LoadCredentials(CancellationToken.None);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just to check my understanding here, because we are not awaiting the task, it doesn't matter if the task throws some exception, like being canceled. And, we don't block for it to complete, right?

I ask because we want the rest of this function to still run and handle closing of the tray window.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep, we run it in the background and ignore all errors. If the credentials fail to load, the state will transition to "invalid" which will show the "please sign in" screen.


// Prevent the TrayWindow from closing, just hide it.
var trayWindow = _services.GetRequiredService<TrayWindow>();
trayWindow.Closed += (sender, args) =>
{
if (!_handleWindowClosed) return;
Expand Down
15 changes: 12 additions & 3 deletions App/Models/CredentialModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,24 @@ namespace Coder.Desktop.App.Models;

public enum CredentialState
{
// Unknown means "we haven't checked yet"
Unknown,

// Invalid means "we checked and there's either no saved credentials or they are not valid"
Invalid,

// Valid means "we checked and there are saved credentials and they are valid"
Valid,
}

public class CredentialModel
{
public CredentialState State { get; set; } = CredentialState.Invalid;
public CredentialState State { get; init; } = CredentialState.Unknown;

public string? CoderUrl { get; init; }
public string? ApiToken { get; init; }

public string? CoderUrl { get; set; }
public string? ApiToken { get; set; }
public string? Username { get; init; }

public CredentialModel Clone()
{
Expand All @@ -20,6 +28,7 @@ public CredentialModel Clone()
State = State,
CoderUrl = CoderUrl,
ApiToken = ApiToken,
Username = Username,
};
}
}
Loading
Loading