Skip to content
Merged
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
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ jobs:

- name: Test characterization harness
run: |
dotnet test --project src\Gemini.Tests\Gemini.Tests.csproj --configuration Release --framework net462 --no-build --no-restore
dotnet test --project src\Gemini.Tests\Gemini.Tests.csproj --configuration Release --framework net48 --no-build --no-restore
dotnet test --project src\Gemini.Tests\Gemini.Tests.csproj --configuration Release --framework net10.0-windows --no-build --no-restore

- name: Pack libraries
Expand Down
19 changes: 11 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,15 +61,18 @@ on other modules, but this is taken care of by the NuGet package dependency syst

## Continuous builds

GitHub Actions builds every push and pull request for the `net462` and
GitHub Actions builds every push and pull request for the `net48` and
`net10.0-windows` targets. Successful runs provide the validated NuGet and
symbol packages as a downloadable workflow artifact. The modern build uses
Caliburn.Micro 5.0.258 and MahApps.Metro 2.4.11; net6/net7 remain available
from the legacy package line. Publication remains restricted to approved
upstream releases. See [the CI workflow](.github/workflows/ci.yml) for the exact
build and release contract.

Applications and extensions moving from the previous package line should read
symbol packages as a downloadable workflow artifact. The project files and
[`eng/package-contract.json`](eng/package-contract.json) are the authoritative
dependency and asset inventory; CI rejects packages that drift from that
contract. Net6/net7 remain available from the legacy package line. Publication
remains restricted to approved upstream releases. See
[the CI workflow](.github/workflows/ci.yml) for the exact build and release
contract.

Applications and extensions moving from the previous package line or the
earlier .NET Framework target should read
the [Caliburn Micro 5 migration notes](doc/caliburn-5-migration.md).

## What does it do?
Expand Down
47 changes: 39 additions & 8 deletions doc/caliburn-5-migration.md
Original file line number Diff line number Diff line change
@@ -1,15 +1,22 @@
# Caliburn Micro 5 migration

Gemini's Caliburn Micro 5 package line targets `net462` and
`net10.0-windows`. Applications that require the previous net6/net7 assets
must remain on the earlier Gemini package line.
Gemini's Caliburn Micro 5 package line targets `net48` and
`net10.0-windows`. Applications that require the previous `net462`, net6, or
net7 assets must remain on an earlier Gemini package line.

Caliburn.Micro 5.0.258 does not publish a native net6 or net7 WPF Platform
asset group. NuGet can select its net462 Platform fallback for those targets,
but reports `NU1701`; the Core package's netstandard2.0 asset does not replace
the WPF Platform assembly. Gemini therefore does not publish new supported
net6/net7 targets on that warning-bearing fallback.

The `net48` Gemini asset uses Caliburn.Micro's compatible .NET Framework
Platform asset. Building that lane requires the .NET Framework 4.8 targeting
pack, and running it requires .NET Framework 4.8. Retarget and rebuild
Framework applications and extensions before adopting this package line;
retaining an older runtime while loading the new Gemini assembly is not a
supported migration path.

## Package and extension compatibility

