diff --git a/.github/workflows/dotnet-build.yml b/.github/workflows/dotnet-build.yml
index edb0731b..39e58ddf 100644
--- a/.github/workflows/dotnet-build.yml
+++ b/.github/workflows/dotnet-build.yml
@@ -15,7 +15,7 @@ env:
jobs:
build-project:
- runs-on: windows-latest
+ runs-on: ubuntu-latest
env:
solution_name: ./uSync.slnx
@@ -40,4 +40,4 @@ jobs:
run: dotnet test ${{ env.solution_name }}
- name: Generate AppSettings Schema
- run: dotnet run -c ${{env.Config}} --project ${{ env.schema_gen_project}}
+ run: dotnet run -c ${{ env.config }} --project ${{ env.schema_gen_project}}
diff --git a/uSync.Core/Extensions/StringExtensions.cs b/uSync.Core/Extensions/StringExtensions.cs
index 5e11f530..e8999d87 100644
--- a/uSync.Core/Extensions/StringExtensions.cs
+++ b/uSync.Core/Extensions/StringExtensions.cs
@@ -16,14 +16,22 @@ public static class StringExtensions
///
/// convert a file name to one that isn't going to cause us any downlevel problems.
///
+ ///
+ /// paths aren't always parsed on the OS they came from (e.g. a Windows-style path
+ /// loaded on Linux), so we split on both separators here rather than using
+ /// Path.GetFileName/GetDirectoryName, which only recognise the current OS's separator.
+ ///
public static string ToAppSafeFileName(this string value)
{
- var filename = Path.GetFileName(value);
+ var separatorIndex = value.LastIndexOfAny(['\\', '/']);
+ var directory = separatorIndex >= 0 ? value[..(separatorIndex + 1)] : string.Empty;
+ var filename = separatorIndex >= 0 ? value[(separatorIndex + 1)..] : value;
+
if (_badNames.InvariantContains(filename))
{
- return Path.Combine(
- Path.GetDirectoryName(value) ?? string.Empty,
- $"__{Path.GetFileNameWithoutExtension(value)}__{Path.GetExtension(value)}");
+ var extension = Path.GetExtension(filename);
+ var nameWithoutExtension = filename[..^extension.Length];
+ return $"{directory}__{nameWithoutExtension}__{extension}";
}
return value;
}