Skip to content

Commit

Permalink
Don't use single file publish, update updater to support it
Browse files Browse the repository at this point in the history
Now the updater on windows does not rely anymore on batch files and instead uses File.Move to move the daemon file handles preventing the update, this will cause an update to fail if there are .old files that couldn't be removed before updating
  • Loading branch information
Fleny113 committed Jul 5, 2024
1 parent dc89cf2 commit ee06e28
Show file tree
Hide file tree
Showing 6 changed files with 72 additions and 61 deletions.
7 changes: 6 additions & 1 deletion Directory.Build.props
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@
<Project>
<!-- See https://aka.ms/dotnet/msbuild/customize for more details on customizing your build -->
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>

<UseArtifactsOutput>true</UseArtifactsOutput>
<NuGetAudit>true</NuGetAudit>
<VersionPrefix>0.2.1</VersionPrefix>
<VersionPrefix>0.3.0</VersionPrefix>
</PropertyGroup>
</Project>
10 changes: 7 additions & 3 deletions Hexus.Daemon/Configuration/HexusConfigurationManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,14 @@ private void LoadConfiguration()
return;
}

var configurationFile = File.ReadAllText(EnvironmentHelper.ConfigurationFile);
var configFile = _yamlDeserializer.Deserialize<HexusConfigurationFile?>(configurationFile) ?? new HexusConfigurationFile();
lock (this)
{
var configurationFile = File.ReadAllText(EnvironmentHelper.ConfigurationFile);

Configuration = configFile.MapToConfig();
var configFile = _yamlDeserializer.Deserialize<HexusConfigurationFile?>(configurationFile) ?? new HexusConfigurationFile();

Configuration = configFile.MapToConfig();
}

SaveConfiguration();
}
Expand Down
3 changes: 0 additions & 3 deletions Hexus.Daemon/Hexus.Daemon.csproj
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<ServerGarbageCollection>false</ServerGarbageCollection>
<InvariantGlobalization>true</InvariantGlobalization>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
Expand Down
103 changes: 56 additions & 47 deletions Hexus/Commands/Utils/UpdateCommand.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
using Spectre.Console;
using System.CommandLine;
using System.CommandLine.Invocation;
using System.Diagnostics;
using System.Formats.Tar;
using System.IO.Compression;
using System.Runtime.InteropServices;
Expand All @@ -28,87 +27,97 @@ static UpdateCommand()
Command.SetHandler(Handler);
}