The package family uses Caliburn.Micro 5.0.258. Extensions should reference the
Expand All @@ -29,14 +36,37 @@ viewModel.Activated += async (sender, args) =>
```

An extension compiled against the Caliburn 4 event accessor is not considered
binary compatible merely because Gemini still provides a `net462` asset.
Rebuild and test extensions against the new package before deployment.
binary compatible merely because Gemini still provides a .NET Framework
asset. Rebuild and test extensions against the new `net48` package before
deployment.

Caliburn 5 also replaces the obsolete `OnInitializeAsync` extension point with
`OnInitializedAsync`. The new hook runs after `IsInitialized` becomes `true`.
Code that retries initialization after a failure must recreate the failed
screen rather than changing Caliburn's lifecycle flags.

## Related dependency migration records

Dependency changes are recorded as migrations rather than a mutable inventory.
Add a new subsection for each later transition so consumers can follow every
upgrade path without rewriting earlier guidance.

### AvalonDock 4.60.0 to 4.74.1

This migration replaces the coherent AvalonDock engine and VS2013 theme pair
with version 4.74.1. The `net48` and `net10.0-windows` Gemini packages resolve
the matching native assets instead of a compatible older framework group. The
classic `DockingManager`, XML serializer entry point, and Light/Dark/Blue
resource URIs remain in use; this transition does not cover AvalonDock 5.

AvalonDock's assembly version changes from 4.60.0.0 to 4.74.1.0. Rebuild and
test external controls or extensions that reference AvalonDock directly rather
than assuming binary compatibility from a successful Gemini source build.
Preserve the complete old Gemini state file and its matching application,
module, extension, and dependency binaries during rollout. AvalonDock XML is
only the trailing section of that state file and is not a complete rollback
artifact by itself.

## Asynchronous shell and startup readiness

`IShell.ShowTool` has been replaced by the awaitable `ShowToolAsync` overloads,
Expand Down Expand Up @@ -160,6 +190,7 @@ being converted into fallback instances.

## Release policy

This package family starts the Gemini 1.1 beta line. Removing net6/net7 and
changing an inherited public lifecycle event are breaking package-contract
changes. The previous 1.0 beta four-target family remains the rollback path.
This package family starts the Gemini 1.1 beta line. Raising the Framework
floor to .NET Framework 4.8, removing net6/net7, and changing an inherited
public lifecycle event are breaking package-contract changes. The previous
1.0 beta four-target family remains the rollback path.
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFrameworks>net462;net10.0-windows</TargetFrameworks>
<TargetFrameworks>net48;net10.0-windows</TargetFrameworks>
<UseWPF>true</UseWPF>
<IsPackable>false</IsPackable>
<GeminiPackageVersion Condition="'$(GeminiPackageVersion)' == ''">0.0.0-local</GeminiPackageVersion>
Expand Down
27 changes: 27 additions & 0 deletions eng/compatibility/Caliburn5PackageHost/Program.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
using System;
using System.IO;
using System.Reflection;
using System.Threading.Tasks;
using System.Windows;
using AvalonDock;
using AvalonDock.Layout.Serialization;
using Caliburn.Micro;
using Gemini.Framework;

Expand All @@ -13,9 +18,31 @@ private static int Main()
var extension = new LifecycleExtension();
extension.Activated += extension.OnActivatedAsync;

var manager = new DockingManager();
var serializer = new XmlLayoutSerializer(manager);
using (var stream = new MemoryStream())
{
serializer.Serialize(stream);
if (stream.Length == 0)
return 1;
}

var theme = new ResourceDictionary
{
Source = new Uri(
"pack://application:,,,/AvalonDock.Themes.VS2013;component/LightTheme.xaml",
UriKind.Absolute)
};
var themeAssembly = Assembly.Load("AvalonDock.Themes.VS2013");
var expectedAvalonDockVersion = new Version(4, 74, 1, 0);

return typeof(WindowBase).Assembly.GetName().Name == "Gemini"
&& typeof(Screen).Assembly.GetName().Name == "Caliburn.Micro.Core"
&& typeof(BootstrapperBase).Assembly.GetName().Name == "Caliburn.Micro.Platform"
&& typeof(DockingManager).Assembly.GetName().Version == expectedAvalonDockVersion
&& themeAssembly.GetName().Version == expectedAvalonDockVersion
&& manager.Layout != null
&& theme.MergedDictionaries.Count > 0
? 0
: 1;
}
Expand Down
16 changes: 9 additions & 7 deletions eng/compatibility/Caliburn5PackageHost/README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Caliburn 5 package-only compatibility host
# Gemini package-only compatibility host

This is durable package-validation infrastructure, not a transient migration
probe. Project-reference builds cannot prove that a packed `GeminiWpf` package
Expand All @@ -11,12 +11,14 @@ version is supplied by the validator after the coherent family has been
inspected. This verifies the consumer boundary that a normal solution build
cannot cover:

- NuGet selects Gemini and the expected Caliburn.Micro Platform, Core, and
Microsoft.Xaml.Behaviors.Wpf packages for each target;
- an external extension compiles against the Caliburn 5 asynchronous
`Activated` event contract; and
- the expected Gemini, Caliburn Core, and Caliburn Platform assemblies load at
runtime.
- NuGet selects Gemini and the complete external dependency graph declared for
each target in `eng\package-contract.json`;
- the validator rejects framework fallback by requiring the exact native
compile and runtime assets declared by that contract;
- an external extension compiles against the public lifecycle event contract;
and
- representative framework, docking, serialization, and theme APIs load and
execute from the resolved package graph.

The host can be removed only when another package-only consumer fixture covers
the same restore, compile, public-contract, dependency-graph, and runtime
Expand Down
24 changes: 17 additions & 7 deletions eng/package-contract.json
Original file line number Diff line number Diff line change
@@ -1,22 +1,32 @@
{
"frameworks": [
{
"nuspec": ".NETFramework4.6.2",
"asset": "net462",
"build": "net462",
"lockFileTarget": "net462"
"nuspec": ".NETFramework4.8",
"asset": "net48",
"build": "net48",
"lockFileTarget": "net48",
"resolvedAssets": {
"Dirkster.AvalonDock": "lib/net48/AvalonDock.dll",
"Dirkster.AvalonDock.Themes.VS2013": "lib/net48/AvalonDock.Themes.VS2013.dll"
}
},
{
"nuspec": "net10.0-windows7.0",
"asset": "net10.0-windows7.0",
"build": "net10.0-windows",
"lockFileTarget": "net10.0-windows"
"lockFileTarget": "net10.0-windows",
"resolvedAssets": {
"Dirkster.AvalonDock": "lib/net10.0-windows7.0/AvalonDock.dll",
"Dirkster.AvalonDock.Themes.VS2013": "lib/net10.0-windows7.0/AvalonDock.Themes.VS2013.dll"
}
}
],
"resolvedDependencies": {
"Caliburn.Micro": "5.0.258",
"Caliburn.Micro.Core": "5.0.258",
"Microsoft.Xaml.Behaviors.Wpf": "1.1.135"
"Microsoft.Xaml.Behaviors.Wpf": "1.1.135",
"Dirkster.AvalonDock": "4.74.1",
"Dirkster.AvalonDock.Themes.VS2013": "4.74.1"
},
"shaders": [
"AddEffect.ps",
Expand All @@ -29,7 +39,7 @@
"internalDependencies": [],
"externalDependencies": {
"Caliburn.Micro": "5.0.258",
"Dirkster.AvalonDock.Themes.VS2013": "4.60.0",
"Dirkster.AvalonDock.Themes.VS2013": "4.74.1",
"DotNetProjects.Extended.Wpf.Toolkit": "4.6.78",
"MahApps.Metro": "2.4.11",
"System.ComponentModel.Composition": "6.0.0"
Expand Down
47 changes: 43 additions & 4 deletions eng/validate-artifacts.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@ reading the validation implementation. The script verifies:
- all internal Gemini dependencies use the same NBGV-calculated version; and
- every symbol package contains the matching portable PDBs; and
- a package-only external host restores, builds, and runs against the expected
Caliburn dependency graph for every retained framework.
Caliburn dependency graph and exact native AvalonDock assets for every
retained framework.

Any framework, package, assembly, or dependency change must update the
declarative contract deliberately. This prevents partial per-framework packages
Expand All @@ -38,7 +39,7 @@ Path to the declarative package contract. Defaults to
`eng\package-contract.json`.

.PARAMETER CompatibilityProjectPath
Path to the package-only Caliburn compatibility host. Defaults to
Path to the package-only compatibility host. Defaults to
`eng\compatibility\Caliburn5PackageHost\Caliburn5PackageHost.csproj`.

.EXAMPLE
Expand Down Expand Up @@ -327,18 +328,28 @@ Assert-Condition -Condition ($distinctVersions.Count -eq 1) `

$packageVersion = $distinctVersions[0]
$nuGetConfigPath = Join-Path $repositoryRoot 'src\NuGet.config'
$compatibilityProjectDirectory = Split-Path -Parent $CompatibilityProjectPath
$geminiPackagePath = Join-Path $PackagePath "GeminiWpf.$packageVersion.nupkg"
Assert-Condition -Condition (Test-Path -LiteralPath $geminiPackagePath -PathType Leaf) `
-Message "Gemini package does not exist: $geminiPackagePath"

# Key validation restores by package content. Distinct working-tree payloads can
# share an ID/version, but each must be restored and inspected independently.
$geminiPackageHash = (Get-FileHash -LiteralPath $geminiPackagePath -Algorithm SHA256).Hash.ToLowerInvariant()
$compatibilityPackagesPath = Join-Path $compatibilityProjectDirectory "obj\package-validation\$geminiPackageHash"
Invoke-DotNet `
-Description 'Package-only compatibility restore' `
-Arguments @(
'restore',
$CompatibilityProjectPath,
'--force-evaluate',
'--configfile',
$nuGetConfigPath,
"-p:RestoreAdditionalProjectSources=$PackagePath",
"-p:RestorePackagesPath=$compatibilityPackagesPath",
"-p:GeminiPackageVersion=$packageVersion"
)

$compatibilityProjectDirectory = Split-Path -Parent $CompatibilityProjectPath
$assetsPath = Join-Path $compatibilityProjectDirectory 'obj\project.assets.json'
Assert-Condition -Condition (Test-Path -LiteralPath $assetsPath -PathType Leaf) `
-Message "Compatibility restore did not create $assetsPath."
Expand All @@ -356,6 +367,33 @@ foreach ($framework in $expectedFrameworks) {
-Message "Compatibility target '$($framework.lockFileTarget)' is missing resolved package '$expectedIdentity'."
}

foreach ($assetExpectation in $framework.resolvedAssets.PSObject.Properties) {
$dependency = $contract.resolvedDependencies.PSObject.Properties[$assetExpectation.Name]
Assert-Condition -Condition ($null -ne $dependency) `
-Message "Asset expectation '$($assetExpectation.Name)' has no resolved dependency version."

$expectedIdentity = "$($assetExpectation.Name)/$($dependency.Value)"
$resolvedPackage = $targetProperty.Value.PSObject.Properties[$expectedIdentity]
Assert-Condition -Condition ($null -ne $resolvedPackage) `
-Message "Compatibility target '$($framework.lockFileTarget)' has no asset data for '$expectedIdentity'."

$compile = $resolvedPackage.Value.PSObject.Properties['compile']
$runtime = $resolvedPackage.Value.PSObject.Properties['runtime']
Assert-Condition -Condition ($null -ne $compile) `
-Message "Compatibility target '$($framework.lockFileTarget)' has no compile assets for '$expectedIdentity'."
Assert-Condition -Condition ($null -ne $runtime) `
-Message "Compatibility target '$($framework.lockFileTarget)' has no runtime assets for '$expectedIdentity'."

Assert-SetEqual `
-Expected @([string] $assetExpectation.Value) `
-Actual @($compile.Value.PSObject.Properties.Name) `
-Description "$expectedIdentity compile assets for $($framework.lockFileTarget)"
Assert-SetEqual `
-Expected @([string] $assetExpectation.Value) `
-Actual @($runtime.Value.PSObject.Properties.Name) `
-Description "$expectedIdentity runtime assets for $($framework.lockFileTarget)"
}

Invoke-DotNet `
-Description "Package-only compatibility run for $($framework.build)" `
-Arguments @(
Expand All @@ -367,9 +405,10 @@ foreach ($framework in $expectedFrameworks) {
'--framework',
$framework.build,
'--no-restore',
"-p:RestorePackagesPath=$compatibilityPackagesPath",
"-p:GeminiPackageVersion=$packageVersion"
)
}

$shaderCount = $expectedFrameworks.Count * @($contract.shaders).Count
Write-Host "Validated $shaderCount compiled shaders, $($expectedPackages.Count) packages with symbols, and the package-only Caliburn host at version $packageVersion."
Write-Host "Validated $shaderCount compiled shaders, $($expectedPackages.Count) packages with symbols, exact resolved assets, and the package-only compatibility host at version $packageVersion."
6 changes: 3 additions & 3 deletions src/Gemini.Demo/Gemini.Demo.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

<PropertyGroup>
<OutputType>WinExe</OutputType>
<TargetFrameworks>net462;net10.0-windows</TargetFrameworks>
<TargetFrameworks>net48;net10.0-windows</TargetFrameworks>
<UseWPF>true</UseWPF>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<IsPackable>false</IsPackable>
Expand Down Expand Up @@ -64,11 +64,11 @@
<PackageReference Include="JeremyAnsel.HLSL.Targets" Version="1.0.9" />
</ItemGroup>

<ItemGroup Condition="$(TargetFramework) != 'net462'">
<ItemGroup Condition="$(TargetFramework) != 'net48'">
<PackageReference Include="HelixToolkit.Core.Wpf" Version="2.10.0" />
</ItemGroup>

<ItemGroup Condition="$(TargetFramework) == 'net462'">
<ItemGroup Condition="$(TargetFramework) == 'net48'">
<PackageReference Include="HelixToolkit.Wpf" Version="2.10.0" />
</ItemGroup>

Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFrameworks>net462;net10.0-windows</TargetFrameworks>
<TargetFrameworks>net48;net10.0-windows</TargetFrameworks>
<PackageTags>core WPF MVVM AvalonDock Caliburn Micro Visual Studio IDE Shell C# compile Roslyn</PackageTags>
<Description>CodeCompiler module for Gemini, providing C# code compilation using Roslyn.</Description>
</PropertyGroup>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk.WindowsDesktop">

<PropertyGroup>
<TargetFrameworks>net462;net10.0-windows</TargetFrameworks>
<TargetFrameworks>net48;net10.0-windows</TargetFrameworks>
<UseWPF>true</UseWPF>
<PackageTags>core WPF MVVM AvalonDock Caliburn Micro Visual Studio IDE Shell C# syntax highlighting AvalonEdit</PackageTags>
<Description>CodeEditor module for Gemini, providing a syntax-highlighted code editor using AvalonEdit.</Description>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk.WindowsDesktop">

<PropertyGroup>
<TargetFrameworks>net462;net10.0-windows</TargetFrameworks>
<TargetFrameworks>net48;net10.0-windows</TargetFrameworks>
<UseWPF>true</UseWPF>
<PackageTags>core WPF MVVM AvalonDock Caliburn Micro Visual Studio IDE Shell error list</PackageTags>
<Description>ErrorList module for Gemini, providing a tool window to display messages, warnings and errors.</Description>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk.WindowsDesktop">

<PropertyGroup>
<TargetFrameworks>net462;net10.0-windows</TargetFrameworks>
<TargetFrameworks>net48;net10.0-windows</TargetFrameworks>
<UseWPF>true</UseWPF>
<PackageTags>core WPF MVVM AvalonDock Caliburn Micro Visual Studio IDE Shell graph node connector connection</PackageTags>
<Description>GraphEditor module for Gemini, providing UI controls to edit a graph of connected nodes.</Description>
Expand Down
Loading