public override void Build(Action <string> output = null, Action <string> error = null)
        {
            DirectoryInfo       projectParent = ShellProvider.GetProjectParentDirectoryOrExit(out FileInfo csprojFile);
            BamSettings         settings      = ShellProvider.GetSettings();
            HandlebarsDirectory handlebars    = ShellProvider.GetHandlebarsDirectory();
            string projectName        = Path.GetFileNameWithoutExtension(csprojFile.Name);
            string dockerFileContents = handlebars.Render("Dockerfile", new { AspNetCoreEnvironment = settings.Environment, ProjectName = projectName });
            string startDir           = Environment.CurrentDirectory;

            Environment.CurrentDirectory = projectParent.FullName;
            string dockerFile = Path.Combine(".", "Dockerfile");

            dockerFileContents.SafeWriteToFile(dockerFile, true);
            ProcessStartInfo startInfo = settings.DockerPath.ToStartInfo($"tag {projectName} bamapps/containers:{projectName}");
            ProcessOutput    tagOutput = startInfo.Run(msg => OutLine(msg, ConsoleColor.Blue));

            Environment.CurrentDirectory = startDir;
            if (tagOutput.ExitCode != 0)
            {
                Message.PrintLine("docker tag command failed: {0}\r\n{1}", tagOutput.StandardOutput, tagOutput.StandardError);
                Exit(1);
            }
            ProcessOutput pushOutput = settings.DockerPath.ToStartInfo("push bamapps/containers:{projectName}").Run(msg => OutLine(msg, ConsoleColor.DarkCyan));

            if (tagOutput.ExitCode != 0)
            {
                Message.PrintLine("docker push command failed: {0}\r\n{1}", tagOutput.StandardOutput, tagOutput.StandardError);
                Exit(1);
            }
        }
        private static Shell PrepareShell(Options options, Monitor monitor)
        {
            Shell shell = null;

            if (options.Port >= 0)
            {
                var io = new IOProvider(new SocketIOSource(options.Port));
                shell = ShellProvider.GenerateShell(io, monitor, true, false);
            }
            else
            {
                ConsoleWindowBackendAnalyzer terminal = null;
                IOProvider io;
                if (options.HideMonitor)
                {
                    io = new IOProvider(new DummyIOSource());
                }
                else
                {
                    terminal = new ConsoleWindowBackendAnalyzer();
                    io       = terminal.IO;
                }

                // forcing vcursor is necessary, because calibrating will never end if the window is not shown
                shell            = ShellProvider.GenerateShell(io, monitor, forceVCursor: options.HideMonitor);
                monitor.Quitted += shell.Stop;

                if (terminal != null)
                {
                    try
                    {
                        terminal.Quitted += Emulator.Exit;
                        terminal.Show();
                    }
                    catch (InvalidOperationException ex)
                    {
                        Console.ForegroundColor = ConsoleColor.Red;
                        Console.Error.WriteLine(ex.Message);
                        Emulator.Exit();
                    }
                }
            }
            shell.Quitted += Emulator.Exit;

            monitor.Interaction     = shell.Writer;
            monitor.MachineChanged += emu => shell.SetPrompt(emu != null ? new Prompt(string.Format("({0}) ", emu), ConsoleColor.DarkYellow) : null);

            if (options.Execute != null)
            {
                shell.Started += s => s.InjectInput(string.Format("{0}\n", options.Execute));
            }
            else if (!string.IsNullOrEmpty(options.ScriptPath))
            {
                shell.Started += s => s.InjectInput(string.Format("i {0}{1}\n", Path.IsPathRooted(options.ScriptPath) ? "@" : "$CWD/", options.ScriptPath));
            }

            return(shell);
        }
        /// <summary>
        /// Tag the docker image and push it to the bamapps docker registry.
        /// </summary>
        /// <param name="output"></param>
        /// <param name="error"></param>
        public override void Push(Action <string> output = null, Action <string> error = null)
        {
            DirectoryInfo projectParent = ShellProvider.GetProjectParentDirectoryOrExit(out FileInfo csprojFile);
            string        projectName   = Path.GetFileNameWithoutExtension(csprojFile.Name);
            BamSettings   settings      = ShellProvider.GetSettings();
            string        startDir      = Environment.CurrentDirectory;

            settings.DockerPath.ToStartInfo($"tag {projectName} bamapps/images:{projectName}").Run(msg => OutLine(msg, ConsoleColor.Cyan));
            settings.DockerPath.ToStartInfo($"push bamapps/images:{projectName}").Run(msg => OutLine(msg, ConsoleColor.DarkCyan));
            Environment.CurrentDirectory = startDir;
        }
        public static void Run(string[] args)
        {
            var options       = new Options();
            var optionsParser = new OptionsParser();

            if (!optionsParser.Parse(options, args))
            {
                return;
            }

            using (var context = ObjectCreator.OpenContext())
            {
                var monitor = new Emul8.UserInterface.Monitor();
                context.RegisterSurrogate(typeof(Emul8.UserInterface.Monitor), monitor);

                // we must initialize plugins AFTER registering monitor surrogate
                // as some plugins might need it for construction
                TypeManager.Instance.PluginManager.Init("CLI");

                Logger.AddBackend(ConsoleBackend.Instance, "console");

                EmulationManager.Instance.ProgressMonitor.Handler = new CLIProgressMonitor();

                var crashHandler = new CrashHandler();
                AppDomain.CurrentDomain.UnhandledException += (sender, e) => crashHandler.HandleCrash(e);

                var resetEvent = new ManualResetEventSlim();
                if (options.StdInOut)
                {
                    Logger.AddBackend(new FileBackend("logger.log"), "file");
                    var world = new StreamIOSource(Console.OpenStandardInput(), Console.OpenStandardOutput());
                    var io    = new DetachableIO(world);
                    monitor.Quitted += resetEvent.Set;
                    var handler = new StdInOutHandler(io, monitor, options.Plain);
                    handler.Start();
                }
                else
                {
                    Shell shell = null;
                    Type  preferredUARTAnalyzer = null;
                    if (options.Port > 0)
                    {
                        var io = new DetachableIO(new SocketIOSource(options.Port));
                        shell          = ShellProvider.GenerateShell(io, monitor, true, false);
                        shell.Quitted += resetEvent.Set;
                    }
                    else if (options.ConsoleMode)
                    {
                        preferredUARTAnalyzer = typeof(UARTMultiplexedBackendAnalyzer);

                        var stream = new PtyUnixStream("/dev/fd/1");
                        var world  = new StreamIOSource(stream, stream.Name);

                        Logger.AddBackend(new FileBackend("logger.log"), "file");
                        var consoleTerm = new ConsoleTerminal(world);
                        UARTMultiplexedBackendAnalyzer.Multiplexer = consoleTerm;

                        monitor.Console = consoleTerm;

                        shell = ShellProvider.GenerateShell(monitor);
                        consoleTerm.AttachTerminal("shell", shell.Terminal);

                        shell.Quitted += resetEvent.Set;
                    }
                    else
                    {
                        preferredUARTAnalyzer = typeof(UARTWindowBackendAnalyzer);

                        var stream   = new PtyUnixStream();
                        var dio      = new DetachableIO(new StreamIOSource(stream, stream.Name));
                        var terminal = new UARTWindowBackendAnalyzer(dio);
                        shell = ShellProvider.GenerateShell(dio, monitor);

                        shell.Quitted   += resetEvent.Set;
                        monitor.Quitted += shell.Stop;

                        try
                        {
                            terminal.Show();
                        }
                        catch (InvalidOperationException ex)
                        {
                            Console.ForegroundColor = ConsoleColor.Red;
                            Console.Error.WriteLine(ex.Message);
                            resetEvent.Set();
                        }
                    }

                    if (preferredUARTAnalyzer != null)
                    {
                        EmulationManager.Instance.CurrentEmulation.BackendManager.SetPreferredAnalyzer(typeof(UARTBackend), preferredUARTAnalyzer);
                        EmulationManager.Instance.EmulationChanged += () =>
                        {
                            EmulationManager.Instance.CurrentEmulation.BackendManager.SetPreferredAnalyzer(typeof(UARTBackend), preferredUARTAnalyzer);
                        };
                    }
                    monitor.Interaction     = shell.Writer;
                    monitor.UseConsole      = options.ConsoleMode;
                    monitor.MachineChanged += emu => shell.SetPrompt(emu != null ? new Prompt(string.Format("({0}) ", emu), ConsoleColor.DarkYellow) : null);

                    if (options.Execute != null)
                    {
                        shell.Started += s => s.InjectInput(string.Format("{0}\n", options.Execute));
                    }
                    else if (!string.IsNullOrEmpty(options.ScriptPath))
                    {
                        shell.Started += s => s.InjectInput(string.Format("i {0}{1}\n", Path.IsPathRooted(options.ScriptPath) ? "@" : "$CWD/", options.ScriptPath));
                    }

                    new Thread(x => shell.Start(true))
                    {
                        IsBackground = true, Name = "Shell thread"
                    }.Start();
                }

                resetEvent.Wait();
                EmulationManager.Instance.Clear();
                TypeManager.Instance.Dispose();
                Logger.Dispose();
            }
        }