Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 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
1 change: 1 addition & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
MutagenSdk/Proto/**/*.proto linguist-generated=true
18 changes: 18 additions & 0 deletions Coder.Desktop.sln
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Vpn.DebugClient", "Vpn.Debu
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Installer", "Installer\Installer.csproj", "{39F5B55A-09D8-477D-A3FA-ADAC29C52605}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MutagenSdk", "MutagenSdk\MutagenSdk.csproj", "{E2477ADC-03DA-490D-9369-79A4CC4A58D2}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -203,6 +205,22 @@ Global
{39F5B55A-09D8-477D-A3FA-ADAC29C52605}.Release|x64.Build.0 = Release|Any CPU
{39F5B55A-09D8-477D-A3FA-ADAC29C52605}.Release|x86.ActiveCfg = Release|Any CPU
{39F5B55A-09D8-477D-A3FA-ADAC29C52605}.Release|x86.Build.0 = Release|Any CPU
{E2477ADC-03DA-490D-9369-79A4CC4A58D2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{E2477ADC-03DA-490D-9369-79A4CC4A58D2}.Debug|Any CPU.Build.0 = Debug|Any CPU
{E2477ADC-03DA-490D-9369-79A4CC4A58D2}.Debug|ARM64.ActiveCfg = Debug|Any CPU
{E2477ADC-03DA-490D-9369-79A4CC4A58D2}.Debug|ARM64.Build.0 = Debug|Any CPU
{E2477ADC-03DA-490D-9369-79A4CC4A58D2}.Debug|x64.ActiveCfg = Debug|Any CPU
{E2477ADC-03DA-490D-9369-79A4CC4A58D2}.Debug|x64.Build.0 = Debug|Any CPU
{E2477ADC-03DA-490D-9369-79A4CC4A58D2}.Debug|x86.ActiveCfg = Debug|Any CPU
{E2477ADC-03DA-490D-9369-79A4CC4A58D2}.Debug|x86.Build.0 = Debug|Any CPU
{E2477ADC-03DA-490D-9369-79A4CC4A58D2}.Release|Any CPU.ActiveCfg = Release|Any CPU
{E2477ADC-03DA-490D-9369-79A4CC4A58D2}.Release|Any CPU.Build.0 = Release|Any CPU
{E2477ADC-03DA-490D-9369-79A4CC4A58D2}.Release|ARM64.ActiveCfg = Release|Any CPU
{E2477ADC-03DA-490D-9369-79A4CC4A58D2}.Release|ARM64.Build.0 = Release|Any CPU
{E2477ADC-03DA-490D-9369-79A4CC4A58D2}.Release|x64.ActiveCfg = Release|Any CPU
{E2477ADC-03DA-490D-9369-79A4CC4A58D2}.Release|x64.Build.0 = Release|Any CPU
{E2477ADC-03DA-490D-9369-79A4CC4A58D2}.Release|x86.ActiveCfg = Release|Any CPU
{E2477ADC-03DA-490D-9369-79A4CC4A58D2}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down
55 changes: 55 additions & 0 deletions MutagenSdk/MutagenClient.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
using Coder.Desktop.MutagenSdk.Proto.Service.Synchronization;
using Grpc.Core;
using Grpc.Net.Client;

namespace Coder.Desktop.MutagenSdk;

public class MutagenClient : IDisposable
{
private readonly GrpcChannel _channel;

public readonly Synchronization.SynchronizationClient Synchronization;

public MutagenClient(string dataDir)
{
// Check for the lock file first, since it should exist if it's running.
var daemonLockFile = Path.Combine(dataDir, "daemon", "daemon.lock");
if (!File.Exists(daemonLockFile))
throw new FileNotFoundException(
"Mutagen daemon lock file not found, did the mutagen daemon start successfully?", daemonLockFile);

// Read the IPC named pipe address from the sock file.
var daemonSockFile = Path.Combine(dataDir, "daemon", "daemon.sock");
if (!File.Exists(daemonSockFile))
throw new FileNotFoundException(
"Mutagen daemon socket file not found, did the mutagen daemon start successfully?", daemonSockFile);
var daemonSockAddress = File.ReadAllText(daemonSockFile).Trim();
if (string.IsNullOrWhiteSpace(daemonSockAddress))
throw new InvalidOperationException(
"Mutagen daemon socket address is empty, did the mutagen daemon start successfully?");

const string namedPipePrefix = @"\\.\pipe\";
if (!daemonSockAddress.StartsWith(namedPipePrefix))
throw new InvalidOperationException("Mutagen daemon socket address is not a named pipe address");
var pipeName = daemonSockAddress[namedPipePrefix.Length..];

var connectionFactory = new NamedPipesConnectionFactory(pipeName);
var socketsHttpHandler = new SocketsHttpHandler
{
ConnectCallback = connectionFactory.ConnectAsync,
};

_channel = GrpcChannel.ForAddress("http://localhost", new GrpcChannelOptions
{
Credentials = ChannelCredentials.Insecure,
HttpHandler = socketsHttpHandler,
});
Synchronization = new Synchronization.SynchronizationClient(_channel);
}

public void Dispose()
{
_channel.Dispose();
GC.SuppressFinalize(this);
}
}
25 changes: 25 additions & 0 deletions MutagenSdk/MutagenSdk.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<AssemblyName>Coder.Desktop.MutagenSdk</AssemblyName>
<RootNamespace>Coder.Desktop.MutagenSdk</RootNamespace>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<RestorePackagesWithLockFile>true</RestorePackagesWithLockFile>
</PropertyGroup>

<ItemGroup>
<Protobuf Include="Proto\**\*.proto" ProtoRoot="Proto" GrpcServices="Client" />
</ItemGroup>

<ItemGroup>
<PackageReference Include="Google.Protobuf" Version="3.29.3" />
<PackageReference Include="Grpc.Net.Client" Version="2.67.0" />
<PackageReference Include="Grpc.Tools" Version="2.69.0">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
</ItemGroup>

</Project>
36 changes: 36 additions & 0 deletions MutagenSdk/NamedPipesConnectionFactory.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
using System.IO.Pipes;
using System.Security.Principal;

namespace Coder.Desktop.MutagenSdk;

public class NamedPipesConnectionFactory
{
private readonly string _pipeName;

public NamedPipesConnectionFactory(string pipeName)
{
_pipeName = pipeName;
}

public async ValueTask<Stream> ConnectAsync(SocketsHttpConnectionContext _,
CancellationToken cancellationToken = default)
{
var client = new NamedPipeClientStream(
".",
_pipeName,
PipeDirection.InOut,
PipeOptions.WriteThrough | PipeOptions.Asynchronous,
TokenImpersonationLevel.Anonymous);

try
{
await client.ConnectAsync(cancellationToken);
return client;
}
catch
{
await client.DisposeAsync();
throw;
}
}
}
51 changes: 51 additions & 0 deletions MutagenSdk/Proto/filesystem/behavior/probe_mode.proto

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

48 changes: 48 additions & 0 deletions MutagenSdk/Proto/selection/selection.proto

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading
Loading