Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion .editorconfig
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,10 @@ tab_width = 4
trim_trailing_whitespace = true
insert_final_newline = true
charset = utf-8
end_of_line = crlf
# end_of_line is intentionally not set: the repository history contains a mix
# of LF and CRLF files, and newer dotnet format releases treat this rule as a
# hard error, turning every diff into whitespace churn. Match the existing
# line endings of the file you are editing.

resharper_csharp_brace_style = next_line
resharper_csharp_braces_for_foreach = not_required
Expand Down
68 changes: 60 additions & 8 deletions .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ on:
jobs:
fmt:
runs-on: windows-latest
timeout-minutes: 10
timeout-minutes: 15
steps:
- uses: actions/checkout@v4
- name: Setup dotnet
Expand All @@ -23,11 +23,11 @@ jobs:
- name: dotnet restore
run: dotnet restore --locked-mode /p:BuildWithNetFrameworkHostedCompiler=true
- name: dotnet format
run: dotnet format --verify-no-changes --no-restore
run: dotnet format Coder.Desktop.sln --verify-no-changes --no-restore

test:
test-windows:
runs-on: windows-latest
timeout-minutes: 10
timeout-minutes: 15
steps:
- uses: actions/checkout@v4
- name: Setup dotnet
Expand All @@ -44,16 +44,45 @@ jobs:
if: failure()
uses: actions/upload-artifact@v4
with:
name: test-results
name: test-results-windows
retention-days: 1
path: |
./**/bin
./**/obj
./**/TestResults

build:
test-linux:
runs-on: ubuntu-latest
timeout-minutes: 20
steps:
- uses: actions/checkout@v4
- name: Setup dotnet
uses: actions/setup-dotnet@v4
with:
dotnet-version: 8.0.x
cache: true
cache-dependency-path: '**/packages.lock.json'
- name: dotnet restore
run: dotnet restore Coder.Desktop.Linux.slnf --locked-mode
- name: dotnet build
run: dotnet build Coder.Desktop.Linux.slnf -c Release --no-restore
- name: dotnet test
run: |
dotnet test Tests.CoderSdk/Tests.CoderSdk.csproj -c Release --no-build -f net8.0
dotnet test Tests.Vpn.Proto/Tests.Vpn.Proto.csproj -c Release --no-build
dotnet test Tests.Vpn/Tests.Vpn.csproj -c Release --no-build -f net8.0
dotnet test Tests.Vpn.Service/Tests.Vpn.Service.csproj -c Release --no-build -f net8.0
- name: Upload TestResults
if: failure()
uses: actions/upload-artifact@v4
with:
name: test-results-linux
retention-days: 1
path: ./**/TestResults

build-windows:
runs-on: windows-latest
timeout-minutes: 10
timeout-minutes: 15
steps:
- uses: actions/checkout@v4
- name: Setup dotnet
Expand All @@ -68,11 +97,34 @@ jobs:
# projects we care about building. Doing a full publish includes test
# libraries and stuff which is pointless.
- name: dotnet publish Coder.Desktop.Vpn.Service
run: dotnet publish .\Vpn.Service\Vpn.Service.csproj --configuration Release --output .\publish\Vpn.Service
run: dotnet publish .\Vpn.Service\Vpn.Service.csproj --configuration Release -f net8.0-windows --output .\publish\Vpn.Service
- name: dotnet publish Coder.Desktop.App
run: dotnet publish .\App\App.csproj --configuration Release --output .\publish\App
- name: Upload artifact
uses: actions/upload-artifact@v4
with:
name: publish
path: .\publish\

build-linux:
runs-on: ubuntu-latest
timeout-minutes: 20
steps:
- uses: actions/checkout@v4
- name: Setup dotnet
uses: actions/setup-dotnet@v4
with:
dotnet-version: 8.0.x
cache: true
cache-dependency-path: '**/packages.lock.json'
- name: dotnet restore
run: dotnet restore Coder.Desktop.Linux.slnf --locked-mode
- name: dotnet publish Coder.Desktop.Vpn.Service
run: dotnet publish Vpn.Service/Vpn.Service.csproj --configuration Release -f net8.0 --output ./publish/Vpn.Service
- name: dotnet publish Coder.Desktop.App.Avalonia
run: dotnet publish App.Avalonia/App.Avalonia.csproj --configuration Release --output ./publish/App.Avalonia
- name: Upload artifact
uses: actions/upload-artifact@v4
with:
name: publish-linux
path: ./publish/
114 changes: 114 additions & 0 deletions .github/workflows/release-linux.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
name: release-linux

on:
push:
tags:
- 'v*'
workflow_dispatch:
inputs:
version:
description: 'Release version (e.g. 1.2.3)'
required: true

permissions:
contents: write

jobs:
prepare:
runs-on: ubuntu-latest
outputs:
version: ${{ steps.resolve_version.outputs.version }}
steps:
- name: Resolve version
id: resolve_version
shell: bash
run: |
set -euo pipefail
if [[ "${{ github.event_name }}" == "workflow_dispatch" ]]; then
version="${{ inputs.version }}"
else
version="${GITHUB_REF_NAME#v}"
fi
if [[ ! "$version" =~ ^[0-9]+\.[0-9]+\.[0-9]+([.-][0-9A-Za-z]+)*$ ]]; then
echo "Invalid version: $version" >&2
exit 1
fi
echo "version=$version" >> "$GITHUB_OUTPUT"
package:
runs-on: ubuntu-latest
needs: prepare
strategy:
fail-fast: false
matrix:
arch: [amd64, arm64]
steps:
- uses: actions/checkout@v4

