Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 1c5e4d9

Browse files
authoredMar 1, 2025··
feat: installer and uninstaller (#32)
Adds new package `Installer` which uses [WixSharp](https://github.com/oleg-shilo/wixsharp) to build WixToolset installers for WIndows. We build two installers: - `Coder Desktop (Core)`, which installs the app files, service files, VPN files (`wintun.dll`), creates a system service and an app shortcut in the start menu - `Coder Desktop`, which will netinstall the .NET runtime if it's not installed and then chain install `Coder Desktop (Core) We will only be shipping the `Coder Desktop` installer, which contains the Core installer. The chained Core installation doesn't show up in Settings > Apps. ![image](https://github.com/user-attachments/assets/0dbcc2fb-8618-4f7e-8973-8a8226c1f3e0) ![image](https://github.com/user-attachments/assets/8a33ac0d-f2aa-4dfe-a0df-efd7da3d1d22) ![image](https://github.com/user-attachments/assets/cc5bfc38-2587-4f83-9586-c21e3d663dda) ![image](https://github.com/user-attachments/assets/6e15ed0e-273c-4dfc-99ce-22b35ef5668f) Follow-up PR will integrate to CI and add authenticode signing to the installer binaries. Closes #11 Closes #30
1 parent e1ef774 commit 1c5e4d9

38 files changed

+1801
-266
lines changed
 

‎.github/workflows/release.yaml

Lines changed: 69 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,18 @@ on:
44
push:
55
tags:
66
- '*'
7+
workflow_dispatch:
8+
inputs:
9+
version:
10+
description: 'Version number (e.g. v1.2.3)'
11+
required: true
12+
default: 'v1.2.3'
713

814
permissions:
915
contents: write
1016

1117
jobs:
12-
build:
18+
release:
1319
runs-on: windows-latest
1420

1521
steps:
@@ -20,42 +26,83 @@ jobs:
2026
with:
2127
dotnet-version: '8.0.x'
2228

29+
# Necessary for signing Windows binaries.
30+
- name: Setup Java
31+
uses: actions/setup-java@3a4f6e1af504cf6a31855fa899c6aa5355ba6c12 # v4.7.0
32+
with:
33+
distribution: "zulu"
34+
java-version: "11.0"
35+
2336
- name: Get version from tag
2437
id: version
2538
shell: pwsh
2639
run: |
27-
$tag = $env:GITHUB_REF -replace 'refs/tags/',''
40+
$ErrorActionPreference = "Stop"
41+
if ($env:INPUT_VERSION) {
42+
$tag = $env:INPUT_VERSION
43+
} else {
44+
$tag = $env:GITHUB_REF -replace 'refs/tags/',''
45+
}
2846
if ($tag -notmatch '^v\d+\.\d+\.\d+$') {
29-
throw "Tag must be in format v1.2.3"
47+
throw "Version must be in format v1.2.3, got $tag"
3048
}
3149
$version = $tag -replace '^v',''
32-
$assemblyVersion = "$version.0"
33-
echo "VERSION=$version" >> $env:GITHUB_OUTPUT
34-
echo "ASSEMBLY_VERSION=$assemblyVersion" >> $env:GITHUB_OUTPUT
50+
$assemblyVersion = "$($version).0"
51+
Add-Content -Path $env:GITHUB_OUTPUT -Value "VERSION=$version"
52+
Add-Content -Path $env:GITHUB_OUTPUT -Value "ASSEMBLY_VERSION=$assemblyVersion"
53+
Write-Host "Version: $version"
54+
Write-Host "Assembly version: $assemblyVersion"
55+
env:
56+
INPUT_VERSION: ${{ inputs.version }}
3557

36-
- name: Build and publish x64
37-
run: |
38-
dotnet publish src/App/App.csproj -c Release -r win-x64 -p:Version=${{ steps.version.outputs.ASSEMBLY_VERSION }} -o publish/x64
39-
dotnet publish src/Vpn.Service/Vpn.Service.csproj -c Release -r win-x64 -p:Version=${{ steps.version.outputs.ASSEMBLY_VERSION }} -o publish/x64
58+
# Setup GCloud for signing Windows binaries.
59+
- name: Authenticate to Google Cloud
60+
id: gcloud_auth
61+
uses: google-github-actions/auth@71f986410dfbc7added4569d411d040a91dc6935 # v2.1.8
62+
with:
63+
workload_identity_provider: ${{ secrets.GCP_CODE_SIGNING_WORKLOAD_ID_PROVIDER }}
64+
service_account: ${{ secrets.GCP_CODE_SIGNING_SERVICE_ACCOUNT }}
65+
token_format: "access_token"
4066

41-
- name: Build and publish arm64
42-
run: |
43-
dotnet publish src/App/App.csproj -c Release -r win-arm64 -p:Version=${{ steps.version.outputs.ASSEMBLY_VERSION }} -o publish/arm64
44-
dotnet publish src/Vpn.Service/Vpn.Service.csproj -c Release -r win-arm64 -p:Version=${{ steps.version.outputs.ASSEMBLY_VERSION }} -o publish/arm64
67+
- name: Setup GCloud SDK
68+
uses: google-github-actions/setup-gcloud@77e7a554d41e2ee56fc945c52dfd3f33d12def9a # v2.1.4
4569

46-
- name: Create ZIP archives
70+
- name: scripts/Release.ps1
71+
id: release
4772
shell: pwsh
4873
run: |
49-
Compress-Archive -Path "publish/x64/*" -DestinationPath "./publish/CoderDesktop-${{ steps.version.outputs.VERSION }}-x64.zip"
50-
Compress-Archive -Path "publish/arm64/*" -DestinationPath "./publish/CoderDesktop-${{ steps.version.outputs.VERSION }}-arm64.zip"
74+
$ErrorActionPreference = "Stop"
5175
52-
- name: Create Release
53-
uses: softprops/action-gh-release@v1
76+
$env:EV_CERTIFICATE_PATH = Join-Path $env:TEMP "ev_cert.pem"
77+
$env:JSIGN_PATH = Join-Path $env:TEMP "jsign-6.0.jar"
78+
Invoke-WebRequest -Uri "https://github.com/ebourg/jsign/releases/download/6.0/jsign-6.0.jar" -OutFile $env:JSIGN_PATH
79+
80+
& ./scripts/Release.ps1 `
81+
-version ${{ steps.version.outputs.VERSION }} `
82+
-assemblyVersion ${{ steps.version.outputs.ASSEMBLY_VERSION }}
83+
if ($LASTEXITCODE -ne 0) { throw "Failed to publish" }
84+
env:
85+
EV_SIGNING_CERT: ${{ secrets.EV_SIGNING_CERT }}
86+
EV_KEYSTORE: ${{ secrets.EV_KEYSTORE }}
87+
EV_KEY: ${{ secrets.EV_KEY }}
88+
EV_TSA_URL: ${{ secrets.EV_TSA_URL }}
89+
GCLOUD_ACCESS_TOKEN: ${{ steps.gcloud_auth.outputs.access_token }}
90+
91+
- name: Upload artifact
92+
uses: actions/upload-artifact@v4
93+
with:
94+
name: publish
95+
path: .\publish\
96+
97+
- name: Create release
98+
uses: softprops/action-gh-release@v2
99+
if: startsWith(github.ref, 'refs/tags/')
54100
with:
55-
files: |
56-
./publish/CoderDesktop-${{ steps.version.outputs.VERSION }}-x64.zip
57-
./publish/CoderDesktop-${{ steps.version.outputs.VERSION }}-arm64.zip
58101
name: Release ${{ steps.version.outputs.VERSION }}
59102
generate_release_notes: true
103+
# We currently only release the bootstrappers, not the MSIs.
104+
files: |
105+
${{ steps.release.outputs.X64_OUTPUT_PATH }}
106+
${{ steps.release.outputs.ARM64_OUTPUT_PATH }}
60107
env:
61108
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

‎App/App.csproj

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,15 +17,22 @@
1717
<LangVersion>preview</LangVersion>
1818
<!-- We have our own implementation of main with exception handling -->
1919
<DefineConstants>DISABLE_XAML_GENERATED_MAIN</DefineConstants>
20+
21+
<AssemblyName>Coder Desktop</AssemblyName>
22+
<ApplicationIcon>coder.ico</ApplicationIcon>
2023
</PropertyGroup>
2124

2225
<PropertyGroup Condition="$(Configuration) == 'Release'">
23-
<PublishTrimmed>true</PublishTrimmed>
24-
<TrimMode>CopyUsed</TrimMode>
26+
<PublishTrimmed>false</PublishTrimmed>
27+
<!-- <TrimMode>CopyUsed</TrimMode> -->
2528
<PublishReadyToRun>true</PublishReadyToRun>
26-
<SelfContained>true</SelfContained>
29+
<SelfContained>false</SelfContained>
2730
</PropertyGroup>
2831

32+
<ItemGroup>
33+
<Content Include="coder.ico" />
34+
</ItemGroup>
35+
2936
<ItemGroup>
3037
<Manifest Include="$(ApplicationManifest)" />
3138
</ItemGroup>

‎App/Services/CredentialManager.cs

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -239,11 +239,9 @@ private struct CREDENTIAL
239239
public int Flags;
240240
public int Type;
241241

242-
[MarshalAs(UnmanagedType.LPWStr)]
243-
public string TargetName;
242+
[MarshalAs(UnmanagedType.LPWStr)] public string TargetName;
244243

245-
[MarshalAs(UnmanagedType.LPWStr)]
246-
public string Comment;
244+
[MarshalAs(UnmanagedType.LPWStr)] public string Comment;
247245

248246
public long LastWritten;
249247
public int CredentialBlobSize;
@@ -252,11 +250,9 @@ private struct CREDENTIAL
252250
public int AttributeCount;
253251
public IntPtr Attributes;
254252

255-
[MarshalAs(UnmanagedType.LPWStr)]
256-
public string TargetAlias;
253+
[MarshalAs(UnmanagedType.LPWStr)] public string TargetAlias;
257254

258-
[MarshalAs(UnmanagedType.LPWStr)]
259-
public string UserName;
255+
[MarshalAs(UnmanagedType.LPWStr)] public string UserName;
260256
}
261257
}
262258
}

