Ejemplo n.º 1
0
        public Telemetry(
            IOptions <TelemetryOptions> options,
            IEnumerable <ITelemetryInitializer> initializers,
            IFirstTimeUseNoticeSentinel sentinel)
        {
            if (options is null)
            {
                throw new ArgumentNullException(nameof(options));
            }

            _options = options.Value;

            Enabled = _options.IsEnabled && !EnvironmentHelper.GetEnvironmentVariableAsBool(_options.TelemetryOptout) && PermissionExists(sentinel);

            if (!Enabled)
            {
                return;
            }

            _telemetryConfig = TelemetryConfiguration.CreateDefault();

            foreach (var initializer in initializers)
            {
                _telemetryConfig.TelemetryInitializers.Add(initializer);
            }

            _client = new TelemetryClient(_telemetryConfig)
            {
                InstrumentationKey = _options.InstrumentationKey
            };

            _client.Context.Session.Id             = _options.CurrentSessionId;
            _client.Context.Device.OperatingSystem = RuntimeEnvironment.OperatingSystem;
        }
Ejemplo n.º 2
0
        private static void ConfigureDotNetForFirstTimeUse(
            INuGetCacheSentinel nugetCacheSentinel,
            IFirstTimeUseNoticeSentinel firstTimeUseNoticeSentinel,
            CliFallbackFolderPathCalculator cliFallbackFolderPathCalculator)
        {
            using (PerfTrace.Current.CaptureTiming())
            {
                var nugetPackagesArchiver = new NuGetPackagesArchiver();
                var environmentProvider   = new EnvironmentProvider();
                var commandFactory        = new DotNetCommandFactory(alwaysRunOutOfProc: true);
                var nugetCachePrimer      = new NuGetCachePrimer(
                    nugetPackagesArchiver,
                    nugetCacheSentinel,
                    cliFallbackFolderPathCalculator);
                var dotnetConfigurer = new DotnetFirstTimeUseConfigurer(
                    nugetCachePrimer,
                    nugetCacheSentinel,
                    firstTimeUseNoticeSentinel,
                    environmentProvider,
                    Reporter.Output,
                    cliFallbackFolderPathCalculator.CliFallbackFolderPath);

                dotnetConfigurer.Configure();
            }
        }
Ejemplo n.º 3
0
        private static void ConfigureDotNetForFirstTimeUse(
            IFirstTimeUseNoticeSentinel firstTimeUseNoticeSentinel,
            IAspNetCertificateSentinel aspNetCertificateSentinel,
            IFileSentinel toolPathSentinel,
            bool isDotnetBeingInvokedFromNativeInstaller,
            DotnetFirstRunConfiguration dotnetFirstRunConfiguration,
            IEnvironmentProvider environmentProvider,
            Dictionary <string, double> performanceMeasurements)
        {
            var environmentPath            = EnvironmentPathFactory.CreateEnvironmentPath(isDotnetBeingInvokedFromNativeInstaller, environmentProvider);
            var commandFactory             = new DotNetCommandFactory(alwaysRunOutOfProc: true);
            var aspnetCertificateGenerator = new AspNetCoreCertificateGenerator();
            var dotnetConfigurer           = new DotnetFirstTimeUseConfigurer(
                firstTimeUseNoticeSentinel,
                aspNetCertificateSentinel,
                aspnetCertificateGenerator,
                toolPathSentinel,
                dotnetFirstRunConfiguration,
                Reporter.Output,
                CliFolderPathCalculator.CliFallbackFolderPath,
                environmentPath,
                performanceMeasurements);

            dotnetConfigurer.Configure();

            if (isDotnetBeingInvokedFromNativeInstaller && OperatingSystem.IsWindows())
            {
                DotDefaultPathCorrector.Correct();
            }
        }
Ejemplo n.º 4
0
        private static void ConfigureDotNetForFirstTimeUse(
            INuGetCacheSentinel nugetCacheSentinel,
            IFirstTimeUseNoticeSentinel firstTimeUseNoticeSentinel,
            IAspNetCertificateSentinel aspNetCertificateSentinel,
            CliFolderPathCalculator cliFolderPathCalculator,
            bool hasSuperUserAccess)
        {
            var environmentProvider = new EnvironmentProvider();

            using (PerfTrace.Current.CaptureTiming())
            {
                var nugetPackagesArchiver = new NuGetPackagesArchiver();
                var environmentPath       =
                    EnvironmentPathFactory.CreateEnvironmentPath(cliFolderPathCalculator, hasSuperUserAccess, environmentProvider);
                var commandFactory   = new DotNetCommandFactory(alwaysRunOutOfProc: true);
                var nugetCachePrimer = new NuGetCachePrimer(
                    nugetPackagesArchiver,
                    nugetCacheSentinel,
                    cliFolderPathCalculator);
                var aspnetCertificateGenerator = new AspNetCoreCertificateGenerator();
                var dotnetConfigurer           = new DotnetFirstTimeUseConfigurer(
                    nugetCachePrimer,
                    nugetCacheSentinel,
                    firstTimeUseNoticeSentinel,
                    aspNetCertificateSentinel,
                    aspnetCertificateGenerator,
                    environmentProvider,
                    Reporter.Output,
                    cliFolderPathCalculator.CliFallbackFolderPath,
                    environmentPath);

                dotnetConfigurer.Configure();
            }
        }
