diff --git a/.gitattributes b/.gitattributes
new file mode 100644
index 0000000..cc4d38c
--- /dev/null
+++ b/.gitattributes
@@ -0,0 +1,3 @@
+* text=auto
+*.cs text
+*.csproj text
diff --git a/.github/workflows/Harp.Olfactometer.yml b/.github/workflows/Harp.Olfactometer.yml
new file mode 100644
index 0000000..9442e25
--- /dev/null
+++ b/.github/workflows/Harp.Olfactometer.yml
@@ -0,0 +1,103 @@
+name: Harp.Olfactometer
+on:
+ push:
+ # This prevents tag pushes from triggering this workflow
+ branches: ['**']
+ pull_request:
+ release:
+ types: [published]
+ workflow_dispatch:
+env:
+ DOTNET_NOLOGO: true
+ DOTNET_CLI_TELEMETRY_OPTOUT: true
+ DOTNET_GENERATE_ASPNET_CERTIFICATE: false
+ ContinuousIntegrationBuild: true
+ CiBuildVersion: ${{github.event.release.tag_name || 'api42.42.42'}}
+jobs:
+ build:
+ strategy:
+ fail-fast: false
+ matrix:
+ configuration: ['Release']
+ include:
+ - configuration: Release
+ collect-packages: true
+ name: Build
+ runs-on: windows-latest
+ if: github.event_name != 'release' || startsWith(github.event.release.tag_name, 'api')
+ steps:
+ # ----------------------------------------------------------------------- Checkout
+ - name: Checkout
+ uses: actions/checkout@v4
+
+ # ----------------------------------------------------------------------- Set up tools
+ - name: Set up .NET
+ uses: actions/setup-dotnet@v4
+ with:
+ dotnet-version: 8.x
+
+ - name: Set up T4
+ run: dotnet tool install -g dotnet-t4 --version 3.0.0
+
+ # ----------------------------------------------------------------------- Regenerate
+ - name: Restore generators
+ run: dotnet restore Generators
+
+ - name: Run generators
+ run: dotnet build Generators --no-restore --configuration ${{matrix.configuration}}
+
+ - name: Verify pre-generated code was up-to-date
+ id: verify-dist
+ run: |
+ git add . --intent-to-add --ignore-removal
+ git diff --name-status --exit-code
+
+ # ----------------------------------------------------------------------- Build interface package
+ - name: Restore interface
+ run: dotnet restore Interface
+
+ - name: Build interface
+ run: dotnet build Interface --no-restore --configuration ${{matrix.configuration}}
+
+ - name: Pack interface
+ id: pack
+ run: dotnet pack Interface --no-restore --no-build --configuration ${{matrix.configuration}}
+
+ # ----------------------------------------------------------------------- Collect artifacts
+ - name: Collect NuGet packages
+ uses: actions/upload-artifact@v4
+ if: matrix.collect-packages && steps.pack.outcome == 'success' && always()
+ with:
+ name: Packages
+ if-no-files-found: error
+ path: Interface/bin/${{matrix.configuration}}/**
+
+ publish-packages-nuget-org:
+ name: Publish packages to NuGet.org
+ runs-on: ubuntu-latest
+ permissions:
+ # Needed to attach files to releases
+ contents: write
+ environment: public-release
+ needs: build
+ if: github.event_name == 'release'
+ steps:
+ # ----------------------------------------------------------------------- Set up .NET
+ - name: Setup .NET
+ uses: actions/setup-dotnet@v4
+ with:
+ dotnet-version: 8.x
+
+ # ----------------------------------------------------------------------- Download built packages
+ - name: Download built packages
+ uses: actions/download-artifact@v4
+ with:
+ name: Packages
+ path: artifacts/packages/
+
+ # ----------------------------------------------------------------------- Push to NuGet.org
+ - name: Push to NuGet.org
+ run: dotnet nuget push "artifacts/packages/*.nupkg" --api-key ${{secrets.NUGET_API_KEY}} --source ${{vars.NUGET_API_URL}}
+ env:
+ # This is a workaround for https://github.com/NuGet/Home/issues/9775
+ DOTNET_SYSTEM_NET_HTTP_USESOCKETSHTTPHANDLER: 0
diff --git a/App/Olfactometer.Design/App.axaml.cs b/App/Olfactometer.Design/App.axaml.cs
index ca2e3fe..5797e4a 100644
--- a/App/Olfactometer.Design/App.axaml.cs
+++ b/App/Olfactometer.Design/App.axaml.cs
@@ -1,28 +1,28 @@
-using Avalonia;
-using Avalonia.Controls.ApplicationLifetimes;
-using Avalonia.Markup.Xaml;
-using Olfactometer.Design.ViewModels;
-using Olfactometer.Design.Views;
-
-namespace Olfactometer.Design;
-
-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
- {
- DataContext = new MainWindowViewModel(),
- };
- }
-
- base.OnFrameworkInitializationCompleted();
- }
-}
+using Avalonia;
+using Avalonia.Controls.ApplicationLifetimes;
+using Avalonia.Markup.Xaml;
+using Olfactometer.Design.ViewModels;
+using Olfactometer.Design.Views;
+
+namespace Olfactometer.Design;
+
+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
+ {
+ DataContext = new MainWindowViewModel(),
+ };
+ }
+
+ base.OnFrameworkInitializationCompleted();
+ }
+}
diff --git a/App/Olfactometer.Design/Olfactometer.Design.csproj b/App/Olfactometer.Design/Olfactometer.Design.csproj
index 83ccc5c..531ad38 100644
--- a/App/Olfactometer.Design/Olfactometer.Design.csproj
+++ b/App/Olfactometer.Design/Olfactometer.Design.csproj
@@ -1,72 +1,72 @@
-
-
- Library
- net6.0
- win-x64;osx-x64;linux-x64
- Olfactometer.Design
- 1.1.0
- enable
-
-
- ../bin/Debug/
-
-
- ../bin/Release/
-
-
-
-
- %(Filename)
-
-
- Designer
-
-
-
- App.axaml
- Code
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+ Library
+ net6.0
+ win-x64;osx-x64;linux-x64
+ Olfactometer.Design
+ 1.1.0
+ enable
+
+
+ ../bin/Debug/
+
+
+ ../bin/Release/
+
+
+
+
+ %(Filename)
+
+
+ Designer
+
+
+
+ App.axaml
+ Code
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/App/Olfactometer.Design/StartApp.cs b/App/Olfactometer.Design/StartApp.cs
index bc1d1a9..d90cd38 100644
--- a/App/Olfactometer.Design/StartApp.cs
+++ b/App/Olfactometer.Design/StartApp.cs
@@ -1,20 +1,20 @@
-using Avalonia;
-using Avalonia.ReactiveUI;
-using ReactiveUI;
-
-namespace Olfactometer.Design
-{
- public class StartApp
- {
- // Avalonia configuration, don't remove; also used by visual designer.
- public static AppBuilder BuildAvaloniaApp()
- {
- RxApp.DefaultExceptionHandler = new MyCustomObservableExceptionHandler();
-
- return AppBuilder.Configure()
- .UsePlatformDetect()
- .LogToTrace()
- .UseReactiveUI();
- }
- }
-}
+using Avalonia;
+using Avalonia.ReactiveUI;
+using ReactiveUI;
+
+namespace Olfactometer.Design
+{
+ public class StartApp
+ {
+ // Avalonia configuration, don't remove; also used by visual designer.
+ public static AppBuilder BuildAvaloniaApp()
+ {
+ RxApp.DefaultExceptionHandler = new MyCustomObservableExceptionHandler();
+
+ return AppBuilder.Configure()
+ .UsePlatformDetect()
+ .LogToTrace()
+ .UseReactiveUI();
+ }
+ }
+}
diff --git a/Interface/Directory.Build.props b/Interface/Directory.Build.props
new file mode 100644
index 0000000..1392799
--- /dev/null
+++ b/Interface/Directory.Build.props
@@ -0,0 +1,15 @@
+
+
+
+ $(WarningsAsErrors);CS7035
+ $(NoWarn);CS1591
+
+
+ $(CiBuildVersion.Substring(3))
+ true
+
+
+
+
+
+
\ No newline at end of file