- name: Setup .NET
uses: actions/setup-dotnet@v4
with:
dotnet-version: 8.0.x

- name: Install packaging dependencies
run: |
sudo apt-get update
sudo apt-get install -y ruby ruby-dev build-essential rpm dpkg-dev
sudo gem install --no-document fpm
- name: Build release packages
env:
VERSION: ${{ needs.prepare.outputs.version }}
OUT_DIR: ${{ github.workspace }}/dist
run: |
chmod +x Packaging.Linux/*.sh
./Packaging.Linux/build-release-packages.sh "${{ matrix.arch }}"
- name: Upload package artifacts (${{ matrix.arch }})
uses: actions/upload-artifact@v4
with:
name: linux-packages-${{ matrix.arch }}
path: dist/*

aur-source:
runs-on: ubuntu-latest
needs: prepare
steps:
- uses: actions/checkout@v4

- name: Build AUR source archive
env:
VERSION: ${{ needs.prepare.outputs.version }}
OUT_DIR: ${{ github.workspace }}/dist
run: |
chmod +x Packaging.Linux/*.sh
./Packaging.Linux/build-aur-source.sh
- name: Upload AUR source artifact
uses: actions/upload-artifact@v4
with:
name: linux-packages-aur
path: dist/*

release:
runs-on: ubuntu-latest
needs: [prepare, package, aur-source]
if: startsWith(github.ref, 'refs/tags/')
steps:
- name: Download all artifacts
uses: actions/download-artifact@v4
with:
pattern: linux-packages-*
path: release-assets
merge-multiple: true

- name: Publish GitHub release
uses: softprops/action-gh-release@v2
with:
name: v${{ needs.prepare.outputs.version }}
generate_release_notes: true
files: |
release-assets/*
42 changes: 42 additions & 0 deletions App.Avalonia/App.Avalonia.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>WinExe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<RootNamespace>Coder.Desktop.App</RootNamespace>
<AssemblyName>Coder Desktop</AssemblyName>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<RestorePackagesWithLockFile>true</RestorePackagesWithLockFile>
<LangVersion>preview</LangVersion>
<!-- AVP1002: DependencyObjectSelector intentionally registers generic AvaloniaProperty owners. -->
<NoWarn>$(NoWarn);AVP1002</NoWarn>
<BuiltInComInteropSupport>true</BuiltInComInteropSupport>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Avalonia" Version="11.2.3" />
<PackageReference Include="Avalonia.Desktop" Version="11.2.3" />
<PackageReference Include="Svg.Skia" Version="2.0.0.6" />
<PackageReference Include="Avalonia.Themes.Fluent" Version="11.2.3" />
<PackageReference Include="Avalonia.Fonts.Inter" Version="11.2.3" />
<PackageReference Include="Avalonia.Diagnostics" Version="11.2.3" Condition="'$(Configuration)' == 'Debug'" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="9.0.9" />
<PackageReference Include="Microsoft.Extensions.Hosting" Version="9.0.9" />
<PackageReference Include="Serilog.Extensions.Hosting" Version="9.0.0" />
<PackageReference Include="Serilog.Sinks.File" Version="7.0.0" />
<PackageReference Include="Serilog.Sinks.Console" Version="6.0.0" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\App.Linux\App.Linux.csproj" />
<ProjectReference Include="..\Vpn.Linux\Vpn.Linux.csproj" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\App.Shared\App.Shared.csproj" Aliases="global,AppShared" />
</ItemGroup>

<ItemGroup>
<AvaloniaResource Include="Assets\\**" />
</ItemGroup>
</Project>
41 changes: 41 additions & 0 deletions App.Avalonia/App.axaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
<Application xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:converters="clr-namespace:Coder.Desktop.App.Converters"
x:Class="Coder.Desktop.App.App"
RequestedThemeVariant="Default">

<!--
Note: pages are implemented in parallel tasks (T5/T6). We register
converters here so future pages can reference them via StaticResource.
-->
<Application.Resources>
<converters:InverseBoolConverter x:Key="InverseBoolConverter" />
<converters:BoolToObjectConverter x:Key="BoolToObjectConverter" />
<converters:FriendlyByteConverter x:Key="FriendlyByteConverter" />
<converters:VpnLifecycleToBoolConverter x:Key="VpnLifecycleToBoolConverter" />
<converters:StringToBrushSelector x:Key="StringToBrushSelector" />
<converters:StringToStringSelector x:Key="StringToStringSelector" />
</Application.Resources>

<Application.Styles>
<FluentTheme />
</Application.Styles>

<TrayIcon.Icons>
<TrayIcons>
<TrayIcon Icon="/Assets/coder.ico"
ToolTipText="Coder Desktop"
Command="{Binding ShowWindowCommand}">
<TrayIcon.Menu>
<NativeMenu>
<NativeMenuItem Header="Show" Command="{Binding ShowWindowCommand}" />
<NativeMenuItemSeparator />
<NativeMenuItem Header="Check for Updates" />
<NativeMenuItemSeparator />
<NativeMenuItem Header="Exit" Command="{Binding ExitCommand}" />
</NativeMenu>
</TrayIcon.Menu>
</TrayIcon>
</TrayIcons>
</TrayIcon.Icons>
</Application>
Loading
Loading