‎App/ViewModels/SignInViewModel.cs

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -33,11 +33,9 @@ public partial class SignInViewModel : ObservableObject
3333
[NotifyPropertyChangedFor(nameof(ApiTokenError))]
3434
public partial bool ApiTokenTouched { get; set; } = false;
3535

36-
[ObservableProperty]
37-
public partial string? SignInError { get; set; } = null;
36+
[ObservableProperty] public partial string? SignInError { get; set; } = null;
3837

39-
[ObservableProperty]
40-
public partial bool SignInLoading { get; set; } = false;
38+
[ObservableProperty] public partial bool SignInLoading { get; set; } = false;
4139

4240
public string? CoderUrlError => CoderUrlTouched ? _coderUrlError : null;
4341

‎App/ViewModels/TrayWindowDisconnectedViewModel.cs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,7 @@ public partial class TrayWindowDisconnectedViewModel : ObservableObject
1010
{
1111
private readonly IRpcController _rpcController;
1212

13-
[ObservableProperty]
14-
public partial bool ReconnectButtonEnabled { get; set; } = true;
13+
[ObservableProperty] public partial bool ReconnectButtonEnabled { get; set; } = true;
1514

1615
public TrayWindowDisconnectedViewModel(IRpcController rpcController)
1716
{

‎App/ViewModels/TrayWindowViewModel.cs

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -23,15 +23,12 @@ public partial class TrayWindowViewModel : ObservableObject
2323

2424
private DispatcherQueue? _dispatcherQueue;
2525

26-
[ObservableProperty]
27-
public partial VpnLifecycle VpnLifecycle { get; set; } = VpnLifecycle.Unknown;
26+
[ObservableProperty] public partial VpnLifecycle VpnLifecycle { get; set; } = VpnLifecycle.Unknown;
2827

2928
// This is a separate property because we need the switch to be 2-way.
30-
[ObservableProperty]
31-
public partial bool VpnSwitchActive { get; set; } = false;
29+
[ObservableProperty] public partial bool VpnSwitchActive { get; set; } = false;
3230

33-
[ObservableProperty]
34-
public partial string? VpnFailedMessage { get; set; } = null;
31+
[ObservableProperty] public partial string? VpnFailedMessage { get; set; } = null;
3532

3633
[ObservableProperty]
3734
[NotifyPropertyChangedFor(nameof(NoAgents))]
@@ -49,8 +46,7 @@ public partial class TrayWindowViewModel : ObservableObject
4946

5047
public IEnumerable<AgentViewModel> VisibleAgents => ShowAllAgents ? Agents : Agents.Take(MaxAgents);
5148

52-
[ObservableProperty]
53-
public partial string DashboardUrl { get; set; } = "https://coder.com";
49+
[ObservableProperty] public partial string DashboardUrl { get; set; } = "https://coder.com";
5450

5551
public TrayWindowViewModel(IRpcController rpcController, ICredentialManager credentialManager)
5652
{

‎App/coder.ico

422 KB
Binary file not shown.

‎App/packages.lock.json

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -112,17 +112,17 @@
112112
"System.Collections.Immutable": "9.0.0"
113113
}
114114
},
115-
"codersdk": {
115+
"Coder.Desktop.CoderSdk": {
116116
"type": "Project"
117117
},
118-
"vpn": {
118+
"Coder.Desktop.Vpn": {
119119
"type": "Project",
120120
"dependencies": {
121-
"System.IO.Pipelines": "[9.0.1, )",
122-
"Vpn.Proto": "[1.0.0, )"
121+
"Coder.Desktop.Vpn.Proto": "[1.0.0, )",
122+
"System.IO.Pipelines": "[9.0.1, )"
123123
}
124124
},
125-
"vpn.proto": {
125+
"Coder.Desktop.Vpn.Proto": {
126126
"type": "Project",
127127
"dependencies": {
128128
"Google.Protobuf": "[3.29.3, )"

‎Coder.Desktop.sln

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "App", "App\App.csproj", "{8
2121
EndProject
2222
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Vpn.DebugClient", "Vpn.DebugClient\Vpn.DebugClient.csproj", "{1BBFDF88-B25F-4C07-99C2-30DA384DB730}"
2323
EndProject
24+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Installer", "Installer\Installer.csproj", "{39F5B55A-09D8-477D-A3FA-ADAC29C52605}"
25+
EndProject
2426
Global
2527
GlobalSection(SolutionConfigurationPlatforms) = preSolution
2628
Debug|Any CPU = Debug|Any CPU
@@ -185,6 +187,22 @@ Global
185187
{1BBFDF88-B25F-4C07-99C2-30DA384DB730}.Release|x64.Build.0 = Release|Any CPU
186188
{1BBFDF88-B25F-4C07-99C2-30DA384DB730}.Release|x86.ActiveCfg = Release|Any CPU
187189
{1BBFDF88-B25F-4C07-99C2-30DA384DB730}.Release|x86.Build.0 = Release|Any CPU
190+
{39F5B55A-09D8-477D-A3FA-ADAC29C52605}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
191+
{39F5B55A-09D8-477D-A3FA-ADAC29C52605}.Debug|Any CPU.Build.0 = Debug|Any CPU
192+
{39F5B55A-09D8-477D-A3FA-ADAC29C52605}.Debug|ARM64.ActiveCfg = Debug|Any CPU
193+
{39F5B55A-09D8-477D-A3FA-ADAC29C52605}.Debug|ARM64.Build.0 = Debug|Any CPU
194+
{39F5B55A-09D8-477D-A3FA-ADAC29C52605}.Debug|x64.ActiveCfg = Debug|Any CPU
195+
{39F5B55A-09D8-477D-A3FA-ADAC29C52605}.Debug|x64.Build.0 = Debug|Any CPU
196+
{39F5B55A-09D8-477D-A3FA-ADAC29C52605}.Debug|x86.ActiveCfg = Debug|Any CPU
197+
{39F5B55A-09D8-477D-A3FA-ADAC29C52605}.Debug|x86.Build.0 = Debug|Any CPU
198+
{39F5B55A-09D8-477D-A3FA-ADAC29C52605}.Release|Any CPU.ActiveCfg = Release|Any CPU
199+
{39F5B55A-09D8-477D-A3FA-ADAC29C52605}.Release|Any CPU.Build.0 = Release|Any CPU
200+
{39F5B55A-09D8-477D-A3FA-ADAC29C52605}.Release|ARM64.ActiveCfg = Release|Any CPU
201+
{39F5B55A-09D8-477D-A3FA-ADAC29C52605}.Release|ARM64.Build.0 = Release|Any CPU
202+
{39F5B55A-09D8-477D-A3FA-ADAC29C52605}.Release|x64.ActiveCfg = Release|Any CPU
203+
{39F5B55A-09D8-477D-A3FA-ADAC29C52605}.Release|x64.Build.0 = Release|Any CPU
204+
{39F5B55A-09D8-477D-A3FA-ADAC29C52605}.Release|x86.ActiveCfg = Release|Any CPU
205+
{39F5B55A-09D8-477D-A3FA-ADAC29C52605}.Release|x86.Build.0 = Release|Any CPU
188206
EndGlobalSection
189207
GlobalSection(SolutionProperties) = preSolution
190208
HideSolutionNode = FALSE

‎CoderSdk/CoderSdk.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
<Project Sdk="Microsoft.NET.Sdk">
22
<PropertyGroup>
3+
<AssemblyName>Coder.Desktop.CoderSdk</AssemblyName>
34
<RootNamespace>Coder.Desktop.CoderSdk</RootNamespace>
45
<TargetFramework>net8.0</TargetFramework>
56
<ImplicitUsings>enable</ImplicitUsings>

‎Installer/Installer.csproj

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<AssemblyName>Coder.Desktop.Installer</AssemblyName>
5+
<RootNamespace>Coder.Desktop.Installer</RootNamespace>
6+
<OutputType>Exe</OutputType>
7+
<TargetFramework>net481</TargetFramework>
8+
<LangVersion>13.0</LangVersion>
9+
</PropertyGroup>
10+
11+
<ItemGroup>
12+
<None Remove="*.msi" />
13+
<None Remove="*.exe" />
14+
<None Remove="*.wxs" />
15+
<None Remove="*.wixpdb" />
16+
<None Remove="*.wixobj" />
17+
</ItemGroup>
18+
19+
<ItemGroup>
20+
<PackageReference Include="WixSharp_wix4" Version="2.6.0" />
21+
<PackageReference Include="WixSharp_wix4.bin" Version="2.6.0" />
22+
<PackageReference Include="CommandLineParser" Version="2.9.1" />
23+
</ItemGroup>
24+
</Project>

‎Installer/Program.cs

Lines changed: 438 additions & 0 deletions
Large diffs are not rendered by default.

‎Publish-Alpha.ps1

Lines changed: 0 additions & 178 deletions
This file was deleted.

‎Tests.Vpn.Proto/Tests.Vpn.Proto.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
<Project Sdk="Microsoft.NET.Sdk">
22

33
<PropertyGroup>
4+
<AssemblyName>Coder.Desktop.Tests.Vpn.Proto</AssemblyName>
45
<RootNamespace>Coder.Desktop.Tests.Vpn.Proto</RootNamespace>
56
<TargetFramework>net8.0</TargetFramework>
67
<ImplicitUsings>enable</ImplicitUsings>

‎Tests.Vpn.Proto/packages.lock.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@
7373
"resolved": "1.6.0",
7474
"contentHash": "COC1aiAJjCoA5GBF+QKL2uLqEBew4JsCkQmoHKbN3TlOZKa2fKLz5CpiRQKDz0RsAOEGsVKqOD5bomsXq/4STQ=="
7575
},
76-
"vpn.proto": {
76+
"Coder.Desktop.Vpn.Proto": {
7777
"type": "Project",
7878
"dependencies": {
7979
"Google.Protobuf": "[3.29.3, )"

‎Tests.Vpn.Service/Tests.Vpn.Service.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
<Project Sdk="Microsoft.NET.Sdk">
22

33
<PropertyGroup>
4+
<AssemblyName>Coder.Desktop.Tests.Vpn.Service</AssemblyName>
45
<RootNamespace>Coder.Desktop.Tests.Vpn.Service</RootNamespace>
56
<TargetFramework>net8.0-windows</TargetFramework>
67
<ImplicitUsings>enable</ImplicitUsings>

‎Tests.Vpn.Service/packages.lock.json

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -467,35 +467,35 @@
467467
"System.Text.Encodings.Web": "9.0.1"
468468
}
469469
},
470-
"codersdk": {
470+
"Coder.Desktop.CoderSdk": {
471471
"type": "Project"
472472
},
473-
"vpn": {
473+
"Coder.Desktop.Vpn": {
474474
"type": "Project",
475475
"dependencies": {
476-
"System.IO.Pipelines": "[9.0.1, )",
477-
"Vpn.Proto": "[1.0.0, )"
476+
"Coder.Desktop.Vpn.Proto": "[1.0.0, )",
477+
"System.IO.Pipelines": "[9.0.1, )"
478478
}
479479
},
480-
"vpn.proto": {
480+
"Coder.Desktop.Vpn.Proto": {
481481
"type": "Project",
482482
"dependencies": {
483483
"Google.Protobuf": "[3.29.3, )"
484484
}
485485
},
486-
"vpn.service": {
486+
"CoderVpnService": {
487487
"type": "Project",
488488
"dependencies": {
489-
"CoderSdk": "[1.0.0, )",
489+
"Coder.Desktop.CoderSdk": "[1.0.0, )",
490+
"Coder.Desktop.Vpn": "[1.0.0, )",
490491
"Microsoft.Extensions.Hosting": "[9.0.1, )",
491492
"Microsoft.Extensions.Hosting.WindowsServices": "[9.0.1, )",
492493
"Microsoft.Extensions.Options.DataAnnotations": "[9.0.1, )",
493494
"Microsoft.Security.Extensions": "[1.3.0, )",
494495
"Semver": "[3.0.0, )",
495496
"Serilog.Extensions.Hosting": "[9.0.0, )",
496497
"Serilog.Sinks.Console": "[6.0.0, )",
497-
"Serilog.Sinks.File": "[6.0.0, )",
498-
"Vpn": "[1.0.0, )"
498+
"Serilog.Sinks.File": "[6.0.0, )"
499499
}
500500
}
501501
}

‎Tests.Vpn/Tests.Vpn.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
<Project Sdk="Microsoft.NET.Sdk">
22

33
<PropertyGroup>
4+
<AssemblyName>Coder.Desktop.Tests.Vpn</AssemblyName>
45
<RootNamespace>Coder.Desktop.Tests.Vpn</RootNamespace>
56
<TargetFramework>net8.0</TargetFramework>
67
<ImplicitUsings>enable</ImplicitUsings>

‎Tests.Vpn/packages.lock.json

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -78,14 +78,14 @@
7878
"resolved": "1.6.0",
7979
"contentHash": "COC1aiAJjCoA5GBF+QKL2uLqEBew4JsCkQmoHKbN3TlOZKa2fKLz5CpiRQKDz0RsAOEGsVKqOD5bomsXq/4STQ=="
8080
},
81-
"vpn": {
81+
"Coder.Desktop.Vpn": {
8282
"type": "Project",
8383
"dependencies": {
84-
"System.IO.Pipelines": "[9.0.1, )",
85-
"Vpn.Proto": "[1.0.0, )"
84+
"Coder.Desktop.Vpn.Proto": "[1.0.0, )",
85+
"System.IO.Pipelines": "[9.0.1, )"
8686
}
8787
},
88-
"vpn.proto": {
88+
"Coder.Desktop.Vpn.Proto": {
8989
"type": "Project",
9090
"dependencies": {
9191
"Google.Protobuf": "[3.29.3, )"

‎Vpn.DebugClient/Vpn.DebugClient.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
<Project Sdk="Microsoft.NET.Sdk">
22

33
<PropertyGroup>
4+
<AssemblyName>Coder.Desktop.Vpn.DebugClient</AssemblyName>
45
<RootNamespace>Coder.Desktop.Vpn.DebugClient</RootNamespace>
56
<OutputType>Exe</OutputType>
67
<TargetFramework>net8.0</TargetFramework>

‎Vpn.DebugClient/packages.lock.json

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,14 +12,14 @@
1212
"resolved": "9.0.1",
1313
"contentHash": "uXf5o8eV/gtzDQY4lGROLFMWQvcViKcF8o4Q6KpIOjloAQXrnscQSu6gTxYJMHuNJnh7szIF9AzkaEq+zDLoEg=="
1414
},
15-
"vpn": {
15+
"Coder.Desktop.Vpn": {
1616
"type": "Project",
1717
"dependencies": {
18-
"System.IO.Pipelines": "[9.0.1, )",
19-
"Vpn.Proto": "[1.0.0, )"
18+
"Coder.Desktop.Vpn.Proto": "[1.0.0, )",
19+
"System.IO.Pipelines": "[9.0.1, )"
2020
}
2121
},
22-
"vpn.proto": {
22+
"Coder.Desktop.Vpn.Proto": {
2323
"type": "Project",
2424
"dependencies": {
2525
"Google.Protobuf": "[3.29.3, )"

‎Vpn.Proto/Vpn.Proto.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
<Project Sdk="Microsoft.NET.Sdk">
22

33
<PropertyGroup>
4+
<AssemblyName>Coder.Desktop.Vpn.Proto</AssemblyName>
45
<RootNamespace>Coder.Desktop.Vpn.Proto</RootNamespace>
56
<TargetFramework>net8.0</TargetFramework>
67
<ImplicitUsings>enable</ImplicitUsings>

‎Vpn.Service/ManagerConfig.cs

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,7 @@ public class ManagerConfig
88
[RegularExpression(@"^([a-zA-Z0-9_-]+\.)*[a-zA-Z0-9_-]+$")]
99
public string ServiceRpcPipeName { get; set; } = "Coder.Desktop.Vpn";
1010

11-
[Required]
12-
public string TunnelBinaryPath { get; set; } = @"C:\coder-vpn.exe";
11+
[Required] public string TunnelBinaryPath { get; set; } = @"C:\coder-vpn.exe";
1312

14-
[Required]
15-
public string LogFileLocation { get; set; } = @"C:\coder-desktop-service.log";
13+
[Required] public string LogFileLocation { get; set; } = @"C:\coder-desktop-service.log";
1614
}

‎Vpn.Service/ManagerRpc.cs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,15 @@ public ManagerRpc(IOptions<ManagerConfig> config, ILogger<ManagerRpc> logger)
5151
public async ValueTask DisposeAsync()
5252
{
5353
await _cts.CancelAsync();
54-
while (!_activeClients.IsEmpty) await Task.WhenAny(_activeClients.Values.Select(c => c.Task));
54+
try
55+
{
56+
while (!_activeClients.IsEmpty)
57+
await Task.WhenAny(_activeClients.Values.Select(c => c.Task));
58+
}
59+
catch
60+
{
61+
}
62+
5563
_cts.Dispose();
5664
GC.SuppressFinalize(this);
5765
}

‎Vpn.Service/Program.cs

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -20,16 +20,17 @@ public static class Program
2020
private const string FileOutputTemplate =
2121
"{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz} [{Level:u3}] {SourceContext} - {Message:lj}{NewLine}{Exception}";
2222

23-
private static readonly ILogger MainLogger = Log.ForContext("SourceContext", "Coder.Desktop.Vpn.Service.Program");
23+
private static ILogger MainLogger => Log.ForContext("SourceContext", "Coder.Desktop.Vpn.Service.Program");
2424

25-
private static LoggerConfiguration LogConfig = new LoggerConfiguration()
25+
private static LoggerConfiguration BaseLogConfig => new LoggerConfiguration()
2626
.Enrich.FromLogContext()
2727
.MinimumLevel.Debug()
2828
.WriteTo.Console(outputTemplate: ConsoleOutputTemplate);
2929

3030
public static async Task<int> Main(string[] args)
3131
{
32-
Log.Logger = LogConfig.CreateLogger();
32+
Log.Logger = BaseLogConfig.CreateLogger();
33+
MainLogger.Information("Application is starting");
3334
try
3435
{
3536
await BuildAndRun(args);
@@ -42,6 +43,7 @@ public static async Task<int> Main(string[] args)
4243
}
4344
finally
4445
{
46+
MainLogger.Information("Application is shutting down");
4547
await Log.CloseAndFlushAsync();
4648
}
4749
}
@@ -53,7 +55,7 @@ private static async Task BuildAndRun(string[] args)
5355
// Configuration sources
5456
builder.Configuration.Sources.Clear();
5557
(builder.Configuration as IConfigurationBuilder).Add(
56-
new RegistryConfigurationSource(Registry.LocalMachine, @"SOFTWARE\Coder\Coder Desktop"));
58+
new RegistryConfigurationSource(Registry.LocalMachine, @"SOFTWARE\Coder Desktop"));
5759
builder.Configuration.AddEnvironmentVariables("CODER_MANAGER_");
5860
builder.Configuration.AddCommandLine(args);
5961

@@ -63,9 +65,9 @@ private static async Task BuildAndRun(string[] args)
6365
.ValidateDataAnnotations()
6466
.PostConfigure(config =>
6567
{
66-
LogConfig = LogConfig
67-
.WriteTo.File(config.LogFileLocation, outputTemplate: FileOutputTemplate);
68-
Log.Logger = LogConfig.CreateLogger();
68+
Log.Logger = BaseLogConfig
69+
.WriteTo.File(config.LogFileLocation, outputTemplate: FileOutputTemplate)
70+
.CreateLogger();
6971
});
7072

7173
// Logging

‎Vpn.Service/Vpn.Service.csproj

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,22 @@
77
<ImplicitUsings>enable</ImplicitUsings>
88
<Nullable>enable</Nullable>
99
<RestorePackagesWithLockFile>true</RestorePackagesWithLockFile>
10+
11+
<AssemblyName>CoderVpnService</AssemblyName>
12+
<ApplicationIcon>coder.ico</ApplicationIcon>
13+
</PropertyGroup>
14+
15+
<PropertyGroup Condition="$(Configuration) == 'Release'">
16+
<PublishTrimmed>false</PublishTrimmed>
17+
<!-- <TrimMode>CopyUsed</TrimMode> -->
18+
<PublishReadyToRun>true</PublishReadyToRun>
19+
<SelfContained>false</SelfContained>
1020
</PropertyGroup>
1121

22+
<ItemGroup>
23+
<Content Include="coder.ico" />
24+
</ItemGroup>
25+
1226
<ItemGroup>
1327
<PackageReference Include="Microsoft.Extensions.Hosting" Version="9.0.1" />
1428
<PackageReference Include="Microsoft.Extensions.Hosting.WindowsServices" Version="9.0.1" />

‎Vpn.Service/coder.ico

422 KB
Binary file not shown.

‎Vpn.Service/packages.lock.json

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -409,17 +409,17 @@
409409
"System.Text.Encodings.Web": "9.0.1"
410410
}
411411
},
412-
"codersdk": {
412+
"Coder.Desktop.CoderSdk": {
413413
"type": "Project"
414414
},
415-
"vpn": {
415+
"Coder.Desktop.Vpn": {
416416
"type": "Project",
417417
"dependencies": {
418-
"System.IO.Pipelines": "[9.0.1, )",
419-
"Vpn.Proto": "[1.0.0, )"
418+
"Coder.Desktop.Vpn.Proto": "[1.0.0, )",
419+
"System.IO.Pipelines": "[9.0.1, )"
420420
}
421421
},
422-
"vpn.proto": {
422+
"Coder.Desktop.Vpn.Proto": {
423423
"type": "Project",
424424
"dependencies": {
425425
"Google.Protobuf": "[3.29.3, )"

‎Vpn/Vpn.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
<Project Sdk="Microsoft.NET.Sdk">
22

33
<PropertyGroup>
4+
<AssemblyName>Coder.Desktop.Vpn</AssemblyName>
45
<RootNamespace>Coder.Desktop.Vpn</RootNamespace>
56
<TargetFramework>net8.0</TargetFramework>
67
<ImplicitUsings>enable</ImplicitUsings>

‎Vpn/packages.lock.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
"resolved": "3.29.3",
1414
"contentHash": "t7nZFFUFwigCwZ+nIXHDLweXvwIpsOXi+P7J7smPT/QjI3EKxnCzTQOhBqyEh6XEzc/pNH+bCFOOSjatrPt6Tw=="
1515
},
16-
"vpn.proto": {
16+
"Coder.Desktop.Vpn.Proto": {
1717
"type": "Project",
1818
"dependencies": {
1919
"Google.Protobuf": "[3.29.3, )"

‎scripts/Publish.ps1

Lines changed: 165 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,165 @@
1+
# Usage: Publish.ps1 -arch <x64|arm64> -version <version> [-buildPath <path>] [-outputPath <path>]
2+
param (
3+
[ValidateSet("x64", "arm64")]
4+
[Parameter(Mandatory = $true)]
5+
[string] $arch,
6+
7+
[Parameter(Mandatory = $true)]
8+
[ValidatePattern("^\d+\.\d+\.\d+\.\d+$")]
9+
[string] $version,
10+
11+
[Parameter(Mandatory = $false)]
12+
[string] $msiOutputPath = "", # defaults to "publish\CoderDesktopCore-$version-$arch.msi"
13+
14+
[Parameter(Mandatory = $false)]
15+
[string] $outputPath = "", # defaults to "publish\CoderDesktop-$version-$arch.exe"
16+
17+
[Parameter(Mandatory = $false)]
18+
[switch] $keepBuildTemp = $false,
19+
20+
[Parameter(Mandatory = $false)]
21+
[switch] $sign = $false
22+
)
23+
24+
$ErrorActionPreference = "Stop"
25+
26+
$ourAssemblies = @(
27+
"Coder Desktop.exe",
28+
"Coder Desktop.dll",
29+
"CoderVpnService.exe",
30+
"CoderVpnService.dll",
31+
32+
"Coder.Desktop.CoderSdk.dll",
33+
"Coder.Desktop.Vpn.dll",
34+
"Coder.Desktop.Vpn.Proto.dll"
35+
)
36+
37+
function Find-Dependencies([string[]] $dependencies) {
38+
foreach ($dependency in $dependencies) {
39+
if (!(Get-Command $dependency -ErrorAction SilentlyContinue)) {
40+
throw "Missing dependency: $dependency"
41+
}
42+
}
43+
}
44+
45+
function Find-EnvironmentVariables([string[]] $variables) {
46+
foreach ($variable in $variables) {
47+
if (!(Get-Item env:$variable -ErrorAction SilentlyContinue)) {
48+
throw "Missing environment variable: $variable"
49+
}
50+
}
51+
}
52+
53+
if ($sign) {
54+
Write-Host "Signing is enabled"
55+
Find-Dependencies java
56+
Find-EnvironmentVariables @("JSIGN_PATH", "EV_KEYSTORE", "EV_KEY", "EV_CERTIFICATE_PATH", "EV_TSA_URL", "GCLOUD_ACCESS_TOKEN")
57+
}
58+
59+
function Add-CoderSignature([string] $path) {
60+
if (!$sign) {
61+
Write-Host "Skipping signing $path"
62+
return
63+
}
64+
65+
Write-Host "Signing $path"
66+
& java.exe -jar $env:JSIGN_PATH `
67+
--storetype GOOGLECLOUD `
68+
--storepass $env:GCLOUD_ACCESS_TOKEN `
69+
--keystore $env:EV_KEYSTORE `
70+
--alias $env:EV_KEY `
71+
--certfile $env:EV_CERTIFICATE_PATH `
72+
--tsmode RFC3161 `
73+
--tsaurl $env:EV_TSA_URL `
74+
$path
75+
if ($LASTEXITCODE -ne 0) { throw "Failed to sign $path" }
76+
}
77+
78+
# CD to the root of the repo
79+
$repoRoot = Resolve-Path (Join-Path $PSScriptRoot "..")
80+
Push-Location $repoRoot
81+
82+
if ($msiOutputPath -eq "") {
83+
$msiOutputPath = Join-Path $repoRoot "publish\CoderDesktopCore-$($version)-$($arch).msi"
84+
}
85+
if (Test-Path $msiOutputPath) {
86+
Remove-Item -Recurse -Force $msiOutputPath
87+
}
88+
89+
if ($outputPath -eq "") {
90+
$outputPath = Join-Path $repoRoot "publish\CoderDesktop-$($version)-$($arch).exe"
91+
}
92+
if (Test-Path $outputPath) {
93+
Remove-Item -Recurse -Force $outputPath
94+
}
95+
if (Test-Path $outputPath.Replace(".exe", ".wixpdb")) {
96+
Remove-Item -Recurse -Force $outputPath.Replace(".exe", ".wixpdb")
97+
}
98+
99+
# Create a publish directory
100+
$buildPath = Join-Path $repoRoot "publish\buildtemp-$($version)-$($arch)"
101+
if (Test-Path $buildPath) {
102+
Remove-Item -Recurse -Force $buildPath
103+
}
104+
New-Item -ItemType Directory -Path $buildPath -Force
105+
106+
# Build in release mode
107+
$servicePublishDir = Join-Path $buildPath "service"
108+
& dotnet.exe publish .\Vpn.Service\Vpn.Service.csproj -c Release -a $arch -o $servicePublishDir
109+
if ($LASTEXITCODE -ne 0) { throw "Failed to build Vpn.Service" }
110+
# App needs to be built with msbuild
111+
$appPublishDir = Join-Path $buildPath "app"
112+
$msbuildBinary = & "${env:ProgramFiles(x86)}\Microsoft Visual Studio\Installer\vswhere.exe" -latest -requires Microsoft.Component.MSBuild -find MSBuild\**\Bin\MSBuild.exe
113+
if ($LASTEXITCODE -ne 0) { throw "Failed to find MSBuild" }
114+
& $msbuildBinary .\App\App.csproj /p:Configuration=Release /p:Platform=$arch /p:OutputPath=$appPublishDir
115+
if ($LASTEXITCODE -ne 0) { throw "Failed to build App" }
116+
117+
# Find any files in the publish directory recursively that match any of our
118+
# assemblies and sign them.
119+
$toSign = Get-ChildItem -Path $buildPath -Recurse | Where-Object { $ourAssemblies -contains $_.Name }
120+
foreach ($file in $toSign) {
121+
Add-CoderSignature $file.FullName
122+
}
123+
124+
# Copy any additional files into the install directory
125+
Copy-Item "scripts\files\License.txt" $buildPath
126+
$vpnFilesPath = Join-Path $buildPath "vpn"
127+
New-Item -ItemType Directory -Path $vpnFilesPath -Force
128+
Copy-Item "scripts\files\LICENSE.WINTUN.txt" $vpnFilesPath
129+
$wintunDllPath = Join-Path $vpnFilesPath "wintun.dll"
130+
Copy-Item "scripts\files\wintun-*-$($arch).dll" $wintunDllPath
131+
132+
# Build the MSI installer
133+
& dotnet.exe run --project .\Installer\Installer.csproj -c Release -- `
134+
build-msi `
135+
--arch $arch `
136+
--version $version `
137+
--license-file "scripts\files\License.rtf" `
138+
--output-path $msiOutputPath `
139+
--icon-file "App\coder.ico" `
140+
--input-dir $buildPath `
141+
--service-exe "service\CoderVpnService.exe" `
142+
--service-name "Coder Desktop" `
143+
--app-exe "app\Coder Desktop.exe" `
144+
--vpn-dir "vpn" `
145+
--banner-bmp "scripts\files\WixUIBannerBmp.bmp" `
146+
--dialog-bmp "scripts\files\WixUIDialogBmp.bmp"
147+
if ($LASTEXITCODE -ne 0) { throw "Failed to build MSI" }
148+
Add-CoderSignature $msiOutputPath
149+
150+
# Build the bootstrapper
151+
& dotnet.exe run --project .\Installer\Installer.csproj -c Release -- `
152+
build-bootstrapper `
153+
--arch $arch `
154+
--version $version `
155+
--license-file "scripts\files\License.rtf" `
156+
--output-path $outputPath `
157+
--icon-file "App\coder.ico" `
158+
--msi-path $msiOutputPath `
159+
--logo-png "scripts\files\logo.png"
160+
if ($LASTEXITCODE -ne 0) { throw "Failed to build bootstrapper" }
161+
Add-CoderSignature $outputPath
162+
163+
if (!$keepBuildTemp) {
164+
Remove-Item -Recurse -Force $buildPath
165+
}

‎scripts/Release.ps1

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
# Usage: Release.ps1 -version <version>
2+
param (
3+
[Parameter(Mandatory = $true)]
4+
[ValidatePattern("^\d+\.\d+\.\d+\.\d+$")]
5+
[string] $version,
6+
7+
[Parameter(Mandatory = $true)]
8+
[ValidatePattern("^\d+\.\d+\.\d+\.\d+$")]
9+
[string] $assemblyVersion
10+
)
11+
12+
$ErrorActionPreference = "Stop"
13+
14+
foreach ($arch in @("x64", "arm64")) {
15+
Write-Host "::group::Publishing $arch"
16+
try {
17+
$archUpper = $arch.ToUpper()
18+
19+
$msiOutputPath = "publish/CoderDesktopCore-$version-$arch.msi"
20+
Add-Content -Path $env:GITHUB_OUTPUT -Value "$($archUpper)_MSI_OUTPUT_PATH=$msiOutputPath"
21+
Write-Host "MSI_OUTPUT_PATH=$msiOutputPath"
22+
23+
$outputPath = "publish/CoderDesktop-$version-$arch.exe"
24+
Add-Content -Path $env:GITHUB_OUTPUT -Value "$($archUpper)_OUTPUT_PATH=$outputPath"
25+
Write-Host "OUTPUT_PATH=$outputPath"
26+
27+
$publishScript = Join-Path $PSScriptRoot "Publish.ps1"
28+
& $publishScript `
29+
-version $assemblyVersion `
30+
-arch $arch `
31+
-msiOutputPath $msiOutputPath `
32+
-outputPath $outputPath `
33+
-sign
34+
if ($LASTEXITCODE -ne 0) { throw "Failed to publish" }
35+
36+
# Verify that the output exe is authenticode signed
37+
$sig = Get-AuthenticodeSignature $outputPath
38+
if ($sig.Status -ne "Valid") {
39+
throw "Output file is not authenticode signed"
40+
}
41+
else {
42+
Write-Host "Output file is authenticode signed"
43+
}
44+
}
45+
finally {
46+
Write-Host "::endgroup::"
47+
}
48+
}

‎scripts/files/LICENSE.WINTUN.txt

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
Prebuilt Binaries License
2+
-------------------------
3+
4+
1. DEFINITIONS. "Software" means the precise contents of the "wintun.dll"
5+
files that are included in the .zip file that contains this document as
6+
downloaded from wintun.net/builds.
7+
8+
2. LICENSE GRANT. WireGuard LLC grants to you a non-exclusive and
9+
non-transferable right to use Software for lawful purposes under certain
10+
obligations and limited rights as set forth in this agreement.
11+
12+
3. RESTRICTIONS. Software is owned and copyrighted by WireGuard LLC. It is
13+
licensed, not sold. Title to Software and all associated intellectual
14+
property rights are retained by WireGuard. You must not:
15+
a. reverse engineer, decompile, disassemble, extract from, or otherwise
16+
modify the Software;
17+
b. modify or create derivative work based upon Software in whole or in
18+
parts, except insofar as only the API interfaces of the "wintun.h" file
19+
distributed alongside the Software (the "Permitted API") are used;
20+
c. remove any proprietary notices, labels, or copyrights from the Software;
21+
d. resell, redistribute, lease, rent, transfer, sublicense, or otherwise
22+
transfer rights of the Software without the prior written consent of
23+
WireGuard LLC, except insofar as the Software is distributed alongside
24+
other software that uses the Software only via the Permitted API;
25+
e. use the name of WireGuard LLC, the WireGuard project, the Wintun
26+
project, or the names of its contributors to endorse or promote products
27+
derived from the Software without specific prior written consent.
28+
29+
4. LIMITED WARRANTY. THE SOFTWARE IS PROVIDED "AS IS" AND WITHOUT WARRANTY OF
30+
ANY KIND. WIREGUARD LLC HEREBY EXCLUDES AND DISCLAIMS ALL IMPLIED OR
31+
STATUTORY WARRANTIES, INCLUDING ANY WARRANTIES OF MERCHANTABILITY, FITNESS
32+
FOR A PARTICULAR PURPOSE, QUALITY, NON-INFRINGEMENT, TITLE, RESULTS,
33+
EFFORTS, OR QUIET ENJOYMENT. THERE IS NO WARRANTY THAT THE PRODUCT WILL BE
34+
ERROR-FREE OR WILL FUNCTION WITHOUT INTERRUPTION. YOU ASSUME THE ENTIRE
35+
RISK FOR THE RESULTS OBTAINED USING THE PRODUCT. TO THE EXTENT THAT
36+
WIREGUARD LLC MAY NOT DISCLAIM ANY WARRANTY AS A MATTER OF APPLICABLE LAW,
37+
THE SCOPE AND DURATION OF SUCH WARRANTY WILL BE THE MINIMUM PERMITTED UNDER
38+
SUCH LAW. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
39+
WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR
40+
A PARTICULAR PURPOSE OR NON-INFRINGEMENT ARE DISCLAIMED, EXCEPT TO THE
41+
EXTENT THAT THESE DISCLAIMERS ARE HELD TO BE LEGALLY INVALID.
42+
43+
5. LIMITATION OF LIABILITY. To the extent not prohibited by law, in no event
44+
WireGuard LLC or any third-party-developer will be liable for any lost
45+
revenue, profit or data or for special, indirect, consequential, incidental
46+
or punitive damages, however caused regardless of the theory of liability,
47+
arising out of or related to the use of or inability to use Software, even
48+
if WireGuard LLC has been advised of the possibility of such damages.
49+
Solely you are responsible for determining the appropriateness of using
50+
Software and accept full responsibility for all risks associated with its
51+
exercise of rights under this agreement, including but not limited to the
52+
risks and costs of program errors, compliance with applicable laws, damage
53+
to or loss of data, programs or equipment, and unavailability or
54+
interruption of operations. The foregoing limitations will apply even if
55+
the above stated warranty fails of its essential purpose. You acknowledge,
56+
that it is in the nature of software that software is complex and not
57+
completely free of errors. In no event shall WireGuard LLC or any
58+
third-party-developer be liable to you under any theory for any damages
59+
suffered by you or any user of Software or for any special, incidental,
60+
indirect, consequential or similar damages (including without limitation
61+
damages for loss of business profits, business interruption, loss of
62+
business information or any other pecuniary loss) arising out of the use or
63+
inability to use Software, even if WireGuard LLC has been advised of the
64+
possibility of such damages and regardless of the legal or quitable theory
65+
(contract, tort, or otherwise) upon which the claim is based.
66+
67+
6. TERMINATION. This agreement is affected until terminated. You may
68+
terminate this agreement at any time. This agreement will terminate
69+
immediately without notice from WireGuard LLC if you fail to comply with
70+
the terms and conditions of this agreement. Upon termination, you must
71+
delete Software and all copies of Software and cease all forms of
72+
distribution of Software.
73+
74+
7. SEVERABILITY. If any provision of this agreement is held to be
75+
unenforceable, this agreement will remain in effect with the provision
76+
omitted, unless omission would frustrate the intent of the parties, in
77+
which case this agreement will immediately terminate.
78+
79+
8. RESERVATION OF RIGHTS. All rights not expressly granted in this agreement
80+
are reserved by WireGuard LLC. For example, WireGuard LLC reserves the
81+
right at any time to cease development of Software, to alter distribution
82+
details, features, specifications, capabilities, functions, licensing
83+
terms, release dates, APIs, ABIs, general availability, or other
84+
characteristics of the Software.

‎scripts/files/License.rtf

Lines changed: 229 additions & 0 deletions
Large diffs are not rendered by default.

‎scripts/files/License.txt

Lines changed: 635 additions & 0 deletions
Large diffs are not rendered by default.

‎scripts/files/WixUIBannerBmp.bmp

112 KB
Binary file not shown.

‎scripts/files/WixUIDialogBmp.bmp

601 KB
Binary file not shown.

‎scripts/files/logo.png

33.8 KB
Loading

0 commit comments

Comments
 (0)
Please sign in to comment.