Skip to content
Draft
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
5 changes: 4 additions & 1 deletion .gitattributes
Original file line number Diff line number Diff line change
@@ -1,2 +1,5 @@
src/UniGetUI.PackageEngine.Managers.Chocolatey/choco-cli/** linguist-vendored
.githooks/* text eol=lf
.githooks/* text eol=lf

policies/samples/** linguist-generated
policies/schemas/** linguist-generated
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -96,3 +96,7 @@ src/UniGetUI.v3.ncrunchsolution
# macOS Finder metadata
.DS_Store
/src/UniGetUI.Avalonia/Generated Files


policies/csharp/.vs/*
src/UniGetUI.PackageEngine.Managers.WinGet
2 changes: 1 addition & 1 deletion global.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"sdk": {
"version": "10.0.103",
"rollForward": "latestPatch"
"rollForward": "latestFeature"
}
}
4 changes: 4 additions & 0 deletions src/UniGetUI.Core.Settings/SettingsEngine_Names.cs
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,9 @@ public enum K
DisableClassicMode,
DisableInstallerHostChangeWarning,
BunPreferLatestVersions,
// NOTE: Set this to true to delegate package operations to Devolutions Agent broker
// instead of using local UAC elevation. Change default here when ready for production.
UseAgentBroker,

Test1,
Test2,
Expand Down Expand Up @@ -195,6 +198,7 @@ public static string ResolveKey(K key)
K.DisableClassicMode => "DisableClassicMode",
K.DisableInstallerHostChangeWarning => "DisableInstallerHostChangeWarning",
K.BunPreferLatestVersions => "BunPreferLatestVersions",
K.UseAgentBroker => "UseAgentBroker",

K.Test1 => "TestSetting1",
K.Test2 => "TestSetting2",
Expand Down
148 changes: 148 additions & 0 deletions src/UniGetUI.PackageEngine.AgentBroker/BrokerRequestBuilder.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
using Devolutions.Now.Policy.Api;
using Devolutions.Now.Policy.Client;
using UniGetUI.PackageEngine.Enums;
using UniGetUI.PackageEngine.Interfaces;
using UniGetUI.PackageEngine.Serializable;
// Aliased to avoid clashing with UniGetUI.PackageEngine.Enums.Architecture.
using BrokerArchitecture = Devolutions.Now.Policy.Api.Architecture;

namespace UniGetUI.PackageEngine.AgentBroker;

/// <summary>
/// Builds broker protocol requests from UniGetUI domain objects.
/// Maps IPackage + InstallOptions + OperationType into the canonical
/// <see cref="PackageRequest"/> consumed by the Devolutions Agent broker.
/// </summary>
public static class BrokerRequestBuilder
{
/// <summary>Build a broker request from UniGetUI package operation parameters.</summary>
public static PackageOperationRequest Build(IPackage package, InstallOptions options, OperationType role)
{
return new PackageOperationRequest
{
RequestId = BrokerClient.GenerateRequestId(),
CreatedAt = DateTimeOffset.UtcNow,
Operation = MapOperation(role),
Manager = MapManagerName(package.Manager.Name),
CaptureOutput = true,
Source = new RequestSource
{
Name = package.Source.Name,
Url = package.Source.Url?.ToString(),
},
Package = new RequestPackage
{
Id = package.Id,
Version = string.IsNullOrEmpty(options.Version) ? null : options.Version,
Architecture = MapArchitecture(options.Architecture),
},
Options = new RequestOptions
{
Scope = MapScope(options.InstallationScope),
Interactive = options.InteractiveInstallation,
SkipHashCheck = options.SkipHashCheck,
PreRelease = options.PreRelease,
CustomParameters = GetCustomParameters(options, role),
CustomInstallLocation = NullIfEmpty(options.CustomInstallLocation),
KillBeforeOperation = options.KillBeforeOperation ?? [],
PreOperationCommand = GetPreCommand(options, role),
PostOperationCommand = GetPostCommand(options, role),
UninstallPrevious = role is OperationType.Update && options.UninstallPreviousVersionsOnUpdate,
NoUpgrade = role is OperationType.Update && options.SkipMinorUpdates,
},
};
}

private static Operation MapOperation(OperationType role) => role switch
{
OperationType.Install => Operation.Install,
OperationType.Update => Operation.Update,
OperationType.Uninstall => Operation.Uninstall,
_ => throw new ArgumentException($"Unsupported operation type: {role}"),
};

/// <summary>
/// Maps UniGetUI manager names to the broker protocol canonical managers.
/// PowerShell 5 and PowerShell 7 are modeled as separate managers.
/// </summary>
private static ManagerName MapManagerName(string managerName)
{
if (managerName.Equals("Winget", StringComparison.OrdinalIgnoreCase))
{
return ManagerName.Winget;
}

if (managerName.Equals("PowerShell", StringComparison.OrdinalIgnoreCase))
{
return ManagerName.PowerShell;
}

if (managerName.Equals("PowerShell7", StringComparison.OrdinalIgnoreCase) ||
managerName.Equals("pwsh", StringComparison.OrdinalIgnoreCase))
{
return ManagerName.PowerShell7;
}

throw new ArgumentException($"Unsupported manager for the broker: {managerName}");
}

private static Scope? MapScope(string? scope)
{
if (string.IsNullOrEmpty(scope))
{
return null;
}

return scope.ToLowerInvariant() switch
{
"user" => Scope.User,
"machine" => Scope.Machine,
"global" => Scope.Machine,
_ => null,
};
}

private static BrokerArchitecture? MapArchitecture(string? architecture)
{
if (string.IsNullOrEmpty(architecture))
{
return null;
}

return architecture.ToLowerInvariant() switch
{
"x86" => BrokerArchitecture.X86,
"x64" => BrokerArchitecture.X64,
"arm64" => BrokerArchitecture.Arm64,
"neutral" => BrokerArchitecture.Neutral,
_ => null,
};
}

private static List<string> GetCustomParameters(InstallOptions options, OperationType role) => role switch
{
OperationType.Install => options.CustomParameters_Install ?? [],
OperationType.Update => options.CustomParameters_Update ?? [],
OperationType.Uninstall => options.CustomParameters_Uninstall ?? [],
_ => [],
};

private static string? GetPreCommand(InstallOptions options, OperationType role) => role switch
{
OperationType.Install => NullIfEmpty(options.PreInstallCommand),
OperationType.Update => NullIfEmpty(options.PreUpdateCommand),
OperationType.Uninstall => NullIfEmpty(options.PreUninstallCommand),
_ => null,
};

private static string? GetPostCommand(InstallOptions options, OperationType role) => role switch
{
OperationType.Install => NullIfEmpty(options.PostInstallCommand),
OperationType.Update => NullIfEmpty(options.PostUpdateCommand),
OperationType.Uninstall => NullIfEmpty(options.PostUninstallCommand),
_ => null,
};

private static string? NullIfEmpty(string? value) =>
string.IsNullOrWhiteSpace(value) ? null : value;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFrameworks>$(SharedTargetFrameworks)</TargetFrameworks>
<RootNamespace>UniGetUI.PackageEngine.AgentBroker</RootNamespace>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Devolutions.Now.Policy.Api" Version="2026.7.7" />
<PackageReference Include="Devolutions.Now.Policy.Client" Version="2026.7.7" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\UniGetUI.Core.Logger\UniGetUI.Core.Logging.csproj" />
<ProjectReference Include="..\UniGetUI.Core.Settings\UniGetUI.Core.Settings.csproj" />
<ProjectReference Include="..\UniGetUI.PackageEngine.Enums\UniGetUI.PackageEngine.Structs.csproj" />
<ProjectReference Include="..\UniGetUI.PAckageEngine.Interfaces\UniGetUI.PackageEngine.Interfaces.csproj" />
<ProjectReference Include="..\UniGetUI.PackageEngine.Serializable\UniGetUI.PackageEngine.Serializable.csproj" />
</ItemGroup>

<ItemGroup>
<Compile Include="..\SharedAssemblyInfo.cs" Link="SharedAssemblyInfo.cs" />
</ItemGroup>

</Project>
Loading