Rework solution structure to allow more types of library projects (#196)

* Rename avalonia & wpf samples

* Rename rest of samples

* fix sample readme

* Fix compat util tests

* rename / move all src projects

* remove package lock files and move libC# again

* Add rust lib and cargo workspace

* Replace locksmith lib with new filelocksmith-rs library

* Deprecated type

* fix setup compile

* Use thiserror for error handling

* Rename some enums and formatting

* Add missing SHA256

* wip c++ library

* cpp wip

* alphabetize readme

* Try to get build working again

* Fix some conditionally compiled bits

* cross config should be in workspace root

* Fix PathHelper for new rust target dir

* Missed one old path to velopack.csproj

* remove obsolete coverage code

* testawareapp.exe no longer exists
This commit is contained in:
Caelan
2024-08-06 22:56:40 +01:00
committed by GitHub
parent 7071d20292
commit 3128d34b14
547 changed files with 11592 additions and 8464 deletions

View File

@@ -0,0 +1,10 @@
<Application xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="CSharpAvalonia.App"
RequestedThemeVariant="Default">
<!-- "Default" ThemeVariant follows system theme variant. "Dark" or "Light" are other available options. -->
<Application.Styles>
<FluentTheme />
</Application.Styles>
</Application>

View File

@@ -0,0 +1,22 @@
using Avalonia;
using Avalonia.Controls.ApplicationLifetimes;
using Avalonia.Markup.Xaml;
namespace CSharpAvalonia;
public partial class App : Application
{
public override void Initialize()
{
AvaloniaXamlLoader.Load(this);
}
public override void OnFrameworkInitializationCompleted()
{
if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) {
desktop.MainWindow = new MainWindow();
}
base.OnFrameworkInitializationCompleted();
}
}

View File

@@ -0,0 +1,26 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>WinExe</OutputType>
<AssemblyName>VelopackCSharpAvalonia</AssemblyName>
<TargetFramework>net8.0</TargetFramework>
<BuiltInComInteropSupport>true</BuiltInComInteropSupport>
<ApplicationManifest>app.manifest</ApplicationManifest>
<AvaloniaUseCompiledBindingsByDefault>true</AvaloniaUseCompiledBindingsByDefault>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Avalonia" Version="11.1.1" />
<PackageReference Include="Avalonia.Desktop" Version="11.1.1" />
<PackageReference Include="Avalonia.Themes.Fluent" Version="11.1.1" />
<PackageReference Include="Avalonia.Fonts.Inter" Version="11.1.1" />
<!--Condition below is needed to remove Avalonia.Diagnostics package from build output in Release configuration. Keep it in your app.-->
<PackageReference Condition="'$(Configuration)' == 'Debug'" Include="Avalonia.Diagnostics" Version="11.1.1" />
</ItemGroup>
<ItemGroup>
<!--Condition below is only needed to test this sample project against the local projects instead of the NuGet package. Remove it in your app.-->
<PackageReference Include="Velopack" Version="0.*" Condition="$(UseLocalVelopack) == ''" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,27 @@
<Window xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d" Width="600" Height="600"
x:Class="CSharpAvalonia.MainWindow"
WindowStartupLocation="CenterScreen"
Title="CSharpAvalonia">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="15" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<StackPanel>
<TextBlock Margin="10" Name="TextStatus" />
<StackPanel Orientation="Horizontal" Margin="10">
<Button Name="BtnCheckUpdate" Content="Check for Updates" Click="BtnCheckUpdateClick" />
<Button Margin="10,0" Name="BtnDownloadUpdate" Content="Download" Click="BtnDownloadUpdateClick" IsEnabled="False" />
<Button Name="BtnRestartApply" Content="Restart &amp; Apply" Click="BtnRestartApplyClick" IsEnabled="False" />
</StackPanel>
</StackPanel>
<ScrollViewer Name="ScrollLog" Grid.Row="2" HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<TextBlock Name="TextLog" Background="BlueViolet" Foreground="White" TextWrapping="Wrap" />
</ScrollViewer>
</Grid>
</Window>

View File

