fix(launcher): guard orphan bake publication
This commit is contained in:
parent
3f68895120
commit
208a70ac83
14 changed files with 829 additions and 55 deletions
|
|
@ -1,24 +1,190 @@
|
|||
if (args.Length != 3)
|
||||
using System.Diagnostics;
|
||||
using System.Reflection;
|
||||
using AcDream.Bake;
|
||||
using AcDream.Launcher.Core.Installation;
|
||||
using AcDream.Platform;
|
||||
|
||||
return args.FirstOrDefault() switch
|
||||
{
|
||||
return 2;
|
||||
"hold-install-lease" => await HoldInstallLeaseAsync(args[1..]),
|
||||
"orphan-parent" => await RunOrphanParentAsync(args[1..]),
|
||||
"orphan-child" => RunOrphanChild(args[1..]),
|
||||
_ => 2,
|
||||
};
|
||||
|
||||
static async Task<int> HoldInstallLeaseAsync(string[] arguments)
|
||||
{
|
||||
if (arguments.Length != 3)
|
||||
{
|
||||
return 2;
|
||||
}
|
||||
|
||||
string lockPath = Path.GetFullPath(arguments[0]);
|
||||
string stagingPath = Path.GetFullPath(arguments[1]);
|
||||
string readyPath = Path.GetFullPath(arguments[2]);
|
||||
Directory.CreateDirectory(
|
||||
Path.GetDirectoryName(lockPath)
|
||||
?? throw new InvalidOperationException("lock path has no parent"));
|
||||
Directory.CreateDirectory(
|
||||
Path.GetDirectoryName(stagingPath)
|
||||
?? throw new InvalidOperationException("staging path has no parent"));
|
||||
|
||||
using var lease = new FileStream(
|
||||
lockPath,
|
||||
FileMode.OpenOrCreate,
|
||||
FileAccess.ReadWrite,
|
||||
FileShare.None);
|
||||
File.WriteAllText(stagingPath, "abandoned bake staging");
|
||||
File.WriteAllText(readyPath, "ready");
|
||||
await Task.Delay(Timeout.InfiniteTimeSpan);
|
||||
return 0;
|
||||
}
|
||||
|
||||
string lockPath = Path.GetFullPath(args[0]);
|
||||
string stagingPath = Path.GetFullPath(args[1]);
|
||||
string readyPath = Path.GetFullPath(args[2]);
|
||||
Directory.CreateDirectory(
|
||||
Path.GetDirectoryName(lockPath)
|
||||
?? throw new InvalidOperationException("lock path has no parent"));
|
||||
Directory.CreateDirectory(
|
||||
Path.GetDirectoryName(stagingPath)
|
||||
?? throw new InvalidOperationException("staging path has no parent"));
|
||||
static async Task<int> RunOrphanParentAsync(string[] arguments)
|
||||
{
|
||||
if (arguments.Length != 8)
|
||||
{
|
||||
return 2;
|
||||
}
|
||||
|
||||
using var lease = new FileStream(
|
||||
lockPath,
|
||||
FileMode.OpenOrCreate,
|
||||
FileAccess.ReadWrite,
|
||||
FileShare.None);
|
||||
File.WriteAllText(stagingPath, "abandoned bake staging");
|
||||
File.WriteAllText(readyPath, "ready");
|
||||
await Task.Delay(Timeout.InfiniteTimeSpan);
|
||||
return 0;
|
||||
string dataDirectory = Path.GetFullPath(arguments[0]);
|
||||
string datDirectory = Path.GetFullPath(arguments[1]);
|
||||
string bakeMarker = Path.GetFullPath(arguments[2]);
|
||||
string schedule = arguments[3];
|
||||
string childReadyPath = Path.GetFullPath(arguments[4]);
|
||||
string childReleasePath = Path.GetFullPath(arguments[5]);
|
||||
string childPidPath = Path.GetFullPath(arguments[6]);
|
||||
string childExitPath = Path.GetFullPath(arguments[7]);
|
||||
var paths = new ApplicationPathSet(
|
||||
Path.Combine(dataDirectory, "fixture-config"),
|
||||
dataDirectory,
|
||||
Path.Combine(dataDirectory, "fixture-cache"),
|
||||
null);
|
||||
var runner = new OrphanBakeProcessRunner(
|
||||
schedule,
|
||||
childReadyPath,
|
||||
childReleasePath,
|
||||
childPidPath,
|
||||
childExitPath);
|
||||
var installer = new LauncherInstaller(
|
||||
paths,
|
||||
bakeMarker,
|
||||
processRunner: runner);
|
||||
|
||||
try
|
||||
{
|
||||
await installer.InstallAsync(datDirectory, 1);
|
||||
return 0;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return 9;
|
||||
}
|
||||
}
|
||||
|
||||
static int RunOrphanChild(string[] arguments)
|
||||
{
|
||||
if (arguments.Length != 5)
|
||||
{
|
||||
return 2;
|
||||
}
|
||||
|
||||
string outputPath = Path.GetFullPath(arguments[0]);
|
||||
string schedule = arguments[1];
|
||||
string readyPath = Path.GetFullPath(arguments[2]);
|
||||
string releasePath = Path.GetFullPath(arguments[3]);
|
||||
string exitPath = Path.GetFullPath(arguments[4]);
|
||||
Action barrier = () =>
|
||||
{
|
||||
File.WriteAllText(readyPath, schedule);
|
||||
while (!File.Exists(releasePath))
|
||||
{
|
||||
Thread.Sleep(10);
|
||||
}
|
||||
};
|
||||
|
||||
int exitCode;
|
||||
try
|
||||
{
|
||||
BakeOutputTransaction.WriteValidateAndPublish(
|
||||
outputPath,
|
||||
temporaryPath =>
|
||||
{
|
||||
File.WriteAllText(temporaryPath, "orphan replacement");
|
||||
return 1;
|
||||
},
|
||||
(temporaryPath, _) =>
|
||||
{
|
||||
if (File.ReadAllText(temporaryPath) != "orphan replacement")
|
||||
{
|
||||
throw new InvalidDataException("staging content changed");
|
||||
}
|
||||
},
|
||||
beforePublicationLock: schedule == "late" ? barrier : null,
|
||||
beforePromotion: schedule == "holds" ? barrier : null,
|
||||
CancellationToken.None);
|
||||
exitCode = 0;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
File.WriteAllText(exitPath + ".error", ex.Message);
|
||||
exitCode = 17;
|
||||
}
|
||||
|
||||
File.WriteAllText(exitPath, exitCode.ToString(
|
||||
System.Globalization.CultureInfo.InvariantCulture));
|
||||
return exitCode;
|
||||
}
|
||||
|
||||
file sealed class OrphanBakeProcessRunner(
|
||||
string schedule,
|
||||
string childReadyPath,
|
||||
string childReleasePath,
|
||||
string childPidPath,
|
||||
string childExitPath) : IBakeProcessRunner
|
||||
{
|
||||
public async Task<BakeProcessResult> RunAsync(
|
||||
BakeProcessRequest request,
|
||||
Action<string> onStandardOutput,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
string dotnetHost = Environment.ProcessPath
|
||||
?? throw new InvalidOperationException("dotnet host path is unavailable");
|
||||
string fixtureDll = Assembly.GetExecutingAssembly().Location;
|
||||
var startInfo = new ProcessStartInfo(dotnetHost)
|
||||
{
|
||||
UseShellExecute = false,
|
||||
CreateNoWindow = true,
|
||||
};
|
||||
startInfo.ArgumentList.Add(fixtureDll);
|
||||
startInfo.ArgumentList.Add("orphan-child");
|
||||
startInfo.ArgumentList.Add(request.OutputPath);
|
||||
startInfo.ArgumentList.Add(schedule);
|
||||
startInfo.ArgumentList.Add(childReadyPath);
|
||||
startInfo.ArgumentList.Add(childReleasePath);
|
||||
startInfo.ArgumentList.Add(childExitPath);
|
||||
startInfo.Environment.Remove(
|
||||
BakePublicationGuardPaths.NonceEnvironmentVariable);
|
||||
startInfo.Environment[
|
||||
BakePublicationGuardPaths.NonceEnvironmentVariable] =
|
||||
request.PublicationNonce
|
||||
?? throw new InvalidOperationException("publication nonce is missing");
|
||||
|
||||
using Process child = Process.Start(startInfo)
|
||||
?? throw new InvalidOperationException("orphan child did not start");
|
||||
File.WriteAllText(
|
||||
childPidPath,
|
||||
child.Id.ToString(System.Globalization.CultureInfo.InvariantCulture));
|
||||
await child.WaitForExitAsync(cancellationToken);
|
||||
if (child.ExitCode == 0)
|
||||
{
|
||||
long bytes = new FileInfo(request.OutputPath).Length;
|
||||
onStandardOutput("{\"v\":1,\"e\":\"started\",\"bakeToolVersion\":4}\n");
|
||||
onStandardOutput($"{{\"v\":1,\"e\":\"completed\","
|
||||
+ $"\"bakeToolVersion\":4,\"outputBytes\":{bytes},"
|
||||
+ "\"failures\":0}\n");
|
||||
}
|
||||
|
||||
return new BakeProcessResult(child.ExitCode, "orphan fixture child");
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue