diff --git a/examples/AvaloniaCrossPlat/AvaloniaCrossPlat.csproj b/examples/AvaloniaCrossPlat/AvaloniaCrossPlat.csproj
index 27e2d91b..3856c3b4 100644
--- a/examples/AvaloniaCrossPlat/AvaloniaCrossPlat.csproj
+++ b/examples/AvaloniaCrossPlat/AvaloniaCrossPlat.csproj
@@ -1,12 +1,11 @@
-
+
WinExe
net8.0
true
app.manifest
true
- $(NoWarn);CA2007
@@ -16,10 +15,29 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+
+ <_Parameter1>AvaloniaSampleReleaseDir
+ <_Parameter2>$(MSBuildThisFileDirectory)releases
+
-
+
diff --git a/examples/AvaloniaCrossPlat/ConsoleLogger.cs b/examples/AvaloniaCrossPlat/ConsoleLogger.cs
deleted file mode 100644
index 44a42823..00000000
--- a/examples/AvaloniaCrossPlat/ConsoleLogger.cs
+++ /dev/null
@@ -1,22 +0,0 @@
-using System;
-using Microsoft.Extensions.Logging;
-
-namespace AvaloniaCrossPlat;
-
-public class ConsoleLogger : ILogger
-{
- public void Log(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func formatter)
- {
- Console.WriteLine(formatter(state, exception));
- }
-
- public bool IsEnabled(LogLevel logLevel)
- {
- return true;
- }
-
- public IDisposable BeginScope(TState state)
- {
- return null;
- }
-}
\ No newline at end of file
diff --git a/examples/AvaloniaCrossPlat/Const.cs b/examples/AvaloniaCrossPlat/Const.cs
deleted file mode 100644
index 6633c5b7..00000000
--- a/examples/AvaloniaCrossPlat/Const.cs
+++ /dev/null
@@ -1 +0,0 @@
-class Const { public const string RELEASES_DIR = @"{REPLACE_ME}"; }
diff --git a/examples/AvaloniaCrossPlat/MainWindow.axaml.cs b/examples/AvaloniaCrossPlat/MainWindow.axaml.cs
index e30d9d63..4f1eb656 100644
--- a/examples/AvaloniaCrossPlat/MainWindow.axaml.cs
+++ b/examples/AvaloniaCrossPlat/MainWindow.axaml.cs
@@ -2,6 +2,7 @@
using System.Text;
using System.Threading.Tasks;
using Avalonia.Controls;
+using Avalonia.Interactivity;
using Avalonia.Threading;
using Microsoft.Extensions.Logging;
using Velopack;
@@ -16,54 +17,61 @@ public partial class MainWindow : Window
public MainWindow()
{
InitializeComponent();
- _um = new UpdateManager(Const.RELEASES_DIR, logger: new TextBoxLogger(Log));
+ _um = new UpdateManager(Program.UpdateUrl, logger: Program.Log);
+ TextLog.Text = Program.Log.ToString();
+ Program.Log.LogUpdated += LogUpdated;
UpdateStatus();
}
- private async void BtnCheckUpdateClick(object sender, Avalonia.Interactivity.RoutedEventArgs e)
+ private async void BtnCheckUpdateClick(object sender, RoutedEventArgs e)
{
Working();
try {
- _update = await _um.CheckForUpdatesAsync();
+ // ConfigureAwait(true) so that UpdateStatus() is called on the UI thread
+ _update = await _um.CheckForUpdatesAsync().ConfigureAwait(true);
} catch (Exception ex) {
- Log("ERROR: " + ex.Message);
+ Program.Log.LogError(ex, "Error checking for updates");
}
UpdateStatus();
}
- private async void BtnDownloadUpdateClick(object sender, Avalonia.Interactivity.RoutedEventArgs e)
+ private async void BtnDownloadUpdateClick(object sender, RoutedEventArgs e)
{
Working();
try {
- await _um.DownloadUpdatesAsync(_update, Progress);
+ // ConfigureAwait(true) so that UpdateStatus() is called on the UI thread
+ await _um.DownloadUpdatesAsync(_update, Progress).ConfigureAwait(true);
} catch (Exception ex) {
- Log("ERROR: " + ex.Message);
+ Program.Log.LogError(ex, "Error downloading updates");
}
- await Task.Delay(10);
UpdateStatus();
}
- private void Log(string text)
- {
- TextLog.Text += text + Environment.NewLine;
- ScrollLog.ScrollToEnd();
- }
-
- private void BtnRestartApplyClick(object sender, Avalonia.Interactivity.RoutedEventArgs e)
+ private void BtnRestartApplyClick(object sender, RoutedEventArgs e)
{
_um.ApplyUpdatesAndRestart();
}
+ 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)
{
- Dispatcher.UIThread.Post(() => {
+ // progress can be sent from other threads
+ Dispatcher.UIThread.InvokeAsync(() => {
TextStatus.Text = $"Downloading ({percent}%)...";
});
}
private void Working()
{
- Log("");
+ Program.Log.LogInformation("");
BtnCheckUpdate.IsEnabled = false;
BtnDownloadUpdate.IsEnabled = false;
BtnRestartApply.IsEnabled = false;
@@ -93,28 +101,4 @@ public partial class MainWindow : Window
TextStatus.Text = sb.ToString();
BtnCheckUpdate.IsEnabled = true;
}
-
- private class TextBoxLogger : ILogger
- {
- private readonly Action _textBox;
-
- public TextBoxLogger(Action textBox)
- {
- _textBox = textBox;
- }
-
- public IDisposable BeginScope(TState state) => null;
-
- public bool IsEnabled(LogLevel logLevel) => true;
-
- public void Log(LogLevel logLevel, EventId eventId, TState state,
- Exception exception, Func formatter)
- {
- if (logLevel < LogLevel.Information) return;
- var text = formatter(state, exception);
- Dispatcher.UIThread.Post(() => {
- _textBox(text);
- });
- }
- }
}
\ No newline at end of file
diff --git a/examples/AvaloniaCrossPlat/MemoryLogger.cs b/examples/AvaloniaCrossPlat/MemoryLogger.cs
new file mode 100644
index 00000000..81836f67
--- /dev/null
+++ b/examples/AvaloniaCrossPlat/MemoryLogger.cs
@@ -0,0 +1,47 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Microsoft.Extensions.Logging;
+
+namespace AvaloniaCrossPlat;
+
+public class LogUpdatedEventArgs : EventArgs
+{
+ public string Text { get; set; }
+}
+
+public class MemoryLogger : ILogger
+{
+ public event EventHandler LogUpdated;
+ private readonly StringBuilder _sb = new StringBuilder();
+
+ public IDisposable BeginScope(TState state)
+ {
+ return null;
+ }
+
+ public bool IsEnabled(LogLevel logLevel)
+ {
+ return true;
+ }
+
+ public void Log(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func 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();
+ }
+ }
+}
diff --git a/examples/AvaloniaCrossPlat/Program.cs b/examples/AvaloniaCrossPlat/Program.cs
index cf43c65e..b80cee3a 100644
--- a/examples/AvaloniaCrossPlat/Program.cs
+++ b/examples/AvaloniaCrossPlat/Program.cs
@@ -1,4 +1,6 @@
using System;
+using System.Linq;
+using System.Reflection;
using Avalonia;
using Velopack;
@@ -6,23 +8,48 @@ namespace AvaloniaCrossPlat;
class Program
{
+ public static string UpdateUrl { get; private set; }
+
+ 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)
{
- VelopackApp.Build()
- .Run(new ConsoleLogger());
+ try {
+ // Logging is essential for debugging! Ideally you should write it to a file.
+ Log = new MemoryLogger();
- BuildAvaloniaApp()
- .StartWithClassicDesktopLifetime(args);
+ // It's important to Run() the VelopackApp as early as possible in app startup.
+ VelopackApp.Build()
+ .Run(Log);
+
+ // This is purely for demonstration purposes, we get the update URL from a
+ // property defined by MSBuild, so we can locate the local releases directory.
+ // In your production app, this should point to your update server.
+ UpdateUrl = Assembly.GetEntryAssembly()
+ .GetCustomAttributes()
+ .Where(x => x.Key == "AvaloniaSampleReleaseDir")
+ .Single().Value;
+
+ // 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; also used by visual designer.
+ // Avalonia configuration, don't remove method; also used by visual designer.
public static AppBuilder BuildAvaloniaApp()
- => AppBuilder.Configure()
+ {
+ return AppBuilder.Configure()
.UsePlatformDetect()
.WithInterFont()
.LogToTrace();
+ }
}
diff --git a/examples/AvaloniaCrossPlat/build-linux.sh b/examples/AvaloniaCrossPlat/build-linux.sh
index 830bcd59..c4082a2d 100755
--- a/examples/AvaloniaCrossPlat/build-linux.sh
+++ b/examples/AvaloniaCrossPlat/build-linux.sh
@@ -5,30 +5,20 @@ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
# Check if version parameter is provided
if [ "$#" -ne 1 ]; then
- echo "Please provide a version number."
- echo "Usage: ./build.sh version_number"
+ echo "Version number is required."
+ echo "Usage: ./build.sh [version]"
exit 1
fi
-echo "Building Velopack"
-cd "$SCRIPT_DIR/../../src/Rust"
-cargo build
-cd "$SCRIPT_DIR/../.."
-dotnet build src/Velopack.Vpk/Velopack.Vpk.csproj
-cd "$SCRIPT_DIR"
-
-version="$1"
-releasesDir="$SCRIPT_DIR/releases"
-
-# Write to Const.cs
-echo "class Const { public const string RELEASES_DIR = @\"$releasesDir\"; } " > "$(dirname "$0")/Const.cs"
-echo "Const.cs file updated with releases directory ($releasesDir)."
+BUILD_VERSION="$1"
+RELEASE_DIR="$SCRIPT_DIR/releases"
+PUBLISH_DIR="$SCRIPT_DIR/publish"
+ICON_PATH="$SCRIPT_DIR/Velopack.png"
+echo ""
echo "Compiling AvaloniaCrossPlat with dotnet..."
-dotnet publish -c Release --self-contained -r linux-x64 -o "$(dirname "$0")/publish"
+dotnet publish -c Release --self-contained -r linux-x64 -o "$PUBLISH_DIR"
-echo "class Const { public const string RELEASES_DIR = @\"{REPLACE_ME}\"; } " > "$(dirname "$0")/Const.cs"
-echo "Const.cs file reset"
-
-echo "Building Velopack Release v$version"
-"$(dirname "$0")/../../build/Debug/net8.0/vpk" pack -u AvaloniaCrossPlat -v "$version" -o "$releasesDir" -p "$(dirname "$0")/publish" -i Velopack.png
\ No newline at end of file
+echo ""
+echo "Building Velopack Release v$BUILD_VERSION"
+vpk pack -u AvaloniaCrossPlat -v $BUILD_VERSION -o "$RELEASE_DIR" -p "$PUBLISH_DIR" -i "$ICON_PATH"
\ No newline at end of file
diff --git a/examples/AvaloniaCrossPlat/build-osx.sh b/examples/AvaloniaCrossPlat/build-osx.sh
index 1a2450aa..5a7008d2 100755
--- a/examples/AvaloniaCrossPlat/build-osx.sh
+++ b/examples/AvaloniaCrossPlat/build-osx.sh
@@ -5,30 +5,20 @@ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
# Check if version parameter is provided
if [ "$#" -ne 1 ]; then
- echo "Please provide a version number."
- echo "Usage: ./build.sh version_number"
+ echo "Version number is required."
+ echo "Usage: ./build.sh [version]"
exit 1
fi
-echo "Building Velopack"
-cd "$SCRIPT_DIR/../../src/Rust"
-cargo build
-cd "$SCRIPT_DIR/../.."
-dotnet build src/Velopack.Vpk/Velopack.Vpk.csproj
-cd "$SCRIPT_DIR"
-
-version="$1"
-releasesDir="$SCRIPT_DIR/releases"
-
-# Write to Const.cs
-echo "class Const { public const string RELEASES_DIR = @\"$releasesDir\"; } " > "$(dirname "$0")/Const.cs"
-echo "Const.cs file updated with releases directory ($releasesDir)."
+BUILD_VERSION="$1"
+RELEASE_DIR="$SCRIPT_DIR/releases"
+PUBLISH_DIR="$SCRIPT_DIR/publish"
+ICON_PATH="$SCRIPT_DIR/Velopack.icns"
+echo ""
echo "Compiling AvaloniaCrossPlat with dotnet..."
-dotnet publish -c Release --self-contained -r osx-x64 -o "$(dirname "$0")/publish"
+dotnet publish -c Release --self-contained -r osx-x64 -o "$PUBLISH_DIR"
-echo "class Const { public const string RELEASES_DIR = @\"{REPLACE_ME}\"; } " > "$(dirname "$0")/Const.cs"
-echo "Const.cs file reset"
-
-echo "Building Velopack Release v$version"
-"$(dirname "$0")/../../build/Debug/net6.0/vpk" pack -u AvaloniaCrossPlat -v "$version" -o "$releasesDir" -p "$(dirname "$0")/publish" -i Velopack.icns
\ No newline at end of file
+echo ""
+echo "Building Velopack Release v$BUILD_VERSION"
+vpk pack -u AvaloniaCrossPlat -v $BUILD_VERSION -o "$RELEASE_DIR" -p "$PUBLISH_DIR" -i "$ICON_PATH"
\ No newline at end of file
diff --git a/examples/AvaloniaCrossPlat/build-win.bat b/examples/AvaloniaCrossPlat/build-win.bat
index 0a479d9d..95cf5e92 100644
--- a/examples/AvaloniaCrossPlat/build-win.bat
+++ b/examples/AvaloniaCrossPlat/build-win.bat
@@ -1,32 +1,18 @@
@echo off
setlocal enabledelayedexpansion
-:: Check if version parameter is provided
if "%~1"=="" (
- echo Please provide a version number.
- echo Usage: build.bat version_number
+ echo Version number is required.
+ echo Usage: build.bat [version]
exit /b 1
)
-echo Building Velopack
-cd %~dp0..\..\src\Rust
-cargo build --features windows
-cd %~dp0..\..\
-dotnet build src/Velopack.Vpk/Velopack.Vpk.csproj
-cd %~dp0
-
set "version=%~1"
-set "releasesDir=%~dp0releases"
-
-:: Write to Const.cs
-echo class Const { public const string RELEASES_DIR = @"%releasesDir%"; } > "%~dp0Const.cs"
-echo Const.cs file updated with releases directory (%releasesDir%).
+echo.
echo Compiling AvaloniaCrossPlat with dotnet...
dotnet publish -c Release --no-self-contained -r win-x64 -o %~dp0publish
-echo class Const { public const string RELEASES_DIR = @"{REPLACE_ME}"; } > "%~dp0Const.cs"
-echo Const.cs file reset
-
+echo.
echo Building Velopack Release v%version%
-%~dp0..\..\build\Debug\net6.0\vpk.exe pack -u AvaloniaCrossPlat -v %version% -o %releasesDir% -p %~dp0publish -f net8-x64-desktop
\ No newline at end of file
+vpk pack -u AvaloniaCrossPlat -v %version% -o %~dp0releases -p %~dp0publish -f net8-x64-desktop
\ No newline at end of file
diff --git a/examples/AvaloniaCrossPlat/dev-scripts/build-linux.sh b/examples/AvaloniaCrossPlat/dev-scripts/build-linux.sh
new file mode 100644
index 00000000..830bcd59
--- /dev/null
+++ b/examples/AvaloniaCrossPlat/dev-scripts/build-linux.sh
@@ -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 "Please provide a version number."
+ echo "Usage: ./build.sh version_number"
+ exit 1
+fi
+
+echo "Building Velopack"
+cd "$SCRIPT_DIR/../../src/Rust"
+cargo build
+cd "$SCRIPT_DIR/../.."
+dotnet build src/Velopack.Vpk/Velopack.Vpk.csproj
+cd "$SCRIPT_DIR"
+
+version="$1"
+releasesDir="$SCRIPT_DIR/releases"
+
+# Write to Const.cs
+echo "class Const { public const string RELEASES_DIR = @\"$releasesDir\"; } " > "$(dirname "$0")/Const.cs"
+echo "Const.cs file updated with releases directory ($releasesDir)."
+
+echo "Compiling AvaloniaCrossPlat with dotnet..."
+dotnet publish -c Release --self-contained -r linux-x64 -o "$(dirname "$0")/publish"
+
+echo "class Const { public const string RELEASES_DIR = @\"{REPLACE_ME}\"; } " > "$(dirname "$0")/Const.cs"
+echo "Const.cs file reset"
+
+echo "Building Velopack Release v$version"
+"$(dirname "$0")/../../build/Debug/net8.0/vpk" pack -u AvaloniaCrossPlat -v "$version" -o "$releasesDir" -p "$(dirname "$0")/publish" -i Velopack.png
\ No newline at end of file
diff --git a/examples/AvaloniaCrossPlat/dev-scripts/build-osx.sh b/examples/AvaloniaCrossPlat/dev-scripts/build-osx.sh
new file mode 100644
index 00000000..1a2450aa
--- /dev/null
+++ b/examples/AvaloniaCrossPlat/dev-scripts/build-osx.sh
@@ -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 "Please provide a version number."
+ echo "Usage: ./build.sh version_number"
+ exit 1
+fi
+
+echo "Building Velopack"
+cd "$SCRIPT_DIR/../../src/Rust"
+cargo build
+cd "$SCRIPT_DIR/../.."
+dotnet build src/Velopack.Vpk/Velopack.Vpk.csproj
+cd "$SCRIPT_DIR"
+
+version="$1"
+releasesDir="$SCRIPT_DIR/releases"
+
+# Write to Const.cs
+echo "class Const { public const string RELEASES_DIR = @\"$releasesDir\"; } " > "$(dirname "$0")/Const.cs"
+echo "Const.cs file updated with releases directory ($releasesDir)."
+
+echo "Compiling AvaloniaCrossPlat with dotnet..."
+dotnet publish -c Release --self-contained -r osx-x64 -o "$(dirname "$0")/publish"
+
+echo "class Const { public const string RELEASES_DIR = @\"{REPLACE_ME}\"; } " > "$(dirname "$0")/Const.cs"
+echo "Const.cs file reset"
+
+echo "Building Velopack Release v$version"
+"$(dirname "$0")/../../build/Debug/net6.0/vpk" pack -u AvaloniaCrossPlat -v "$version" -o "$releasesDir" -p "$(dirname "$0")/publish" -i Velopack.icns
\ No newline at end of file
diff --git a/examples/AvaloniaCrossPlat/dev-scripts/build-win.bat b/examples/AvaloniaCrossPlat/dev-scripts/build-win.bat
new file mode 100644
index 00000000..5d988ccc
--- /dev/null
+++ b/examples/AvaloniaCrossPlat/dev-scripts/build-win.bat
@@ -0,0 +1,29 @@
+@echo off
+setlocal enabledelayedexpansion
+
+if "%~1"=="" (
+ echo Version number is required.
+ echo Usage: build.bat [version]
+ 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 AvaloniaCrossPlat with dotnet...
+dotnet publish -c Release --no-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 AvaloniaCrossPlat -v %version% -o releases -p publish -f net8-x64-desktop
\ No newline at end of file
diff --git a/examples/VeloWpfSample/build.bat b/examples/VeloWpfSample/build.bat
index 67ec3ddc..892bbaeb 100644
--- a/examples/VeloWpfSample/build.bat
+++ b/examples/VeloWpfSample/build.bat
@@ -9,8 +9,10 @@ if "%~1"=="" (
set "version=%~1"
+echo.
echo Compiling VeloWpfSample with dotnet...
dotnet publish -c Release --no-self-contained -r win-x64 -o %~dp0publish
+echo.
echo Building Velopack Release v%version%
vpk pack -u VeloWpfSample -v %version% -o %~dp0releases -p %~dp0publish -f net8-x64-desktop
\ No newline at end of file