dotnet / command-line-api

Command line parsing, invocation, and rendering of terminal output.
https://github.com/dotnet/command-line-api/wiki
MIT License
3.35k stars 375 forks source link

CLI fails on read-only file system #2379

Open gornostal opened 2 months ago

gornostal commented 2 months ago

Steps to reproduce:

  1. Use CommandLineBuilder with .UseDefaults()
  2. Build a Docker image and run with --read-only to use a read-only file system

Expected result: CommandLine shouldn't use RegisterWithDotnetSuggest by default (or at least shouldn't create new files)

Actual result:

Unhandled exception: System.IO.IOException: Read-only file system : '/tmp/system-commandline-sentinel-files'
   at [System.IO](http://system.io/).FileSystem.CreateDirectory(String fullPath, UnixFileMode unixCreateMode)
   at [System.IO](http://system.io/).DirectoryInfo.Create()
   at System.CommandLine.Invocation.FeatureRegistration.EnsureRegistered(Func`1 onInitialize)
   at System.CommandLine.Builder.CommandLineBuilderExtensions.<>c.<<RegisterWithDotnetSuggest>b__5_0>d.MoveNext()
--- End of stack trace from previous location ---
   at System.CommandLine.Builder.CommandLineBuilderExtensions.<>c__DisplayClass8_0.<<UseExceptionHandler>b__0>d.MoveNext()

Workaround:

// call extensions explicitly because .UseDefaults() calls .RegisterWithDotnetSuggest()
var rootCommand = new RootCommand("My App");
var commandLineBuilder = new CommandLineBuilder(rootCommand);
commandLineBuilder
    .UseVersionOption()
    .UseHelp()
    .UseParseDirective()
    .UseSuggestDirective()
    .UseTypoCorrections()
    .UseParseErrorReporting()
    .UseExceptionHandler()
    .CancelOnProcessTermination();
var parser = commandLineBuilder.Build();
return await parser.InvokeAsync(args);
kmc059000 commented 3 weeks ago

I recently ran into this as well, except instead of it being a docker readonly file system, it is a readonly file system managed by kubernetes.

Our code avoids the CommandLineBuilder and invokes the command directly. I assume it is some default middleware that needs file system access, seemingly the UseSuggestDirective() based on searching I've done.

var rootCmd = new RootCommand("My Service");
Environment.ExitCode = rootCmd.Invoice(args);