Ejemplo n.º 5
0
        private static void ConfigureDotNetForFirstTimeUse(
            IFirstTimeUseNoticeSentinel firstTimeUseNoticeSentinel,
            IAspNetCertificateSentinel aspNetCertificateSentinel,
            IFileSentinel toolPathSentinel,
            bool isDotnetBeingInvokedFromNativeInstaller,
            DotnetFirstRunConfiguration dotnetFirstRunConfiguration,
            IEnvironmentProvider environmentProvider)
        {
            using (PerfTrace.Current.CaptureTiming())
            {
                var environmentPath            = EnvironmentPathFactory.CreateEnvironmentPath(isDotnetBeingInvokedFromNativeInstaller, environmentProvider);
                var commandFactory             = new DotNetCommandFactory(alwaysRunOutOfProc: true);
                var aspnetCertificateGenerator = new AspNetCoreCertificateGenerator();
                var dotnetConfigurer           = new DotnetFirstTimeUseConfigurer(
                    firstTimeUseNoticeSentinel,
                    aspNetCertificateSentinel,
                    aspnetCertificateGenerator,
                    toolPathSentinel,
                    dotnetFirstRunConfiguration,
                    Reporter.Output,
                    CliFolderPathCalculator.CliFallbackFolderPath,
                    environmentPath);

                dotnetConfigurer.Configure();

                if (isDotnetBeingInvokedFromNativeInstaller && RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
                {
                    DotDefaultPathCorrector.Correct();
                }
            }
        }
Ejemplo n.º 6
0
        public Telemetry(
            IFirstTimeUseNoticeSentinel sentinel,
            string sessionId,
            bool blockThreadInitialization           = false,
            IEnvironmentProvider environmentProvider = null,
            int senderCount = 3)
        {
            if (environmentProvider == null)
            {
                environmentProvider = new EnvironmentProvider();
            }

            Enabled = !environmentProvider.GetEnvironmentVariableAsBool(TelemetryOptout, false) && PermissionExists(sentinel);

            if (!Enabled)
            {
                return;
            }

            // Store the session ID in a static field so that it can be reused
            CurrentSessionId = sessionId ?? Guid.NewGuid().ToString();
            _senderCount     = senderCount;
            if (blockThreadInitialization)
            {
                InitializeTelemetry();
            }
            else
            {
                //initialize in task to offload to parallel thread
                _trackEventTask = Task.Run(() => InitializeTelemetry());
            }
        }
Ejemplo n.º 7
0
        public Telemetry(
            string productVersion,
            IFirstTimeUseNoticeSentinel sentinel = null,
            string sessionId = null,
            bool blockThreadInitialization = false)
        {
            FirstTimeUseNoticeSentinel = sentinel ?? new FirstTimeUseNoticeSentinel(productVersion);
            Enabled = !EnvironmentHelper.GetEnvironmentVariableAsBool(TelemetryOptout) && PermissionExists(FirstTimeUseNoticeSentinel);

            if (!Enabled)
            {
                return;
            }

            // Store the session ID in a static field so that it can be reused
            CurrentSessionId = sessionId ?? Guid.NewGuid().ToString();

            if (blockThreadInitialization)
            {
                InitializeTelemetry(productVersion);
            }
            else
            {
                //initialize in task to offload to parallel thread
                _trackEventTask = Task.Factory.StartNew(() => InitializeTelemetry(productVersion), CancellationToken.None, TaskCreationOptions.None, TaskScheduler.Default);
            }
        }
Ejemplo n.º 8
0
        private static void ConfigureDotNetForFirstTimeUse(
            IFirstTimeUseNoticeSentinel firstTimeUseNoticeSentinel,
            IAspNetCertificateSentinel aspNetCertificateSentinel,
            IFileSentinel toolPathSentinel,
            bool hasSuperUserAccess,
            DotnetFirstRunConfiguration dotnetFirstRunConfiguration,
            IEnvironmentProvider environmentProvider)
        {
            using (PerfTrace.Current.CaptureTiming())
            {
                var environmentPath            = EnvironmentPathFactory.CreateEnvironmentPath(hasSuperUserAccess, environmentProvider);
                var commandFactory             = new DotNetCommandFactory(alwaysRunOutOfProc: true);
                var aspnetCertificateGenerator = new AspNetCoreCertificateGenerator();
                var dotnetConfigurer           = new DotnetFirstTimeUseConfigurer(
                    firstTimeUseNoticeSentinel,
                    aspNetCertificateSentinel,
                    aspnetCertificateGenerator,
                    toolPathSentinel,
                    dotnetFirstRunConfiguration,
                    Reporter.Output,
                    CliFolderPathCalculator.CliFallbackFolderPath,
                    environmentPath);

                dotnetConfigurer.Configure();
            }
        }
Ejemplo n.º 9
0
        public Telemetry(
            string productVersion,
            IFirstTimeUseNoticeSentinel sentinel,
            string eventsNamespace,
            string sessionId = null,
            bool blockThreadInitialization = false)
        {
            if (string.IsNullOrWhiteSpace(eventsNamespace))
            {
                throw new ArgumentException("Value cannot be null or whitespace.", nameof(eventsNamespace));
            }
            _eventsNamespace = eventsNamespace;
            Enabled          = !GetEnvironmentVariableAsBool(TelemetryOptout) && PermissionExists(sentinel);

            if (!Enabled)
            {
                return;
            }

            // Store the session ID in a static field so that it can be reused
            CurrentSessionId = sessionId ?? Guid.NewGuid().ToString();

            if (blockThreadInitialization)
            {
                InitializeTelemetry(productVersion);
            }
            else
            {
                //initialize in task to offload to parallel thread
                _trackEventTask = Task.Factory.StartNew(() => InitializeTelemetry(productVersion));
            }
        }
Ejemplo n.º 10
0
        private bool PermissionExists(IFirstTimeUseNoticeSentinel sentinel)
        {
            if (sentinel == null)
            {
                return(false);
            }

            return(sentinel.Exists());
        }
Ejemplo n.º 11
0
        public Telemetry(IFirstTimeUseNoticeSentinel sentinel, string sessionId)
        {
            Enabled = !Env.GetEnvironmentVariableAsBool(TelemetryOptout) && PermissionExists(sentinel);

            if (!Enabled)
            {
                return;
            }

            // Store the session ID in a static field so that it can be reused
            CurrentSessionId = sessionId ?? Guid.NewGuid().ToString();

            //initialize in task to offload to parallel thread
            _trackEventTask = Task.Factory.StartNew(() => InitializeTelemetry());
        }
 public DotnetFirstTimeUseConfigurer(
     INuGetCachePrimer nugetCachePrimer,
     INuGetCacheSentinel nugetCacheSentinel,
     IFirstTimeUseNoticeSentinel firstTimeUseNoticeSentinel,
     IEnvironmentProvider environmentProvider,
     IReporter reporter,
     string cliFallbackFolderPath)
 {
     _nugetCachePrimer           = nugetCachePrimer;
     _nugetCacheSentinel         = nugetCacheSentinel;
     _firstTimeUseNoticeSentinel = firstTimeUseNoticeSentinel;
     _environmentProvider        = environmentProvider;
     _reporter = reporter;
     _cliFallbackFolderPath = cliFallbackFolderPath;
 }
Ejemplo n.º 13
0
 public DotnetFirstTimeUseConfigurer(
     INuGetCachePrimer nugetCachePrimer,
     INuGetCacheSentinel nugetCacheSentinel,
     IFirstTimeUseNoticeSentinel firstTimeUseNoticeSentinel,
     IEnvironmentProvider environmentProvider,
     IReporter reporter,
     string cliFallbackFolderPath,
     IEnvironmentPath pathAdder)
 {
     _nugetCachePrimer           = nugetCachePrimer;
     _nugetCacheSentinel         = nugetCacheSentinel;
     _firstTimeUseNoticeSentinel = firstTimeUseNoticeSentinel;
     _environmentProvider        = environmentProvider;
     _reporter = reporter;
     _cliFallbackFolderPath = cliFallbackFolderPath;
     _pathAdder             = pathAdder ?? throw new ArgumentNullException(nameof(pathAdder));
 }
Ejemplo n.º 14
0
 public DotnetFirstTimeUseConfigurer(
     IFirstTimeUseNoticeSentinel firstTimeUseNoticeSentinel,
     IAspNetCertificateSentinel aspNetCertificateSentinel,
     IAspNetCoreCertificateGenerator aspNetCoreCertificateGenerator,
     IFileSentinel toolPathSentinel,
     DotnetFirstRunConfiguration dotnetFirstRunConfiguration,
     IReporter reporter,
     string cliFallbackFolderPath,
     IEnvironmentPath pathAdder)
 {
     _firstTimeUseNoticeSentinel     = firstTimeUseNoticeSentinel;
     _aspNetCertificateSentinel      = aspNetCertificateSentinel;
     _aspNetCoreCertificateGenerator = aspNetCoreCertificateGenerator;
     _toolPathSentinel            = toolPathSentinel;
     _dotnetFirstRunConfiguration = dotnetFirstRunConfiguration;
     _reporter = reporter;
     _cliFallbackFolderPath = cliFallbackFolderPath;
     _pathAdder             = pathAdder ?? throw new ArgumentNullException(nameof(pathAdder));
 }
Ejemplo n.º 15
0
 public DotnetFirstTimeUseConfigurer(
     INuGetCachePrimer nugetCachePrimer,
     INuGetCacheSentinel nugetCacheSentinel,
     IFirstTimeUseNoticeSentinel firstTimeUseNoticeSentinel,
     IAspNetCertificateSentinel aspNetCertificateSentinel,
     IAspNetCoreCertificateGenerator aspNetCoreCertificateGenerator,
     IFileSentinel toolPathSentinel,
     IEnvironmentProvider environmentProvider,
     IReporter reporter,
     string cliFallbackFolderPath,
     IEnvironmentPath pathAdder)
 {
     _nugetCachePrimer               = nugetCachePrimer;
     _nugetCacheSentinel             = nugetCacheSentinel;
     _firstTimeUseNoticeSentinel     = firstTimeUseNoticeSentinel;
     _aspNetCertificateSentinel      = aspNetCertificateSentinel;
     _aspNetCoreCertificateGenerator = aspNetCoreCertificateGenerator;
     _toolPathSentinel               = toolPathSentinel;
     _environmentProvider            = environmentProvider;
     _reporter = reporter;
     _cliFallbackFolderPath = cliFallbackFolderPath;
     _pathAdder             = pathAdder ?? throw new ArgumentNullException(nameof(pathAdder));
 }
Ejemplo n.º 16
0
        internal static int ProcessArgs(string[] args, TimeSpan startupTime, ITelemetry telemetryClient = null)
        {
            Dictionary <string, double> performanceData = new Dictionary <string, double>();

            PerformanceLogEventSource.Log.BuiltInCommandParserStart();
            Stopwatch parseStartTime = Stopwatch.StartNew();
            var       parseResult    = Parser.Instance.Parse(args);

            // Avoid create temp directory with root permission and later prevent access in non sudo
            // This method need to be run very early before temp folder get created
            // https://github.com/dotnet/sdk/issues/20195
            SudoEnvironmentDirectoryOverride.OverrideEnvironmentVariableToTmp(parseResult);

            performanceData.Add("Parse Time", parseStartTime.Elapsed.TotalMilliseconds);
            PerformanceLogEventSource.Log.BuiltInCommandParserStop();

            using (IFirstTimeUseNoticeSentinel disposableFirstTimeUseNoticeSentinel =
                       new FirstTimeUseNoticeSentinel())
            {
                IFirstTimeUseNoticeSentinel firstTimeUseNoticeSentinel = disposableFirstTimeUseNoticeSentinel;
                IAspNetCertificateSentinel  aspNetCertificateSentinel  = new AspNetCertificateSentinel();
                IFileSentinel toolPathSentinel = new FileSentinel(
                    new FilePath(
                        Path.Combine(
                            CliFolderPathCalculator.DotnetUserProfileFolderPath,
                            ToolPathSentinelFileName)));
                if (parseResult.GetValueForOption(Parser.DiagOption) && parseResult.IsDotnetBuiltInCommand())
                {
                    Environment.SetEnvironmentVariable(CommandContext.Variables.Verbose, bool.TrueString);
                    CommandContext.SetVerbose(true);
                    Reporter.Reset();
                }
                if (parseResult.HasOption(Parser.VersionOption) && parseResult.IsTopLevelDotnetCommand())
                {
                    CommandLineInfo.PrintVersion();
                    return(0);
                }
                else if (parseResult.HasOption(Parser.InfoOption) && parseResult.IsTopLevelDotnetCommand())
                {
                    CommandLineInfo.PrintInfo();
                    return(0);
                }
                else
                {
                    PerformanceLogEventSource.Log.FirstTimeConfigurationStart();

                    var environmentProvider = new EnvironmentProvider();

                    bool generateAspNetCertificate =
                        environmentProvider.GetEnvironmentVariableAsBool("DOTNET_GENERATE_ASPNET_CERTIFICATE", defaultValue: true);
                    bool telemetryOptout =
                        environmentProvider.GetEnvironmentVariableAsBool("DOTNET_CLI_TELEMETRY_OPTOUT", defaultValue: false);
                    bool addGlobalToolsToPath =
                        environmentProvider.GetEnvironmentVariableAsBool("DOTNET_ADD_GLOBAL_TOOLS_TO_PATH", defaultValue: true);
                    bool nologo =
                        environmentProvider.GetEnvironmentVariableAsBool("DOTNET_NOLOGO", defaultValue: false);

                    ReportDotnetHomeUsage(environmentProvider);

                    var isDotnetBeingInvokedFromNativeInstaller = false;
                    if (parseResult.CommandResult.Command.Name.Equals(Parser.InstallSuccessCommand.Name))
                    {
                        aspNetCertificateSentinel  = new NoOpAspNetCertificateSentinel();
                        firstTimeUseNoticeSentinel = new NoOpFirstTimeUseNoticeSentinel();
                        toolPathSentinel           = new NoOpFileSentinel(exists: false);
                        isDotnetBeingInvokedFromNativeInstaller = true;
                    }

                    var dotnetFirstRunConfiguration = new DotnetFirstRunConfiguration(
                        generateAspNetCertificate: generateAspNetCertificate,
                        telemetryOptout: telemetryOptout,
                        addGlobalToolsToPath: addGlobalToolsToPath,
                        nologo: nologo);

                    ConfigureDotNetForFirstTimeUse(
                        firstTimeUseNoticeSentinel,
                        aspNetCertificateSentinel,
                        toolPathSentinel,
                        isDotnetBeingInvokedFromNativeInstaller,
                        dotnetFirstRunConfiguration,
                        environmentProvider,
                        performanceData);
                    PerformanceLogEventSource.Log.FirstTimeConfigurationStop();
                }

                PerformanceLogEventSource.Log.TelemetryRegistrationStart();

                if (telemetryClient == null)
                {
                    telemetryClient = new Telemetry.Telemetry(firstTimeUseNoticeSentinel);
                }
                TelemetryEventEntry.Subscribe(telemetryClient.TrackEvent);
                TelemetryEventEntry.TelemetryFilter = new TelemetryFilter(Sha256Hasher.HashWithNormalizedCasing);

                PerformanceLogEventSource.Log.TelemetryRegistrationStop();
            }

            if (CommandContext.IsVerbose())
            {
                Console.WriteLine($"Telemetry is: {(telemetryClient.Enabled ? "Enabled" : "Disabled")}");
            }
            PerformanceLogEventSource.Log.TelemetrySaveIfEnabledStart();
            performanceData.Add("Startup Time", startupTime.TotalMilliseconds);
            TelemetryEventEntry.SendFiltered(Tuple.Create(parseResult, performanceData));
            PerformanceLogEventSource.Log.TelemetrySaveIfEnabledStop();

            int exitCode;

            if (parseResult.CanBeInvoked())
            {
                PerformanceLogEventSource.Log.BuiltInCommandStart();
                exitCode = parseResult.Invoke();
                PerformanceLogEventSource.Log.BuiltInCommandStop();
            }
            else
            {
                PerformanceLogEventSource.Log.ExtensibleCommandResolverStart();
                var resolvedCommand = CommandFactoryUsingResolver.Create(
                    "dotnet-" + parseResult.GetValueForArgument(Parser.DotnetSubCommand),
                    args.GetSubArguments(),
                    FrameworkConstants.CommonFrameworks.NetStandardApp15);
                PerformanceLogEventSource.Log.ExtensibleCommandResolverStop();

                PerformanceLogEventSource.Log.ExtensibleCommandStart();
                var result = resolvedCommand.Execute();
                PerformanceLogEventSource.Log.ExtensibleCommandStop();

                exitCode = result.ExitCode;
            }

            PerformanceLogEventSource.Log.TelemetryClientFlushStart();
            telemetryClient.Flush();
            PerformanceLogEventSource.Log.TelemetryClientFlushStop();

            return(exitCode);
        }
Ejemplo n.º 17
0
 public ConsoleFirstTimeUserNotifier(IFirstTimeUseNoticeSentinel sentinel, InputOutputStreams io)
 {
     _sentinel = sentinel ?? throw new ArgumentNullException(nameof(sentinel));
     _io       = io ?? throw new ArgumentNullException(nameof(io));
 }
Ejemplo n.º 18
0
        public static Parser Create(
            IServiceCollection services,
            StartServer startServer = null,
            Jupyter jupyter         = null,
            StartStdIO startStdIO   = null,
            StartHttp startHttp     = null,
            Action onServerStarted  = null,
            ITelemetry telemetry    = null,
            IFirstTimeUseNoticeSentinel firstTimeUseNoticeSentinel = null)
        {
            var operation = Log.OnEnterAndExit();

            if (services == null)
            {
                throw new ArgumentNullException(nameof(services));
            }
            var disposeOnQuit = new CompositeDisposable();

            startServer ??= (startupOptions, invocationContext) =>
            {
                operation.Info("constructing webhost");
                var webHost = Program.ConstructWebHost(startupOptions);
                disposeOnQuit.Add(webHost);
                operation.Info("starting  kestrel server");
                webHost.Start();
                onServerStarted?.Invoke();
                webHost.WaitForShutdown();
                operation.Dispose();
            };

            jupyter ??= JupyterCommand.Do;

            startStdIO ??= StdIOCommand.Do;

            startHttp ??= HttpCommand.Do;

            // Setup first time use notice sentinel.
            firstTimeUseNoticeSentinel ??= new FirstTimeUseNoticeSentinel(VersionSensor.Version().AssemblyInformationalVersion);

            var clearTextProperties = new[]
            {
                "frontend"
            };

            // Setup telemetry.
            telemetry ??= new Telemetry.Telemetry(
                VersionSensor.Version().AssemblyInformationalVersion,
                firstTimeUseNoticeSentinel,
                "dotnet/interactive/cli");

            var filter = new TelemetryFilter(
                Sha256Hasher.HashWithNormalizedCasing,
                clearTextProperties,
                (commandResult, directives, entryItems) =>
            {
                // add frontend
                var frontendTelemetryAdded = false;
                foreach (var directive in directives)
                {
                    switch (directive.Key)
                    {
                    case "vscode":
                    case "jupyter":
                    case "synapse":
                        frontendTelemetryAdded = true;
                        entryItems.Add(new KeyValuePair <string, string>("frontend", directive.Key));
                        break;
                    }
                }

                if (!frontendTelemetryAdded)
                {
                    if (commandResult.Command.Name == "jupyter")
                    {
                        entryItems.Add(new KeyValuePair <string, string>("frontend", "jupyter"));
                    }
                    else
                    {
                        entryItems.Add(new KeyValuePair <string, string>("frontend", "unknown"));
                    }
                }
            });

            var verboseOption = new Option <bool>(
                "--verbose",
                "Enable verbose logging to the console");

            var logPathOption = new Option <DirectoryInfo>(
                "--log-path",
                "Enable file logging to the specified directory");

            var pathOption = new Option <DirectoryInfo>(
                "--path",
                "Installs the kernelspecs to the specified directory")
                             .ExistingOnly();

            var defaultKernelOption = new Option <string>(
                "--default-kernel",
                description: "The default language for the kernel",
                getDefaultValue: () => "csharp");

            var rootCommand = DotnetInteractive();

            rootCommand.AddCommand(Jupyter());
            rootCommand.AddCommand(StdIO());
            rootCommand.AddCommand(HttpServer());

            return(new CommandLineBuilder(rootCommand)
                   .UseDefaults()
                   .UseMiddleware(async(context, next) =>
            {
                if (context.ParseResult.Errors.Count == 0)
                {
                    telemetry.SendFiltered(filter, context.ParseResult);
                }

                // If sentinel does not exist, print the welcome message showing the telemetry notification.
                if (!Telemetry.Telemetry.SkipFirstTimeExperience &&
                    !firstTimeUseNoticeSentinel.Exists())
                {
                    context.Console.Out.WriteLine();
                    context.Console.Out.WriteLine(Telemetry.Telemetry.WelcomeMessage);

                    firstTimeUseNoticeSentinel.CreateIfNotExists();
                }

                await next(context);
            })
                   .Build());

            RootCommand DotnetInteractive()
            {
                var command = new RootCommand
                {
                    Name        = "dotnet-interactive",
                    Description = "Interactive programming for .NET."
                };

                command.AddGlobalOption(logPathOption);
                command.AddGlobalOption(verboseOption);

                return(command);
            }

            Command Jupyter()
            {
                var httpPortRangeOption = new Option <HttpPortRange>(
                    "--http-port-range",
                    parseArgument: result => result.Tokens.Count == 0 ? HttpPortRange.Default : ParsePortRangeOption(result),
                    description: "Specifies the range of ports to use to enable HTTP services",
                    isDefault: true);

                var jupyterCommand = new Command("jupyter", "Starts dotnet-interactive as a Jupyter kernel")
                {
                    defaultKernelOption,
                    httpPortRangeOption,
                    new Argument <FileInfo>
                    {
                        Name = "connection-file"
                    }.ExistingOnly()
                };

                jupyterCommand.Handler = CommandHandler.Create <StartupOptions, JupyterOptions, IConsole, InvocationContext, CancellationToken>(JupyterHandler);

                var installCommand = new Command("install", "Install the .NET kernel for Jupyter")
                {
                    httpPortRangeOption,
                    pathOption
                };

                installCommand.Handler = CommandHandler.Create <IConsole, InvocationContext, HttpPortRange, DirectoryInfo>(InstallHandler);

                jupyterCommand.AddCommand(installCommand);

                return(jupyterCommand);

                Task <int> JupyterHandler(StartupOptions startupOptions, JupyterOptions options, IConsole console, InvocationContext context, CancellationToken cancellationToken)
                {
                    var frontendEnvironment = new HtmlNotebookFrontedEnvironment();
                    var kernel = CreateKernel(options.DefaultKernel, frontendEnvironment, startupOptions);

                    services.AddKernel(kernel);

                    services.AddSingleton(c => ConnectionInformation.Load(options.ConnectionFile))
                    .AddSingleton(c =>
                    {
                        return(new JupyterRequestContextScheduler(delivery => c.GetRequiredService <JupyterRequestContextHandler>()
                                                                  .Handle(delivery)));
                    })
                    .AddSingleton(c => new JupyterRequestContextHandler(kernel))
                    .AddSingleton <IHostedService, Shell>()
                    .AddSingleton <IHostedService, Heartbeat>();
                    return(jupyter(startupOptions, console, startServer, context));
                }

                Task <int> InstallHandler(IConsole console, InvocationContext context, HttpPortRange httpPortRange, DirectoryInfo path)
                {
                    var jupyterInstallCommand = new JupyterInstallCommand(console, new JupyterKernelSpecInstaller(console), httpPortRange, path);

                    return(jupyterInstallCommand.InvokeAsync());
                }
            }

            Command HttpServer()
            {
                var httpPortOption = new Option <HttpPort>(
                    "--http-port",
                    description: "Specifies the port on which to enable HTTP services",
                    parseArgument: result =>
                {
                    if (result.Tokens.Count == 0)
                    {
                        return(HttpPort.Auto);
                    }

                    var source = result.Tokens[0].Value;

                    if (source == "*")
                    {
                        return(HttpPort.Auto);
                    }

                    if (!int.TryParse(source, out var portNumber))
                    {
                        result.ErrorMessage = "Must specify a port number or *.";
                        return(null);
                    }

                    return(new HttpPort(portNumber));
                },
                    isDefault: true);

                var httpCommand = new Command("http", "Starts dotnet-interactive with kernel functionality exposed over http")
                {
                    defaultKernelOption,
                    httpPortOption
                };

                httpCommand.Handler = CommandHandler.Create <StartupOptions, KernelHttpOptions, IConsole, InvocationContext>(
                    (startupOptions, options, console, context) =>
                {
                    var frontendEnvironment = new BrowserFrontendEnvironment();
                    var kernel = CreateKernel(options.DefaultKernel, frontendEnvironment, startupOptions);
                    services.AddKernel(kernel);

                    onServerStarted ??= () =>
                    {
                        console.Out.WriteLine("Application started. Press Ctrl+C to shut down.");
                    };
                    return(startHttp(startupOptions, console, startServer, context));
                });

                return(httpCommand);
            }

            Command StdIO()
            {
                var httpPortRangeOption = new Option <HttpPortRange>(
                    "--http-port-range",
                    parseArgument: result => result.Tokens.Count == 0 ? HttpPortRange.Default : ParsePortRangeOption(result),
                    description: "Specifies the range of ports to use to enable HTTP services");

                var httpPortOption = new Option <HttpPort>(
                    "--http-port",
                    description: "Specifies the port on which to enable HTTP services",
                    parseArgument: result =>
                {
                    if (result.FindResultFor(httpPortRangeOption) is { } conflictingOption)
                    {
                        var parsed          = result.Parent as OptionResult;
                        result.ErrorMessage = $"Cannot specify both {conflictingOption.Token.Value} and {parsed.Token.Value} together";
                        return(null);
                    }

                    if (result.Tokens.Count == 0)
                    {
                        return(HttpPort.Auto);
                    }

                    var source = result.Tokens[0].Value;

                    if (source == "*")
                    {
                        return(HttpPort.Auto);
                    }

                    if (!int.TryParse(source, out var portNumber))
                    {
                        result.ErrorMessage = "Must specify a port number or *.";
                        return(null);
                    }

                    return(new HttpPort(portNumber));
                });

                var workingDirOption = new Option <DirectoryInfo>(
                    "--working-dir",
                    () => new DirectoryInfo(Environment.CurrentDirectory),
                    "Working directory to which to change after launching the kernel.");

                var stdIOCommand = new Command(
                    "stdio",
                    "Starts dotnet-interactive with kernel functionality exposed over standard I/O")
                {
                    defaultKernelOption,
                    httpPortRangeOption,
                    httpPortOption,
                    workingDirOption
                };

                stdIOCommand.Handler = CommandHandler.Create <StartupOptions, StdIOOptions, IConsole, InvocationContext, CancellationToken>(
                    (startupOptions, options, console, context, cancellationToken) =>
                {
                    FrontendEnvironment frontendEnvironment = startupOptions.EnableHttpApi
                            ? new HtmlNotebookFrontedEnvironment()
                            : new BrowserFrontendEnvironment();

                    var kernel = CreateKernel(options.DefaultKernel, frontendEnvironment, startupOptions);
                    services.AddKernel(kernel);

                    kernel.UseQuitCommand(disposeOnQuit, cancellationToken);

                    var kernelServer = kernel.CreateKernelServer(startupOptions.WorkingDir);

                    if (startupOptions.EnableHttpApi)
                    {
                        if (context.ParseResult.Directives.Contains("vscode"))
                        {
                            ((HtmlNotebookFrontedEnvironment)frontendEnvironment).RequiresAutomaticBootstrapping =
                                false;
                        }

                        kernelServer.Start();
                        onServerStarted ??= () =>
                        {
                            kernelServer.NotifyIsReady();
                        };
                        return(startHttp(startupOptions, console, startServer, context));
                    }

                    disposeOnQuit.Add(kernel);

                    return(startStdIO(
                               startupOptions,
                               kernelServer,
                               console));
                });

                return(stdIOCommand);
            }
Ejemplo n.º 19
0
        public static Parser Create(
            IServiceCollection services,
            StartServer startServer = null,
            Demo demo           = null,
            TryGitHub tryGithub = null,
            Pack pack           = null,
            Install install     = null,
            Verify verify       = null,
            Jupyter jupyter     = null,
            StartKernelServer startKernelServer = null,
            ITelemetry telemetry = null,
            IFirstTimeUseNoticeSentinel firstTimeUseNoticeSentinel = null)
        {
            if (services == null)
            {
                throw new ArgumentNullException(nameof(services));
            }

            startServer = startServer ??
                          ((startupOptions, invocationContext) =>
                           Program.ConstructWebHost(startupOptions).Run());

            jupyter = jupyter ??
                      ((startupOptions, console, server, context) =>
                       JupyterCommand.Do(startupOptions, console, server, context));

            demo = demo ??
                   DemoCommand.Do;

            tryGithub = tryGithub ??
                        ((repo, console) =>
                         GitHubHandler.Handler(repo,
                                               console,
                                               new GitHubRepoLocator()));

            verify = verify ??
                     ((options, console, startupOptions) =>
                      VerifyCommand.Do(options,
                                       console,
                                       startupOptions));

            pack = pack ??
                   PackCommand.Do;

            install = install ??
                      InstallCommand.Do;

            startKernelServer = startKernelServer ??
                                ((startupOptions, kernel, console) =>
                                 KernelServerCommand.Do(startupOptions, kernel, console));

            // Setup first time use notice sentinel.
            firstTimeUseNoticeSentinel = firstTimeUseNoticeSentinel ?? new FirstTimeUseNoticeSentinel();

            // Setup telemetry.
            telemetry = telemetry ?? new Telemetry.Telemetry(firstTimeUseNoticeSentinel);
            var filter = new TelemetryFilter(Sha256Hasher.HashWithNormalizedCasing);
            Action <ParseResult> track = o => telemetry.SendFiltered(filter, o);

            var dirArgument = new Argument <FileSystemDirectoryAccessor>(() => new FileSystemDirectoryAccessor(Directory.GetCurrentDirectory()))
            {
                Name        = nameof(StartupOptions.RootDirectory),
                Arity       = ArgumentArity.ZeroOrOne,
                Description = "Specify the path to the root directory for your documentation",
            };

            dirArgument.AddValidator(symbolResult =>
            {
                var directory = symbolResult.Tokens
                                .Select(t => t.Value)
                                .FirstOrDefault();

                if (!string.IsNullOrEmpty(directory) && !Directory.Exists(directory))
                {
                    return($"Directory does not exist: {directory}");
                }

                return(null);
            });

            var rootCommand = StartInTryMode();

            rootCommand.AddCommand(StartInHostedMode());
            rootCommand.AddCommand(Demo());
            rootCommand.AddCommand(GitHub());
            rootCommand.AddCommand(Pack());
            rootCommand.AddCommand(Install());
            rootCommand.AddCommand(Verify());
            rootCommand.AddCommand(Jupyter());
            rootCommand.AddCommand(KernelServer());

            return(new CommandLineBuilder(rootCommand)
                   .UseDefaults()
                   .UseMiddleware(async(context, next) =>
            {
                // If sentinel does not exist, print the welcome message showing the telemetry notification.
                if (!firstTimeUseNoticeSentinel.Exists() && !Telemetry.Telemetry.SkipFirstTimeExperience)
                {
                    context.Console.Out.WriteLine();
                    context.Console.Out.WriteLine(Telemetry.Telemetry.WelcomeMessage);

                    firstTimeUseNoticeSentinel.CreateIfNotExists();
                }

                if (context.ParseResult.Directives.Contains("debug") &&
                    !(Clock.Current is VirtualClock))
                {
                    VirtualClock.Start();
                }

                await next(context);
            })
                   .Build());

            RootCommand StartInTryMode()
            {
                var command = new RootCommand
                {
                    Name        = "dotnet-try",
                    Description = "Interactive documentation in your browser"
                };

                command.AddArgument(dirArgument);

                command.AddOption(new Option(
                                      "--add-package-source",
                                      "Specify an additional NuGet package source")
                {
                    Argument = new Argument <PackageSource>(() => new PackageSource(Directory.GetCurrentDirectory()))
                    {
                        Name = "NuGet source"
                    }
                });

                command.AddOption(new Option(
                                      "--package",
                                      "Specify a Try .NET package or path to a .csproj to run code samples with")
                {
                    Argument = new Argument <string>
                    {
                        Name = "name or .csproj"
                    }
                });

                command.AddOption(new Option(
                                      "--package-version",
                                      "Specify a Try .NET package version to use with the --package option")
                {
                    Argument = new Argument <string>
                    {
                        Name = "version"
                    }
                });

                command.AddOption(new Option(
                                      "--uri",
                                      "Specify a URL or a relative path to a Markdown file")
                {
                    Argument = new Argument <Uri>()
                });

                command.AddOption(new Option(
                                      "--enable-preview-features",
                                      "Enable preview features")
                {
                    Argument = new Argument <bool>()
                });

                command.AddOption(new Option(
                                      "--log-path",
                                      "Enable file logging to the specified directory")
                {
                    Argument = new Argument <DirectoryInfo>
                    {
                        Name = "dir"
                    }
                });

                command.AddOption(new Option(
                                      "--verbose",
                                      "Enable verbose logging to the console")
                {
                    Argument = new Argument <bool>()
                });

                var portArgument = new Argument <ushort>();

                portArgument.AddValidator(symbolResult =>
                {
                    if (symbolResult.Tokens
                        .Select(t => t.Value)
                        .Count(value => !ushort.TryParse(value, out _)) > 0)
                    {
                        return("Invalid argument for --port option");
                    }

                    return(null);
                });

                command.AddOption(new Option(
                                      "--port",
                                      "Specify the port for dotnet try to listen on")
                {
                    Argument = portArgument
                });

                command.Handler = CommandHandler.Create <InvocationContext, StartupOptions>((context, options) =>
                {
                    services.AddSingleton(_ => PackageRegistry.CreateForTryMode(
                                              options.RootDirectory,
                                              options.AddPackageSource));

                    startServer(options, context);
                });

                return(command);
            }

            Command StartInHostedMode()
            {
                var command = new Command("hosted")
                {
                    new Option(
                        "--id",
                        "A unique id for the agent instance (e.g. its development environment id).")
                    {
                        Argument = new Argument <string>(defaultValue: () => Environment.MachineName)
                    },
                    new Option(
                        "--production",
                        "Specifies whether the agent is being run using production resources")
                    {
                        Argument = new Argument <bool>()
                    },
                    new Option(
                        "--language-service",
                        "Specifies whether the agent is being run in language service-only mode")
                    {
                        Argument = new Argument <bool>()
                    },
                    new Option(
                        new[]
                    {
                        "-k",
                        "--key"
                    },
                        "The encryption key")
                    {
                        Argument = new Argument <string>()
                    },
                    new Option(
                        new[]
                    {
                        "--ai-key",
                        "--application-insights-key"
                    },
                        "Application Insights key.")
                    {
                        Argument = new Argument <string>()
                    },
                    new Option(
                        "--region-id",
                        "A unique id for the agent region")
                    {
                        Argument = new Argument <string>()
                    },
                    new Option(
                        "--log-to-file",
                        "Writes a log file")
                    {
                        Argument = new Argument <bool>()
                    }
                };

                command.Description = "Starts the Try .NET agent";

                command.IsHidden = true;

                command.Handler = CommandHandler.Create <InvocationContext, StartupOptions>((context, options) =>
                {
                    services.AddSingleton(_ => PackageRegistry.CreateForHostedMode());
                    services.AddSingleton(c => new MarkdownProject(c.GetRequiredService <PackageRegistry>()));
                    services.AddSingleton <IHostedService, Warmup>();

                    startServer(options, context);
                });

                return(command);
            }

            Command Demo()
            {
                var demoCommand = new Command(
                    "demo",
                    "Learn how to create Try .NET content with an interactive demo")
                {
                    new Option("--output", "Where should the demo project be written to?")
                    {
                        Argument = new Argument <DirectoryInfo>(
                            defaultValue: () => new DirectoryInfo(Directory.GetCurrentDirectory()))
                    }
                };

                demoCommand.Handler = CommandHandler.Create <DemoOptions, InvocationContext>((options, context) =>
                {
                    demo(options, context.Console, startServer, context);
                });

                return(demoCommand);
            }

            Command GitHub()
            {
                var argument = new Argument <string>
                {
                    // System.CommandLine parameter binding does lookup by name,
                    // so name the argument after the github command's string param
                    Name = nameof(TryGitHubOptions.Repo)
                };

                var github = new Command("github", "Try a GitHub repo")
                {
                    argument
                };

                github.IsHidden = true;

                github.Handler = CommandHandler.Create <TryGitHubOptions, IConsole>((repo, console) => tryGithub(repo, console));

                return(github);
            }

            Command Jupyter()
            {
                var jupyterCommand      = new Command("jupyter", "Starts dotnet try as a Jupyter kernel");
                var defaultKernelOption = new Option("--default-kernel", "The default .NET kernel language for the notebook.")
                {
                    Argument = new Argument <string>(defaultValue: () => "csharp")
                };

                jupyterCommand.AddOption(defaultKernelOption);
                var connectionFileArgument = new Argument <FileInfo>
                {
                    Name  = "ConnectionFile",
                    Arity = ArgumentArity.ZeroOrOne //should be removed once the commandlineapi allows subcommands to not have arguments from the main command
                }.ExistingOnly();

                jupyterCommand.AddArgument(connectionFileArgument);

                jupyterCommand.Handler = CommandHandler.Create <StartupOptions, JupyterOptions, IConsole, InvocationContext>((startupOptions, options, console, context) =>
                {
                    track(context.ParseResult);

                    services
                    .AddSingleton(c => ConnectionInformation.Load(options.ConnectionFile))
                    .AddSingleton(
                        c =>
                    {
                        return(CommandScheduler
                               .Create <JupyterRequestContext>(delivery => c.GetRequiredService <ICommandHandler <JupyterRequestContext> >()
                                                               .Trace()
                                                               .Handle(delivery)));
                    })
                    .AddSingleton(c => CreateKernel(options.DefaultKernel))
                    .AddSingleton(c => new JupyterRequestContextHandler(c.GetRequiredService <IKernel>())
                                  .Trace())
                    .AddSingleton <IHostedService, Shell>()
                    .AddSingleton <IHostedService, Heartbeat>();

                    return(jupyter(startupOptions, console, startServer, context));
                });

                var installCommand = new Command("install", "Install the .NET kernel for Jupyter");

                installCommand.Handler = CommandHandler.Create <IConsole, InvocationContext>((console, context) =>
                {
                    track(context.ParseResult);
                    return(new JupyterCommandLine(console, new FileSystemJupyterKernelSpec()).InvokeAsync());
                });

                jupyterCommand.AddCommand(installCommand);

                return(jupyterCommand);
            }

            Command KernelServer()
            {
                var startKernelServerCommand = new Command("kernel-server", "Starts dotnet-try with kernel functionality exposed over standard I/O");
                var defaultKernelOption      = new Option("--default-kernel", "The default .NET kernel language for the notebook.")
                {
                    Argument = new Argument <string>(defaultValue: () => "csharp")
                };

                startKernelServerCommand.AddOption(defaultKernelOption);

                startKernelServerCommand.Handler = CommandHandler.Create <StartupOptions, KernelServerOptions, IConsole, InvocationContext>(
                    (startupOptions, options, console, context) =>
                {
                    track(context.ParseResult);
                    return(startKernelServer(startupOptions, CreateKernel(options.DefaultKernel), console));
                });

                return(startKernelServerCommand);
            }

            Command Pack()
            {
                var packCommand = new Command("pack", "Create a Try .NET package")
                {
                    new Argument <DirectoryInfo>
                    {
                        Name = nameof(PackOptions.PackTarget)
                    },
                    new Option("--version", "The version of the Try .NET package")
                    {
                        Argument = new Argument <string>()
                    },
                    new Option("--enable-wasm", "Enables web assembly code execution")
                };

                packCommand.IsHidden = true;

                packCommand.Handler = CommandHandler.Create <PackOptions, IConsole>(
                    (options, console) =>
                {
                    return(pack(options, console));
                });

                return(packCommand);
            }

            Command Install()
            {
                var installCommand = new Command("install", "Install a Try .NET package")
                {
                    new Argument <string>
                    {
                        Name  = nameof(InstallOptions.PackageName),
                        Arity = ArgumentArity.ExactlyOne
                    },
                    new Option("--add-source")
                    {
                        Argument = new Argument <PackageSource>()
                    }
                };

                installCommand.IsHidden = true;

                installCommand.Handler = CommandHandler.Create <InstallOptions, IConsole>((options, console) => install(options, console));

                return(installCommand);
            }

            Command Verify()
            {
                var verifyCommand = new Command("verify", "Verify Markdown files in the target directory and its children.")
                {
                    dirArgument
                };

                verifyCommand.Handler = CommandHandler.Create <VerifyOptions, IConsole, StartupOptions>(
                    (options, console, startupOptions) =>
                {
                    return(verify(options, console, startupOptions));
                });

                return(verifyCommand);
            }
        }
Ejemplo n.º 20
0
        public static Parser Create(
            IServiceCollection services,
            StartServer startServer             = null,
            Jupyter jupyter                     = null,
            StartKernelServer startKernelServer = null,
            ITelemetry telemetry                = null,
            IFirstTimeUseNoticeSentinel firstTimeUseNoticeSentinel = null)
        {
            if (services == null)
            {
                throw new ArgumentNullException(nameof(services));
            }

            startServer ??= ((startupOptions, invocationContext) =>
                             Program.ConstructWebHost(startupOptions).Run());

            jupyter ??= JupyterCommand.Do;

            startKernelServer ??= (async(startupOptions, kernel, console) =>
            {
                var disposable = Program.StartToolLogging(startupOptions);
                if (kernel is KernelBase kernelBase)
                {
                    kernelBase.RegisterForDisposal(disposable);
                }
                var server = new StandardIOKernelServer(
                    kernel,
                    Console.In,
                    Console.Out);

                await server.Input.LastAsync();
            });

            // Setup first time use notice sentinel.
            firstTimeUseNoticeSentinel ??= new FirstTimeUseNoticeSentinel(VersionSensor.Version().AssemblyInformationalVersion);

            // Setup telemetry.
            telemetry ??= new Telemetry.Telemetry(
                VersionSensor.Version().AssemblyInformationalVersion,
                firstTimeUseNoticeSentinel);
            var filter = new TelemetryFilter(Sha256Hasher.HashWithNormalizedCasing);

            void Track(ParseResult o) => telemetry.SendFiltered(filter, o);

            var verboseOption = new Option <bool>(
                "--verbose",
                "Enable verbose logging to the console");

            var logPathOption = new Option <DirectoryInfo>(
                "--log-path",
                "Enable file logging to the specified directory");

            var defaultKernelOption = new Option <string>(
                "--default-kernel",
                description: "The default language for the kernel",
                getDefaultValue: () => "csharp");
            var rootCommand = DotnetInteractive();

            rootCommand.AddCommand(Jupyter());
            rootCommand.AddCommand(KernelServer());

            return(new CommandLineBuilder(rootCommand)
                   .UseDefaults()
                   .UseMiddleware(async(context, next) =>
            {
                // If sentinel does not exist, print the welcome message showing the telemetry notification.
                if (!firstTimeUseNoticeSentinel.Exists() && !Telemetry.Telemetry.SkipFirstTimeExperience)
                {
                    context.Console.Out.WriteLine();
                    context.Console.Out.WriteLine(Telemetry.Telemetry.WelcomeMessage);

                    firstTimeUseNoticeSentinel.CreateIfNotExists();
                }

                await next(context);
            })
                   .Build());

            RootCommand DotnetInteractive()
            {
                var command = new RootCommand
                {
                    Name        = "dotnet-interactive",
                    Description = "Interactive programming for .NET."
                };

                command.AddOption(logPathOption);
                command.AddOption(verboseOption);

                return(command);
            }

            Command Jupyter()
            {
                var jupyterCommand = new Command("jupyter", "Starts dotnet-interactive as a Jupyter kernel")
                {
                    defaultKernelOption,
                    logPathOption,
                    verboseOption,
                    new Argument <FileInfo>
                    {
                        Name = "connection-file"
                    }.ExistingOnly()
                };

                jupyterCommand.Handler = CommandHandler.Create <StartupOptions, JupyterOptions, IConsole, InvocationContext>((startupOptions, options, console, context) =>
                {
                    Track(context.ParseResult);

                    services
                    .AddSingleton(c => ConnectionInformation.Load(options.ConnectionFile))
                    .AddSingleton(
                        c =>
                    {
                        return(CommandScheduler
                               .Create <JupyterRequestContext>(delivery => c.GetRequiredService <ICommandHandler <JupyterRequestContext> >()
                                                               .Trace()
                                                               .Handle(delivery)));
                    })
                    .AddSingleton(c => CreateKernel(options.DefaultKernel))
                    .AddSingleton(c => new JupyterRequestContextHandler(c.GetRequiredService <IKernel>())
                                  .Trace())
                    .AddSingleton <IHostedService, Shell>()
                    .AddSingleton <IHostedService, Heartbeat>();

                    return(jupyter(startupOptions, console, startServer, context));
                });

                var installCommand = new Command("install", "Install the .NET kernel for Jupyter")
                {
                    logPathOption,
                    verboseOption
                };

                installCommand.Handler = CommandHandler.Create <IConsole, InvocationContext>((console, context) =>
                {
                    Track(context.ParseResult);
                    return(new JupyterInstallCommand(console, new JupyterKernelSpec()).InvokeAsync());
                });

                jupyterCommand.AddCommand(installCommand);

                return(jupyterCommand);
            }

            Command KernelServer()
            {
                var startKernelServerCommand = new Command("kernel-server", "Starts dotnet-interactive with kernel functionality exposed over standard I/O")
                {
                    defaultKernelOption,
                    logPathOption,
                };

                startKernelServerCommand.Handler = CommandHandler.Create <StartupOptions, KernelServerOptions, IConsole, InvocationContext>(
                    (startupOptions, options, console, context) =>
                {
                    Track(context.ParseResult);
                    return(startKernelServer(startupOptions, CreateKernel(options.DefaultKernel), console));
                });

                return(startKernelServerCommand);
            }
        }
Ejemplo n.º 21
0
 public Telemetry(IFirstTimeUseNoticeSentinel sentinel) : this(sentinel, null)
 {
 }
Ejemplo n.º 22
0
        internal static int ProcessArgs(string[] args, ITelemetry telemetryClient = null)
        {
            // CommandLineApplication is a bit restrictive, so we parse things ourselves here. Individual apps should use CLA.

            var success = true;
            var command = string.Empty;
            var lastArg = 0;
            TopLevelCommandParserResult topLevelCommandParserResult = TopLevelCommandParserResult.Empty;

            using (IFirstTimeUseNoticeSentinel disposableFirstTimeUseNoticeSentinel =
                       new FirstTimeUseNoticeSentinel())
            {
                IFirstTimeUseNoticeSentinel firstTimeUseNoticeSentinel = disposableFirstTimeUseNoticeSentinel;
                IAspNetCertificateSentinel  aspNetCertificateSentinel  = new AspNetCertificateSentinel();
                IFileSentinel toolPathSentinel = new FileSentinel(
                    new FilePath(
                        Path.Combine(
                            CliFolderPathCalculator.DotnetUserProfileFolderPath,
                            ToolPathSentinelFileName)));

                for (; lastArg < args.Length; lastArg++)
                {
                    if (IsArg(args[lastArg], "d", "diagnostics"))
                    {
                        Environment.SetEnvironmentVariable(CommandContext.Variables.Verbose, bool.TrueString);
                        CommandContext.SetVerbose(true);
                    }
                    else if (IsArg(args[lastArg], "version"))
                    {
                        PrintVersion();
                        return(0);
                    }
                    else if (IsArg(args[lastArg], "info"))
                    {
                        PrintInfo();
                        return(0);
                    }
                    else if (IsArg(args[lastArg], "h", "help") ||
                             args[lastArg] == "-?" ||
                             args[lastArg] == "/?")
                    {
                        HelpCommand.PrintHelp();
                        return(0);
                    }
                    else if (args[lastArg].StartsWith("-", StringComparison.OrdinalIgnoreCase))
                    {
                        Reporter.Error.WriteLine($"Unknown option: {args[lastArg]}");
                        success = false;
                    }
                    else
                    {
                        // It's the command, and we're done!
                        command = args[lastArg];
                        if (string.IsNullOrEmpty(command))
                        {
                            command = "help";
                        }

                        var environmentProvider = new EnvironmentProvider();

                        bool generateAspNetCertificate =
                            environmentProvider.GetEnvironmentVariableAsBool("DOTNET_GENERATE_ASPNET_CERTIFICATE", true);
                        bool skipFirstRunExperience =
                            environmentProvider.GetEnvironmentVariableAsBool("DOTNET_SKIP_FIRST_TIME_EXPERIENCE", false);
                        bool telemetryOptout =
                            environmentProvider.GetEnvironmentVariableAsBool("DOTNET_CLI_TELEMETRY_OPTOUT", false);

                        ReportDotnetHomeUsage(environmentProvider);

                        topLevelCommandParserResult = new TopLevelCommandParserResult(command);
                        var hasSuperUserAccess = false;
                        if (IsDotnetBeingInvokedFromNativeInstaller(topLevelCommandParserResult))
                        {
                            aspNetCertificateSentinel  = new NoOpAspNetCertificateSentinel();
                            firstTimeUseNoticeSentinel = new NoOpFirstTimeUseNoticeSentinel();
                            toolPathSentinel           = new NoOpFileSentinel(exists: false);
                            hasSuperUserAccess         = true;

                            // When running through a native installer, we want the cache expansion to happen, so
                            // we need to override this.
                            skipFirstRunExperience = false;
                        }

                        var dotnetFirstRunConfiguration = new DotnetFirstRunConfiguration(
                            generateAspNetCertificate: generateAspNetCertificate,
                            skipFirstRunExperience: skipFirstRunExperience,
                            telemetryOptout: telemetryOptout);

                        ConfigureDotNetForFirstTimeUse(
                            firstTimeUseNoticeSentinel,
                            aspNetCertificateSentinel,
                            toolPathSentinel,
                            hasSuperUserAccess,
                            dotnetFirstRunConfiguration,
                            environmentProvider);

                        break;
                    }
                }
                if (!success)
                {
                    HelpCommand.PrintHelp();
                    return(1);
                }

                if (telemetryClient == null)
                {
                    telemetryClient = new Telemetry.Telemetry(firstTimeUseNoticeSentinel);
                }
                TelemetryEventEntry.Subscribe(telemetryClient.TrackEvent);
                TelemetryEventEntry.TelemetryFilter = new TelemetryFilter(Sha256Hasher.HashWithNormalizedCasing);
            }

            IEnumerable <string> appArgs =
                (lastArg + 1) >= args.Length
                ? Enumerable.Empty <string>()
                : args.Skip(lastArg + 1).ToArray();

            if (CommandContext.IsVerbose())
            {
                Console.WriteLine($"Telemetry is: {(telemetryClient.Enabled ? "Enabled" : "Disabled")}");
            }

            TelemetryEventEntry.SendFiltered(topLevelCommandParserResult);

            int exitCode;

            if (BuiltInCommandsCatalog.Commands.TryGetValue(topLevelCommandParserResult.Command, out var builtIn))
            {
                var parseResult = Parser.Instance.ParseFrom($"dotnet {topLevelCommandParserResult.Command}", appArgs.ToArray());
                if (!parseResult.Errors.Any())
                {
                    TelemetryEventEntry.SendFiltered(parseResult);
                }

                exitCode = builtIn.Command(appArgs.ToArray());
            }
            else
            {
                CommandResult result = CommandFactoryUsingResolver.Create(
                    "dotnet-" + topLevelCommandParserResult.Command,
                    appArgs,
                    FrameworkConstants.CommonFrameworks.NetStandardApp15)
                                       .Execute();
                exitCode = result.ExitCode;
            }
            return(exitCode);
        }
Ejemplo n.º 23
0
    public static Parser Create(
        IServiceCollection services,
        StartServer startServer                 = null,
        Jupyter jupyter                         = null,
        StartKernelHost startKernelHost         = null,
        StartNotebookParser startNotebookParser = null,
        StartHttp startHttp                     = null,
        Action onServerStarted                  = null,
        ITelemetry telemetry                    = null,
        IFirstTimeUseNoticeSentinel firstTimeUseNoticeSentinel = null)
    {
        var operation = Log.OnEnterAndExit();

        if (services is null)
        {
            throw new ArgumentNullException(nameof(services));
        }
        var disposeOnQuit = new CompositeDisposable();

        startServer ??= (startupOptions, invocationContext) =>
        {
            operation.Info("constructing webhost");
            var webHost = Program.ConstructWebHost(startupOptions);
            disposeOnQuit.Add(webHost);
            operation.Info("starting  kestrel server");
            webHost.Start();
            onServerStarted?.Invoke();
            webHost.WaitForShutdown();
            operation.Dispose();
        };

        jupyter ??= JupyterCommand.Do;

        startKernelHost ??= KernelHostLauncher.Do;

        startNotebookParser ??= ParseNotebookCommand.Do;

        startHttp ??= HttpCommand.Do;

        var isVSCode = false;

        // Setup first time use notice sentinel.
        firstTimeUseNoticeSentinel ??= new FirstTimeUseNoticeSentinel(VersionSensor.Version().AssemblyInformationalVersion);

        var clearTextProperties = new[]
        {
            "frontend"
        };

        // Setup telemetry.
        telemetry ??= new Telemetry.Telemetry(
            VersionSensor.Version().AssemblyInformationalVersion,
            firstTimeUseNoticeSentinel,
            "dotnet/interactive/cli");

        var filter = new TelemetryFilter(
            Sha256Hasher.HashWithNormalizedCasing,
            clearTextProperties,
            (commandResult, directives, entryItems) =>
        {
            // add frontend
            var frontendTelemetryAdded = false;

            // check if is codespaces
            if (!string.IsNullOrWhiteSpace(Environment.GetEnvironmentVariable("CODESPACES")))
            {
                frontendTelemetryAdded = true;
                isVSCode = true;
                entryItems.Add(new KeyValuePair <string, string>("frontend", "gitHubCodeSpaces"));
            }

            if (!frontendTelemetryAdded)
            {
                foreach (var directive in directives)
                {
                    switch (directive.Key)
                    {
                    case "jupyter":
                    case "synapse":
                    case "vscode":
                        frontendTelemetryAdded = true;
                        isVSCode = directive.Key.ToLowerInvariant() == "vscode";
                        entryItems.Add(new KeyValuePair <string, string>("frontend", directive.Key));
                        break;
                    }
                }
            }

            if (!frontendTelemetryAdded)
            {
                switch (commandResult.Command.Name)
                {
                case "jupyter":
                    entryItems.Add(new KeyValuePair <string, string>("frontend", commandResult.Command.Name));
                    frontendTelemetryAdded = true;
                    break;
                }
            }

            if (!frontendTelemetryAdded)
            {
                var frontendName = Environment.GetEnvironmentVariable("DOTNET_INTERACTIVE_FRONTEND_NAME");
                if (string.IsNullOrWhiteSpace(frontendName))
                {
                    frontendName = "unknown";
                }
                entryItems.Add(new KeyValuePair <string, string>("frontend", frontendName));
            }
        });

        var verboseOption = new Option <bool>(
            "--verbose",
            "Enable verbose logging to the console");

        var logPathOption = new Option <DirectoryInfo>(
            "--log-path",
            "Enable file logging to the specified directory");

        var pathOption = new Option <DirectoryInfo>(
            "--path",
            "Installs the kernelspecs to the specified directory")
                         .ExistingOnly();

        var defaultKernelOption = new Option <string>(
            "--default-kernel",
            description: "The default language for the kernel",
            getDefaultValue: () => "csharp").AddCompletions("fsharp", "csharp", "pwsh");

        var rootCommand = DotnetInteractive();

        rootCommand.AddCommand(Jupyter());
        rootCommand.AddCommand(StdIO());
        rootCommand.AddCommand(NotebookParser());
        rootCommand.AddCommand(HttpServer());

        return(new CommandLineBuilder(rootCommand)
               .UseDefaults()
               .AddMiddleware(async(context, next) =>
        {
            if (context.ParseResult.Errors.Count == 0)
            {
                telemetry.SendFiltered(filter, context.ParseResult);
            }

            // If sentinel does not exist, print the welcome message showing the telemetry notification.
            if (!Telemetry.Telemetry.SkipFirstTimeExperience &&
                !firstTimeUseNoticeSentinel.Exists())
            {
                context.Console.Out.WriteLine();
                context.Console.Out.WriteLine(Telemetry.Telemetry.WelcomeMessage);

                firstTimeUseNoticeSentinel.CreateIfNotExists();
            }

            await next(context);
        })
               .Build());

        RootCommand DotnetInteractive()
        {
            var command = new RootCommand
            {
                Name        = "dotnet-interactive",
                Description = "Interactive programming for .NET."
            };

            command.AddGlobalOption(logPathOption);
            command.AddGlobalOption(verboseOption);

            return(command);
        }

        Command Jupyter()
        {
            var httpPortRangeOption = new Option <HttpPortRange>(
                "--http-port-range",
                parseArgument: result => result.Tokens.Count == 0 ? HttpPortRange.Default : ParsePortRangeOption(result),
                description: "Specifies the range of ports to use to enable HTTP services",
                isDefault: true);

            var jupyterCommand = new Command("jupyter", "Starts dotnet-interactive as a Jupyter kernel")
            {
                defaultKernelOption,
                httpPortRangeOption,
                new Argument <FileInfo>
                {
                    Name        = "connection-file",
                    Description = "The path to a connection file provided by Jupyter"
                }.ExistingOnly()
            };

            jupyterCommand.Handler = CommandHandler.Create <StartupOptions, JupyterOptions, IConsole, InvocationContext, CancellationToken>(JupyterHandler);

            var installCommand = new Command("install", "Install the .NET kernel for Jupyter")
            {
                httpPortRangeOption,
                pathOption
            };

            installCommand.Handler = CommandHandler.Create <IConsole, InvocationContext, HttpPortRange, DirectoryInfo>(InstallHandler);

            jupyterCommand.AddCommand(installCommand);

            return(jupyterCommand);

            async Task <int> JupyterHandler(StartupOptions startupOptions, JupyterOptions options, IConsole console, InvocationContext context, CancellationToken cancellationToken)
            {
                var frontendEnvironment = new HtmlNotebookFrontendEnvironment();
                var kernel = CreateKernel(options.DefaultKernel, frontendEnvironment, startupOptions);

                kernel.Add(
                    new JavaScriptKernel(),
                    new[] { "js" });

                services.AddKernel(kernel);

                kernel.VisitSubkernels(k =>
                {
                    switch (k)
                    {
                    case CSharpKernel csharpKernel:
                        csharpKernel.UseJupyterHelpers();
                        break;

                    case FSharpKernel fsharpKernel:
                        fsharpKernel.UseJupyterHelpers();
                        break;

                    case PowerShellKernel powerShellKernel:
                        powerShellKernel.UseJupyterHelpers();
                        break;
                    }
                });


                var clientSideKernelClient = new SignalRBackchannelKernelClient();

                services.AddSingleton(c => ConnectionInformation.Load(options.ConnectionFile))
                .AddSingleton(clientSideKernelClient)
                .AddSingleton(c =>
                {
                    return(new JupyterRequestContextScheduler(delivery => c.GetRequiredService <JupyterRequestContextHandler>()
                                                              .Handle(delivery)));
                })
                .AddSingleton(c => new JupyterRequestContextHandler(kernel))
                .AddSingleton <IHostedService, Shell>()
                .AddSingleton <IHostedService, Heartbeat>();
                var result = await jupyter(startupOptions, console, startServer, context);

                return(result);
            }

            Task <int> InstallHandler(IConsole console, InvocationContext context, HttpPortRange httpPortRange, DirectoryInfo path)
            {
                var jupyterInstallCommand = new JupyterInstallCommand(console, new JupyterKernelSpecInstaller(console), httpPortRange, path);

                return(jupyterInstallCommand.InvokeAsync());
            }
        }

        Command HttpServer()
        {
            var httpPortOption = new Option <HttpPort>(
                "--http-port",
                description: "Specifies the port on which to enable HTTP services",
                parseArgument: result =>
            {
                if (result.Tokens.Count == 0)
                {
                    return(HttpPort.Auto);
                }

                var source = result.Tokens[0].Value;

                if (source == "*")
                {
                    return(HttpPort.Auto);
                }

                if (!int.TryParse(source, out var portNumber))
                {
                    result.ErrorMessage = "Must specify a port number or *.";
                    return(null);
                }

                return(new HttpPort(portNumber));
            },
                isDefault: true);

            var httpCommand = new Command("http", "Starts dotnet-interactive with kernel functionality exposed over http")
            {
                defaultKernelOption,
                httpPortOption
            };

            httpCommand.Handler = CommandHandler.Create <StartupOptions, KernelHttpOptions, IConsole, InvocationContext>(
                (startupOptions, options, console, context) =>
            {
                var frontendEnvironment = new BrowserFrontendEnvironment();
                var kernel = CreateKernel(options.DefaultKernel, frontendEnvironment, startupOptions);

                kernel.Add(
                    new JavaScriptKernel(),
                    new[] { "js" });

                services.AddKernel(kernel)
                .AddSingleton(new SignalRBackchannelKernelClient());

                onServerStarted ??= () =>
                {
                    console.Out.WriteLine("Application started. Press Ctrl+C to shut down.");
                };
                return(startHttp(startupOptions, console, startServer, context));
            });

            return(httpCommand);
        }

        Command StdIO()
        {
            var httpPortRangeOption = new Option <HttpPortRange>(
                "--http-port-range",
                parseArgument: result => result.Tokens.Count == 0 ? HttpPortRange.Default : ParsePortRangeOption(result),
                description: "Specifies the range of ports to use to enable HTTP services");

            var httpPortOption = new Option <HttpPort>(
                "--http-port",
                description: "Specifies the port on which to enable HTTP services",
                parseArgument: result =>
            {
                if (result.FindResultFor(httpPortRangeOption) is { } conflictingOption)
                {
                    var parsed          = result.Parent as OptionResult;
                    result.ErrorMessage = $"Cannot specify both {conflictingOption.Token.Value} and {parsed.Token.Value} together";
                    return(null);
                }

                if (result.Tokens.Count == 0)
                {
                    return(HttpPort.Auto);
                }

                var source = result.Tokens[0].Value;

                if (source == "*")
                {
                    return(HttpPort.Auto);
                }

                if (!int.TryParse(source, out var portNumber))
                {
                    result.ErrorMessage = "Must specify a port number or *.";
                    return(null);
                }

                return(new HttpPort(portNumber));
            });

            var workingDirOption = new Option <DirectoryInfo>(
                "--working-dir",
                () => new DirectoryInfo(Environment.CurrentDirectory),
                "Working directory to which to change after launching the kernel.");

            var stdIOCommand = new Command(
                "stdio",
                "Starts dotnet-interactive with kernel functionality exposed over standard I/O")
            {
                defaultKernelOption,
                httpPortRangeOption,
                httpPortOption,
                workingDirOption
            };

            stdIOCommand.Handler = CommandHandler.Create <StartupOptions, StdIOOptions, IConsole, InvocationContext>(
                async(startupOptions, options, console, context) =>
            {
                Console.InputEncoding        = Encoding.UTF8;
                Console.OutputEncoding       = Encoding.UTF8;
                Environment.CurrentDirectory = startupOptions.WorkingDir.FullName;

                FrontendEnvironment frontendEnvironment = startupOptions.EnableHttpApi
                        ? new HtmlNotebookFrontendEnvironment()
                        : new BrowserFrontendEnvironment();

                var kernel = CreateKernel(options.DefaultKernel, frontendEnvironment, startupOptions);

                services.AddKernel(kernel);

                kernel = kernel.UseQuitCommand();

                var host = new KernelHost(kernel, new KernelCommandAndEventTextStreamSender(Console.Out),
                                          new MultiplexingKernelCommandAndEventReceiver(
                                              new KernelCommandAndEventTextReceiver(Console.In)));

                if (isVSCode)
                {
                    var vscodeSetup = new VSCodeClientKernelsExtension();
                    await vscodeSetup.OnLoadAsync(kernel);
                }

                if (startupOptions.EnableHttpApi)
                {
                    var clientSideKernelClient = new SignalRBackchannelKernelClient();

                    services.AddSingleton(clientSideKernelClient);

                    if (isVSCode)
                    {
                        ((HtmlNotebookFrontendEnvironment)frontendEnvironment).RequiresAutomaticBootstrapping = false;
                    }
                    else
                    {
                        kernel.Add(
                            new JavaScriptKernel(clientSideKernelClient),
                            new[] { "js" });
                    }


                    onServerStarted ??= () =>
                    {
                        var _ = host.ConnectAsync();
                    };
                    await startHttp(startupOptions, console, startServer, context);
                }
                else
                {
                    if (!isVSCode)
                    {
                        await host.CreateProxyKernelOnDefaultConnectorAsync(new KernelInfo("javascript", "javascript")
                        {
                            Aliases        = new[] { "js" },
                            DestinationUri = new Uri("kernel://webview/javascript")
                        });
                    }

                    await startKernelHost(startupOptions, host, console);
                }

                return(0);
            });

            return(stdIOCommand);
        }
Ejemplo n.º 24
0
        internal static int ProcessArgs(string[] args, TimeSpan startupTime, ITelemetry telemetryClient = null)
        {
            Dictionary <string, double> performanceData = new Dictionary <string, double>();

            PerformanceLogEventSource.Log.BuiltInCommandParserStart();
            Stopwatch parseStartTime = Stopwatch.StartNew();
            var       parseResult    = Parser.Instance.Parse(args);

            performanceData.Add("Parse Time", parseStartTime.Elapsed.TotalMilliseconds);
            PerformanceLogEventSource.Log.BuiltInCommandParserStop();

            using (IFirstTimeUseNoticeSentinel disposableFirstTimeUseNoticeSentinel =
                       new FirstTimeUseNoticeSentinel())
            {
                IFirstTimeUseNoticeSentinel firstTimeUseNoticeSentinel = disposableFirstTimeUseNoticeSentinel;
                IAspNetCertificateSentinel  aspNetCertificateSentinel  = new AspNetCertificateSentinel();
                IFileSentinel toolPathSentinel = new FileSentinel(
                    new FilePath(
                        Path.Combine(
                            CliFolderPathCalculator.DotnetUserProfileFolderPath,
                            ToolPathSentinelFileName)));
                if (parseResult.ValueForOption <bool>(Parser.DiagOption) && parseResult.IsDotnetBuiltInCommand())
                {
                    Environment.SetEnvironmentVariable(CommandContext.Variables.Verbose, bool.TrueString);
                    CommandContext.SetVerbose(true);
                    Reporter.Reset();
                }
                if (parseResult.HasOption(Parser.VersionOption) && parseResult.IsTopLevelDotnetCommand())
                {
                    CommandLineInfo.PrintVersion();
                    return(0);
                }
                else if (parseResult.HasOption(Parser.InfoOption) && parseResult.IsTopLevelDotnetCommand())
                {
                    CommandLineInfo.PrintInfo();
                    return(0);
                }
                else if (parseResult.HasOption("-h") && parseResult.IsTopLevelDotnetCommand())
                {
                    HelpCommand.PrintHelp();
                    return(0);
                }
                else if (parseResult.Directives.Count() > 0)
                {
                    return(parseResult.Invoke());
                }
                else
                {
                    PerformanceLogEventSource.Log.FirstTimeConfigurationStart();

                    var environmentProvider = new EnvironmentProvider();

                    bool generateAspNetCertificate =
                        environmentProvider.GetEnvironmentVariableAsBool("DOTNET_GENERATE_ASPNET_CERTIFICATE", defaultValue: true);
                    bool telemetryOptout =
                        environmentProvider.GetEnvironmentVariableAsBool("DOTNET_CLI_TELEMETRY_OPTOUT", defaultValue: false);
                    bool addGlobalToolsToPath =
                        environmentProvider.GetEnvironmentVariableAsBool("DOTNET_ADD_GLOBAL_TOOLS_TO_PATH", defaultValue: true);
                    bool nologo =
                        environmentProvider.GetEnvironmentVariableAsBool("DOTNET_NOLOGO", defaultValue: false);

                    ReportDotnetHomeUsage(environmentProvider);

                    var isDotnetBeingInvokedFromNativeInstaller = false;
                    if (parseResult.CommandResult.Command.Name.Equals(Parser.InstallSuccessCommand.Name))
                    {
                        aspNetCertificateSentinel  = new NoOpAspNetCertificateSentinel();
                        firstTimeUseNoticeSentinel = new NoOpFirstTimeUseNoticeSentinel();
                        toolPathSentinel           = new NoOpFileSentinel(exists: false);
                        isDotnetBeingInvokedFromNativeInstaller = true;
                    }

                    var dotnetFirstRunConfiguration = new DotnetFirstRunConfiguration(
                        generateAspNetCertificate: generateAspNetCertificate,
                        telemetryOptout: telemetryOptout,
                        addGlobalToolsToPath: addGlobalToolsToPath,
                        nologo: nologo);

                    ConfigureDotNetForFirstTimeUse(
                        firstTimeUseNoticeSentinel,
                        aspNetCertificateSentinel,
                        toolPathSentinel,
                        isDotnetBeingInvokedFromNativeInstaller,
                        dotnetFirstRunConfiguration,
                        environmentProvider,
                        performanceData);
                    PerformanceLogEventSource.Log.FirstTimeConfigurationStop();
                }

                PerformanceLogEventSource.Log.TelemetryRegistrationStart();

                if (telemetryClient == null)
                {
                    telemetryClient = new Telemetry.Telemetry(firstTimeUseNoticeSentinel);
                }
                TelemetryEventEntry.Subscribe(telemetryClient.TrackEvent);
                TelemetryEventEntry.TelemetryFilter = new TelemetryFilter(Sha256Hasher.HashWithNormalizedCasing);

                PerformanceLogEventSource.Log.TelemetryRegistrationStop();
            }

            if (CommandContext.IsVerbose())
            {
                Console.WriteLine($"Telemetry is: {(telemetryClient.Enabled ? "Enabled" : "Disabled")}");
            }
            PerformanceLogEventSource.Log.TelemetrySaveIfEnabledStart();
            performanceData.Add("Startup Time", startupTime.TotalMilliseconds);
            TelemetryEventEntry.SendFiltered(Tuple.Create(parseResult, performanceData));
            PerformanceLogEventSource.Log.TelemetrySaveIfEnabledStop();

            var topLevelCommands = new string[] { "dotnet", parseResult.RootSubCommandResult() }.Concat(Parser.DiagOption.Aliases);
            int exitCode;

            if (parseResult.CommandResult.Command.Name.Equals("dotnet") && string.IsNullOrEmpty(parseResult.ValueForArgument <string>(Parser.DotnetSubCommand)))
            {
                exitCode = 0;
            }
            else if (BuiltInCommandsCatalog.Commands.TryGetValue(parseResult.RootSubCommandResult(), out var builtIn))
            {
                PerformanceLogEventSource.Log.BuiltInCommandStart();
                exitCode = builtIn.Command(args.Where(t => !topLevelCommands.Contains(t)).ToArray());
                PerformanceLogEventSource.Log.BuiltInCommandStop();
            }
            else
            {
                PerformanceLogEventSource.Log.ExtensibleCommandResolverStart();
                var resolvedCommand = CommandFactoryUsingResolver.Create(
                    "dotnet-" + parseResult.ValueForArgument <string>(Parser.DotnetSubCommand),
                    args.Where(t => !topLevelCommands.Contains(t)).ToArray(),
                    FrameworkConstants.CommonFrameworks.NetStandardApp15);
                PerformanceLogEventSource.Log.ExtensibleCommandResolverStop();

                PerformanceLogEventSource.Log.ExtensibleCommandStart();
                var result = resolvedCommand.Execute();
                PerformanceLogEventSource.Log.ExtensibleCommandStop();

                exitCode = result.ExitCode;
            }

            PerformanceLogEventSource.Log.TelemetryClientFlushStart();
            telemetryClient.Flush();
            PerformanceLogEventSource.Log.TelemetryClientFlushStop();

            return(exitCode);
        }
Ejemplo n.º 25
0
        public static Parser Create(
            IServiceCollection services,
            StartServer startServer = null,
            Install install         = null,
            Demo demo            = null,
            TryGitHub tryGithub  = null,
            Pack pack            = null,
            Verify verify        = null,
            Publish publish      = null,
            ITelemetry telemetry = null,
            IFirstTimeUseNoticeSentinel firstTimeUseNoticeSentinel = null)
        {
            if (services == null)
            {
                throw new ArgumentNullException(nameof(services));
            }

            startServer ??= (startupOptions, invocationContext) =>
            Program.ConstructWebHost(startupOptions).Run();

            demo ??= DemoCommand.Do;

            tryGithub ??= (repo, console) =>
            GitHubHandler.Handler(repo,
                                  console,
                                  new GitHubRepoLocator());

            verify ??= VerifyCommand.Do;

            publish ??= (options, console, startupOptions) =>
            PublishCommand.Do(options,
                              console,
                              startupOptions);

            pack ??= PackCommand.Do;

            // Setup first time use notice sentinel.
            firstTimeUseNoticeSentinel ??=
            new FirstTimeUseNoticeSentinel(VersionSensor.Version().AssemblyInformationalVersion);

            // Setup telemetry.
            telemetry ??= new Telemetry(
                VersionSensor.Version().AssemblyInformationalVersion,
                firstTimeUseNoticeSentinel);
            var filter = new TelemetryFilter(Sha256Hasher.HashWithNormalizedCasing);
            Action <ParseResult> track = o => telemetry.SendFiltered(filter, o);

            var dirArgument = new Argument <FileSystemDirectoryAccessor>(result =>
            {
                var directory = result.Tokens
                                .Select(t => t.Value)
                                .FirstOrDefault();

                if (!string.IsNullOrEmpty(directory) &&
                    !Directory.Exists(directory))
                {
                    result.ErrorMessage = $"Directory does not exist: {directory}";
                    return(null);
                }

                return(new FileSystemDirectoryAccessor(
                           directory ??
                           Directory.GetCurrentDirectory()));
            }, isDefault: true)
            {
                Name        = "root-directory",
                Arity       = ArgumentArity.ZeroOrOne,
                Description = "The root directory for your documentation"
            };

            var rootCommand = StartInTryMode();

            rootCommand.AddCommand(StartInHostedMode());
            rootCommand.AddCommand(Demo());
            rootCommand.AddCommand(GitHub());
            rootCommand.AddCommand(Install());
            rootCommand.AddCommand(Pack());
            rootCommand.AddCommand(Verify());
            rootCommand.AddCommand(Publish());

            return(new CommandLineBuilder(rootCommand)
                   .UseDefaults()
                   .UseMiddleware(async(context, next) =>
            {
                if (context.ParseResult.Errors.Count == 0)
                {
                    telemetry.SendFiltered(filter, context.ParseResult);
                }

                // If sentinel does not exist, print the welcome message showing the telemetry notification.
                if (!firstTimeUseNoticeSentinel.Exists() && !Telemetry.SkipFirstTimeExperience)
                {
                    context.Console.Out.WriteLine();
                    context.Console.Out.WriteLine(Telemetry.WelcomeMessage);

                    firstTimeUseNoticeSentinel.CreateIfNotExists();
                }

                if (context.ParseResult.Directives.Contains("debug") &&
                    !(Clock.Current is VirtualClock))
                {
                    VirtualClock.Start();
                }

                await next(context);
            })
                   .Build());

            RootCommand StartInTryMode()
            {
                var command = new RootCommand
                {
                    Name        = "dotnet-try",
                    Description = "Interactive documentation in your browser"
                };

                command.AddArgument(dirArgument);

                command.AddOption(new Option(
                                      "--add-package-source",
                                      "Specify an additional NuGet package source")
                {
                    Argument = new Argument <PackageSource>(() => new PackageSource(Directory.GetCurrentDirectory()))
                    {
                        Name = "NuGet source"
                    }
                });

                command.AddOption(new Option(
                                      "--package",
                                      "Specify a Try .NET package or path to a .csproj to run code samples with")
                {
                    Argument = new Argument <string>
                    {
                        Name = "name or .csproj"
                    }
                });

                command.AddOption(new Option(
                                      "--package-version",
                                      "Specify a Try .NET package version to use with the --package option")
                {
                    Argument = new Argument <string>
                    {
                        Name = "version"
                    }
                });

                command.AddOption(new Option <Uri>(
                                      "--uri",
                                      "Specify a URL or a relative path to a Markdown file"));

                command.AddOption(new Option <bool>(
                                      "--enable-preview-features",
                                      "Enable preview features"));

                command.AddOption(new Option(
                                      "--log-path",
                                      "Enable file logging to the specified directory")
                {
                    Argument = new Argument <DirectoryInfo>
                    {
                        Name = "dir"
                    }
                });

                command.AddOption(new Option <bool>(
                                      "--verbose",
                                      "Enable verbose logging to the console"));

                var portArgument = new Argument <ushort>();

                portArgument.AddValidator(symbolResult =>
                {
                    if (symbolResult.Tokens
                        .Select(t => t.Value)
                        .Count(value => !ushort.TryParse(value, out _)) > 0)
                    {
                        return("Invalid argument for --port option");
                    }

                    return(null);
                });

                command.AddOption(new Option(
                                      "--port",
                                      "Specify the port for dotnet try to listen on")
                {
                    Argument = portArgument
                });

                command.Handler = CommandHandler.Create <InvocationContext, StartupOptions>((context, options) =>
                {
                    services.AddSingleton(_ => PackageRegistry.CreateForTryMode(
                                              options.RootDirectory,
                                              options.AddPackageSource));

                    startServer(options, context);
                });

                return(command);
            }

            Command StartInHostedMode()
            {
                var command = new Command("hosted")
                {
                    new Option <string>(
                        "--id",
                        description: "A unique id for the agent instance (e.g. its development environment id).",
                        getDefaultValue: () => Environment.MachineName),
                    new Option <bool>(
                        "--production",
                        "Specifies whether the agent is being run using production resources"),
                    new Option <bool>(
                        "--language-service",
                        "Specifies whether the agent is being run in language service-only mode"),
                    new Option <string>(
                        new[]
                    {
                        "-k",
                        "--key"
                    },
                        "The encryption key"),
                    new Option <string>(
                        new[]
                    {
                        "--ai-key",
                        "--application-insights-key"
                    },
                        "Application Insights key."),
                    new Option <string>(
                        "--region-id",
                        "A unique id for the agent region"),
                    new Option <bool>(
                        "--log-to-file",
                        "Writes a log file")
                };

                command.Description = "Starts the Try .NET agent";

                command.IsHidden = true;

                command.Handler = CommandHandler.Create <InvocationContext, StartupOptions>((context, options) =>
                {
                    services.AddSingleton(_ => PackageRegistry.CreateForHostedMode());
                    services.AddSingleton(c => new MarkdownProject(c.GetRequiredService <PackageRegistry>()));
                    startServer(options, context);
                });

                return(command);
            }

            Command Demo()
            {
                var demoCommand = new Command(
                    "demo",
                    "Learn how to create Try .NET content with an interactive demo")
                {
                    new Option <DirectoryInfo>(
                        "--output",
                        description: "Where should the demo project be written to?",
                        getDefaultValue: () => new DirectoryInfo(Directory.GetCurrentDirectory()))
                };

                demoCommand.Handler = CommandHandler.Create <DemoOptions, InvocationContext>((options, context) => { demo(options, context.Console, startServer, context); });

                return(demoCommand);
            }

            Command GitHub()
            {
                var argument = new Argument <string>
                {
                    // System.CommandLine parameter binding does lookup by name,
                    // so name the argument after the github command's string param
                    Name = nameof(TryGitHubOptions.Repo)
                };

                var github = new Command("github", "Try a GitHub repo")
                {
                    argument
                };

                github.IsHidden = true;

                github.Handler = CommandHandler.Create <TryGitHubOptions, IConsole>((repo, console) => tryGithub(repo, console));

                return(github);
            }

            Command Install()
            {
                var installCommand = new Command("install", "Install a Try .NET package")
                {
                    new Argument <string>
                    {
                        Name  = nameof(InstallOptions.PackageName),
                        Arity = ArgumentArity.ExactlyOne
                    },
                    new Option <PackageSource>("--add-source")
                };

                installCommand.IsHidden = true;

                installCommand.Handler = CommandHandler.Create <InstallOptions, IConsole>((options, console) => install(options, console));

                return(installCommand);
            }

            Command Pack()
            {
                var packCommand = new Command("pack", "Create a Try .NET package")
                {
                    new Argument <DirectoryInfo>
                    {
                        Name = nameof(PackOptions.PackTarget)
                    },
                    new Option <string>("--version", "The version of the Try .NET package"),
                    new Option <bool>("--enable-wasm", "Enables web assembly code execution")
                };

                packCommand.IsHidden = true;

                packCommand.Handler = CommandHandler.Create <PackOptions, IConsole>(
                    (options, console) =>
                {
                    return(pack(options, console));
                });

                return(packCommand);
            }

            Command Verify()
            {
                var verifyCommand = new Command("verify", "Verify Markdown files found under the root directory.")
                {
                    dirArgument
                };

                verifyCommand.Handler = CommandHandler.Create <VerifyOptions, IConsole, StartupOptions>(
                    (options, console, startupOptions) =>
                {
                    return(verify(options, console, startupOptions));
                });

                return(verifyCommand);
            }

            Command Publish()
            {
                var publishCommand = new Command("publish", "Publish code from sample projects found under the root directory into Markdown files in the target directory")
                {
                    new Option <PublishFormat>(
                        "--format",
                        description: "Format of the files to publish",
                        getDefaultValue: () => PublishFormat.Markdown),
                    new Option <IDirectoryAccessor>(
                        "--target-directory",
                        description: "The path where the output files should go. This can be the same as the root directory, which will overwrite files in place.",
                        parseArgument: result =>
                    {
                        var directory = result.Tokens
                                        .Select(t => t.Value)
                                        .Single();

                        return(new FileSystemDirectoryAccessor(directory));
                    }
                        ),
                    dirArgument
                };

                publishCommand.Handler = CommandHandler.Create <PublishOptions, IConsole, StartupOptions>(
                    (options, console, startupOptions) => publish(options, console, startupOptions));

                return(publishCommand);
            }
        }
Ejemplo n.º 26
0
        internal static int ProcessArgs(string[] args, ITelemetry telemetryClient = null)
        {
            // CommandLineApplication is a bit restrictive, so we parse things ourselves here. Individual apps should use CLA.

            var success = true;
            var command = string.Empty;
            var lastArg = 0;
            var cliFallbackFolderPathCalculator = new CliFallbackFolderPathCalculator();

            using (INuGetCacheSentinel nugetCacheSentinel = new NuGetCacheSentinel(cliFallbackFolderPathCalculator))
                using (IFirstTimeUseNoticeSentinel disposableFirstTimeUseNoticeSentinel =
                           new FirstTimeUseNoticeSentinel(cliFallbackFolderPathCalculator))
                {
                    IFirstTimeUseNoticeSentinel firstTimeUseNoticeSentinel = disposableFirstTimeUseNoticeSentinel;
                    for (; lastArg < args.Length; lastArg++)
                    {
                        if (IsArg(args[lastArg], "d", "diagnostics"))
                        {
                            Environment.SetEnvironmentVariable(CommandContext.Variables.Verbose, bool.TrueString);
                            CommandContext.SetVerbose(true);
                        }
                        else if (IsArg(args[lastArg], "version"))
                        {
                            PrintVersion();
                            return(0);
                        }
                        else if (IsArg(args[lastArg], "info"))
                        {
                            PrintInfo();
                            return(0);
                        }
                        else if (IsArg(args[lastArg], "h", "help") ||
                                 args[lastArg] == "-?" ||
                                 args[lastArg] == "/?")
                        {
                            HelpCommand.PrintHelp();
                            return(0);
                        }
                        else if (args[lastArg].StartsWith("-"))
                        {
                            Reporter.Error.WriteLine($"Unknown option: {args[lastArg]}");
                            success = false;
                        }
                        else
                        {
                            // It's the command, and we're done!
                            command = args[lastArg];

                            if (IsDotnetBeingInvokedFromNativeInstaller(command))
                            {
                                firstTimeUseNoticeSentinel = new NoOpFirstTimeUseNoticeSentinel();
                            }

                            ConfigureDotNetForFirstTimeUse(
                                nugetCacheSentinel,
                                firstTimeUseNoticeSentinel,
                                cliFallbackFolderPathCalculator);

                            break;
                        }
                    }
                    if (!success)
                    {
                        HelpCommand.PrintHelp();
                        return(1);
                    }

                    if (telemetryClient == null)
                    {
                        telemetryClient = new Telemetry.Telemetry(firstTimeUseNoticeSentinel);
                    }
                    TelemetryEventEntry.Subscribe(telemetryClient.TrackEvent);
                    TelemetryEventEntry.TelemetryFilter = new TelemetryFilter();
                }

            IEnumerable <string> appArgs =
                (lastArg + 1) >= args.Length
                ? Enumerable.Empty <string>()
                : args.Skip(lastArg + 1).ToArray();

            if (CommandContext.IsVerbose())
            {
                Console.WriteLine($"Telemetry is: {(telemetryClient.Enabled ? "Enabled" : "Disabled")}");
            }

            if (string.IsNullOrEmpty(command))
            {
                command = "help";
            }

            TelemetryEventEntry.TrackEvent(command, null, null);

            int exitCode;

            if (BuiltInCommandsCatalog.Commands.TryGetValue(command, out var builtIn))
            {
                TelemetryEventEntry.SendFiltered(Parser.Instance.ParseFrom($"dotnet {command}", appArgs.ToArray()));
                exitCode = builtIn.Command(appArgs.ToArray());
            }
            else
            {
                CommandResult result = Command.Create(
                    "dotnet-" + command,
                    appArgs,
                    FrameworkConstants.CommonFrameworks.NetStandardApp15)
                                       .Execute();
                exitCode = result.ExitCode;
            }
            return(exitCode);
        }
Ejemplo n.º 27
0
        public static Parser Create(
            IServiceCollection services,
            StartServer startServer = null,
            Jupyter jupyter         = null,
            StartStdIO startStdIO   = null,
            ITelemetry telemetry    = null,
            IFirstTimeUseNoticeSentinel firstTimeUseNoticeSentinel = null)
        {
            if (services == null)
            {
                throw new ArgumentNullException(nameof(services));
            }

            startServer ??= (startupOptions, invocationContext) =>
            Program.ConstructWebHost(startupOptions).Run();

            jupyter ??= JupyterCommand.Do;

            startStdIO ??= StdIOCommand.Do;

            // Setup first time use notice sentinel.
            firstTimeUseNoticeSentinel ??= new FirstTimeUseNoticeSentinel(VersionSensor.Version().AssemblyInformationalVersion);

            // Setup telemetry.
            telemetry ??= new Telemetry.Telemetry(
                VersionSensor.Version().AssemblyInformationalVersion,
                firstTimeUseNoticeSentinel);
            var filter = new TelemetryFilter(Sha256Hasher.HashWithNormalizedCasing);

            var verboseOption = new Option <bool>(
                "--verbose",
                "Enable verbose logging to the console");

            var httpPortOption = new Option <HttpPort>(
                "--http-port",
                description: "Specifies the port on which to enable HTTP services",
                parseArgument: result =>
            {
                var source = result.Tokens[0].Value;

                if (source == "*")
                {
                    return(HttpPort.Auto);
                }

                if (!int.TryParse(source, out var portNumber))
                {
                    result.ErrorMessage = "Must specify a port number or *.";
                    return(null);
                }

                return(new HttpPort(portNumber));
            });

            var httpPortRangeOption = new Option <PortRange>(
                "--http-port-range",
                parseArgument: result =>
            {
                if (result.Parent.Parent.Children.FirstOrDefault(c => c.Symbol == httpPortOption) is OptionResult conflictingOption)
                {
                    var parsed          = result.Parent as OptionResult;
                    result.ErrorMessage = $"Cannot specify both {conflictingOption.Token.Value} and {parsed.Token.Value} together";
                    return(null);
                }

                var source = result.Tokens[0].Value;

                if (string.IsNullOrWhiteSpace(source))
                {
                    result.ErrorMessage = "Must specify a port range";
                    return(null);
                }

                var parts = source.Split(new[] { "-" }, StringSplitOptions.RemoveEmptyEntries);

                if (parts.Length != 2)
                {
                    result.ErrorMessage = "Must specify a port range";
                    return(null);
                }

                if (!int.TryParse(parts[0], out var start) || !int.TryParse(parts[1], out var end))
                {
                    result.ErrorMessage = "Must specify a port range as StartPort-EndPort";
                    return(null);
                }

                if (start > end)
                {
                    result.ErrorMessage = "Start port must be lower then end port";
                    return(null);
                }

                var pr = new PortRange(start, end);
                return(pr);
            },
                description: "Specifies the range of port to use to enable HTTP services");

            var logPathOption = new Option <DirectoryInfo>(
                "--log-path",
                "Enable file logging to the specified directory");

            var pathOption = new Option <DirectoryInfo>(
                "--path",
                "Installs the kernelspecs to the specified directory")
                             .ExistingOnly();

            var defaultKernelOption = new Option <string>(
                "--default-kernel",
                description: "The default language for the kernel",
                getDefaultValue: () => "csharp");

            var rootCommand = DotnetInteractive();

            rootCommand.AddCommand(Jupyter());
            rootCommand.AddCommand(StdIO());
            rootCommand.AddCommand(HttpServer());

            return(new CommandLineBuilder(rootCommand)
                   .UseDefaults()
                   .UseMiddleware(async(context, next) =>
            {
                if (context.ParseResult.Errors.Count == 0)
                {
                    telemetry.SendFiltered(filter, context.ParseResult);
                }

                // If sentinel does not exist, print the welcome message showing the telemetry notification.
                if (!firstTimeUseNoticeSentinel.Exists() && !Telemetry.Telemetry.SkipFirstTimeExperience)
                {
                    context.Console.Out.WriteLine();
                    context.Console.Out.WriteLine(Telemetry.Telemetry.WelcomeMessage);

                    firstTimeUseNoticeSentinel.CreateIfNotExists();
                }

                await next(context);
            })
                   .Build());

            RootCommand DotnetInteractive()
            {
                var command = new RootCommand
                {
                    Name        = "dotnet-interactive",
                    Description = "Interactive programming for .NET."
                };

                command.AddOption(logPathOption);
                command.AddOption(verboseOption);

                return(command);
            }

            Command Jupyter()
            {
                var command = new Command("jupyter", "Starts dotnet-interactive as a Jupyter kernel")
                {
                    defaultKernelOption,
                    logPathOption,
                    verboseOption,
                    httpPortRangeOption,
                    new Argument <FileInfo>
                    {
                        Name = "connection-file"
                    }.ExistingOnly()
                };

                command.Handler = CommandHandler.Create <StartupOptions, JupyterOptions, IConsole, InvocationContext>(JupyterHandler);

                var installCommand = new Command("install", "Install the .NET kernel for Jupyter")
                {
                    logPathOption,
                    verboseOption,
                    httpPortRangeOption,
                    pathOption
                };

                installCommand.Handler = CommandHandler.Create <IConsole, InvocationContext, PortRange, DirectoryInfo>(InstallHandler);

                command.AddCommand(installCommand);

                return(command);

                Task <int> JupyterHandler(StartupOptions startupOptions, JupyterOptions options, IConsole console, InvocationContext context)
                {
                    services = RegisterKernelInServiceCollection(services, startupOptions, options.DefaultKernel);

                    services.AddSingleton(c => ConnectionInformation.Load(options.ConnectionFile))
                    .AddSingleton <FrontendEnvironment>(c => c.GetService <BrowserFrontendEnvironment>())
                    .AddSingleton(c =>
                    {
                        return(CommandScheduler.Create <JupyterRequestContext>(delivery => c.GetRequiredService <ICommandHandler <JupyterRequestContext> >()
                                                                               .Trace()
                                                                               .Handle(delivery)));
                    })
                    .AddSingleton(c => new JupyterRequestContextHandler(
                                      c.GetRequiredService <IKernel>())
                                  .Trace())
                    .AddSingleton <IHostedService, Shell>()
                    .AddSingleton <IHostedService, Heartbeat>();

                    return(jupyter(startupOptions, console, startServer, context));
                }

                Task <int> InstallHandler(IConsole console, InvocationContext context, PortRange httpPortRange, DirectoryInfo location) =>
                new JupyterInstallCommand(console, jupyterKernelSpecInstaller: new JupyterKernelSpecInstaller(console), httpPortRange, location).InvokeAsync();
            }

            Command HttpServer()
            {
                var command = new Command("http", "Starts dotnet-interactive with kernel functionality exposed over http")
                {
                    defaultKernelOption,
                    httpPortOption,
                    logPathOption,
                    httpPortRangeOption
                };

                command.Handler = CommandHandler.Create <StartupOptions, KernelHttpOptions, IConsole, InvocationContext>(
                    (startupOptions, options, console, context) =>
                {
                    if (startupOptions.HttpPort == null && startupOptions.HttpPortRange == null)
                    {
                        startupOptions.HttpPort = HttpPort.Auto;
                    }

                    RegisterKernelInServiceCollection(services, startupOptions, options.DefaultKernel);

                    return(jupyter(startupOptions, console, startServer, context));
                });

                return(command);
            }

            Command StdIO()
            {
                var command = new Command(
                    "stdio",
                    "Starts dotnet-interactive with kernel functionality exposed over standard I/O")
                {
                    defaultKernelOption,
                    logPathOption,
                    httpPortOption,
                    httpPortRangeOption
                };

                command.Handler = CommandHandler.Create <StartupOptions, StdIOOptions, IConsole, InvocationContext>(
                    (startupOptions, options, console, context) =>
                {
                    if (startupOptions.EnableHttpApi)
                    {
                        RegisterKernelInServiceCollection(services, startupOptions, options.DefaultKernel,
                                                          kernel => StdIOCommand.CreateServer(kernel, console));
                        startServer?.Invoke(startupOptions, context);
                        return(Task.FromResult(0));
                    }

                    return(startStdIO(
                               startupOptions,
                               CreateKernel(options.DefaultKernel, new BrowserFrontendEnvironment(), startupOptions, null),
                               console));
                });

                return(command);
            }
        }
Ejemplo n.º 28
0
        public static Parser Create(
            IServiceCollection services,
            StartServer startServer = null,
            Jupyter jupyter         = null,
            StartStdIO startStdIO   = null,
            StartHttp startHttp     = null,
            ITelemetry telemetry    = null,
            IFirstTimeUseNoticeSentinel firstTimeUseNoticeSentinel = null)
        {
            if (services == null)
            {
                throw new ArgumentNullException(nameof(services));
            }
            var disposeOnQuit = new CompositeDisposable();

            startServer ??= (startupOptions, invocationContext) =>
            {
                var webHost = Program.ConstructWebHost(startupOptions);
                disposeOnQuit.Add(webHost);
                webHost.Run();
            };

            jupyter ??= JupyterCommand.Do;

            startStdIO ??= StdIOCommand.Do;

            startHttp ??= HttpCommand.Do;

            // Setup first time use notice sentinel.
            firstTimeUseNoticeSentinel ??= new FirstTimeUseNoticeSentinel(VersionSensor.Version().AssemblyInformationalVersion);

            // Setup telemetry.
            telemetry ??= new Telemetry.Telemetry(
                VersionSensor.Version().AssemblyInformationalVersion,
                firstTimeUseNoticeSentinel);
            var filter = new TelemetryFilter(Sha256Hasher.HashWithNormalizedCasing);

            var verboseOption = new Option <bool>(
                "--verbose",
                "Enable verbose logging to the console");

            var logPathOption = new Option <DirectoryInfo>(
                "--log-path",
                "Enable file logging to the specified directory");

            var pathOption = new Option <DirectoryInfo>(
                "--path",
                "Installs the kernelspecs to the specified directory")
                             .ExistingOnly();

            var defaultKernelOption = new Option <string>(
                "--default-kernel",
                description: "The default language for the kernel",
                getDefaultValue: () => "csharp");

            var rootCommand = DotnetInteractive();

            rootCommand.AddCommand(Jupyter());
            rootCommand.AddCommand(StdIO());
            rootCommand.AddCommand(HttpServer());

            return(new CommandLineBuilder(rootCommand)
                   .UseDefaults()
                   .UseMiddleware(async(context, next) =>
            {
                if (context.ParseResult.Errors.Count == 0)
                {
                    telemetry.SendFiltered(filter, context.ParseResult);
                }

                // If sentinel does not exist, print the welcome message showing the telemetry notification.
                if (!firstTimeUseNoticeSentinel.Exists() && !Telemetry.Telemetry.SkipFirstTimeExperience)
                {
                    context.Console.Out.WriteLine();
                    context.Console.Out.WriteLine(Telemetry.Telemetry.WelcomeMessage);

                    firstTimeUseNoticeSentinel.CreateIfNotExists();
                }

                await next(context);
            })
                   .Build());

            RootCommand DotnetInteractive()
            {
                var command = new RootCommand
                {
                    Name        = "dotnet-interactive",
                    Description = "Interactive programming for .NET."
                };

                command.AddOption(logPathOption);
                command.AddOption(verboseOption);

                return(command);
            }

            Command Jupyter()
            {
                var httpPortRangeOption = new Option <HttpPortRange>(
                    "--http-port-range",
                    parseArgument: result => result.Tokens.Count == 0 ? HttpPortRange.Default : ParsePortRangeOption(result),
                    description: "Specifies the range of ports to use to enable HTTP services",
                    isDefault: true);

                var command = new Command("jupyter", "Starts dotnet-interactive as a Jupyter kernel")
                {
                    defaultKernelOption,
                    logPathOption,
                    verboseOption,
                    httpPortRangeOption,
                    new Argument <FileInfo>
                    {
                        Name = "connection-file"
                    }.ExistingOnly()
                };

                command.Handler = CommandHandler.Create <StartupOptions, JupyterOptions, IConsole, InvocationContext, CancellationToken>(JupyterHandler);

                var installCommand = new Command("install", "Install the .NET kernel for Jupyter")
                {
                    httpPortRangeOption,
                    logPathOption,
                    verboseOption,
                    pathOption
                };

                installCommand.Handler = CommandHandler.Create <IConsole, InvocationContext, HttpPortRange, DirectoryInfo>(InstallHandler);

                command.AddCommand(installCommand);

                return(command);

                Task <int> JupyterHandler(StartupOptions startupOptions, JupyterOptions options, IConsole console, InvocationContext context, CancellationToken cancellationToken)
                {
                    services = RegisterKernelInServiceCollection(
                        services,
                        startupOptions,
                        options.DefaultKernel,
                        serviceCollection =>
                    {
                        serviceCollection.AddSingleton(_ => new HtmlNotebookFrontedEnvironment());
                        serviceCollection.AddSingleton <FrontendEnvironment>(c =>
                                                                             c.GetService <HtmlNotebookFrontedEnvironment>());
                    });

                    services.AddSingleton(c => ConnectionInformation.Load(options.ConnectionFile))
                    .AddSingleton(c =>
                    {
                        return(CommandScheduler.Create <JupyterRequestContext>(delivery => c.GetRequiredService <ICommandHandler <JupyterRequestContext> >()
                                                                               .Trace()
                                                                               .Handle(delivery)));
                    })
                    .AddSingleton(c => new JupyterRequestContextHandler(
                                      c.GetRequiredService <IKernel>())
                                  .Trace())
                    .AddSingleton <IHostedService, Shell>()
                    .AddSingleton <IHostedService, Heartbeat>();

                    return(jupyter(startupOptions, console, startServer, context));
                }

                Task <int> InstallHandler(IConsole console, InvocationContext context, HttpPortRange httpPortRange, DirectoryInfo path)
                {
                    var jupyterInstallCommand = new JupyterInstallCommand(console, new JupyterKernelSpecInstaller(console), httpPortRange, path);

                    return(jupyterInstallCommand.InvokeAsync());
                }
            }

            Command HttpServer()
            {
                var httpPortOption = new Option <HttpPort>(
                    "--http-port",
                    description: "Specifies the port on which to enable HTTP services",
                    parseArgument: result =>
                {
                    if (result.Tokens.Count == 0)
                    {
                        return(HttpPort.Auto);
                    }

                    var source = result.Tokens[0].Value;

                    if (source == "*")
                    {
                        return(HttpPort.Auto);
                    }

                    if (!int.TryParse(source, out var portNumber))
                    {
                        result.ErrorMessage = "Must specify a port number or *.";
                        return(null);
                    }

                    return(new HttpPort(portNumber));
                },
                    isDefault: true);

                var command = new Command("http", "Starts dotnet-interactive with kernel functionality exposed over http")
                {
                    defaultKernelOption,
                    httpPortOption,
                    logPathOption
                };

                command.Handler = CommandHandler.Create <StartupOptions, KernelHttpOptions, IConsole, InvocationContext>(
                    (startupOptions, options, console, context) =>
                {
                    RegisterKernelInServiceCollection(
                        services,
                        startupOptions,
                        options.DefaultKernel,
                        serviceCollection =>
                    {
                        serviceCollection.AddSingleton(_ =>
                        {
                            var frontendEnvironment = new BrowserFrontendEnvironment();
                            return(frontendEnvironment);
                        });
                        serviceCollection.AddSingleton <FrontendEnvironment>(c => c.GetRequiredService <BrowserFrontendEnvironment>());
                    });
                    return(startHttp(startupOptions, console, startServer, context));
                });

                return(command);
            }

            Command StdIO()
            {
                var httpPortRangeOption = new Option <HttpPortRange>(
                    "--http-port-range",
                    parseArgument: result => result.Tokens.Count == 0 ? HttpPortRange.Default : ParsePortRangeOption(result),
                    description: "Specifies the range of ports to use to enable HTTP services");

                var command = new Command(
                    "stdio",
                    "Starts dotnet-interactive with kernel functionality exposed over standard I/O")
                {
                    defaultKernelOption,
                    httpPortRangeOption
                };

                command.Handler = CommandHandler.Create <StartupOptions, StdIOOptions, IConsole, InvocationContext, CancellationToken>(
                    (startupOptions, options, console, context, cancellationToken) =>
                {
                    if (startupOptions.EnableHttpApi)
                    {
                        RegisterKernelInServiceCollection(
                            services,
                            startupOptions,
                            options.DefaultKernel,
                            serviceCollection =>
                        {
                            serviceCollection.AddSingleton(_ => new HtmlNotebookFrontedEnvironment());
                            serviceCollection.AddSingleton <FrontendEnvironment>(c =>
                                                                                 c.GetService <HtmlNotebookFrontedEnvironment>());
                        }, kernel =>
                        {
                            StdIOCommand.CreateServer(kernel, console);

                            kernel.UseQuiCommand(disposeOnQuit, cancellationToken);
                        });

                        return(startHttp(startupOptions, console, startServer, context));
                    }

                    {
                        var kernel = CreateKernel(options.DefaultKernel, new BrowserFrontendEnvironment(),
                                                  startupOptions);
                        disposeOnQuit.Add(kernel);
                        kernel.UseQuiCommand(disposeOnQuit, cancellationToken);


                        return(startStdIO(
                                   startupOptions,
                                   kernel,
                                   console));
                    }
                });

                return(command);
            }