fix: allow optional Preview automation paths
This commit is contained in:
@@ -133,7 +133,12 @@ internal static class PilotCli
|
|||||||
Console.Error.WriteLine("AUTOMATION_PREPARE_BLOCKED:" + string.Join(',', prepared.SafetyDecision.Violations));
|
Console.Error.WriteLine("AUTOMATION_PREPARE_BLOCKED:" + string.Join(',', prepared.SafetyDecision.Violations));
|
||||||
return new(false, false, false, false, false);
|
return new(false, false, false, false, false);
|
||||||
}
|
}
|
||||||
var execution = await service.ExecuteAsync(new(request, prepared.Plan, PermitService.Issue(prepared.Plan, config.ExecutionPermitLifetimeSeconds)), CancellationToken.None); if (!execution.Started || execution.ProcessId is null) return new(false, false, false, false, false);
|
var execution = await service.ExecuteAsync(new(request, prepared.Plan, PermitService.Issue(prepared.Plan, config.ExecutionPermitLifetimeSeconds)), CancellationToken.None);
|
||||||
|
if (!execution.Started || execution.ProcessId is null)
|
||||||
|
{
|
||||||
|
Console.Error.WriteLine("AUTOMATION_EXECUTION_BLOCKED:" + (execution.BlockCode ?? "PROCESS_START_FAILED"));
|
||||||
|
return new(false, false, false, false, false);
|
||||||
|
}
|
||||||
var forcedCleanup = false;
|
var forcedCleanup = false;
|
||||||
var gracefulExit = false;
|
var gracefulExit = false;
|
||||||
try
|
try
|
||||||
|
|||||||
@@ -35,7 +35,12 @@ public sealed class PathResolver(IReadOnlyDictionary<string, string?>? environme
|
|||||||
public ResolvedLauncherPaths Resolve(LauncherPathVariables variables, IEnumerable<string>? required = null)
|
public ResolvedLauncherPaths Resolve(LauncherPathVariables variables, IEnumerable<string>? required = null)
|
||||||
{
|
{
|
||||||
var resolved = new Dictionary<string, string?>(StringComparer.OrdinalIgnoreCase);
|
var resolved = new Dictionary<string, string?>(StringComparer.OrdinalIgnoreCase);
|
||||||
foreach (var name in variables.Values.Keys) resolved[name] = ResolveVariable(name, variables, resolved, []);
|
// Explicit null values represent optional paths (for example the Apply
|
||||||
|
// permit during Preview). They are retained as null rather than being
|
||||||
|
// treated as an unknown variable; required values are still enforced
|
||||||
|
// below.
|
||||||
|
foreach (var name in variables.Values.Keys)
|
||||||
|
resolved[name] = variables[name] is null ? null : ResolveVariable(name, variables, resolved, []);
|
||||||
foreach (var name in resolved.Keys.ToArray())
|
foreach (var name in resolved.Keys.ToArray())
|
||||||
if (resolved[name] is { } value && value.StartsWith("A:\\", StringComparison.OrdinalIgnoreCase))
|
if (resolved[name] is { } value && value.StartsWith("A:\\", StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ public sealed class PathAndLaunchTests
|
|||||||
[Fact] public void PathResolver_ResolvesEnvironmentVariable() => Assert.Equal("C:\\From Environment", Resolver(new Dictionary<string, string?> { ["ROOT"] = "C:\\From Environment" }).Resolve(new(new Dictionary<string, string?> { ["Value"] = "${ROOT}" })).Require("Value"));
|
[Fact] public void PathResolver_ResolvesEnvironmentVariable() => Assert.Equal("C:\\From Environment", Resolver(new Dictionary<string, string?> { ["ROOT"] = "C:\\From Environment" }).Resolve(new(new Dictionary<string, string?> { ["Value"] = "${ROOT}" })).Require("Value"));
|
||||||
[Fact] public void PathResolver_RejectsUnknownVariable() => Assert.Throws<InvalidOperationException>(() => Resolver().Resolve(new(new Dictionary<string, string?> { ["Value"] = "${Missing}" })));
|
[Fact] public void PathResolver_RejectsUnknownVariable() => Assert.Throws<InvalidOperationException>(() => Resolver().Resolve(new(new Dictionary<string, string?> { ["Value"] = "${Missing}" })));
|
||||||
[Fact] public void PathResolver_RejectsEmptyRequiredVariable() => Assert.Throws<InvalidOperationException>(() => Resolver().Resolve(new(new Dictionary<string, string?> { ["Value"] = "" }), ["Value"]));
|
[Fact] public void PathResolver_RejectsEmptyRequiredVariable() => Assert.Throws<InvalidOperationException>(() => Resolver().Resolve(new(new Dictionary<string, string?> { ["Value"] = "" }), ["Value"]));
|
||||||
|
[Fact] public void PathResolver_PreservesOptionalNullVariable() => Assert.Null(Resolver().Resolve(new(new Dictionary<string, string?> { ["Optional"] = null })).Get("Optional"));
|
||||||
[Fact] public void PathResolver_RejectsCycle() => Assert.Throws<InvalidOperationException>(() => Resolver().Resolve(new(new Dictionary<string, string?> { ["A"] = "${B}", ["B"] = "${A}" })));
|
[Fact] public void PathResolver_RejectsCycle() => Assert.Throws<InvalidOperationException>(() => Resolver().Resolve(new(new Dictionary<string, string?> { ["A"] = "${B}", ["B"] = "${A}" })));
|
||||||
[Fact] public void PathResolver_PreservesUnicode() => Assert.Contains("Проект", Resolver().Resolve(new(Paths)).Require(LauncherPathVariableNames.ProjectRoot));
|
[Fact] public void PathResolver_PreservesUnicode() => Assert.Contains("Проект", Resolver().Resolve(new(Paths)).Require(LauncherPathVariableNames.ProjectRoot));
|
||||||
[Fact] public void PathResolver_NormalizesSeparators() => Assert.DoesNotContain('/', Resolver().Resolve(new(Paths)).Require(LauncherPathVariableNames.DisciplineRoot));
|
[Fact] public void PathResolver_NormalizesSeparators() => Assert.DoesNotContain('/', Resolver().Resolve(new(Paths)).Require(LauncherPathVariableNames.DisciplineRoot));
|
||||||
|
|||||||
Reference in New Issue
Block a user