Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit46849a5

Browse files
authored
feat: add auto updater (#117)
1 parent56003ed commit46849a5

File tree

38 files changed

+2960
-65
lines changed

38 files changed

+2960
-65
lines changed

‎.github/workflows/release.yaml

Lines changed: 48 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,9 @@ jobs:
6868
service_account:${{ secrets.GCP_SERVICE_ACCOUNT }}
6969
token_format:"access_token"
7070

71+
-name:Install gcloud
72+
uses:google-github-actions/setup-gcloud@77e7a554d41e2ee56fc945c52dfd3f33d12def9a# 2.1.4
73+
7174
-name:Install wix
7275
shell:pwsh
7376
run:|
@@ -120,6 +123,51 @@ jobs:
120123
env:
121124
GITHUB_TOKEN:${{ secrets.GITHUB_TOKEN }}
122125

126+
-name:Update appcast
127+
if:startsWith(github.ref, 'refs/tags/')
128+
shell:pwsh
129+
run:|
130+
$ErrorActionPreference = "Stop"
131+
132+
# The Update-AppCast.ps1 script fetches the release notes from GitHub,
133+
# which might take a few seconds to be ready.
134+
Start-Sleep -Seconds 10
135+
136+
# Save the appcast signing key to a temporary file.
137+
$keyPath = Join-Path $env:TEMP "appcast-key.pem"
138+
$key = [System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String($env:APPCAST_SIGNATURE_KEY_BASE64))
139+
Set-Content -Path $keyPath -Value $key
140+
141+
# Download the old appcast from GCS.
142+
$oldAppCastPath = Join-Path $env:TEMP "appcast.old.xml"
143+
& gsutil cp $env:APPCAST_GCS_URI $oldAppCastPath
144+
if ($LASTEXITCODE -ne 0) { throw "Failed to download appcast" }
145+
146+
# Generate the new appcast and signature.
147+
$newAppCastPath = Join-Path $env:TEMP "appcast.new.xml"
148+
$newAppCastSignaturePath = $newAppCastPath + ".signature"
149+
& ./scripts/Update-AppCast.ps1 `
150+
-tag "${{ github.ref_name }}" `
151+
-channel stable `
152+
-x64Path "${{ steps.release.outputs.X64_OUTPUT_PATH }}" `
153+
-arm64Path "${{ steps.release.outputs.ARM64_OUTPUT_PATH }}" `
154+
-keyPath $keyPath `
155+
-inputAppCastPath $oldAppCastPath `
156+
-outputAppCastPath $newAppCastPath `
157+
-outputAppCastSignaturePath $newAppCastSignaturePath
158+
if ($LASTEXITCODE -ne 0) { throw "Failed to generate new appcast" }
159+
160+
# Upload the new appcast and signature to GCS.
161+
& gsutil -h "Cache-Control:no-cache,max-age=0" cp $newAppCastPath $env:APPCAST_GCS_URI
162+
if ($LASTEXITCODE -ne 0) { throw "Failed to upload new appcast" }
163+
& gsutil -h "Cache-Control:no-cache,max-age=0" cp $newAppCastSignaturePath $env:APPCAST_SIGNATURE_GCS_URI
164+
if ($LASTEXITCODE -ne 0) { throw "Failed to upload new appcast signature" }
165+
env:
166+
APPCAST_GCS_URI:gs://releases.coder.com/coder-desktop/windows/appcast.xml
167+
APPCAST_SIGNATURE_GCS_URI:gs://releases.coder.com/coder-desktop/windows/appcast.xml.signature
168+
APPCAST_SIGNATURE_KEY_BASE64:${{ secrets.APPCAST_SIGNATURE_KEY_BASE64 }}
169+
GCLOUD_ACCESS_TOKEN:${{ steps.gcloud_auth.outputs.access_token }}
170+
123171
winget:
124172
runs-on:depot-windows-latest
125173
needs:release
@@ -177,7 +225,6 @@ jobs:
177225
# to GitHub and then making a PR in a different repo.
178226
WINGET_GH_TOKEN:${{ secrets.CDRCI_GITHUB_TOKEN }}
179227

180-
181228
-name:Comment on PR
182229
run:|
183230
# wait 30 seconds

‎.gitignore

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -411,3 +411,12 @@ publish
411411
*.wixmdb
412412
*.wixprj
413413
*.wixproj
414+
415+
appcast.xml
416+
appcast.xml.signature
417+
*.key
418+
*.key.*
419+
*.pem
420+
*.pem.*
421+
*.pub
422+
*.pub.*

‎App/App.csproj

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,10 @@
1919
<DefineConstants>DISABLE_XAML_GENERATED_MAIN;DISABLE_XAML_GENERATED_BREAK_ON_UNHANDLED_EXCEPTION</DefineConstants>
2020

2121
<AssemblyName>Coder Desktop</AssemblyName>
22+
<AssemblyTitle>Coder Desktop</AssemblyTitle>
23+
<Company>Coder Technologies Inc.</Company>
24+
<Product>Coder Desktop</Product>
25+
<Copyright>© Coder Technologies Inc.</Copyright>
2226
<ApplicationIcon>coder.ico</ApplicationIcon>
2327
</PropertyGroup>
2428

@@ -31,9 +35,7 @@
3135

3236
<ItemGroup>
3337
<ContentInclude="coder.ico" />
34-
</ItemGroup>
35-
36-
<ItemGroup>
38+
<EmbeddedResourceInclude="Assets\changelog.css" />
3739
<ManifestInclude="$(ApplicationManifest)" />
3840
</ItemGroup>
3941

@@ -68,12 +70,17 @@
6870
<PackageReferenceInclude="Microsoft.Extensions.Hosting"Version="9.0.4" />
6971
<PackageReferenceInclude="Microsoft.Extensions.Options"Version="9.0.4" />
7072
<PackageReferenceInclude="Microsoft.WindowsAppSDK"Version="1.6.250108002" />
73+
<PackageReferenceInclude="NetSparkleUpdater.SparkleUpdater"Version="3.0.2" />
7174
<PackageReferenceInclude="Serilog.Extensions.Hosting"Version="9.0.0" />
7275
<PackageReferenceInclude="Serilog.Settings.Configuration"Version="9.0.0" />
7376
<PackageReferenceInclude="Serilog.Sinks.File"Version="6.0.0" />
7477
<PackageReferenceInclude="WinUIEx"Version="2.5.1" />
7578
</ItemGroup>
7679

80+
<ItemGroupCondition="'$(Configuration)' == 'Debug'">
81+
<PackageReferenceInclude="Serilog.Sinks.Debug"Version="3.0.0" />
82+
</ItemGroup>
83+
7784
<ItemGroup>
7885
<ProjectReferenceInclude="..\CoderSdk\CoderSdk.csproj" />
7986
<ProjectReferenceInclude="..\MutagenSdk\MutagenSdk.csproj" />

‎App/App.xaml.cs

Lines changed: 69 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -16,37 +16,46 @@
1616
usingMicrosoft.Extensions.DependencyInjection;
1717
usingMicrosoft.Extensions.Hosting;
1818
usingMicrosoft.Extensions.Logging;
19+
usingMicrosoft.UI.Dispatching;
1920
usingMicrosoft.UI.Xaml;
2021
usingMicrosoft.Win32;
2122
usingMicrosoft.Windows.AppLifecycle;
2223
usingMicrosoft.Windows.AppNotifications;
24+
usingNetSparkleUpdater.Interfaces;
2325
usingSerilog;
2426
usingLaunchActivatedEventArgs=Microsoft.UI.Xaml.LaunchActivatedEventArgs;
2527

2628
namespaceCoder.Desktop.App;
2729

28-
publicpartialclassApp:Application
30+
publicpartialclassApp:Application,IDispatcherQueueManager
2931
{
30-
privatereadonlyIServiceProvider_services;
31-
32-
privatebool_handleWindowClosed=true;
3332
privateconststringMutagenControllerConfigSection="MutagenController";
33+
privateconststringUpdaterConfigSection="Updater";
3434

3535
#if!DEBUG
3636
privateconststringConfigSubKey=@"SOFTWARE\Coder Desktop\App";
37-
privateconststringlogFilename="app.log";
37+
privateconststringLogFilename="app.log";
38+
privateconststringDefaultLogLevel="Information";
3839
#else
3940
privateconststringConfigSubKey=@"SOFTWARE\Coder Desktop\DebugApp";
40-
privateconststringlogFilename="debug-app.log";
41+
privateconststringLogFilename="debug-app.log";
42+
privateconststringDefaultLogLevel="Debug";
4143
#endif
4244

45+
// HACK: This is exposed for dispatcher queue access. The notifier uses
46+
// this to ensure action callbacks run in the UI thread (as
47+
// activation events aren't in the main thread).
48+
publicTrayWindow?TrayWindow;
49+
50+
privatereadonlyIServiceProvider_services;
4351
privatereadonlyILogger<App>_logger;
4452
privatereadonlyIUriHandler_uriHandler;
45-
53+
privatereadonlyIUserNotifier_userNotifier;
4654
privatereadonlyISettingsManager<CoderConnectSettings>_settingsManager;
47-
4855
privatereadonlyIHostApplicationLifetime_appLifetime;
4956

57+
privatebool_handleWindowClosed=true;
58+
5059
publicApp()
5160
{
5261
varbuilder=Host.CreateApplicationBuilder();
@@ -58,7 +67,17 @@ public App()
5867
configBuilder.Add(
5968
newRegistryConfigurationSource(Registry.LocalMachine,ConfigSubKey));
6069
configBuilder.Add(
61-
newRegistryConfigurationSource(Registry.CurrentUser,ConfigSubKey));
70+
newRegistryConfigurationSource(
71+
Registry.CurrentUser,
72+
ConfigSubKey,
73+
// Block "Updater:" configuration from HKCU, so that updater
74+
// settings can only be set at the HKLM level.
75+
//
76+
// HACK: This isn't super robust, but the security risk is
77+
// minor anyway. Malicious apps running as the user could
78+
// likely override this setting by altering the memory of
79+
// this app.
80+
UpdaterConfigSection+":"));
6281

6382
varservices=builder.Services;
6483

@@ -71,6 +90,7 @@ public App()
7190
services.AddSingleton<ICoderApiClientFactory,CoderApiClientFactory>();
7291
services.AddSingleton<IAgentApiClientFactory,AgentApiClientFactory>();
7392

93+
services.AddSingleton<IDispatcherQueueManager>(_=>this);
7494
services.AddSingleton<ICredentialBackend>(_=>
7595
newWindowsCredentialBackend(WindowsCredentialBackend.CoderCredentialsTargetName));
7696
services.AddSingleton<ICredentialManager,CredentialManager>();
@@ -84,6 +104,12 @@ public App()
84104
services.AddSingleton<IRdpConnector,RdpConnector>();
85105
services.AddSingleton<IUriHandler,UriHandler>();
86106

107+
services.AddOptions<UpdaterConfig>()
108+
.Bind(builder.Configuration.GetSection(UpdaterConfigSection));
109+
services.AddSingleton<IUpdaterUpdateAvailableViewModelFactory,UpdaterUpdateAvailableViewModelFactory>();
110+
services.AddSingleton<IUIFactory,CoderSparkleUIFactory>();
111+
services.AddSingleton<IUpdateController,SparkleUpdateController>();
112+
87113
// SignInWindow views and view models
88114
services.AddTransient<SignInViewModel>();
89115
services.AddTransient<SignInWindow>();
@@ -119,6 +145,7 @@ public App()
119145
_services=services.BuildServiceProvider();
120146
_logger=_services.GetRequiredService<ILogger<App>>();
121147
_uriHandler=_services.GetRequiredService<IUriHandler>();
148+
_userNotifier=_services.GetRequiredService<IUserNotifier>();
122149
_settingsManager=_services.GetRequiredService<ISettingsManager<CoderConnectSettings>>();
123150
_appLifetime=_services.GetRequiredService<IHostApplicationLifetime>();
124151

@@ -142,16 +169,18 @@ protected override void OnLaunched(LaunchActivatedEventArgs args)
142169
{
143170
_logger.LogInformation("new instance launched");
144171

145-
_=InitializeServicesAsync(_appLifetime.ApplicationStopping);
146-
147172
// Prevent the TrayWindow from closing, just hide it.
148-
vartrayWindow=_services.GetRequiredService<TrayWindow>();
149-
trayWindow.Closed+=(_,closedArgs)=>
173+
if(TrayWindow!=null)
174+
thrownewInvalidOperationException("OnLaunched was called multiple times? TrayWindow is already set");
175+
TrayWindow=_services.GetRequiredService<TrayWindow>();
176+
TrayWindow.Closed+=(_,closedArgs)=>
150177
{
151178
if(!_handleWindowClosed)return;
152179
closedArgs.Handled=true;
153-
trayWindow.AppWindow.Hide();
180+
TrayWindow.AppWindow.Hide();
154181
};
182+
183+
_=InitializeServicesAsync(_appLifetime.ApplicationStopping);
155184
}
156185

157186
/// <summary>
@@ -261,27 +290,49 @@ public void OnActivated(object? sender, AppActivationArguments args)
261290

262291
publicvoidHandleNotification(AppNotificationManager?sender,AppNotificationActivatedEventArgsargs)
263292
{
264-
// right now, we don't do anything other than log
265-
_logger.LogInformation("handled notification activation");
293+
_logger.LogInformation("handled notification activation: {Argument}",args.Argument);
294+
_userNotifier.HandleNotificationActivation(args.Arguments);
266295
}
267296

268297
privatestaticvoidAddDefaultConfig(IConfigurationBuilderbuilder)
269298
{
270299
varlogPath=Path.Combine(
271300
Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),
272301
"CoderDesktop",
273-
logFilename);
302+
LogFilename);
274303
builder.AddInMemoryCollection(newDictionary<string,string?>
275304
{
276305
[MutagenControllerConfigSection+":MutagenExecutablePath"]=@"C:\mutagen.exe",
306+
277307
["Serilog:Using:0"]="Serilog.Sinks.File",
278-
["Serilog:MinimumLevel"]="Information",
308+
["Serilog:MinimumLevel"]=DefaultLogLevel,
279309
["Serilog:Enrich:0"]="FromLogContext",
280310
["Serilog:WriteTo:0:Name"]="File",
281311
["Serilog:WriteTo:0:Args:path"]=logPath,
282312
["Serilog:WriteTo:0:Args:outputTemplate"]=
283313
"{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz} [{Level:u3}] {SourceContext} - {Message:lj}{NewLine}{Exception}",
284314
["Serilog:WriteTo:0:Args:rollingInterval"]="Day",
315+
316+
#ifDEBUG
317+
["Serilog:Using:1"]="Serilog.Sinks.Debug",
318+
["Serilog:Enrich:1"]="FromLogContext",
319+
["Serilog:WriteTo:1:Name"]="Debug",
320+
["Serilog:WriteTo:1:Args:outputTemplate"]=
321+
"{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz} [{Level:u3}] {SourceContext} - {Message:lj}{NewLine}{Exception}",
322+
#endif
285323
});
286324
}
325+
326+
publicvoidRunInUiThread(DispatcherQueueHandleraction)
327+
{
328+
vardispatcherQueue=TrayWindow?.DispatcherQueue;
329+
if(dispatcherQueueisnull)
330+
thrownewInvalidOperationException("DispatcherQueue is not available");
331+
if(dispatcherQueue.HasThreadAccess)
332+
{
333+
action();
334+
return;
335+
}
336+
dispatcherQueue.TryEnqueue(action);
337+
}
287338
}

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp