Skip to content

Commit

Permalink
setup FW Lite as a maui app (#968)
Browse files Browse the repository at this point in the history
* add initial maui project
* switch to windows-latest for fw-lite builds to support maui
* allow configuration of cache file location and crdt project path
* introduce some navigation retry logic as sometimes the first navigation after we get the server url fails.
* disable publish single file as it makes incremental updates larger.
* fix error caused by reusing elements when changing views.
* change SingleOptionEditor.svelte to log an error when no options are found and return an empty list instead of breaking the app.
* start LocalWebApp in it's own forground thread to ensure that the process is not terminated before the server is shutdown.
---------

Co-authored-by: Chris Hirt <[email protected]>
  • Loading branch information
hahn-kev and megahirt authored Jul 26, 2024
1 parent 26df6bd commit 7bf70f4
Show file tree
Hide file tree
Showing 54 changed files with 1,557 additions and 121 deletions.
159 changes: 140 additions & 19 deletions .github/workflows/fw-lite.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,12 @@ on:
- develop

jobs:
build-app:
name: Build FW Lite
build-and-test:
name: Build FW Lite and run tests
timeout-minutes: 20
runs-on: ubuntu-latest
runs-on: windows-latest
env:
NuGetPackageSourceCredentials_github: ${{ secrets.GH_NUGET_PACKAGE_CREDS }}
steps:
- name: Checkout
uses: actions/checkout@v4
Expand All @@ -34,44 +36,163 @@ jobs:
- uses: actions/setup-node@v4
with:
node-version-file: './frontend/package.json'

- name: Dotnet build
working-directory: backend/FwLite/LocalWebApp
run: dotnet build --configuration Release
working-directory: backend/FwLite/FwLiteDesktop
run: |
dotnet nuget enable source github
dotnet build --configuration Release
- name: Dotnet test
working-directory: backend/FwLite/LcmCrdt.Tests
run: dotnet test --configuration Release --logger:"xunit;LogFileName={assembly}.results.xml" --results-directory ./test-results
- name: Publish test results
uses: EnricoMi/publish-unit-test-result-action@8885e273a4343cd7b48eaa72428dea0c3067ea98 # v2.14.0
if: always()
run: dotnet test --configuration Release --logger GitHubActions

- name: Build viewer
working-directory: frontend/viewer
run: |
corepack enable
pnpm install
pnpm run build-app
publish-app:
name: Publish FW Lite app

# only publish if tag matches fwlite-v* pattern
# iif: startsWith(github.ref, 'refs/tags/fwlite-v')
needs: build-and-test
timeout-minutes: 30
runs-on: windows-latest
env:
NuGetPackageSourceCredentials_github: ${{ secrets.GH_NUGET_PACKAGE_CREDS }}
enable-msix: false #we can't sign the msix installer so we'll disable the build for now
steps:
- name: Checkout
uses: actions/checkout@v4
with:
submodules: true
- uses: actions/setup-dotnet@v4
with:
check_name: FW Lite Unit Tests
files: ./backend/FwLite/LcmCrdt.Tests/test-results/*.xml
dotnet-version: '8.x'
- uses: actions/setup-node@v4
with:
node-version-file: './frontend/package.json'

- name: Setup Maui
run: dotnet workload install maui-windows
- name: Enable GitHub Packages
working-directory: backend/FwLite
run: dotnet nuget enable source github

- name: Build viewer
working-directory: frontend/viewer
run: |
corepack enable
pnpm install
pnpm run build-app
- name: Publish linux
- name: Dotnet build
working-directory: backend/FwLite/LocalWebApp
run: dotnet build --configuration Release

- name: Publish Linux
working-directory: backend/FwLite/LocalWebApp
run: dotnet publish -r linux-x64 --artifacts-path ../artifacts
- name: Publish osx

- name: Publish OSX
working-directory: backend/FwLite/LocalWebApp
run: dotnet publish -r osx-x64 --artifacts-path ../artifacts
- name: Publish osx-arm

- name: Publish OSX ARM
working-directory: backend/FwLite/LocalWebApp
run: dotnet publish -r osx-arm64 --artifacts-path ../artifacts
- name: Publish win

- name: Publish Windows
working-directory: backend/FwLite/LocalWebApp
run: dotnet publish -r win-x64 --artifacts-path ../artifacts
- name: Publish win-arm

- name: Publish Windows ARM
working-directory: backend/FwLite/LocalWebApp
run: dotnet publish -r win-arm64 --artifacts-path ../artifacts
- name: Upload artifacts
# uploading in one artifact as there's a lot of duplication between builds so compression goes far

- name: Upload local web app artifacts
uses: actions/upload-artifact@v4
with:
name: fw-lite
name: fw-lite-local-web-app
if-no-files-found: error
path: backend/FwLite/artifacts/publish/LocalWebApp/*

- name: Publish Windows MAUI unpackaged app
working-directory: backend/FwLite/FwLiteDesktop
run: |
dotnet publish -r win-x64 --artifacts-path ../artifacts -p:WindowsPackageType=None
dotnet publish -r win-arm64 --artifacts-path ../artifacts -p:WindowsPackageType=None
- name: Upload FWLite Desktop artifacts
uses: actions/upload-artifact@v4
with:
name: fw-lite-unpackaged
if-no-files-found: error
path: backend/FwLite/artifacts/publish/FwLiteDesktop/*

- name: Publish Windows MAUI msix app
if: ${{env.enable-msix}}
working-directory: backend/FwLite/FwLiteDesktop
run: |
dotnet publish -f net8.0-windows10.0.19041.0 -r win-x64 --artifacts-path ../artifacts -p:Platform=x64
dotnet publish -f net8.0-windows10.0.19041.0 -r win-arm64 --artifacts-path ../artifacts -p:Platform=arm64
mkdir -p ../artifacts/msix
cp ../artifacts/bin/FwLiteDesktop/*/AppPackages/*/*.msix ../artifacts/msix/
- name: Upload FWLite Desktop artifacts
uses: actions/upload-artifact@v4
if: ${{env.enable-msix}}
with:
name: fw-lite-msix
if-no-files-found: error
path: backend/FwLite/artifacts/msix/*.msix

sign-installer:
#disabled as this doesn't work since ltops-signing doesn't have the signtool
if: false
name: Sign FWLite MSIX installer
needs: publish-app
runs-on: [self-hosted, ltops-signing]
steps:
- uses: actions/download-artifact@v4
with:
name: fw-lite-msix
path: target-msix
- name: Sign MSIX installer
shell: pwsh
env:
signtool: C:/"Program Files (x86)"/"Windows Kits"/10/bin/10.0.17763.0/x86/signtool.exe
run: |
$PublicCert = [System.Convert]::FromBase64String('${{ secrets.CODESIGN_LSDEVSECTIGOEV }}')
Set-Content $ENV:TEMP\certificate -Value ($PublicCert) -AsByteStream
${{env.signtool}} sign /f $ENV:TEMP\certificate /fd sha256 /du https://software.sil.org /tr http://timestamp.sectigo.com /td sha256 /v target-msix/*.msix
- name: Upload signed MSIX installer
uses: actions/upload-artifact@v4
with:
name: fw-lite-msix-signed
if-no-files-found: error
path: target-msix/*.msix

create-release:
#disabled since we don't have a release yet
if: false
name: Create Release
needs: sign-installer
runs-on: windows-latest

steps:
- uses: actions/download-artifact@v4
with:
name: fw-lite

- name: Create Release
uses: softprops/action-gh-release@v2
with:
files: |
backend/FwLite/artifacts/bin/FwLiteDesktop/*/AppPackages/FwLiteDesktop_1.0.0.1_Test/*.msix
body: |
Release for version ${{ github.ref }}
draft: true
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,4 @@ test-results/
**/*.sqlite
**/*.sqlite-*
msal.cache
artifacts/
23 changes: 19 additions & 4 deletions LexBox.sln
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FwDataMiniLcmBridge", "back
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FwDataMiniLcmBridge.Tests", "backend\FwLite\FwDataMiniLcmBridge.Tests\FwDataMiniLcmBridge.Tests.csproj", "{B0299A49-C0B2-4553-A72E-1670D4CB5138}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FwLiteDesktop", "backend\FwLite\FwLiteDesktop\FwLiteDesktop.csproj", "{9001FE0F-DBBF-4A78-9EB9-9B5042CF8A78}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -79,10 +81,6 @@ Global
{6D4062DC-1B1A-4A24-9C61-0F6A18A39882}.Debug|Any CPU.Build.0 = Debug|Any CPU
{6D4062DC-1B1A-4A24-9C61-0F6A18A39882}.Release|Any CPU.ActiveCfg = Release|Any CPU
{6D4062DC-1B1A-4A24-9C61-0F6A18A39882}.Release|Any CPU.Build.0 = Release|Any CPU
{2CDF7126-733B-46E8-9B93-7DC31DE39682}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{2CDF7126-733B-46E8-9B93-7DC31DE39682}.Debug|Any CPU.Build.0 = Debug|Any CPU
{2CDF7126-733B-46E8-9B93-7DC31DE39682}.Release|Any CPU.ActiveCfg = Release|Any CPU
{2CDF7126-733B-46E8-9B93-7DC31DE39682}.Release|Any CPU.Build.0 = Release|Any CPU
{6B857F83-72E3-43E6-91C3-A946F3F988D8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{6B857F83-72E3-43E6-91C3-A946F3F988D8}.Debug|Any CPU.Build.0 = Debug|Any CPU
{6B857F83-72E3-43E6-91C3-A946F3F988D8}.Release|Any CPU.ActiveCfg = Release|Any CPU
Expand All @@ -107,6 +105,19 @@ Global
{B0299A49-C0B2-4553-A72E-1670D4CB5138}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B0299A49-C0B2-4553-A72E-1670D4CB5138}.Release|Any CPU.ActiveCfg = Release|Any CPU
{B0299A49-C0B2-4553-A72E-1670D4CB5138}.Release|Any CPU.Build.0 = Release|Any CPU
{9001FE0F-DBBF-4A78-9EB9-9B5042CF8A78}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{9001FE0F-DBBF-4A78-9EB9-9B5042CF8A78}.Debug|Any CPU.Build.0 = Debug|Any CPU
{9001FE0F-DBBF-4A78-9EB9-9B5042CF8A78}.Debug|Any CPU.Deploy.0 = Debug|Any CPU
{9001FE0F-DBBF-4A78-9EB9-9B5042CF8A78}.Release|Any CPU.ActiveCfg = Release|Any CPU
{9001FE0F-DBBF-4A78-9EB9-9B5042CF8A78}.Release|Any CPU.Build.0 = Release|Any CPU
{9001FE0F-DBBF-4A78-9EB9-9B5042CF8A78}.Release|Any CPU.Deploy.0 = Release|Any CPU
{2CDF7126-733B-46E8-9B93-7DC31DE39682}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{2CDF7126-733B-46E8-9B93-7DC31DE39682}.Debug|Any CPU.Build.0 = Debug|Any CPU
{2CDF7126-733B-46E8-9B93-7DC31DE39682}.Release|Any CPU.ActiveCfg = Release|Any CPU
{2CDF7126-733B-46E8-9B93-7DC31DE39682}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{E8BB768B-C3DC-4BE6-9B9F-82319E05AF86} = {7B6E21C4-5AF4-4505-B7D9-59A3886C5090}
Expand All @@ -118,5 +129,9 @@ Global
{8B54FFB5-0BDF-403E-83CC-A3B3861EC507} = {7B6E21C4-5AF4-4505-B7D9-59A3886C5090}
{279197B6-EC06-4DE0-94F8-625379C3AD83} = {7B6E21C4-5AF4-4505-B7D9-59A3886C5090}
{B0299A49-C0B2-4553-A72E-1670D4CB5138} = {7B6E21C4-5AF4-4505-B7D9-59A3886C5090}
{9001FE0F-DBBF-4A78-9EB9-9B5042CF8A78} = {7B6E21C4-5AF4-4505-B7D9-59A3886C5090}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {440AE83C-6DB0-4F18-B2C1-BCD33F0645B6}
EndGlobalSection
EndGlobal
25 changes: 23 additions & 2 deletions backend/FwLite/FwDataMiniLcmBridge/FwDataFactory.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using FwDataMiniLcmBridge.Api;
using FwDataMiniLcmBridge.LcmUtils;
using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using SIL.LCModel;

Expand All @@ -13,6 +14,22 @@ public class FwDataFactory(
ILogger<FwDataFactory> logger,
IProjectLoader projectLoader) : IDisposable
{
private bool _shuttingDown = false;
public FwDataFactory(FwDataProjectContext context,
ILogger<FwDataMiniLcmApi> fwdataLogger,
IMemoryCache cache,
ILogger<FwDataFactory> logger,
IProjectLoader projectLoader,
IHostApplicationLifetime lifetime) : this(context, fwdataLogger, cache, logger, projectLoader)
{
lifetime.ApplicationStopping.Register(() =>
{
//this gets called immediately after the shutdown is triggered, we need this so we can ignore project disconnects during shutdown.
//and delegate those to the disposal of this class.
_shuttingDown = true;
});
}

public FwDataMiniLcmApi GetFwDataMiniLcmApi(string projectName, bool saveOnDispose)
{
var project = FieldWorksProjectList.GetProject(projectName) ?? throw new InvalidOperationException($"Project {projectName} not found.");
Expand Down Expand Up @@ -61,7 +78,7 @@ private static void OnLcmProjectCacheEviction(object key, object? value, Evictio
// one way around this would be to return a lease object, only after a timeout and no more references to the lease object would the service be disposed.
var lcmCache = (LcmCache)value;
var (logger, projects) = ((ILogger<FwDataFactory>, HashSet<string>))state!;
var name = lcmCache.ProjectId.Name;
var name = key.ToString()?.Split('|')[1] ?? "Unknown";
logger.LogInformation("Evicting project {ProjectFileName} from cache", name);
projects.Remove((string)key);
if (!lcmCache.IsDisposed)
Expand All @@ -73,12 +90,13 @@ private static void OnLcmProjectCacheEviction(object key, object? value, Evictio

public void Dispose()
{
logger.LogInformation("Closing all projects");
foreach (var project in _projects)
{
var lcmCache = cache.Get<LcmCache>(project);
if (lcmCache is null || lcmCache.IsDisposed) continue;
var name = lcmCache.ProjectId.Name;
lcmCache.Dispose();//need to explicitly call dispose as that blocks, just removing from the cache does not block, meaning it will not finish disposing before the program exits.
lcmCache.Dispose(); //need to explicitly call dispose as that blocks, just removing from the cache does not block, meaning it will not finish disposing before the program exits.
logger.LogInformation("FW Data Project {ProjectFileName} disposed", name);
}
}
Expand All @@ -102,6 +120,9 @@ public void CloseCurrentProject()

private void CloseProject(FwDataProject project)
{
// if we are shutting down, don't do anything because we want project dispose to be called as part of the shutdown process.
if (_shuttingDown) return;
logger.LogInformation("Explicitly Closing project {ProjectFileName}", project.Name);
var cacheKey = CacheKey(project);
var lcmCache = cache.Get<LcmCache>(cacheKey);
if (lcmCache is null) return;
Expand Down
13 changes: 6 additions & 7 deletions backend/FwLite/FwDataMiniLcmBridge/FwDataMiniLcmBridge.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -8,23 +8,22 @@
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="8.0.0" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="8.0.1" />
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="8.0.0" />
<PackageReference Include="Microsoft.Extensions.Logging" Version="8.0.0" />
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="8.0.1" />
<PackageReference Include="Microsoft.ICU.ICU4C.Runtime" Version="72.1.0.3" />
<PackageReference Include="SIL.LCModel" Version="11.0.0-beta0083"/>
<PackageReference Include="SIL.LCModel.Core" Version="11.0.0-beta0083"/>
<PackageReference Include="SIL.LCModel.Utils" Version="11.0.0-beta0083"/>
<PackageReference Include="structuremap.patched" Version="4.7.3"/>
<PackageReference Include="SIL.LCModel" Version="11.0.0-beta0100 " />
<PackageReference Include="structuremap.patched" Version="4.7.3" />
<PackageReference Include="System.Linq.Async" Version="6.0.1" />
<PackageReference Include="System.Net.NameResolution" Version="4.3.0"/>
<PackageReference Include="System.Net.NameResolution" Version="4.3.0" />
<PackageReference Include="System.Threading.ThreadPool" Version="4.3.0" />
</ItemGroup>

<ItemGroup>
<InternalsVisibleTo Include="FwDataMiniLcmBridge.Tests"/>
<InternalsVisibleTo Include="FwDataMiniLcmBridge.Tests" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\MiniLcm\MiniLcm.csproj"/>
<ProjectReference Include="..\MiniLcm\MiniLcm.csproj" />
</ItemGroup>

</Project>
14 changes: 14 additions & 0 deletions backend/FwLite/FwLiteDesktop/App.xaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<?xml version = "1.0" encoding = "UTF-8" ?>
<Application xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:FwLiteDesktop"
x:Class="FwLiteDesktop.App">
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Resources/Styles/Colors.xaml" />
<ResourceDictionary Source="Resources/Styles/Styles.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
</Application>
11 changes: 11 additions & 0 deletions backend/FwLite/FwLiteDesktop/App.xaml.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
namespace FwLiteDesktop;

public partial class App : Application
{
public App(MainPage mainPage)
{
InitializeComponent();

MainPage = mainPage;
}
}
Loading

0 comments on commit 7bf70f4

Please sign in to comment.