@@ -0,0 +1,106 @@
using System;
using System.Text;
using Avalonia.Controls;
using Avalonia.Interactivity;
using Avalonia.Threading;
using Microsoft.Extensions.Logging;
using Velopack;
namespace CSharpAvalonia;
public partial class MainWindow : Window
{
private UpdateManager _um;
private UpdateInfo _update;
public MainWindow()
{
InitializeComponent();
var updateUrl = SampleHelper.GetReleasesDir(); // replace with your update path/url
_um = new UpdateManager(updateUrl, logger: Program.Log);
TextLog.Text = Program.Log.ToString();
Program.Log.LogUpdated += LogUpdated;
UpdateStatus();
}
private async void BtnCheckUpdateClick(object sender, RoutedEventArgs e)
{
Working();
try {
// ConfigureAwait(true) so that UpdateStatus() is called on the UI thread
_update = await _um.CheckForUpdatesAsync().ConfigureAwait(true);
} catch (Exception ex) {
Program.Log.LogError(ex, "Error checking for updates");
}
UpdateStatus();
}
private async void BtnDownloadUpdateClick(object sender, RoutedEventArgs e)
{
Working();
try {
// ConfigureAwait(true) so that UpdateStatus() is called on the UI thread
await _um.DownloadUpdatesAsync(_update, Progress).ConfigureAwait(true);
} catch (Exception ex) {
Program.Log.LogError(ex, "Error downloading updates");
}
UpdateStatus();
}
private void BtnRestartApplyClick(object sender, RoutedEventArgs e)
{
_um.ApplyUpdatesAndRestart(_update);
}
private void LogUpdated(object sender, LogUpdatedEventArgs e)
{
// logs can be sent from other threads
Dispatcher.UIThread.InvokeAsync(() => {
TextLog.Text = e.Text;
ScrollLog.ScrollToEnd();
});
}
private void Progress(int percent)
{
// progress can be sent from other threads
Dispatcher.UIThread.InvokeAsync(() => {
TextStatus.Text = $"Downloading ({percent}%)...";
});
}
private void Working()
{
Program.Log.LogInformation("");
BtnCheckUpdate.IsEnabled = false;
BtnDownloadUpdate.IsEnabled = false;
BtnRestartApply.IsEnabled = false;
TextStatus.Text = "Working...";
}
private void UpdateStatus()
{
StringBuilder sb = new StringBuilder();
sb.AppendLine($"Velopack version: {VelopackRuntimeInfo.VelopackNugetVersion}");
sb.AppendLine($"This app version: {(_um.IsInstalled ? _um.CurrentVersion : "(n/a - not installed)")}");
if (_update != null) {
sb.AppendLine($"Update available: {_update.TargetFullRelease.Version}");
BtnDownloadUpdate.IsEnabled = true;
} else {
BtnDownloadUpdate.IsEnabled = false;
}
if (_um.IsUpdatePendingRestart) {
sb.AppendLine("Update ready, pending restart to install");
BtnRestartApply.IsEnabled = true;
} else {
BtnRestartApply.IsEnabled = false;
}
TextStatus.Text = sb.ToString();
BtnCheckUpdate.IsEnabled = true;
}
}

View File

@@ -0,0 +1,44 @@
using System;
using System.Text;
using Microsoft.Extensions.Logging;
namespace CSharpAvalonia;
public class LogUpdatedEventArgs : EventArgs
{
public string Text { get; set; }
}
public class MemoryLogger : ILogger
{
public event EventHandler<LogUpdatedEventArgs> LogUpdated;
private readonly StringBuilder _sb = new StringBuilder();
public IDisposable BeginScope<TState>(TState state)
{
return null;
}
public bool IsEnabled(LogLevel logLevel)
{
return true;
}
public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func<TState, Exception, string> formatter)
{
lock (_sb) {
var message = formatter(state, exception);
if (exception != null) message += "\n" + exception.ToString();
Console.WriteLine("log: " + message);
_sb.AppendLine(message);
LogUpdated?.Invoke(this, new LogUpdatedEventArgs { Text = _sb.ToString() });
}
}
public override string ToString()
{
lock (_sb) {
return _sb.ToString();
}
}
}

View File

@@ -0,0 +1,44 @@
using System;
using Avalonia;
using Velopack;
namespace CSharpAvalonia;
class Program
{
public static MemoryLogger Log { get; private set; }
// Initialization code. Don't use any Avalonia, third-party APIs or any
// SynchronizationContext-reliant code before AppMain is called: things aren't initialized
// yet and stuff might break.
[STAThread]
public static void Main(string[] args)
{
try {
// Logging is essential for debugging! Ideally you should write it to a file.
Log = new MemoryLogger();
// It's important to Run() the VelopackApp as early as possible in app startup.
VelopackApp.Build()
.WithFirstRun((v) => { /* Your first run code here */ })
.Run(Log);
// Now it's time to run Avalonia
BuildAvaloniaApp().StartWithClassicDesktopLifetime(args);
} catch (Exception ex) {
string message = "Unhandled exception: " + ex.ToString();
Console.WriteLine(message);
throw;
}
}
// Avalonia configuration, don't remove method; also used by visual designer.
public static AppBuilder BuildAvaloniaApp()
{
return AppBuilder.Configure<App>()
.UsePlatformDetect()
.WithInterFont()
.LogToTrace();
}
}

