command-line-api icon indicating copy to clipboard operation
command-line-api copied to clipboard

CLI fails on read-only file system

Open gornostal opened this issue 10 months ago • 1 comments

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);

gornostal avatar Apr 16 '24 15:04 gornostal

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);

kmc059000 avatar Jun 14 '24 04:06 kmc059000