-
Notifications
You must be signed in to change notification settings - Fork 1.2k
fix(#45761): Stop forwarding dotnet-watch run arguments to all commands #50558
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: release/10.0.1xx
Are you sure you want to change the base?
Changes from 1 commit
5026d2e
9db77ff
d16bf3c
db68b03
d70c25a
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -61,6 +61,12 @@ internal sealed class CommandLineOptions | |||||
| } | ||||||
| }); | ||||||
|
|
||||||
| // Options we need to know about that are passed through to the subcommand: | ||||||
| var shortProjectOption = new Option<string>("-p") { Hidden = true, Arity = ArgumentArity.ZeroOrOne, AllowMultipleArgumentsPerToken = false }; | ||||||
| var longProjectOption = new Option<string>("--project") { Hidden = true, Arity = ArgumentArity.ZeroOrOne, AllowMultipleArgumentsPerToken = false }; | ||||||
| var launchProfileOption = new Option<string>("--launch-profile", "-lp") { Hidden = true, Arity = ArgumentArity.ZeroOrOne, AllowMultipleArgumentsPerToken = false }; | ||||||
| var noLaunchProfileOption = new Option<bool>("--no-launch-profile") { Hidden = true, Arity = ArgumentArity.Zero }; | ||||||
|
|
||||||
| Option[] watchOptions = | ||||||
| [ | ||||||
| quietOption, | ||||||
|
|
@@ -69,12 +75,13 @@ internal sealed class CommandLineOptions | |||||
| noHotReloadOption, | ||||||
| NonInteractiveOption | ||||||
| ]; | ||||||
|
|
||||||
| // Options we need to know about that are passed through to the subcommand: | ||||||
| var shortProjectOption = new Option<string>("-p") { Hidden = true, Arity = ArgumentArity.ZeroOrOne, AllowMultipleArgumentsPerToken = false }; | ||||||
| var longProjectOption = new Option<string>("--project") { Hidden = true, Arity = ArgumentArity.ZeroOrOne, AllowMultipleArgumentsPerToken = false }; | ||||||
| var launchProfileOption = new Option<string>("--launch-profile", "-lp") { Hidden = true, Arity = ArgumentArity.ZeroOrOne, AllowMultipleArgumentsPerToken = false }; | ||||||
| var noLaunchProfileOption = new Option<bool>("--no-launch-profile") { Hidden = true, Arity = ArgumentArity.Zero }; | ||||||
| Option[] forwardToRunOptions = | ||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'd call this
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nit: You can replace https://github.com/dotnet/sdk/pull/50558/files#diff-6a6fa2fea5af87fda145d79165f529cef772a3227d58494098b7a22511c4aecaR96-R99 with foreach (var option in forwardToSubcommandOptions)
{
rootCommand.Options.Add(option);
} |
||||||
| [ | ||||||
| shortProjectOption, | ||||||
| longProjectOption, | ||||||
| launchProfileOption, | ||||||
| noLaunchProfileOption | ||||||
| ]; | ||||||
|
|
||||||
| var rootCommand = new RootCommand(Resources.Help) | ||||||
| { | ||||||
|
|
@@ -86,11 +93,6 @@ internal sealed class CommandLineOptions | |||||
| rootCommand.Options.Add(watchOption); | ||||||
| } | ||||||
|
|
||||||
| rootCommand.Options.Add(longProjectOption); | ||||||
| rootCommand.Options.Add(shortProjectOption); | ||||||
| rootCommand.Options.Add(launchProfileOption); | ||||||
| rootCommand.Options.Add(noLaunchProfileOption); | ||||||
|
|
||||||
| // We process all tokens that do not match any of the above options | ||||||
| // to find the subcommand (the first unmatched token preceding "--") | ||||||
| // and all its options and arguments. | ||||||
|
|
@@ -158,7 +160,7 @@ internal sealed class CommandLineOptions | |||||
| } | ||||||
| } | ||||||
|
|
||||||
| var commandArguments = GetCommandArguments(parseResult, watchOptions, explicitCommand, out var binLogToken, out var binLogPath); | ||||||
| var commandArguments = GetCommandArguments(parseResult, watchOptions, forwardToRunOptions, explicitCommand, out var binLogToken, out var binLogPath); | ||||||
|
|
||||||
| // We assume that forwarded options, if any, are intended for dotnet build. | ||||||
| var buildArguments = buildOptions.Select(option => ((IForwardedOption)option).GetForwardingFunction()(parseResult)).SelectMany(args => args).ToList(); | ||||||
|
|
@@ -216,6 +218,7 @@ internal sealed class CommandLineOptions | |||||
| private static IReadOnlyList<string> GetCommandArguments( | ||||||
| ParseResult parseResult, | ||||||
| IReadOnlyList<Option> watchOptions, | ||||||
| IReadOnlyList<Option> forwardToRunOptions, | ||||||
| Command? explicitCommand, | ||||||
| out string? binLogToken, | ||||||
| out string? binLogPath) | ||||||
|
|
@@ -224,37 +227,47 @@ private static IReadOnlyList<string> GetCommandArguments( | |||||
| binLogToken = null; | ||||||
| binLogPath = null; | ||||||
|
|
||||||
| bool isRunCommand = explicitCommand == null || explicitCommand?.Name == "run"; | ||||||
|
|
||||||
| foreach (var child in parseResult.CommandResult.Children) | ||||||
| { | ||||||
| var optionResult = (OptionResult)child; | ||||||
|
|
||||||
| // skip watch options: | ||||||
| if (!watchOptions.Contains(optionResult.Option)) | ||||||
|
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Inverting the logic allows a continue to short-circuit the loop. |
||||||
| if (watchOptions.Contains(optionResult.Option)) | ||||||
| { | ||||||
| if (optionResult.Option.Name.Equals("--interactive", StringComparison.Ordinal) && parseResult.GetValue(NonInteractiveOption)) | ||||||
| { | ||||||
| // skip forwarding the interactive token (which may be computed by default) when users pass --non-interactive to watch itself | ||||||
| continue; | ||||||
| } | ||||||
| continue; | ||||||
| } | ||||||
|
|
||||||
| // skip forwarding run options to other commands. | ||||||
| if (isRunCommand == false && forwardToRunOptions.Contains(optionResult.Option)) | ||||||
|
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The meat and potatoes of this PR. Prevents options which only work with the
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit:
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can we pass
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Of course. Give me a bit and I'll get some commits up. Further ThoughtsThis is outside the scope of this fix, but a different PR could go a step further towards generalization by:
For example:
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is very concerning to me. Are we ignoring Also, it means we might be regressing the scenario for the MTP-based @tmat I don't have much context on
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @Youssef1313 No, we are not ignoring Also, please look at #45761 as it seems like there is a catch22 here. If we don't fix it, we leave
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What happens for Microsoft.Testing.Platform then? In that case I think it will need to be passed to test command, similar to run.
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah, that's the catch 22. Looking over docs, it looks like But, I dislike the additional brittleness that would add to the check. I'd prefer if we could query for the subcommand's acceptable arguments without running the subcommand. If there is a better way to query the acceptable args, please point them out for me. I'll change course to use that.
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. MCP is unrelated. MTP is "Microsoft.Testing.Platform" (so only relevant to test command - and is determined by global.json). I think you can just get the test command and look into its options. When you retrieve the test command, it will return the right thing depending on what is there in global.json.
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Apologies, Alright, I think I'm in the right place. Test's parser has
Even better to go one level up to dotnet's parser with Line 189 in f099c19
Then we'd be able to check any subcommand's arguments for pass-through viability. |
||||||
| { | ||||||
| continue; | ||||||
| } | ||||||
|
|
||||||
| // skip Option<bool> zero-arity options with an implicit optionresult - these weren't actually specified by the user: | ||||||
| if (optionResult.Option is Option<bool> boolOpt && boolOpt.Arity.Equals(ArgumentArity.Zero) && optionResult.Implicit) | ||||||
| { | ||||||
| continue; | ||||||
| } | ||||||
| // skip forwarding the interactive token (which may be computed by default) when users pass --non-interactive to watch itself | ||||||
| if (optionResult.Option.Name.Equals("--interactive", StringComparison.Ordinal) && parseResult.GetValue(NonInteractiveOption)) | ||||||
| { | ||||||
| continue; | ||||||
| } | ||||||
|
|
||||||
| // skip Option<bool> zero-arity options with an implicit optionresult - these weren't actually specified by the user: | ||||||
| if (optionResult.Option is Option<bool> boolOpt && boolOpt.Arity.Equals(ArgumentArity.Zero) && optionResult.Implicit) | ||||||
| { | ||||||
| continue; | ||||||
| } | ||||||
|
|
||||||
| var optionNameToForward = GetOptionNameToForward(optionResult); | ||||||
| if (optionResult.Tokens.Count == 0 && !optionResult.Implicit) | ||||||
| var optionNameToForward = GetOptionNameToForward(optionResult); | ||||||
| if (optionResult.Tokens.Count == 0 && !optionResult.Implicit) | ||||||
| { | ||||||
| arguments.Add(optionNameToForward); | ||||||
| } | ||||||
| else | ||||||
| { | ||||||
| foreach (var token in optionResult.Tokens) | ||||||
| { | ||||||
| arguments.Add(optionNameToForward); | ||||||
| } | ||||||
| else | ||||||
| { | ||||||
| foreach (var token in optionResult.Tokens) | ||||||
| { | ||||||
| arguments.Add(optionNameToForward); | ||||||
| arguments.Add(token.Value); | ||||||
| } | ||||||
| arguments.Add(token.Value); | ||||||
| } | ||||||
| } | ||||||
| } | ||||||
|
|
||||||
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
add ...
if the subcommand supports them