View File

@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="utf-8"?>
<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1">
<!-- This manifest is used on Windows only.
Don't remove it as it might cause problems with window transparency and embedded controls.
For more details visit https://learn.microsoft.com/windows/win32/sbscs/application-manifests -->
<assemblyIdentity version="1.0.0.0" name="AvaloniaCrossPlat.Desktop"/>
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
<application>
<!-- A list of the Windows versions that this application has been tested on
and is designed to work with. Uncomment the appropriate elements
and Windows will automatically select the most compatible environment. -->
<!-- Windows 10 -->
<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}" />
</application>
</compatibility>
</assembly>

View File

@@ -0,0 +1,23 @@
#!/bin/bash
# Find the absolute path of the script
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
# Check if version parameter is provided
if [ "$#" -ne 1 ]; then
echo "Version number is required."
echo "Usage: ./build.sh [version]"
exit 1
fi
BUILD_VERSION="$1"
RELEASE_DIR="$SCRIPT_DIR/releases"
PUBLISH_DIR="$SCRIPT_DIR/publish"
echo ""
echo "Compiling VelopackCSharpAvalonia with dotnet..."
dotnet publish -c Release --self-contained -r linux-x64 -o "$PUBLISH_DIR"
echo ""
echo "Building Velopack Release v$BUILD_VERSION"
vpk pack -u VelopackCSharpAvalonia -v $BUILD_VERSION -o "$RELEASE_DIR" -p "$PUBLISH_DIR"

View File

@@ -0,0 +1,23 @@
#!/bin/bash
# Find the absolute path of the script
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
# Check if version parameter is provided
if [ "$#" -ne 1 ]; then
echo "Version number is required."
echo "Usage: ./build.sh [version]"
exit 1
fi
BUILD_VERSION="$1"
RELEASE_DIR="$SCRIPT_DIR/releases"
PUBLISH_DIR="$SCRIPT_DIR/publish"
echo ""
echo "Compiling VelopackCSharpAvalonia with dotnet..."
dotnet publish -c Release --self-contained -r osx-x64 -o "$PUBLISH_DIR"
echo ""
echo "Building Velopack Release v$BUILD_VERSION"
vpk pack -u VelopackCSharpAvalonia -v $BUILD_VERSION -o "$RELEASE_DIR" -p "$PUBLISH_DIR"

View File

@@ -0,0 +1,18 @@
@echo off
setlocal enabledelayedexpansion
if "%~1"=="" (
echo Version number is required.
echo Usage: build.bat [version] [extra_args...]
exit /b 1
)
set "version=%~1"
echo.
echo Compiling VelopackCSharpAvalonia with dotnet...
dotnet publish -c Release --no-self-contained -r win-x64 -o %~dp0publish
echo.
echo Building Velopack Release v%version%
vpk pack -u VelopackCSharpAvalonia -o %~dp0releases -p %~dp0publish -f net8-x64-desktop -v %*

View File

@@ -0,0 +1,35 @@
#!/bin/bash
# Find the absolute path of the script
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
# Check if version parameter is provided
if [ "$#" -ne 1 ]; then
echo "Version number is required."
echo "Usage: ./build.sh [version]"
exit 1
fi
BUILD_VERSION="$1"
RELEASE_DIR="$SCRIPT_DIR/../releases"
PUBLISH_DIR="$SCRIPT_DIR/../publish"
echo ""
echo "Building Velopack Rust"
cd "$SCRIPT_DIR/../../../src/Rust"
cargo build --target x86_64-unknown-linux-gnu
cp target/x86_64-unknown-linux-gnu/debug/update target/debug/update
echo ""
echo "Building Velopack Vpk"
cd "$SCRIPT_DIR/../../.."
dotnet build src/Velopack.Vpk/Velopack.Vpk.csproj
echo ""
cd "$SCRIPT_DIR/.."
echo "Compiling VelopackCSharpAvalonia with dotnet..."
dotnet publish -c Release --self-contained -r linux-x64 -o "$PUBLISH_DIR" -p:UseLocalVelopack=true
echo ""
echo "Building Velopack Release v$BUILD_VERSION"
"$SCRIPT_DIR/../../../build/Debug/net8.0/vpk" pack -u VelopackCSharpAvalonia -v $BUILD_VERSION -o "$RELEASE_DIR" -p "$PUBLISH_DIR"

