Skip to content

Commit 5072e24

Browse files
committed
Merge branch 'main' into dean/updater
2 parents c92ba0b + 74b8658 commit 5072e24

File tree

11 files changed

+237
-27
lines changed

11 files changed

+237
-27
lines changed

.github/workflows/ci.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ jobs:
2121
cache: true
2222
cache-dependency-path: '**/packages.lock.json'
2323
- name: dotnet restore
24-
run: dotnet restore --locked-mode
24+
run: dotnet restore --locked-mode /p:BuildWithNetFrameworkHostedCompiler=true
2525
- name: dotnet format
2626
run: dotnet format --verify-no-changes --no-restore
2727

@@ -75,7 +75,7 @@ jobs:
7575
cache: true
7676
cache-dependency-path: '**/packages.lock.json'
7777
- name: dotnet restore
78-
run: dotnet restore --locked-mode
78+
run: dotnet restore --locked-mode /p:BuildWithNetFrameworkHostedCompiler=true
7979
# This doesn't call `dotnet publish` on the entire solution, just the
8080
# projects we care about building. Doing a full publish includes test
8181
# libraries and stuff which is pointless.

App/App.xaml.cs

Lines changed: 14 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -204,20 +204,21 @@ protected override void OnLaunched(LaunchActivatedEventArgs args)
204204
}, CancellationToken.None);
205205

206206
// Initialize file sync.
207-
var syncSessionCts = new CancellationTokenSource(TimeSpan.FromSeconds(10));
208-
var syncSessionController = _services.GetRequiredService<ISyncSessionController>();
209-
_ = syncSessionController.RefreshState(syncSessionCts.Token).ContinueWith(t =>
207+
// We're adding a 5s delay here to avoid race conditions when loading the mutagen binary.
208+
_ = Task.Delay(5000).ContinueWith((_) =>
210209
{
211-
if (t.IsCanceled || t.Exception != null)
212-
{
213-
_logger.LogError(t.Exception, "failed to refresh sync state (canceled = {canceled})", t.IsCanceled);
214-
#if DEBUG
215-
Debugger.Break();
216-
#endif
217-
}
218-
219-
syncSessionCts.Dispose();
220-
}, CancellationToken.None);
210+
var syncSessionCts = new CancellationTokenSource(TimeSpan.FromSeconds(10));
211+
var syncSessionController = _services.GetRequiredService<ISyncSessionController>();
212+
syncSessionController.RefreshState(syncSessionCts.Token).ContinueWith(
213+
t =>
214+
{
215+
if (t.IsCanceled || t.Exception != null)
216+
{
217+
_logger.LogError(t.Exception, "failed to refresh sync state (canceled = {canceled})", t.IsCanceled);
218+
}
219+
syncSessionCts.Dispose();
220+
}, CancellationToken.None);
221+
});
221222
}
222223

223224
public void OnActivated(object? sender, AppActivationArguments args)

App/Services/RpcController.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -313,7 +313,7 @@ private void SpeakerOnError(Exception e)
313313
Debug.WriteLine($"Error: {e}");
314314
try
315315
{
316-
Reconnect(CancellationToken.None).Wait();
316+
using var _ = Reconnect(CancellationToken.None);
317317
}
318318
catch
319319
{

App/ViewModels/FileSyncListViewModel.cs

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -143,9 +143,9 @@ public void Initialize(Window window, DispatcherQueue dispatcherQueue)
143143

144144
var rpcModel = _rpcController.GetState();
145145
var credentialModel = _credentialManager.GetCachedCredentials();
146-
MaybeSetUnavailableMessage(rpcModel, credentialModel);
147146
var syncSessionState = _syncSessionController.GetState();
148147
UpdateSyncSessionState(syncSessionState);
148+
MaybeSetUnavailableMessage(rpcModel, credentialModel, syncSessionState);
149149
}
150150

151151
private void RpcControllerStateChanged(object? sender, RpcModel rpcModel)
@@ -159,7 +159,8 @@ private void RpcControllerStateChanged(object? sender, RpcModel rpcModel)
159159
}
160160

161161
var credentialModel = _credentialManager.GetCachedCredentials();
162-
MaybeSetUnavailableMessage(rpcModel, credentialModel);
162+
var syncSessionState = _syncSessionController.GetState();
163+
MaybeSetUnavailableMessage(rpcModel, credentialModel, syncSessionState);
163164
}
164165

165166
private void CredentialManagerCredentialsChanged(object? sender, CredentialModel credentialModel)
@@ -173,7 +174,8 @@ private void CredentialManagerCredentialsChanged(object? sender, CredentialModel
173174
}
174175

175176
var rpcModel = _rpcController.GetState();
176-
MaybeSetUnavailableMessage(rpcModel, credentialModel);
177+
var syncSessionState = _syncSessionController.GetState();
178+
MaybeSetUnavailableMessage(rpcModel, credentialModel, syncSessionState);
177179
}
178180

