Skip to content

Commit 7b16edf

Browse files
Use prebuilt pinget package
Resolve bundled Avalonia launches back to the installation root so root-level tools and assets are found when UniGetUI.Avalonia.exe runs from the Avalonia subfolder. Replace the nested UniGetUI.Pinget.Cli publish step with the Devolutions.Pinget.Cli.Rust package and remove the obsolete C# pinget project from the solution. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent 57b22a8 commit 7b16edf

9 files changed

Lines changed: 125 additions & 1941 deletions

File tree

src/UniGetUI.Avalonia/UniGetUI.Avalonia.csproj

Lines changed: 6 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -30,13 +30,11 @@
3030
</PropertyGroup>
3131

3232
<PropertyGroup Condition="$([MSBuild]::IsOSPlatform('Windows'))">
33-
<PingetCliProject>$(MSBuildThisFileDirectory)..\UniGetUI.Pinget.Cli\UniGetUI.Pinget.Cli.csproj</PingetCliProject>
3433
<PingetCliRuntimeIdentifier Condition="'$(RuntimeIdentifier)' != ''">$(RuntimeIdentifier)</PingetCliRuntimeIdentifier>
3534
<PingetCliRuntimeIdentifier Condition="'$(PingetCliRuntimeIdentifier)' == '' and '$(Platform)' == 'arm64'">win-arm64</PingetCliRuntimeIdentifier>
3635
<PingetCliRuntimeIdentifier Condition="'$(PingetCliRuntimeIdentifier)' == ''">win-x64</PingetCliRuntimeIdentifier>
37-
<PingetCliPublishDir>$(MSBuildProjectDirectory)\obj\$(Platform)\$(Configuration)\BundledPinget\$(PingetCliRuntimeIdentifier)\</PingetCliPublishDir>
38-
<PingetCliExecutablePath>$(PingetCliPublishDir)pinget.exe</PingetCliExecutablePath>
39-
<PingetCliNativeSqlitePath>$(PingetCliPublishDir)e_sqlite3.dll</PingetCliNativeSqlitePath>
36+
<PingetCliPackageNativePath>$(PkgDevolutions_Pinget_Cli_Rust)\runtimes\$(PingetCliRuntimeIdentifier)\native</PingetCliPackageNativePath>
37+
<PingetCliExecutablePath>$(PingetCliPackageNativePath)\pinget.exe</PingetCliExecutablePath>
4038
</PropertyGroup>
4139

4240
<PropertyGroup Condition="'$(EnableAvaloniaDiagnostics)' == 'true'">
@@ -48,18 +46,13 @@
4846
AfterTargets="Build;Publish"
4947
Condition="'$(DesignTimeBuild)' != 'true' and '$(SkipBundledPingetCli)' != 'true' and $([MSBuild]::IsOSPlatform('Windows'))"
5048
>
51-
<MSBuild
52-
Projects="$(PingetCliProject)"
53-
Targets="Restore;Publish"
54-
Properties="Configuration=$(Configuration);Platform=$(Platform);TargetFramework=net10.0;RuntimeIdentifier=$(PingetCliRuntimeIdentifier);SelfContained=true;PublishSingleFile=true;PublishTrimmed=true;TrimMode=partial;JsonSerializerIsReflectionEnabledByDefault=true;PublishDir=$(PingetCliPublishDir);AppendRuntimeIdentifierToOutputPath=false"
49+
<Error
50+
Condition="!Exists('$(PingetCliExecutablePath)')"
51+
Text="NuGet pinget executable not found at '$(PingetCliExecutablePath)'. Ensure package restore has completed for Devolutions.Pinget.Cli.Rust."
5552
/>
5653
<ItemGroup>
5754
<PingetCliOutputFiles Include="$(PingetCliExecutablePath)" Condition="Exists('$(PingetCliExecutablePath)')" />
58-
<PingetCliOutputFiles Include="$(PingetCliNativeSqlitePath)" Condition="Exists('$(PingetCliNativeSqlitePath)')" />
59-
<PingetCliOutputFiles Include="$(TargetDir)runtimes\$(PingetCliRuntimeIdentifier)\native\e_sqlite3.dll" Condition="!Exists('$(PingetCliNativeSqlitePath)') and Exists('$(TargetDir)runtimes\$(PingetCliRuntimeIdentifier)\native\e_sqlite3.dll')" />
6055
<PingetCliPublishOutputFiles Include="$(PingetCliExecutablePath)" Condition="Exists('$(PingetCliExecutablePath)')" />
61-
<PingetCliPublishOutputFiles Include="$(PingetCliNativeSqlitePath)" Condition="Exists('$(PingetCliNativeSqlitePath)')" />
62-
<PingetCliPublishOutputFiles Include="$(PublishDir)runtimes\$(PingetCliRuntimeIdentifier)\native\e_sqlite3.dll" Condition="!Exists('$(PingetCliNativeSqlitePath)') and Exists('$(PublishDir)runtimes\$(PingetCliRuntimeIdentifier)\native\e_sqlite3.dll')" />
6356
</ItemGroup>
6457
<Copy
6558
SourceFiles="@(PingetCliOutputFiles)"
@@ -101,6 +94,7 @@
10194
<PackageReference Include="Octokit" Version="14.0.0" />
10295
<PackageReference Include="Avalonia.Controls.WebView" Version="12.0.0-rc1" />
10396
<PackageReference Include="Tmds.DBus.Protocol" Version="0.92.0" />
97+
<PackageReference Include="Devolutions.Pinget.Cli.Rust" Version="0.4.0" GeneratePathProperty="true" ExcludeAssets="build;buildTransitive;native" />
10498
</ItemGroup>
10599

106100
<ItemGroup>

src/UniGetUI.Core.Data.Tests/CoreTests.cs

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,36 @@ public void CheckOtherAttributes()
3939
);
4040
}
4141

42+
[Fact]
43+
public void ResolveInstallationDirectoryReturnsParentForBundledAvaloniaDirectory()
44+
{
45+
string installDirectory = Path.GetFullPath(Path.Join("install-root"));
46+
string avaloniaDirectory = Path.Join(installDirectory, "Avalonia");
47+
string classicExecutable = Path.Join(installDirectory, "UniGetUI.exe");
48+
49+
string resolvedDirectory = CoreData.ResolveInstallationDirectory(
50+
avaloniaDirectory,
51+
filePath => filePath == classicExecutable,
52+
static _ => false
53+
);
54+
55+
Assert.Equal(installDirectory, resolvedDirectory);
56+
}
57+
58+
[Fact]
59+
public void ResolveInstallationDirectoryKeepsStandaloneAvaloniaDirectory()
60+
{
61+
string avaloniaDirectory = Path.GetFullPath(Path.Join("standalone", "Avalonia"));
62+
63+
string resolvedDirectory = CoreData.ResolveInstallationDirectory(
64+
avaloniaDirectory,
65+
static _ => false,
66+
static _ => false
67+
);
68+
69+
Assert.Equal(avaloniaDirectory, resolvedDirectory);
70+
}
71+
4272
[Theory]
4373
[InlineData("3.3.7", "3.3.7")]
4474
[InlineData("2026.1.2", "v2026.1.2")]

src/UniGetUI.Core.Data/CoreData.cs

Lines changed: 59 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@ public static class CoreData
88
{
99
private const string GitHubReleasePageBaseUrl = "https://github.com/Devolutions/UniGetUI/releases/tag/";
1010
private const string GitHubReleaseApiBaseUrl = "https://api.github.com/repos/Devolutions/UniGetUI/releases/tags/";
11+
private const string BundledModernAppDirectoryName = "Avalonia";
12+
private const string ClassicExecutableName = "UniGetUI.exe";
13+
private const string BundledPingetExecutableName = "pinget.exe";
1114

1215
private static int? __code_page;
1316
public static int CODE_PAGE
@@ -326,22 +329,47 @@ public static string UniGetUIExecutableDirectory
326329
{
327330
get
328331
{
329-
string? dir = AppContext.BaseDirectory.TrimEnd(
330-
Path.DirectorySeparatorChar,
331-
Path.AltDirectorySeparatorChar
332-
);
333-
if (dir is not null)
332+
string dir = NormalizeDirectoryPath(AppContext.BaseDirectory);
333+
if (!string.IsNullOrEmpty(dir))
334334
{
335-
return dir;
335+
return ResolveInstallationDirectory(dir);
336336
}
337337

338338
Logger.Error("AppContext.BaseDirectory returned an empty path");
339339

340-
return AppContext.BaseDirectory.TrimEnd(
341-
Path.DirectorySeparatorChar,
342-
Path.AltDirectorySeparatorChar
343-
);
340+
return ResolveInstallationDirectory(NormalizeDirectoryPath(AppContext.BaseDirectory));
341+
}
342+
}
343+
344+
public static string ResolveInstallationDirectory(
345+
string executableDirectory,
346+
Func<string, bool>? fileExists = null,
347+
Func<string, bool>? directoryExists = null
348+
)
349+
{
350+
fileExists ??= File.Exists;
351+
directoryExists ??= Directory.Exists;
352+
353+
string normalizedDirectory = NormalizeDirectoryPath(executableDirectory);
354+
if (!string.Equals(
355+
Path.GetFileName(normalizedDirectory),
356+
BundledModernAppDirectoryName,
357+
StringComparison.OrdinalIgnoreCase
358+
))
359+
{
360+
return normalizedDirectory;
361+
}
362+
363+
string? parentDirectory = Path.GetDirectoryName(normalizedDirectory);
364+
if (string.IsNullOrEmpty(parentDirectory))
365+
{
366+
return normalizedDirectory;
344367
}
368+
369+
parentDirectory = NormalizeDirectoryPath(parentDirectory);
370+
return IsInstallRoot(parentDirectory, fileExists, directoryExists)
371+
? parentDirectory
372+
: normalizedDirectory;
345373
}
346374

347375
/// <summary>
@@ -598,6 +626,14 @@ private static string GetUserHomeDirectory()
598626
return Environment.GetEnvironmentVariable("HOME") ?? AppContext.BaseDirectory;
599627
}
600628

629+
private static string NormalizeDirectoryPath(string path)
630+
{
631+
return Path.GetFullPath(path).TrimEnd(
632+
Path.DirectorySeparatorChar,
633+
Path.AltDirectorySeparatorChar
634+
);
635+
}
636+
601637
private static string NormalizeExecutablePath(string path)
602638
{
603639
if (
@@ -610,5 +646,18 @@ private static string NormalizeExecutablePath(string path)
610646

611647
return path;
612648
}
649+
650+
private static bool IsInstallRoot(
651+
string directory,
652+
Func<string, bool> fileExists,
653+
Func<string, bool> directoryExists
654+
)
655+
{
656+
return fileExists(Path.Join(directory, ClassicExecutableName))
657+
|| fileExists(Path.Join(directory, BundledPingetExecutableName))
658+
|| fileExists(Path.Join(directory, "IntegrityTree.json"))
659+
|| directoryExists(Path.Join(directory, "Assets", "Utilities"))
660+
|| directoryExists(Path.Join(directory, "Assets", "Data"));
661+
}
613662
}
614663
}

src/UniGetUI.PackageEngine.Managers.WinGet/WinGet.cs

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -336,14 +336,19 @@ internal static string GetBundledPingetExecutablePath(
336336
Func<string, bool> fileExists
337337
)
338338
{
339-
string rootPingetPath = Path.Join(executableDirectory, PingetExecutableName);
339+
string installDirectory = CoreData.ResolveInstallationDirectory(
340+
executableDirectory,
341+
fileExists,
342+
static _ => false
343+
);
344+
string rootPingetPath = Path.Join(installDirectory, PingetExecutableName);
340345
if (fileExists(rootPingetPath))
341346
{
342347
return rootPingetPath;
343348
}
344349

345350
string avaloniaPingetPath = Path.Join(
346-
executableDirectory,
351+
installDirectory,
347352
"Avalonia",
348353
PingetExecutableName
349354
);

src/UniGetUI.PackageEngine.Tests/WinGetManagerTests.cs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,21 @@ public void GetBundledPingetExecutablePathPrefersRootExecutable()
158158
Assert.Equal(rootPinget, path);
159159
}
160160

161+
[Fact]
162+
public void GetBundledPingetExecutablePathFindsRootExecutableFromAvaloniaDirectory()
163+
{
164+
const string installDir = @"C:\Program Files\UniGetUI";
165+
string avaloniaDir = Path.Join(installDir, "Avalonia");
166+
string rootPinget = Path.Join(installDir, "pinget.exe");
167+
168+
string path = WinGet.GetBundledPingetExecutablePath(
169+
avaloniaDir,
170+
filePath => filePath == rootPinget
171+
);
172+
173+
Assert.Equal(rootPinget, path);
174+
}
175+
161176
[Fact]
162177
public void GetBundledPingetExecutablePathFallsBackToAvaloniaExecutable()
163178
{

0 commit comments

Comments
 (0)