View File

@@ -0,0 +1,34 @@
#!/bin/bash
# Find the absolute path of the script
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
# Check if version parameter is provided
if [ "$#" -ne 1 ]; then
echo "Version number is required."
echo "Usage: ./build.sh [version]"
exit 1
fi
BUILD_VERSION="$1"
RELEASE_DIR="$SCRIPT_DIR/../releases"
PUBLISH_DIR="$SCRIPT_DIR/../publish"
echo ""
echo "Building Velopack Rust"
cd "$SCRIPT_DIR/../../../src/Rust"
cargo build
echo ""
echo "Building Velopack Vpk"
cd "$SCRIPT_DIR/../../.."
dotnet build src/Velopack.Vpk/Velopack.Vpk.csproj
echo ""
cd "$SCRIPT_DIR/.."
echo "Compiling VelopackCSharpAvalonia with dotnet..."
dotnet publish -c Release --self-contained -r osx-x64 -o "$PUBLISH_DIR" -p:UseLocalVelopack=true
echo ""
echo "Building Velopack Release v$BUILD_VERSION"
"$SCRIPT_DIR/../../../build/Debug/net8.0/vpk" pack -u VelopackCSharpAvalonia -v $BUILD_VERSION -o "$RELEASE_DIR" -p "$PUBLISH_DIR"

View File

@@ -0,0 +1,34 @@
@echo off
REM This script requires several tools to be installed for it to work:
REM cargo (rust): winget install Rustlang.Rustup
REM Nerdbank.GitVersioning (nbgv): dotnet tool install --global nbgv
REM C++ Build Tools, typically installed via "Desktop development with C++" workload.
setlocal enabledelayedexpansion
if "%~1"=="" (
echo Version number is required.
echo Usage: build.bat [version] [extra_args...]
exit /b 1
)
echo.
echo Building Velopack Rust
cd %~dp0..\..\..\src\Rust
cargo build --features windows
echo.
echo Building Velopack Vpk
cd %~dp0..\..\..\
dotnet build src/Velopack.Vpk/Velopack.Vpk.csproj
cd %~dp0..
set "version=%~1"
echo.
echo Compiling VelopackCSharpAvalonia with dotnet...
dotnet publish -c Release --self-contained -r win-x64 -o publish -p:UseLocalVelopack=true
echo.
echo Building Velopack Release v%version%
%~dp0..\..\..\build\Debug\net8.0\vpk pack -u VelopackCSharpAvalonia -o releases -p publish -v %*

View File

@@ -0,0 +1,45 @@
# AvaloniaCrossPlat
_Prerequisites: vpk command line tool installed_
This app demonstrates how to use Avalonia to provide a desktop UI, installer, and updates for Mac, Linux, and Windows.
You can run this sample by executing the build script with a version number (eg. `build-win.bat 1.0.0`).
There are build scripts provided for each platform (`build-win.bat`, `build-linux.sh`, `build-osx.bat`).
Once built, you can install the app - build more updates, and then test updates and so forth. The sample app will check the local release dir for new update packages.
In your production apps, you should deploy your updates to some kind of update server instead.
On Linux, there is no installer, since the program is shipped as a `.AppImage`, it is only portable - however it can still update itself by replacing it's own `.AppImage` (even if that `.AppImage` is inside priveleged directories)
## Avalonia Implementation Notes
The Avalonia Template will generate a `Program.Main()` for you. You need to be careful when editing this file as to not break the Avalonia designer. You must not delete the `BuildAvaloniaApp()` function, but you must add the `VelopackApp` builder to the `Main()` method. For example:
```cs
class Program
{
// Initialization code. Don't use any Avalonia, third-party APIs or any
// SynchronizationContext-reliant code before AppMain is called.
// things aren't initialized yet and stuff might break.
[STAThread]
public static void Main(string[] args)
{
VelopackApp.Build().Run();
BuildAvaloniaApp().StartWithClassicDesktopLifetime(args);
}
// Avalonia configuration, don't remove method; also used by visual designer.
public static AppBuilder BuildAvaloniaApp()
{
return AppBuilder.Configure<App>()
.UsePlatformDetect()
.WithInterFont()
.LogToTrace();
}
}
```
## Testing changes to Velopack
This project has a folder of development build scripts (e.g. `.\dev-scripts\build-win.bat`) which will create a release in same way as the main scripts, except with a project reference to Velopack, and it will invoke the local vpk tool as well.
If you have made a change to Velopack and would like to test it in the sample app, these are the scripts you should run instead.