179181
private void SyncSessionStateChanged(object? sender, SyncSessionControllerStateModel syncSessionState)
@@ -189,7 +191,7 @@ private void SyncSessionStateChanged(object? sender, SyncSessionControllerStateM
189191
UpdateSyncSessionState(syncSessionState);
190192
}
191193

192-
private void MaybeSetUnavailableMessage(RpcModel rpcModel, CredentialModel credentialModel)
194+
private void MaybeSetUnavailableMessage(RpcModel rpcModel, CredentialModel credentialModel, SyncSessionControllerStateModel syncSessionState)
193195
{
194196
var oldMessage = UnavailableMessage;
195197
if (rpcModel.RpcLifecycle != RpcLifecycle.Connected)
@@ -205,6 +207,10 @@ private void MaybeSetUnavailableMessage(RpcModel rpcModel, CredentialModel crede
205207
{
206208
UnavailableMessage = "Please start Coder Connect from the tray window to access file sync.";
207209
}
210+
else if (syncSessionState.Lifecycle == SyncSessionControllerLifecycle.Uninitialized)
211+
{
212+
UnavailableMessage = "Sync session controller is not initialized. Please wait...";
213+
}
208214
else
209215
{
210216
UnavailableMessage = null;
@@ -219,6 +225,13 @@ private void MaybeSetUnavailableMessage(RpcModel rpcModel, CredentialModel crede
219225

220226
private void UpdateSyncSessionState(SyncSessionControllerStateModel syncSessionState)
221227
{
228+
// This should never happen.
229+
if (syncSessionState == null)
230+
return;
231+
if (syncSessionState.Lifecycle == SyncSessionControllerLifecycle.Uninitialized)
232+
{
233+
MaybeSetUnavailableMessage(_rpcController.GetState(), _credentialManager.GetCachedCredentials(), syncSessionState);
234+
}
222235
Error = syncSessionState.DaemonError;
223236
Sessions = syncSessionState.SyncSessions.Select(s => new SyncSessionViewModel(this, s)).ToList();
224237
}

App/ViewModels/TrayWindowDisconnectedViewModel.cs

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
1-
using System.Threading.Tasks;
21
using Coder.Desktop.App.Models;
32
using Coder.Desktop.App.Services;
43
using CommunityToolkit.Mvvm.ComponentModel;
54
using CommunityToolkit.Mvvm.Input;
5+
using System;
6+
using System.Threading.Tasks;
67

78
namespace Coder.Desktop.App.ViewModels;
89

@@ -11,6 +12,8 @@ public partial class TrayWindowDisconnectedViewModel : ObservableObject
1112
private readonly IRpcController _rpcController;
1213

1314
[ObservableProperty] public partial bool ReconnectButtonEnabled { get; set; } = true;
15+
[ObservableProperty] public partial string ErrorMessage { get; set; } = string.Empty;
16+
[ObservableProperty] public partial bool ReconnectFailed { get; set; } = false;
1417

1518
public TrayWindowDisconnectedViewModel(IRpcController rpcController)
1619
{
@@ -26,6 +29,16 @@ private void UpdateFromRpcModel(RpcModel rpcModel)
2629
[RelayCommand]
2730
public async Task Reconnect()
2831
{
29-
await _rpcController.Reconnect();
32+
try
33+
{
34+
ReconnectFailed = false;
35+
ErrorMessage = string.Empty;
36+
await _rpcController.Reconnect();
37+
}
38+
catch (Exception ex)
39+
{
40+
ErrorMessage = ex.Message;
41+
ReconnectFailed = true;
42+
}
3043
}
3144
}

App/Views/Pages/TrayWindowDisconnectedPage.xaml

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,19 @@
3030

3131
<controls:HorizontalRule />
3232

33+
<TextBlock FontWeight="semibold"
34+
TextWrapping="Wrap"
35+
Foreground="Red"
36+
Visibility="{x:Bind ViewModel.ReconnectFailed, Converter={StaticResource BoolToVisibilityConverter}, Mode=OneWay}"
37+
Text="Reconnect failed"/>
38+
39+
<TextBlock
40+
TextWrapping="Wrap"
41+
Margin="0,0,0,10"
42+
Foreground="Red"
43+
Visibility="{x:Bind ViewModel.ReconnectFailed, Converter={StaticResource BoolToVisibilityConverter}, Mode=OneWay}"
44+
Text="{x:Bind ViewModel.ErrorMessage, Mode=OneWay}" />
45+
3346
<HyperlinkButton
3447
HorizontalContentAlignment="Left"
3548
HorizontalAlignment="Stretch"

App/Views/TrayWindow.xaml.cs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -124,8 +124,7 @@ public TrayWindow(IRpcController rpcController, ICredentialManager credentialMan
124124
private void SetPageByState(RpcModel rpcModel, CredentialModel credentialModel,
125125
SyncSessionControllerStateModel syncSessionModel)
126126
{
127-
if (credentialModel.State == CredentialState.Unknown ||
128-
syncSessionModel.Lifecycle == SyncSessionControllerLifecycle.Uninitialized)
127+
if (credentialModel.State == CredentialState.Unknown)
129128
{
130129
SetRootFrame(_loadingPage);
131130
return;

Installer/Program.cs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,9 @@ public class BootstrapperOptions : SharedOptions
119119
[Option('w', "windows-app-sdk-path", Required = true, HelpText = "Path to the Windows App Sdk package to embed")]
120120
public string WindowsAppSdkPath { get; set; }
121121

122+
[Option('t', "theme-xml-path", Required = false, HelpText = "Path to the theme .xml file to use for the installer")]
123+
public string ThemeXmlPath { get; set; }
124+
122125
public new void Validate()
123126
{
124127
base.Validate();
@@ -130,6 +133,8 @@ public class BootstrapperOptions : SharedOptions
130133
if (!SystemFile.Exists(WindowsAppSdkPath))
131134
throw new ArgumentException($"Windows App Sdk package not found at '{WindowsAppSdkPath}'",
132135
nameof(WindowsAppSdkPath));
136+
if (ThemeXmlPath != null && !SystemFile.Exists(ThemeXmlPath))
137+
throw new ArgumentException($"Theme XML file not found at '{ThemeXmlPath}'", nameof(ThemeXmlPath));
133138
}
134139
}
135140

@@ -415,6 +420,20 @@ private static int BuildBundle(BootstrapperOptions opts)
415420
bundle.Application.LicensePath = opts.LicenseFile;
416421
bundle.Application.LogoFile = opts.LogoPng;
417422

423+
if (opts.ThemeXmlPath != null)
424+
{
425+
bundle.Application.ThemeFile = opts.ThemeXmlPath;
426+
bundle.Application.Payloads =
427+
[
428+
new ExePackagePayload
429+
{
430+
Name = "icon.ico",
431+
SourceFile = opts.IconFile,
432+
Compressed = true,
433+
},
434+
];
435+
}
436+
418437
// Set the default install folder, which will eventually be passed into
419438
// the MSI.
420439
bundle.Variables =

scripts/Get-WindowsAppSdk.ps1

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ function Download-File([string] $url, [string] $outputPath, [string] $etagFile)
1010
# We use `curl.exe` here because `Invoke-WebRequest` is notoriously slow.
1111
& curl.exe `
1212
--progress-bar `
13-
-v `
1413
--show-error `
1514
--fail `
1615
--location `
@@ -31,4 +30,4 @@ $windowsAppSdkFullVersion = "1.6.250228001"
3130
$windowsAppSdkPath = Join-Path $PSScriptRoot "files\windows-app-sdk-$($arch).exe"
3231
$windowsAppSdkUri = "https://aka.ms/windowsappsdk/$($windowsAppSdkMajorVersion)/$($windowsAppSdkFullVersion)/windowsappruntimeinstall-$($arch).exe"
3332
$windowsAppSdkEtagFile = $windowsAppSdkPath + ".etag"
34-
Download-File $windowsAppSdkUri $windowsAppSdkPath $windowsAppSdkEtagFile
33+
Download-File $windowsAppSdkUri $windowsAppSdkPath $windowsAppSdkEtagFile

scripts/Publish.ps1

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,7 @@ if (Test-Path $buildPath) {
113113
New-Item -ItemType Directory -Path $buildPath -Force
114114

115115
# Build in release mode
116-
& dotnet.exe restore
116+
& dotnet.exe restore /p:BuildWithNetFrameworkHostedCompiler=true
117117
if ($LASTEXITCODE -ne 0) { throw "Failed to dotnet restore" }
118118
$servicePublishDir = Join-Path $buildPath "service"
119119
& dotnet.exe publish .\Vpn.Service\Vpn.Service.csproj -c Release -a $arch -o $servicePublishDir /p:Version=$version
@@ -189,6 +189,7 @@ $windowsAppSdkPath = Join-Path $scriptRoot "files\windows-app-sdk-$($arch).exe"
189189
--icon-file "App\coder.ico" `
190190
--msi-path $msiOutputPath `
191191
--windows-app-sdk-path $windowsAppSdkPath `
192+
--theme-xml-path "scripts\files\RtfThemeLarge.xml" `
192193
--logo-png "scripts\files\logo.png"
193194
if ($LASTEXITCODE -ne 0) { throw "Failed to build bootstrapper" }
194195

0 commit comments

Comments
 (0)