private static async Task Handler(InvocationContext context)
private static async Task<int> Handler(InvocationContext context)
{
var ci = context.ParseResult.GetValueForOption(CiBuildOption);
var ct = context.GetCancellationToken();

if (await HttpInvocation.CheckForRunningDaemon(ct) && OperatingSystem.IsWindows())
{
PrettyConsole.Error.MarkupLine("The [indianred1]daemon needs to not be running[/] to update hexus on Windows. Stop it first using the '[indianred1]daemon[/] [darkseagreen1_1]stop[/]' command.");
return;
}

var file = $"{RuntimeInformation.RuntimeIdentifier}-{Variant}.tar.gz";

var daemonRunning = await HttpInvocation.CheckForRunningDaemon(ct);
var oldFilesRequired = OperatingSystem.IsWindows();

var link = ci
? $"https://github.com/Fleny113/Hexus/releases/download/ci/{file}"
: $"https://github.com/Fleny113/Hexus/releases/latest/download/{file}";

var currentPath = Environment.ProcessPath;
var currentDir = Path.GetDirectoryName(currentPath);

if (currentPath is null)
if (currentPath is null || currentDir is null)
{
PrettyConsole.Error.MarkupLine("There [indianred1]was an error[/] fetching the current executable path.");
return;
PrettyConsole.Error.MarkupLine("There [indianred1]was an error[/] fetching the Hexus files path.");
return 1;
}

PrettyConsole.Out.MarkupLineInterpolated($"Downloading the updated binary from \"{link}\".");
if (!CleanOldFiles(currentDir))
{
PrettyConsole.Error.MarkupLine("[lightsteelblue].old[/] files where found and they [red1]couldn't be removed[/]. [aquamarine1]Restart the daemon[/] and try again");
return 1;
}

PrettyConsole.Out.MarkupLineInterpolated($"[mediumpurple]Downloading[/] the updated files from \"[link]{link}[/]\".");

using var httpClient = new HttpClient();
using var tar = await httpClient.GetAsync(link, ct);
using var request = await httpClient.GetAsync(link, ct);

if (!tar.IsSuccessStatusCode)
if (!request.IsSuccessStatusCode)
{
var body = await tar.Content.ReadAsStringAsync(ct);
PrettyConsole.Error.MarkupLineInterpolated($"There [indianred1]was an error[/] fetching the updated binary. HTTP status code: {tar.StatusCode}, body: \"{body}\"");
return;
var body = await request.Content.ReadAsStringAsync(ct);
PrettyConsole.Error.MarkupLineInterpolated($"There [indianred1]was an error[/] fetching the updated files. HTTP status code: {request.StatusCode}, body: \"{body}\"");
return 1;
}

await using var stream = await tar.Content.ReadAsStreamAsync(ct);

await using var stream = await request.Content.ReadAsStreamAsync(ct);
await using var gzipStream = new GZipStream(stream, CompressionMode.Decompress);
await using var tarReader = new TarReader(gzipStream);

var tempFileExec = Path.GetTempFileName();

while (await tarReader.GetNextEntryAsync(cancellationToken: ct) is { DataStream: not null } entry)
while (await tarReader.GetNextEntryAsync(cancellationToken: ct) is { } entry)
{
// Find the hexus (or hexus.exe) file
if (!entry.Name.StartsWith("hexus"))
var path = Path.Combine(currentDir, entry.Name);

if (entry.EntryType is TarEntryType.Directory)
{
Directory.CreateDirectory(path);
continue;
}

await entry.ExtractToFileAsync(tempFileExec, overwrite: true, cancellationToken: ct);
break;
// On Windows we need to rename the files to change the handles and being able to update the files
if (oldFilesRequired)
{
File.Move(path, $"{path}.old", overwrite: true);
}

await entry.ExtractToFileAsync(path, overwrite: true, cancellationToken: ct);
}

// Under windows the file will be locked, so we need to use a script to bypass the file locking
if (OperatingSystem.IsWindows())
if (daemonRunning)
{
var tempFileScript = $"{Path.GetTempFileName()}.bat";
PrettyConsole.Out.MarkupLine("The [lightcoral]daemon[/] could not be updated as it is running. [aquamarine1]Restart the daemon[/] to finish the update.");
return 0;
}

// there is a timeout delay to allow for the CLI to exit
var script = $"""
@echo off
timeout /t 3 > NUL
del "{currentPath}"
move "{tempFileExec}" "{currentPath}"
del "%~f0"
""";
PrettyConsole.Out.MarkupLine("Update [springgreen1]done[/].");

await File.WriteAllTextAsync(tempFileScript, script, ct);
return 0;
}

PrettyConsole.Out.MarkupLine("[yellow]WARNING[/]: To update hexus a batch script will run to replace the file. Please wait about 5 seconds before restarting hexus.");
Process.Start(new ProcessStartInfo(tempFileScript)
{
UseShellExecute = false,
CreateNoWindow = true,
});
private static bool CleanOldFiles(string searchPath)
{
var oldFiles = Directory.EnumerateFiles(searchPath, "*.old", SearchOption.AllDirectories);

return;
try
{
foreach (var file in oldFiles)
{
File.Delete(file);
}
}
catch (UnauthorizedAccessException)
{
return false;
}

File.Move(tempFileExec, currentPath, overwrite: true);
PrettyConsole.Out.MarkupLine("[springgreen1]Update done[/], restart the daemon to make the update have effect on it too.");
return true;
}
}
6 changes: 0 additions & 6 deletions Hexus/Hexus.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,6 @@

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>

<PublishSingleFile>true</PublishSingleFile>
<IncludeNativeLibrariesForSelfExtract>true</IncludeNativeLibrariesForSelfExtract>
</PropertyGroup>

<PropertyGroup Condition="'$(SelfContained)' == 'true'">
Expand Down
4 changes: 3 additions & 1 deletion Hexus/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -33,4 +33,6 @@
builder.UseDefaults();
builder.UseExceptionHandler((exception, _) => PrettyConsole.Error.WriteException(exception), 1);

return await builder.Build().InvokeAsync(args);
var app = builder.Build();

return await app.InvokeAsync(args);

0 comments on commit ee06e28

Please sign in to comment.