-
Notifications
You must be signed in to change notification settings - Fork 380
Full interactive mode (ask for target) #1436
New issue
Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? # to your account
Comments
Note: I digged up some old code which was referenced on some other issues and tried to implement a custom interactive mode similar. But the build log seems to be locked by the current process when I launch the second build. public class InteractiveBuildAttribute : BuildExtensionAttributeBase, IOnBuildInitialized
{
public void OnBuildInitialized(
IReadOnlyCollection<ExecutableTarget> executableTargets,
IReadOnlyCollection<ExecutableTarget> executionPlan)
{
if (!Build.Help && executionPlan.Count == 0 && NukeBuild.Host is Terminal)
{
Log.Information("No target specified, please select the target you want to run:");
foreach (var target in executableTargets.Where(x => x.Listed))
{
Log.Information("{TargetName}", target.Name);
if (!string.IsNullOrWhiteSpace(target.Description))
{
Log.Information(" {Description}", target.Description);
}
}
var defaultTarget = nameof(MySharedNukeBuild.Compile);
Log.Information(
"Type the name of the target you want to execute and hit [ENTER] (default: {DefaultTarget})",
defaultTarget);
Console.Write("> ");
var selectedTarget = Console.ReadLine();
if (string.IsNullOrEmpty(selectedTarget))
{
selectedTarget = defaultTarget;
}
else if (!executableTargets.Any(t => selectedTarget.EqualsOrdinalIgnoreCase(t.Name)))
{
Log.Error("Unknown target '{SelectedTarget}' specified, exiting", selectedTarget);
Environment.Exit(1);
}
Log.Information("Starting target {SelectedTarget}", selectedTarget);
var assembly = Assembly.GetEntryAssembly()!.Location;
var exitCode = 0;
DotNetTasks.DotNet($"{assembly} -- --nologo --target {selectedTarget}",
logInvocation: false,
exitHandler: p => exitCode = p.ExitCode);
Environment.Exit(exitCode);
}
}
}
// HandleHelpRequests has Priority 5, we want to be before it
[InteractiveBuild(Priority = 7)]
public class MySharedNukeBuild;
Even though it would still be great to have this built-in I'd be fine with implementing it myself via |
I would not implement it that way. I would rather extend |
Assuming we'd extend things here. How would you prefer to trigger the interactive flow? If we can agree on some path that also makes you happy, I'd could work on a PR 😉 Here some thoughts and proposals: One idea could be an extension which allows handling this scenario in custom ways and devs implement the logic as they like: var invokedTargets = ParameterService.GetParameter<string[]>(() => build.InvokedTargets);
if (build.IsInteractive || invokedTargets is not { Length: > 0 })
{
var newTargetList = new List<ExecutableTarget>();
build.ExecuteExtension<IOnBuildInteractiveTargets>(x => x.OnSelectTargetsInteractively(build.ExecutableTargets, newTargetList));
if (newTargetList.Count > 0)
{
invokedTargets = newTargetList.Select(t => t.Name).ToArray();
}
}
build.ExecutionPlan = ExecutionPlanner.GetExecutionPlan(
build.ExecutableTargets,
invokedTargets ); The alternative would be to go for a more direct call path to
In code similar to: // BuildManager.cs
var invokedTargets = ParameterService.GetParameter<string[]>(() => build.InvokedTargets);
if (invokedTargets is not { Length: > 0 })
{
invokedTargets = build.OnSelectTargetsInteractively(build.ExecutableTargets)?.Select(t => t.Name).ToArray();
}
build.ExecutionPlan = ExecutionPlanner.GetExecutionPlan(
build.ExecutableTargets,
invokedTargets);
// NukeBuild.cs
protected bool SelectTargetsInteractively { get; set; } // opt-in
protected virtual IReadOnlyCollection<ExecutableTarget>? OnSelectTargetsInteractively(IReadOnlyCollection<ExecutableTarget> executableTargets)
{
if (IsInteractive || SelectTargetsInteractively)
{
return Host.OnSelectTargetsInteractively(executableTargets);
}
else
{
return null;
}
}
// Host.cs
protected virtual IReadOnlyCollection<ExecutableTarget>? OnSelectTargetsInteractively(IReadOnlyCollection<ExecutableTarget> executableTargets)
{
return null;
}
// Terminal.cs
protected override IReadOnlyCollection<ExecutableTarget>? OnSelectTargetsInteractively(IReadOnlyCollection<ExecutableTarget> executableTargets)
{
PrintPrompt(executableTargets);
var userInput = Console.ReadLine().Split(',');
var target = userInput.Select(i => executableTargets.FirstOrDefault(t => i.EqualsOrdinalIgnoreCase(t.Name)).Where(t => t != null).ToReadOnlyCollection();
if (target.Count != userInput.Length)
{
PrintError(userInput);
return null;
}
return target;
} |
In the first iteration, I would keep it simpler and implement it without extension points. Without much research, I think |
Description
Nuke is great and very flexible when it comes to CLI usage and is very flexible in the usage from CI/CD systems. What I am missing a bit, is a modern user experience when developers use the "_build" project locally.
If the project/build is started without any input, and we detect a "Terminal" host, we enter an interactive mode to ask the user what to do. e.g. we have 5 different entry points to our build depending on what you want to do.
Usage Example
When I launch the "_build.exe" (or dotnet run) Nuke should prompt the user:
Then it starts the respective target just as if
nuke --target PrepareEnvironment
is called. The way of asking could be either fully interactive like very modern console apps, or simply asking for the user to type it.Alternative
I thought of making some unlisted target "interactive" the default one. Then I do any console interactivity myself (by loading the targets and stuff myself).
After the user has chosen the target, I would launch the target.
a) I cannot directly trigger another target within the same process. But it would be nice if I can have a dynamic "Triggers" in this target.
b) I trigger the target via
DotNetTasks.DotNetRun(<current assembly, target and parameters>)
Could you help with a pull-request?
Yes
The text was updated successfully, but these errors were encountered: