natemcmaster / CommandLineUtils

Command line parsing and utilities for .NET

Home Page:https://natemcmaster.github.io/CommandLineUtils/

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

context.WorkingDirectory does not pick up changes in the working directory

Simonl9l opened this issue · comments

Describe the bug

In a parent Command you have an Option with [DirectoryExists] attribute and you use that (with validation) to perform a Directory.SetWorkingDirectory the context is not updated.

This has the impact in a SubCommand of that parent you have an Option that has a [FileExists] attribute the validation does not pick up the updated working directory, and the validation fails.

To Reproduce
Steps to reproduce the behavior:

  1. Using this version of the library '3.1.0.'
  2. Run this code
    [Subcommand(
        typeof(SubCmd)
    )]
    class ParentCommand
    {
       string? _configurationRoot;

        [DirectoryExists]
        [Option(CommandOptionType.SingleValue,  ShortName = "cr", LongName = "configuration-root", Description = "path to configuration files", ValueName = "absolute or relative path", ShowInHelpText = true)]

        public string ConfigurationRoot
        {
            get => _configurationRoot;
            set
            {
                if (Path.DirectorySeparatorChar == '/' && !value.Contains(Path.DirectorySeparatorChar))
                {
                    value = value.Replace('\\', Path.DirectorySeparatorChar);
                }
                else if (Path.DirectorySeparatorChar == '\\' && !value.Contains(Path.DirectorySeparatorChar))
                {
                    value = value.Replace('/', Path.DirectorySeparatorChar);
                }
                
                if (Path.IsPathRooted(value))
                {
                    _configurationRoot = value;
                }
                else
                {
                    _configurationRoot = new Uri(Path.GetFullPath(value)).LocalPath;
                }
                Directory.SetCurrentDirectory(_configurationRoot);
            }
        }
    }

    class SubCmd 
    {

        [FileExists]
        [Option(CommandOptionType.SingleValue,  ShortName = "cp", LongName = "configuration-path", Description = "path to configuration file", ValueName = "absolute or relative path", ShowInHelpText = true) ]
        private string? ConfigurationPath { get; set; }
    }
  1. With these arguments

-cr {path not working directory} subcmd -cp {path to file in cr directory}

  1. See error

The file '{path to file in cr directory}' does not exist.

Expected behavior
automatically detects or there is a manual way to change to context Working Directory?

Screenshots
If applicable, add screenshots to help explain your problem.

Additional context
Add any other context about the problem here.

@Simonl9l can you share a full repro? What is your Program.Main entry point?

@natemcmaster thank for the reply! Sorry source is highly proprietary. I tried best above to summarize.

I've made a tact change and have reverted to the basic Using You Own Service approach almost identically, dropping the Host.

I've also abandoned the old entry point (before a commit) so no longer have it; but it was using the Sample HostBuilder example ore or less exactly, given or take a number of configured services; but have not recreated the code to change the Working Directory at this time.

Is there a know way to update the context's WorkingDirectory?

As an aside, we may look to implement an alternate version of UseConstructorInjection such that we can leverage the StrongInject container if you have any pointers.

We also note that UseDefaultConventions also calls UseConstructorInjection... not sure of the sample is erroneous in this regard, bit assume we'll also have to Create our own version of this method to use our alternate implementation too.

Working directory is captured in the context of the app as soon as you initialize CommandLineApplication. If you want to change the working directory, you can use one of the constructors overloads on CommandLineApplication allow passing in the WorkingDirectory property.

https://natemcmaster.github.io/CommandLineUtils/v3.0/api/McMaster.Extensions.CommandLineUtils.CommandLineApplication.html#McMaster_Extensions_CommandLineUtils_CommandLineApplication__ctor_

You can also implement CommandLineContext, which is what provides the [DirectoryExists] attribute with its working directory value. If you want to see how this works under the hood, see

public string WorkingDirectory
{
get => _workDir;
protected set
{
if (string.IsNullOrEmpty(value))
{
throw new ArgumentNullException(nameof(value));
}
if (!Path.IsPathRooted(value))
{
throw new ArgumentException(Strings.PathMustNotBeRelative, nameof(value));
}
_workDir = value;
}
}

and

/// <summary>
/// Initializes a new instance of <see cref="CommandLineApplication"/>.
/// </summary>
public CommandLineApplication()
: this(null, DefaultHelpTextGenerator.Singleton, new DefaultCommandLineContext())
{
}
/// <summary>
/// Initializes a new instance of <see cref="CommandLineApplication"/>.
/// </summary>
/// <param name="console">The console implementation to use.</param>
public CommandLineApplication(IConsole console)
: this(null, DefaultHelpTextGenerator.Singleton, new DefaultCommandLineContext(console))
{ }
/// <summary>
/// Initializes a new instance of <see cref="CommandLineApplication"/>.
/// </summary>
/// <param name="console">The console implementation to use.</param>
/// <param name="workingDirectory">The current working directory.</param>
public CommandLineApplication(IConsole console, string workingDirectory)
: this(null, DefaultHelpTextGenerator.Singleton, new DefaultCommandLineContext(console, workingDirectory))
{ }
/// <summary>
/// Initializes a new instance of <see cref="CommandLineApplication"/>.
/// </summary>
/// <param name="helpTextGenerator">The help text generator to use.</param>
/// <param name="console">The console implementation to use.</param>
/// <param name="workingDirectory">The current working directory.</param>
public CommandLineApplication(IHelpTextGenerator helpTextGenerator, IConsole console, string workingDirectory)
: this(null, helpTextGenerator, new DefaultCommandLineContext(console, workingDirectory))
{
}

So, I can't reproduce your sample, but I'm guessing that the call to Directory.SetCurrentDirectory(_configurationRoot) is happening too late because it occurred within the application after it is running, after validation has already happened and Directory/FileExists attributes were run.

Yes it did... as it runs as part of the setter on an option in the parent command.

Let's close and thanks for the hints and response

Cool, best of luck on your project :)